• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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 package com.android.launcher3.taskbar
17 
18 import android.view.MotionEvent
19 import com.android.launcher3.R
20 import com.android.launcher3.Utilities
21 import com.android.launcher3.anim.Interpolators.LINEAR
22 import com.android.launcher3.testing.shared.ResourceUtils
23 import com.android.launcher3.touch.SingleAxisSwipeDetector
24 import com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE
25 import com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL
26 import com.android.launcher3.util.DisplayController
27 import com.android.launcher3.util.TouchController
28 import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer
29 
30 /**
31  * A helper [TouchController] for [TaskbarDragLayerController], specifically to handle touch events
32  * to stash Transient Taskbar. There are two cases to handle:
33  * - A touch outside of Transient Taskbar bounds will immediately stash on [MotionEvent.ACTION_DOWN]
34  *   or [MotionEvent.ACTION_OUTSIDE].
35  * - Touches inside Transient Taskbar bounds will stash if it is detected as a swipe down gesture.
36  *
37  * Note: touches to *unstash* Taskbar are handled by [TaskbarStashInputConsumer].
38  */
39 class TaskbarStashViaTouchController(val controllers: TaskbarControllers) : TouchController {
40 
41     private val activity: TaskbarActivityContext = controllers.taskbarActivityContext
42     private val enabled = DisplayController.isTransientTaskbar(activity)
43     private val swipeDownDetector: SingleAxisSwipeDetector
44     private val translationCallback = controllers.taskbarTranslationController.transitionCallback
45     /** Interpolator to apply resistance as user swipes down to the bottom of the screen. */
46     private val displacementInterpolator = LINEAR
47     /** How far we can translate the TaskbarView before it's offscreen. */
48     private val maxVisualDisplacement =
49         activity.resources.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin).toFloat()
50     /** How far the swipe could go, if user swiped from the very top of TaskbarView. */
51     private val maxTouchDisplacement = maxVisualDisplacement + activity.deviceProfile.taskbarHeight
52     private val touchDisplacementToStash =
53         activity.resources.getDimensionPixelSize(R.dimen.taskbar_to_nav_threshold).toFloat()
54 
55     /** The height of the system gesture region, so we don't stash when touching down there. */
56     private var gestureHeightYThreshold = 0f
57 
58     init {
59         updateGestureHeight()
60         swipeDownDetector = SingleAxisSwipeDetector(activity, createSwipeListener(), VERTICAL)
61         swipeDownDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false)
62     }
63 
updateGestureHeightnull64     fun updateGestureHeight() {
65         if (!enabled) return
66 
67         val gestureHeight: Int =
68             ResourceUtils.getNavbarSize(
69                 ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
70                 activity.resources
71             )
72         gestureHeightYThreshold = (activity.deviceProfile.heightPx - gestureHeight).toFloat()
73     }
74 
createSwipeListenernull75     private fun createSwipeListener() =
76         object : SingleAxisSwipeDetector.Listener {
77             private var lastDisplacement = 0f
78 
79             override fun onDragStart(start: Boolean, startDisplacement: Float) {}
80 
81             override fun onDrag(displacement: Float): Boolean {
82                 lastDisplacement = displacement
83                 if (displacement < 0) return false
84                 // Apply resistance so that the visual displacement doesn't go beyond the screen.
85                 translationCallback.onActionMove(
86                     Utilities.mapToRange(
87                         displacement,
88                         0f,
89                         maxTouchDisplacement,
90                         0f,
91                         maxVisualDisplacement,
92                         displacementInterpolator
93                     )
94                 )
95                 return false
96             }
97 
98             override fun onDragEnd(velocity: Float) {
99                 val isFlingDown = swipeDownDetector.isFling(velocity) && velocity > 0
100                 val isSignificantDistance = lastDisplacement > touchDisplacementToStash
101                 if (isFlingDown || isSignificantDistance) {
102                     // Successfully triggered stash.
103                     controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
104                 }
105                 translationCallback.onActionEnd()
106                 swipeDownDetector.finishedScrolling()
107             }
108         }
109 
onControllerInterceptTouchEventnull110     override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
111         if (!enabled || controllers.taskbarStashController.isStashed) {
112             return false
113         }
114 
115         val screenCoordinatesEv = MotionEvent.obtain(ev)
116         screenCoordinatesEv.setLocation(ev.rawX, ev.rawY)
117         if (ev.action == MotionEvent.ACTION_OUTSIDE) {
118             controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
119         } else if (controllers.taskbarViewController.isEventOverAnyItem(screenCoordinatesEv)) {
120             swipeDownDetector.onTouchEvent(ev)
121             if (swipeDownDetector.isDraggingState) {
122                 return true
123             }
124         } else if (ev.action == MotionEvent.ACTION_DOWN) {
125             if (screenCoordinatesEv.y < gestureHeightYThreshold) {
126                 controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
127             }
128         }
129         return false
130     }
131 
onControllerTouchEventnull132     override fun onControllerTouchEvent(ev: MotionEvent) = swipeDownDetector.onTouchEvent(ev)
133 }
134