1 /* 2 * Copyright (C) 2021 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.systemui.statusbar.gesture 18 19 import android.content.Context 20 import android.view.InputEvent 21 import android.view.MotionEvent 22 import android.view.MotionEvent.ACTION_CANCEL 23 import android.view.MotionEvent.ACTION_DOWN 24 import android.view.MotionEvent.ACTION_MOVE 25 import android.view.MotionEvent.ACTION_UP 26 import com.android.systemui.dagger.SysUISingleton 27 import com.android.systemui.settings.DisplayTracker 28 29 /** 30 * A class to detect a generic "swipe up" gesture. To be notified when the swipe up gesture is 31 * detected, add a callback via [addOnGestureDetectedCallback]. 32 */ 33 @SysUISingleton 34 abstract class SwipeUpGestureHandler( 35 context: Context, 36 displayTracker: DisplayTracker, 37 private val logger: SwipeUpGestureLogger, 38 private val loggerTag: String 39 ) : GenericGestureDetector(SwipeUpGestureHandler::class.simpleName!!, displayTracker) { 40 41 private var startY: Float = 0f 42 private var startTime: Long = 0L 43 private var monitoringCurrentTouch: Boolean = false 44 45 private var swipeDistanceThreshold: Int = context.resources.getDimensionPixelSize( 46 com.android.internal.R.dimen.system_gestures_start_threshold 47 ) 48 onInputEventnull49 override fun onInputEvent(ev: InputEvent) { 50 if (ev !is MotionEvent) { 51 return 52 } 53 54 when (ev.actionMasked) { 55 ACTION_DOWN -> { 56 if ( 57 startOfGestureIsWithinBounds(ev) 58 ) { 59 logger.logGestureDetectionStarted(loggerTag, ev.y.toInt()) 60 startY = ev.y 61 startTime = ev.eventTime 62 monitoringCurrentTouch = true 63 } else { 64 monitoringCurrentTouch = false 65 } 66 } 67 ACTION_MOVE -> { 68 if (!monitoringCurrentTouch) { 69 return 70 } 71 if ( 72 // Gesture is up 73 ev.y < startY && 74 // Gesture went far enough 75 (startY - ev.y) >= swipeDistanceThreshold && 76 // Gesture completed quickly enough 77 (ev.eventTime - startTime) < SWIPE_TIMEOUT_MS 78 ) { 79 monitoringCurrentTouch = false 80 logger.logGestureDetected(loggerTag, ev.y.toInt()) 81 onGestureDetected(ev) 82 } 83 } 84 ACTION_CANCEL, ACTION_UP -> { 85 if (monitoringCurrentTouch) { 86 logger.logGestureDetectionEndedWithoutTriggering(loggerTag, ev.y.toInt()) 87 } 88 monitoringCurrentTouch = false 89 } 90 } 91 } 92 93 /** 94 * Returns true if the [ACTION_DOWN] event falls within bounds for this specific swipe-up 95 * gesture. 96 * 97 * Implementations must override this method to specify what part(s) of the screen are valid 98 * locations for the swipe up gesture to start at. 99 */ startOfGestureIsWithinBoundsnull100 abstract fun startOfGestureIsWithinBounds(ev: MotionEvent): Boolean 101 102 override fun startGestureListening() { 103 super.startGestureListening() 104 logger.logInputListeningStarted(loggerTag) 105 } 106 stopGestureListeningnull107 override fun stopGestureListening() { 108 super.stopGestureListening() 109 logger.logInputListeningStopped(loggerTag) 110 } 111 } 112 113 private const val SWIPE_TIMEOUT_MS: Long = 500 114