1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.ui.draganddrop
18 
19 import android.view.DragEvent
20 import android.view.View
21 import androidx.collection.ArraySet
22 import androidx.compose.ui.Modifier
23 import androidx.compose.ui.geometry.Offset
24 import androidx.compose.ui.geometry.Size
25 import androidx.compose.ui.graphics.drawscope.DrawScope
26 import androidx.compose.ui.node.ModifierNodeElement
27 import androidx.compose.ui.platform.InspectorInfo
28 
29 /** A Class that provides access [View.OnDragListener] APIs for a [DragAndDropNode]. */
30 internal class AndroidDragAndDropManager(
31     private val startDrag:
32         (
33             transferData: DragAndDropTransferData,
34             decorationSize: Size,
35             drawDragDecoration: DrawScope.() -> Unit
36         ) -> Boolean
37 ) : View.OnDragListener, DragAndDropManager {
38 
39     private val rootDragAndDropNode = DragAndDropNode()
40 
41     /**
42      * A collection [DragAndDropNode] instances that registered interested in a drag and drop
43      * session by returning true in [DragAndDropNode.onStarted].
44      */
45     private val interestedTargets = ArraySet<DragAndDropTarget>()
46 
47     override val modifier: Modifier =
48         object : ModifierNodeElement<DragAndDropNode>() {
createnull49             override fun create() = rootDragAndDropNode
50 
51             override fun update(node: DragAndDropNode) = Unit
52 
53             override fun InspectorInfo.inspectableProperties() {
54                 name = "RootDragAndDropNode"
55             }
56 
hashCodenull57             override fun hashCode(): Int = rootDragAndDropNode.hashCode()
58 
59             override fun equals(other: Any?) = other === this
60         }
61 
62     override val isRequestDragAndDropTransferRequired: Boolean
63         get() = true
64 
65     override fun requestDragAndDropTransfer(node: DragAndDropNode, offset: Offset) {
66         var isTransferStarted = false
67         val dragAndDropSourceScope =
68             object : DragAndDropStartTransferScope {
69                 override fun startDragAndDropTransfer(
70                     transferData: DragAndDropTransferData,
71                     decorationSize: Size,
72                     drawDragDecoration: DrawScope.() -> Unit
73                 ): Boolean {
74                     isTransferStarted =
75                         startDrag(
76                             transferData,
77                             decorationSize,
78                             drawDragDecoration,
79                         )
80                     return isTransferStarted
81                 }
82             }
83         with(node) { dragAndDropSourceScope.startDragAndDropTransfer(offset) { isTransferStarted } }
84     }
85 
onDragnull86     override fun onDrag(view: View, event: DragEvent): Boolean {
87         val dragAndDropEvent = DragAndDropEvent(dragEvent = event)
88         return when (event.action) {
89             DragEvent.ACTION_DRAG_STARTED -> {
90                 val accepted = rootDragAndDropNode.acceptDragAndDropTransfer(dragAndDropEvent)
91                 interestedTargets.forEach { it.onStarted(dragAndDropEvent) }
92                 accepted
93             }
94             DragEvent.ACTION_DROP -> {
95                 rootDragAndDropNode.onDrop(dragAndDropEvent)
96             }
97             DragEvent.ACTION_DRAG_ENTERED -> {
98                 rootDragAndDropNode.onEntered(dragAndDropEvent)
99                 false
100             }
101             DragEvent.ACTION_DRAG_LOCATION -> {
102                 rootDragAndDropNode.onMoved(dragAndDropEvent)
103                 false
104             }
105             DragEvent.ACTION_DRAG_EXITED -> {
106                 rootDragAndDropNode.onExited(dragAndDropEvent)
107                 false
108             }
109             DragEvent.ACTION_DRAG_ENDED -> {
110                 rootDragAndDropNode.onEnded(dragAndDropEvent)
111                 interestedTargets.clear()
112                 false
113             }
114             else -> false
115         }
116     }
117 
registerTargetInterestnull118     override fun registerTargetInterest(target: DragAndDropTarget) {
119         interestedTargets.add(target)
120     }
121 
isInterestedTargetnull122     override fun isInterestedTarget(target: DragAndDropTarget): Boolean {
123         return interestedTargets.contains(target)
124     }
125 }
126