• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 
18 package com.android.systemui.statusbar.gesture
19 
20 import android.annotation.CallSuper
21 import android.os.Looper
22 import android.view.Choreographer
23 import android.view.InputEvent
24 import android.view.MotionEvent
25 import com.android.systemui.settings.DisplayTracker
26 import com.android.systemui.shared.system.InputChannelCompat
27 import com.android.systemui.shared.system.InputMonitorCompat
28 
29 /**
30  * An abstract class to help detect gestures that occur anywhere on the display (not specific to a
31  * certain view).
32  *
33  * This class handles starting/stopping the gesture detection system as well as
34  * registering/unregistering callbacks for when gestures occur. Note that the class will only listen
35  * for gestures when there's at least one callback registered.
36  *
37  * Subclasses should implement [onInputEvent] to detect their specific gesture. Once a specific
38  * gesture is detected, they should call [onGestureDetected] (which will notify the callbacks).
39  */
40 abstract class GenericGestureDetector(
41     private val tag: String,
42     private val displayTracker: DisplayTracker
43 ) {
44     /**
45      * Active callbacks, each associated with a tag. Gestures will only be monitored if
46      * [callbacks.size] > 0.
47      */
48     private val callbacks: MutableMap<String, (MotionEvent) -> Unit> = mutableMapOf()
49 
50     private var inputMonitor: InputMonitorCompat? = null
51     private var inputReceiver: InputChannelCompat.InputEventReceiver? = null
52 
53     /**
54      * Adds a callback that will be triggered when the tap gesture is detected.
55      *
56      * The callback receive the last motion event in the gesture.
57      */
addOnGestureDetectedCallbacknull58     fun addOnGestureDetectedCallback(tag: String, callback: (MotionEvent) -> Unit) {
59         val callbacksWasEmpty = callbacks.isEmpty()
60         callbacks[tag] = callback
61         if (callbacksWasEmpty) {
62             startGestureListening()
63         }
64     }
65 
66     /** Removes the callback. */
removeOnGestureDetectedCallbacknull67     fun removeOnGestureDetectedCallback(tag: String) {
68         callbacks.remove(tag)
69         if (callbacks.isEmpty()) {
70             stopGestureListening()
71         }
72     }
73 
74     /** Triggered each time a touch event occurs (and at least one callback is registered). */
onInputEventnull75     abstract fun onInputEvent(ev: InputEvent)
76 
77     /**
78      * Should be called by subclasses when their specific gesture is detected with the last motion
79      * event in the gesture.
80      */
81     internal fun onGestureDetected(e: MotionEvent) {
82         callbacks.values.forEach { it.invoke(e) }
83     }
84 
85     /** Start listening to touch events. */
86     @CallSuper
startGestureListeningnull87     internal open fun startGestureListening() {
88         stopGestureListening()
89 
90         inputMonitor = InputMonitorCompat(tag, displayTracker.defaultDisplayId).also {
91             inputReceiver = it.getInputReceiver(
92                 Looper.getMainLooper(),
93                 Choreographer.getInstance(),
94                 this::onInputEvent
95             )
96         }
97     }
98 
99     /** Stop listening to touch events. */
100     @CallSuper
stopGestureListeningnull101     internal open fun stopGestureListening() {
102         inputMonitor?.let {
103             inputMonitor = null
104             it.dispose()
105         }
106         inputReceiver?.let {
107             inputReceiver = null
108             it.dispose()
109         }
110     }
111 }
112