1 /*
<lambda>null2  * Copyright (C) 2023 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 com.android.wm.shell.bubbles.bar
18 
19 import android.annotation.SuppressLint
20 import android.view.MotionEvent
21 import android.view.View
22 import com.android.wm.shell.bubbles.BubblePositioner
23 import com.android.wm.shell.common.bubbles.DismissView
24 import com.android.wm.shell.common.bubbles.RelativeTouchListener
25 import com.android.wm.shell.common.magnetictarget.MagnetizedObject
26 
27 /** Controller for handling drag interactions with [BubbleBarExpandedView] */
28 @SuppressLint("ClickableViewAccessibility")
29 class BubbleBarExpandedViewDragController(
30     private val expandedView: BubbleBarExpandedView,
31     private val dismissView: DismissView,
32     private val animationHelper: BubbleBarAnimationHelper,
33     private val bubblePositioner: BubblePositioner,
34     private val pinController: BubbleExpandedViewPinController,
35     private val dragListener: DragListener
36 ) {
37 
38     var isStuckToDismiss: Boolean = false
39         private set
40 
41     private var expandedViewInitialTranslationX = 0f
42     private var expandedViewInitialTranslationY = 0f
43     private val magnetizedExpandedView: MagnetizedObject<BubbleBarExpandedView> =
44         MagnetizedObject.magnetizeView(expandedView)
45     private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget
46 
47     init {
48         magnetizedExpandedView.magnetListener = MagnetListener()
49         magnetizedExpandedView.animateStuckToTarget =
50             {
51                 target: MagnetizedObject.MagneticTarget,
52                 _: Float,
53                 _: Float,
54                 _: Boolean,
55                 after: (() -> Unit)? ->
56                 animationHelper.animateIntoTarget(target, after)
57             }
58 
59         magnetizedDismissTarget =
60             MagnetizedObject.MagneticTarget(dismissView.circle, dismissView.circle.width)
61         magnetizedExpandedView.addTarget(magnetizedDismissTarget)
62 
63         val dragMotionEventHandler = HandleDragListener()
64 
65         expandedView.handleView.setOnTouchListener { view, event ->
66             if (event.actionMasked == MotionEvent.ACTION_DOWN) {
67                 expandedViewInitialTranslationX = expandedView.translationX
68                 expandedViewInitialTranslationY = expandedView.translationY
69             }
70             val magnetConsumed = magnetizedExpandedView.maybeConsumeMotionEvent(event)
71             // Move events can be consumed by the magnetized object
72             if (event.actionMasked == MotionEvent.ACTION_MOVE && magnetConsumed) {
73                 return@setOnTouchListener true
74             }
75             return@setOnTouchListener dragMotionEventHandler.onTouch(view, event) || magnetConsumed
76         }
77     }
78 
79     /** Listener to get notified about drag events */
80     interface DragListener {
81         /**
82          * Bubble bar was released
83          *
84          * @param inDismiss `true` if view was release in dismiss target
85          */
86         fun onReleased(inDismiss: Boolean)
87     }
88 
89     private inner class HandleDragListener : RelativeTouchListener() {
90 
91         private var isMoving = false
92 
93         override fun onDown(v: View, ev: MotionEvent): Boolean {
94             // While animating, don't allow new touch events
95             if (expandedView.isAnimating) return false
96             pinController.onDragStart(bubblePositioner.isBubbleBarOnLeft)
97             return true
98         }
99 
100         override fun onMove(
101             v: View,
102             ev: MotionEvent,
103             viewInitialX: Float,
104             viewInitialY: Float,
105             dx: Float,
106             dy: Float
107         ) {
108             if (!isMoving) {
109                 isMoving = true
110                 animationHelper.animateStartDrag()
111             }
112             expandedView.translationX = expandedViewInitialTranslationX + dx
113             expandedView.translationY = expandedViewInitialTranslationY + dy
114             dismissView.show()
115             pinController.onDragUpdate(ev.rawX, ev.rawY)
116         }
117 
118         override fun onUp(
119             v: View,
120             ev: MotionEvent,
121             viewInitialX: Float,
122             viewInitialY: Float,
123             dx: Float,
124             dy: Float,
125             velX: Float,
126             velY: Float
127         ) {
128             finishDrag()
129         }
130 
131         override fun onCancel(v: View, ev: MotionEvent, viewInitialX: Float, viewInitialY: Float) {
132             isStuckToDismiss = false
133             finishDrag()
134         }
135 
136         private fun finishDrag() {
137             if (!isStuckToDismiss) {
138                 pinController.onDragEnd()
139                 dragListener.onReleased(inDismiss = false)
140                 animationHelper.animateToRestPosition()
141                 dismissView.hide()
142             }
143             isMoving = false
144         }
145     }
146 
147     private inner class MagnetListener : MagnetizedObject.MagnetListener {
148         override fun onStuckToTarget(
149             target: MagnetizedObject.MagneticTarget,
150             draggedObject: MagnetizedObject<*>
151         ) {
152             isStuckToDismiss = true
153             pinController.onStuckToDismissTarget()
154         }
155 
156         override fun onUnstuckFromTarget(
157             target: MagnetizedObject.MagneticTarget,
158             draggedObject: MagnetizedObject<*>,
159             velX: Float,
160             velY: Float,
161             wasFlungOut: Boolean
162         ) {
163             isStuckToDismiss = false
164             animationHelper.animateUnstuckFromDismissView(target)
165         }
166 
167         override fun onReleasedInTarget(
168             target: MagnetizedObject.MagneticTarget,
169             draggedObject: MagnetizedObject<*>
170         ) {
171             dragListener.onReleased(inDismiss = true)
172             pinController.onDragEnd()
173             dismissView.hide()
174         }
175     }
176 }
177