Add falsing to undo button
Add falsing to undo button in Media TTT view for sender. When it appears on lock screen, we prevent unintended taps by user. Delayed the creation of Falsing Manager to overcome the regression issue happened in ag/19584050. Bug: 244330924 Bug: 236956557 Test: atest MediaTttChipControllerSenderTest Change-Id: I4d65d9a4c1e9d94a7720615be9d3a9693e04efd5
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
<!-- Wrap in a frame layout so that we can update the margins on the inner layout. (Since this view
|
||||
is the root view of a window, we cannot change the root view's margins.) -->
|
||||
<!-- Alphas start as 0 because the view will be animated in. -->
|
||||
<FrameLayout
|
||||
<com.android.systemui.media.taptotransfer.sender.MediaTttChipRootView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
android:id="@+id/media_ttt_sender_chip"
|
||||
@@ -97,4 +97,4 @@
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</com.android.systemui.media.taptotransfer.sender.MediaTttChipRootView>
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.annotation.StringRes
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
|
||||
|
||||
/**
|
||||
@@ -107,12 +108,15 @@ enum class ChipStateSender(
|
||||
controllerSender: MediaTttChipControllerSender,
|
||||
routeInfo: MediaRoute2Info,
|
||||
undoCallback: IUndoMediaTransferCallback?,
|
||||
uiEventLogger: MediaTttSenderUiEventLogger
|
||||
uiEventLogger: MediaTttSenderUiEventLogger,
|
||||
falsingManager: FalsingManager,
|
||||
): View.OnClickListener? {
|
||||
if (undoCallback == null) {
|
||||
return null
|
||||
}
|
||||
return View.OnClickListener {
|
||||
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@OnClickListener
|
||||
|
||||
uiEventLogger.logUndoClicked(
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED
|
||||
)
|
||||
@@ -143,12 +147,15 @@ enum class ChipStateSender(
|
||||
controllerSender: MediaTttChipControllerSender,
|
||||
routeInfo: MediaRoute2Info,
|
||||
undoCallback: IUndoMediaTransferCallback?,
|
||||
uiEventLogger: MediaTttSenderUiEventLogger
|
||||
uiEventLogger: MediaTttSenderUiEventLogger,
|
||||
falsingManager: FalsingManager,
|
||||
): View.OnClickListener? {
|
||||
if (undoCallback == null) {
|
||||
return null
|
||||
}
|
||||
return View.OnClickListener {
|
||||
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@OnClickListener
|
||||
|
||||
uiEventLogger.logUndoClicked(
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED
|
||||
)
|
||||
@@ -215,7 +222,8 @@ enum class ChipStateSender(
|
||||
controllerSender: MediaTttChipControllerSender,
|
||||
routeInfo: MediaRoute2Info,
|
||||
undoCallback: IUndoMediaTransferCallback?,
|
||||
uiEventLogger: MediaTttSenderUiEventLogger
|
||||
uiEventLogger: MediaTttSenderUiEventLogger,
|
||||
falsingManager: FalsingManager,
|
||||
): View.OnClickListener? = null
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -22,25 +22,30 @@ import android.media.MediaRoute2Info
|
||||
import android.os.PowerManager
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import android.widget.TextView
|
||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
import com.android.systemui.Gefingerpoken
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.animation.Interpolators
|
||||
import com.android.systemui.animation.ViewHierarchyAnimator
|
||||
import com.android.systemui.classifier.FalsingCollector
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
|
||||
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
|
||||
import com.android.systemui.temporarydisplay.TemporaryViewInfo
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import dagger.Lazy
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@@ -57,7 +62,11 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
accessibilityManager: AccessibilityManager,
|
||||
configurationController: ConfigurationController,
|
||||
powerManager: PowerManager,
|
||||
private val uiEventLogger: MediaTttSenderUiEventLogger
|
||||
private val uiEventLogger: MediaTttSenderUiEventLogger,
|
||||
// Added Lazy<> to delay the time we create Falsing instances.
|
||||
// And overcome performance issue, check [b/247817628] for details.
|
||||
private val falsingManager: Lazy<FalsingManager>,
|
||||
private val falsingCollector: Lazy<FalsingCollector>,
|
||||
) : TemporaryViewDisplayController<ChipSenderInfo, MediaTttLogger>(
|
||||
context,
|
||||
logger,
|
||||
@@ -70,6 +79,9 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
MediaTttUtils.WINDOW_TITLE,
|
||||
MediaTttUtils.WAKE_REASON,
|
||||
) {
|
||||
|
||||
private lateinit var parent: MediaTttChipRootView
|
||||
|
||||
override val windowLayoutParams = commonWindowLayoutParams.apply {
|
||||
gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
|
||||
}
|
||||
@@ -120,6 +132,15 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
|
||||
val chipState = newInfo.state
|
||||
|
||||
// Detect falsing touches on the chip.
|
||||
parent = currentView.requireViewById(R.id.media_ttt_sender_chip)
|
||||
parent.touchHandler = object : Gefingerpoken {
|
||||
override fun onTouchEvent(ev: MotionEvent?): Boolean {
|
||||
falsingCollector.get().onTouchEvent(ev)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// App icon
|
||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
|
||||
context, newInfo.routeInfo.clientPackageName, logger
|
||||
@@ -142,7 +163,11 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
// Undo
|
||||
val undoView = currentView.requireViewById<View>(R.id.undo)
|
||||
val undoClickListener = chipState.undoClickListener(
|
||||
this, newInfo.routeInfo, newInfo.undoCallback, uiEventLogger
|
||||
this,
|
||||
newInfo.routeInfo,
|
||||
newInfo.undoCallback,
|
||||
uiEventLogger,
|
||||
falsingManager.get(),
|
||||
)
|
||||
undoView.setOnClickListener(undoClickListener)
|
||||
undoView.visibility = (undoClickListener != null).visibleIfTrue()
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.media.taptotransfer.sender
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.widget.FrameLayout
|
||||
import com.android.systemui.Gefingerpoken
|
||||
|
||||
/** A simple subclass that allows for observing touch events on chip. */
|
||||
class MediaTttChipRootView(
|
||||
context: Context,
|
||||
attrs: AttributeSet?
|
||||
) : FrameLayout(context, attrs) {
|
||||
|
||||
/** Assign this field to observe touch events. */
|
||||
var touchHandler: Gefingerpoken? = null
|
||||
|
||||
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
||||
touchHandler?.onTouchEvent(ev)
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,9 @@ import com.android.internal.logging.testing.UiEventLoggerFake
|
||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.classifier.FalsingCollector
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
@@ -43,10 +45,12 @@ import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.eq
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import dagger.Lazy
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
@@ -75,6 +79,14 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
private lateinit var windowManager: WindowManager
|
||||
@Mock
|
||||
private lateinit var commandQueue: CommandQueue
|
||||
@Mock
|
||||
private lateinit var lazyFalsingManager: Lazy<FalsingManager>
|
||||
@Mock
|
||||
private lateinit var falsingManager: FalsingManager
|
||||
@Mock
|
||||
private lateinit var lazyFalsingCollector: Lazy<FalsingCollector>
|
||||
@Mock
|
||||
private lateinit var falsingCollector: FalsingCollector
|
||||
private lateinit var commandQueueCallback: CommandQueue.Callbacks
|
||||
private lateinit var fakeAppIconDrawable: Drawable
|
||||
private lateinit var fakeClock: FakeSystemClock
|
||||
@@ -101,6 +113,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
senderUiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
|
||||
|
||||
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
|
||||
whenever(lazyFalsingManager.get()).thenReturn(falsingManager)
|
||||
whenever(lazyFalsingCollector.get()).thenReturn(falsingCollector)
|
||||
|
||||
controllerSender = MediaTttChipControllerSender(
|
||||
commandQueue,
|
||||
@@ -111,7 +125,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
accessibilityManager,
|
||||
configurationController,
|
||||
powerManager,
|
||||
senderUiEventLogger
|
||||
senderUiEventLogger,
|
||||
lazyFalsingManager,
|
||||
lazyFalsingCollector
|
||||
)
|
||||
|
||||
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
||||
@@ -416,6 +432,38 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
assertThat(undoCallbackCalled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverSucceeded_withUndoRunnable_falseTap_callbackNotRun() {
|
||||
whenever(lazyFalsingManager.get().isFalseTap(anyInt())).thenReturn(true)
|
||||
var undoCallbackCalled = false
|
||||
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
|
||||
override fun onUndoTriggered() {
|
||||
undoCallbackCalled = true
|
||||
}
|
||||
}
|
||||
|
||||
controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
|
||||
getChipView().getUndoButton().performClick()
|
||||
|
||||
assertThat(undoCallbackCalled).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverSucceeded_withUndoRunnable_realTap_callbackRun() {
|
||||
whenever(lazyFalsingManager.get().isFalseTap(anyInt())).thenReturn(false)
|
||||
var undoCallbackCalled = false
|
||||
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
|
||||
override fun onUndoTriggered() {
|
||||
undoCallbackCalled = true
|
||||
}
|
||||
}
|
||||
|
||||
controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
|
||||
getChipView().getUndoButton().performClick()
|
||||
|
||||
assertThat(undoCallbackCalled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() {
|
||||
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
|
||||
|
||||
Reference in New Issue
Block a user