[Media TTT] Hide the chip if the user taps the screen.
This CL defines a generic gesture detector class since we now have multiple cases where we want to detect gestures across the whole display. Still TODO: Don't hide the chip if the user taps the chip itself. Bug: 214274529 Bug: 211487971 Test: manual: verify tapping the screen hides the chip Test: atest GenericGestureDetectorTest Test: atest media.taptotransfer Change-Id: I3951d0bbb3a04d5b82a54339a548b4e293bbf527 Merged-In: I3951d0bbb3a04d5b82a54339a548b4e293bbf527
This commit is contained in:
@@ -29,6 +29,7 @@ import androidx.annotation.VisibleForTesting
|
||||
import com.android.internal.widget.CachingIconView
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.statusbar.gesture.TapGestureDetector
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
|
||||
/**
|
||||
@@ -42,6 +43,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
internal val context: Context,
|
||||
private val windowManager: WindowManager,
|
||||
@Main private val mainExecutor: DelayableExecutor,
|
||||
private val tapGestureDetector: TapGestureDetector,
|
||||
@LayoutRes private val chipLayoutRes: Int
|
||||
) {
|
||||
/** The window layout parameters we'll use when attaching the view to a window. */
|
||||
@@ -82,6 +84,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
|
||||
// Add view if necessary
|
||||
if (oldChipView == null) {
|
||||
tapGestureDetector.addOnGestureDetectedCallback(TAG, this::removeChip)
|
||||
windowManager.addView(chipView, windowLayoutParams)
|
||||
}
|
||||
|
||||
@@ -96,6 +99,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
// TransferTriggered state: Once the user has initiated the transfer, they should be able
|
||||
// to move away from the receiver device but still see the status of the transfer.
|
||||
if (chipView == null) { return }
|
||||
tapGestureDetector.removeOnGestureDetectedCallback(TAG)
|
||||
windowManager.removeView(chipView)
|
||||
chipView = null
|
||||
}
|
||||
@@ -128,5 +132,6 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
// Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
|
||||
// UpdateMediaTapToTransferReceiverDisplayTest
|
||||
private const val WINDOW_TITLE = "Media Transfer Chip View"
|
||||
private val TAG = MediaTttChipControllerCommon::class.simpleName!!
|
||||
@VisibleForTesting
|
||||
const val TIMEOUT_MILLIS = 3000L
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.gesture.TapGestureDetector
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -43,9 +44,10 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
context: Context,
|
||||
windowManager: WindowManager,
|
||||
mainExecutor: DelayableExecutor,
|
||||
tapGestureDetector: TapGestureDetector,
|
||||
@Main private val mainHandler: Handler,
|
||||
) : MediaTttChipControllerCommon<ChipStateReceiver>(
|
||||
context, windowManager, mainExecutor, R.layout.media_ttt_chip_receiver
|
||||
context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip_receiver
|
||||
) {
|
||||
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
|
||||
override fun updateMediaTapToTransferReceiverDisplay(
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.gesture.TapGestureDetector
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -42,9 +43,10 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
commandQueue: CommandQueue,
|
||||
context: Context,
|
||||
windowManager: WindowManager,
|
||||
@Main private val mainExecutor: DelayableExecutor,
|
||||
@Main mainExecutor: DelayableExecutor,
|
||||
tapGestureDetector: TapGestureDetector,
|
||||
) : MediaTttChipControllerCommon<ChipStateSender>(
|
||||
context, windowManager, mainExecutor, R.layout.media_ttt_chip
|
||||
context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip
|
||||
) {
|
||||
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
|
||||
override fun updateMediaTapToTransferSenderDisplay(
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package com.android.systemui.statusbar.gesture
|
||||
|
||||
import android.annotation.CallSuper
|
||||
import android.os.Looper
|
||||
import android.view.Choreographer
|
||||
import android.view.Display
|
||||
import android.view.InputEvent
|
||||
import com.android.systemui.shared.system.InputChannelCompat
|
||||
import com.android.systemui.shared.system.InputMonitorCompat
|
||||
|
||||
/**
|
||||
* An abstract class to help detect gestures that occur anywhere on the display (not specific to a
|
||||
* certain view).
|
||||
*
|
||||
* This class handles starting/stopping the gesture detection system as well as
|
||||
* registering/unregistering callbacks for when gestures occur. Note that the class will only listen
|
||||
* for gestures when there's at least one callback registered.
|
||||
*
|
||||
* Subclasses should implement [onInputEvent] to detect their specific gesture. Once a specific
|
||||
* gesture is detected, they should call [onGestureDetected] (which will notify the callbacks).
|
||||
*/
|
||||
abstract class GenericGestureDetector(
|
||||
private val tag: String
|
||||
) {
|
||||
/**
|
||||
* Active callbacks, each associated with a tag. Gestures will only be monitored if
|
||||
* [callbacks.size] > 0.
|
||||
*/
|
||||
private val callbacks: MutableMap<String, () -> Unit> = mutableMapOf()
|
||||
|
||||
private var inputMonitor: InputMonitorCompat? = null
|
||||
private var inputReceiver: InputChannelCompat.InputEventReceiver? = null
|
||||
|
||||
/** Adds a callback that will be triggered when the tap gesture is detected. */
|
||||
fun addOnGestureDetectedCallback(tag: String, callback: () -> Unit) {
|
||||
val callbacksWasEmpty = callbacks.isEmpty()
|
||||
callbacks[tag] = callback
|
||||
if (callbacksWasEmpty) {
|
||||
startGestureListening()
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the callback. */
|
||||
fun removeOnGestureDetectedCallback(tag: String) {
|
||||
callbacks.remove(tag)
|
||||
if (callbacks.isEmpty()) {
|
||||
stopGestureListening()
|
||||
}
|
||||
}
|
||||
|
||||
/** Triggered each time a touch event occurs (and at least one callback is registered). */
|
||||
abstract fun onInputEvent(ev: InputEvent)
|
||||
|
||||
/** Should be called by subclasses when their specific gesture is detected. */
|
||||
internal fun onGestureDetected() {
|
||||
callbacks.values.forEach { it.invoke() }
|
||||
}
|
||||
|
||||
/** Start listening to touch events. */
|
||||
@CallSuper
|
||||
internal open fun startGestureListening() {
|
||||
stopGestureListening()
|
||||
|
||||
inputMonitor = InputMonitorCompat(tag, Display.DEFAULT_DISPLAY).also {
|
||||
inputReceiver = it.getInputReceiver(
|
||||
Looper.getMainLooper(),
|
||||
Choreographer.getInstance(),
|
||||
this::onInputEvent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Stop listening to touch events. */
|
||||
@CallSuper
|
||||
internal open fun stopGestureListening() {
|
||||
inputMonitor?.let {
|
||||
inputMonitor = null
|
||||
it.dispose()
|
||||
}
|
||||
inputReceiver?.let {
|
||||
inputReceiver = null
|
||||
it.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,13 @@
|
||||
package com.android.systemui.statusbar.gesture
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Looper
|
||||
import android.view.Choreographer
|
||||
import android.view.Display
|
||||
import android.view.InputEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.MotionEvent.*
|
||||
import android.view.MotionEvent.ACTION_CANCEL
|
||||
import android.view.MotionEvent.ACTION_DOWN
|
||||
import android.view.MotionEvent.ACTION_MOVE
|
||||
import android.view.MotionEvent.ACTION_UP
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.shared.system.InputChannelCompat
|
||||
import com.android.systemui.shared.system.InputMonitorCompat
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowController
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -38,43 +36,17 @@ open class SwipeStatusBarAwayGestureHandler @Inject constructor(
|
||||
context: Context,
|
||||
private val statusBarWindowController: StatusBarWindowController,
|
||||
private val logger: SwipeStatusBarAwayGestureLogger
|
||||
) {
|
||||
|
||||
/**
|
||||
* Active callbacks, each associated with a tag. Gestures will only be monitored if
|
||||
* [callbacks.size] > 0.
|
||||
*/
|
||||
private val callbacks: MutableMap<String, () -> Unit> = mutableMapOf()
|
||||
) : GenericGestureDetector(SwipeStatusBarAwayGestureHandler::class.simpleName!!) {
|
||||
|
||||
private var startY: Float = 0f
|
||||
private var startTime: Long = 0L
|
||||
private var monitoringCurrentTouch: Boolean = false
|
||||
|
||||
private var inputMonitor: InputMonitorCompat? = null
|
||||
private var inputReceiver: InputChannelCompat.InputEventReceiver? = null
|
||||
|
||||
private var swipeDistanceThreshold: Int = context.resources.getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.system_gestures_start_threshold
|
||||
)
|
||||
|
||||
/** Adds a callback that will be triggered when the swipe away gesture is detected. */
|
||||
fun addOnGestureDetectedCallback(tag: String, callback: () -> Unit) {
|
||||
val callbacksWasEmpty = callbacks.isEmpty()
|
||||
callbacks[tag] = callback
|
||||
if (callbacksWasEmpty) {
|
||||
startGestureListening()
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes the callback. */
|
||||
fun removeOnGestureDetectedCallback(tag: String) {
|
||||
callbacks.remove(tag)
|
||||
if (callbacks.isEmpty()) {
|
||||
stopGestureListening()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onInputEvent(ev: InputEvent) {
|
||||
override fun onInputEvent(ev: InputEvent) {
|
||||
if (ev !is MotionEvent) {
|
||||
return
|
||||
}
|
||||
@@ -108,7 +80,7 @@ open class SwipeStatusBarAwayGestureHandler @Inject constructor(
|
||||
) {
|
||||
monitoringCurrentTouch = false
|
||||
logger.logGestureDetected(ev.y.toInt())
|
||||
callbacks.values.forEach { it.invoke() }
|
||||
onGestureDetected()
|
||||
}
|
||||
}
|
||||
ACTION_CANCEL, ACTION_UP -> {
|
||||
@@ -120,33 +92,15 @@ open class SwipeStatusBarAwayGestureHandler @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/** Start listening for the swipe gesture. */
|
||||
private fun startGestureListening() {
|
||||
stopGestureListening()
|
||||
|
||||
override fun startGestureListening() {
|
||||
super.startGestureListening()
|
||||
logger.logInputListeningStarted()
|
||||
inputMonitor = InputMonitorCompat(TAG, Display.DEFAULT_DISPLAY).also {
|
||||
inputReceiver = it.getInputReceiver(
|
||||
Looper.getMainLooper(),
|
||||
Choreographer.getInstance(),
|
||||
this::onInputEvent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Stop listening for the swipe gesture. */
|
||||
private fun stopGestureListening() {
|
||||
inputMonitor?.let {
|
||||
logger.logInputListeningStopped()
|
||||
inputMonitor = null
|
||||
it.dispose()
|
||||
}
|
||||
inputReceiver?.let {
|
||||
inputReceiver = null
|
||||
it.dispose()
|
||||
}
|
||||
override fun stopGestureListening() {
|
||||
super.stopGestureListening()
|
||||
logger.logInputListeningStopped()
|
||||
}
|
||||
}
|
||||
|
||||
private const val SWIPE_TIMEOUT_MS: Long = 500
|
||||
private val TAG = SwipeStatusBarAwayGestureHandler::class.simpleName
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.gesture
|
||||
|
||||
import android.content.Context
|
||||
import android.view.GestureDetector
|
||||
import android.view.InputEvent
|
||||
import android.view.MotionEvent
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A class to detect when a user taps the screen. To be notified when the tap is detected, add a
|
||||
* callback via [addOnGestureDetectedCallback].
|
||||
*/
|
||||
@SysUISingleton
|
||||
class TapGestureDetector @Inject constructor(
|
||||
private val context: Context
|
||||
) : GenericGestureDetector(TapGestureDetector::class.simpleName!!) {
|
||||
|
||||
private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapUp(e: MotionEvent?): Boolean {
|
||||
onGestureDetected()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private var gestureDetector: GestureDetector? = null
|
||||
|
||||
override fun onInputEvent(ev: InputEvent) {
|
||||
if (ev !is MotionEvent) {
|
||||
return
|
||||
}
|
||||
// Pass all events to [gestureDetector], which will then notify [gestureListener] when a tap
|
||||
// is detected.
|
||||
gestureDetector!!.onTouchEvent(ev)
|
||||
}
|
||||
|
||||
/** Start listening for the tap gesture. */
|
||||
override fun startGestureListening() {
|
||||
super.startGestureListening()
|
||||
gestureDetector = GestureDetector(context, gestureListener)
|
||||
}
|
||||
|
||||
/** Stop listening for the swipe gesture. */
|
||||
override fun stopGestureListening() {
|
||||
super.stopGestureListening()
|
||||
gestureDetector = null
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.statusbar.gesture.TapGestureDetector
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.any
|
||||
@@ -50,6 +51,8 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
private lateinit var appIconDrawable: Drawable
|
||||
@Mock
|
||||
private lateinit var windowManager: WindowManager
|
||||
@Mock
|
||||
private lateinit var tapGestureDetector: TapGestureDetector
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -58,23 +61,28 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
fakeClock = FakeSystemClock()
|
||||
fakeExecutor = FakeExecutor(fakeClock)
|
||||
|
||||
controllerCommon = TestControllerCommon(context, windowManager, fakeExecutor)
|
||||
controllerCommon = TestControllerCommon(
|
||||
context, windowManager, fakeExecutor, tapGestureDetector
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun displayChip_chipAdded() {
|
||||
fun displayChip_chipAddedAndGestureDetectionStarted() {
|
||||
controllerCommon.displayChip(getState())
|
||||
|
||||
verify(windowManager).addView(any(), any())
|
||||
verify(tapGestureDetector).addOnGestureDetectedCallback(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun displayChip_twice_chipNotAddedTwice() {
|
||||
fun displayChip_twice_chipAndGestureDetectionNotAddedTwice() {
|
||||
controllerCommon.displayChip(getState())
|
||||
reset(windowManager)
|
||||
reset(tapGestureDetector)
|
||||
|
||||
controllerCommon.displayChip(getState())
|
||||
verify(windowManager, never()).addView(any(), any())
|
||||
verify(tapGestureDetector, never()).addOnGestureDetectedCallback(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,7 +138,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeChip_chipRemoved() {
|
||||
fun removeChip_chipRemovedAndGestureDetectionStopped() {
|
||||
// First, add the chip
|
||||
controllerCommon.displayChip(getState())
|
||||
|
||||
@@ -138,6 +146,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
controllerCommon.removeChip()
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
verify(tapGestureDetector).removeOnGestureDetectedCallback(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -174,8 +183,9 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
context: Context,
|
||||
windowManager: WindowManager,
|
||||
@Main mainExecutor: DelayableExecutor,
|
||||
) : MediaTttChipControllerCommon<MediaTttChipState>(
|
||||
context, windowManager, mainExecutor, R.layout.media_ttt_chip
|
||||
tapGestureDetector: TapGestureDetector,
|
||||
) : MediaTttChipControllerCommon<MediaTttChipState>(
|
||||
context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip
|
||||
) {
|
||||
override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) {
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.MediaRoute2Info
|
||||
import android.os.Handler
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
@@ -30,6 +32,7 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.gesture.TapGestureDetector
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.eq
|
||||
@@ -37,6 +40,7 @@ import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.never
|
||||
@@ -45,6 +49,8 @@ import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
private lateinit var controllerReceiver: MediaTttChipControllerReceiver
|
||||
|
||||
@@ -76,7 +82,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
context,
|
||||
windowManager,
|
||||
FakeExecutor(FakeSystemClock()),
|
||||
Handler.getMain()
|
||||
TapGestureDetector(context),
|
||||
Handler.getMain(),
|
||||
)
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.MediaRoute2Info
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.ImageView
|
||||
@@ -31,6 +33,7 @@ import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.gesture.TapGestureDetector
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.eq
|
||||
@@ -38,6 +41,7 @@ import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.never
|
||||
@@ -46,6 +50,8 @@ import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
private lateinit var controllerSender: MediaTttChipControllerSender
|
||||
|
||||
@@ -73,7 +79,11 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
context.setMockPackageManager(packageManager)
|
||||
|
||||
controllerSender = MediaTttChipControllerSender(
|
||||
commandQueue, context, windowManager, FakeExecutor(FakeSystemClock())
|
||||
commandQueue,
|
||||
context,
|
||||
windowManager,
|
||||
FakeExecutor(FakeSystemClock()),
|
||||
TapGestureDetector(context)
|
||||
)
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.android.systemui.statusbar.gesture
|
||||
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.InputEvent
|
||||
import android.view.MotionEvent
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
class GenericGestureDetectorTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var gestureDetector: TestGestureDetector
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
gestureDetector = TestGestureDetector()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noCallbacksRegistered_notGestureListening() {
|
||||
assertThat(gestureDetector.isGestureListening).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun callbackRegistered_isGestureListening() {
|
||||
gestureDetector.addOnGestureDetectedCallback("tag"){}
|
||||
|
||||
assertThat(gestureDetector.isGestureListening).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multipleCallbacksRegistered_isGestureListening() {
|
||||
gestureDetector.addOnGestureDetectedCallback("tag"){}
|
||||
gestureDetector.addOnGestureDetectedCallback("tag2"){}
|
||||
|
||||
assertThat(gestureDetector.isGestureListening).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun allCallbacksUnregistered_notGestureListening() {
|
||||
gestureDetector.addOnGestureDetectedCallback("tag"){}
|
||||
gestureDetector.addOnGestureDetectedCallback("tag2"){}
|
||||
|
||||
gestureDetector.removeOnGestureDetectedCallback("tag")
|
||||
gestureDetector.removeOnGestureDetectedCallback("tag2")
|
||||
|
||||
assertThat(gestureDetector.isGestureListening).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun someButNotAllCallbacksUnregistered_isGestureListening() {
|
||||
gestureDetector.addOnGestureDetectedCallback("tag"){}
|
||||
gestureDetector.addOnGestureDetectedCallback("tag2"){}
|
||||
|
||||
gestureDetector.removeOnGestureDetectedCallback("tag2")
|
||||
|
||||
assertThat(gestureDetector.isGestureListening).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputEvent_meetsGestureCriteria_allCallbacksNotified() {
|
||||
var callbackNotified = false
|
||||
gestureDetector.addOnGestureDetectedCallback("tag"){
|
||||
callbackNotified = true
|
||||
}
|
||||
|
||||
gestureDetector.onInputEvent(
|
||||
MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X, 0f, 0)
|
||||
)
|
||||
|
||||
assertThat(callbackNotified).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputEvent_doesNotMeetGestureCriteria_callbackNotNotified() {
|
||||
var callbackNotified = false
|
||||
gestureDetector.addOnGestureDetectedCallback("tag"){
|
||||
callbackNotified = true
|
||||
}
|
||||
|
||||
gestureDetector.onInputEvent(
|
||||
MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X - 5, 0f, 0)
|
||||
)
|
||||
|
||||
assertThat(callbackNotified).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun callbackUnregisteredThenGestureDetected_oldCallbackNotNotified() {
|
||||
var oldCallbackNotified = false
|
||||
gestureDetector.addOnGestureDetectedCallback("tag"){
|
||||
oldCallbackNotified = true
|
||||
}
|
||||
gestureDetector.addOnGestureDetectedCallback("tag2"){}
|
||||
|
||||
gestureDetector.removeOnGestureDetectedCallback("tag")
|
||||
gestureDetector.onInputEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X, 0f, 0))
|
||||
|
||||
assertThat(oldCallbackNotified).isFalse()
|
||||
}
|
||||
|
||||
inner class TestGestureDetector : GenericGestureDetector("fakeTag") {
|
||||
var isGestureListening = false
|
||||
|
||||
override fun onInputEvent(ev: InputEvent) {
|
||||
if (ev is MotionEvent && ev.x == CORRECT_X) {
|
||||
onGestureDetected()
|
||||
}
|
||||
}
|
||||
|
||||
override fun startGestureListening() {
|
||||
super.startGestureListening()
|
||||
isGestureListening = true
|
||||
}
|
||||
|
||||
override fun stopGestureListening() {
|
||||
super.stopGestureListening()
|
||||
isGestureListening = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val CORRECT_X = 1234f
|
||||
Reference in New Issue
Block a user