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