Merge "Add falsing to undo button" into tm-qpr-dev am: a170029655
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19995524 Change-Id: Ibf8ccaa839af541e38ad16e11ac9a6edce8700ac Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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
|
<!-- 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.) -->
|
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. -->
|
<!-- 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:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||||
android:id="@+id/media_ttt_sender_chip"
|
android:id="@+id/media_ttt_sender_chip"
|
||||||
@@ -97,4 +97,4 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
</LinearLayout>
|
</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.logging.UiEventLogger
|
||||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.plugins.FalsingManager
|
||||||
import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
|
import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,12 +108,15 @@ enum class ChipStateSender(
|
|||||||
controllerSender: MediaTttChipControllerSender,
|
controllerSender: MediaTttChipControllerSender,
|
||||||
routeInfo: MediaRoute2Info,
|
routeInfo: MediaRoute2Info,
|
||||||
undoCallback: IUndoMediaTransferCallback?,
|
undoCallback: IUndoMediaTransferCallback?,
|
||||||
uiEventLogger: MediaTttSenderUiEventLogger
|
uiEventLogger: MediaTttSenderUiEventLogger,
|
||||||
|
falsingManager: FalsingManager,
|
||||||
): View.OnClickListener? {
|
): View.OnClickListener? {
|
||||||
if (undoCallback == null) {
|
if (undoCallback == null) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return View.OnClickListener {
|
return View.OnClickListener {
|
||||||
|
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@OnClickListener
|
||||||
|
|
||||||
uiEventLogger.logUndoClicked(
|
uiEventLogger.logUndoClicked(
|
||||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED
|
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_RECEIVER_CLICKED
|
||||||
)
|
)
|
||||||
@@ -143,12 +147,15 @@ enum class ChipStateSender(
|
|||||||
controllerSender: MediaTttChipControllerSender,
|
controllerSender: MediaTttChipControllerSender,
|
||||||
routeInfo: MediaRoute2Info,
|
routeInfo: MediaRoute2Info,
|
||||||
undoCallback: IUndoMediaTransferCallback?,
|
undoCallback: IUndoMediaTransferCallback?,
|
||||||
uiEventLogger: MediaTttSenderUiEventLogger
|
uiEventLogger: MediaTttSenderUiEventLogger,
|
||||||
|
falsingManager: FalsingManager,
|
||||||
): View.OnClickListener? {
|
): View.OnClickListener? {
|
||||||
if (undoCallback == null) {
|
if (undoCallback == null) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return View.OnClickListener {
|
return View.OnClickListener {
|
||||||
|
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@OnClickListener
|
||||||
|
|
||||||
uiEventLogger.logUndoClicked(
|
uiEventLogger.logUndoClicked(
|
||||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED
|
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_UNDO_TRANSFER_TO_THIS_DEVICE_CLICKED
|
||||||
)
|
)
|
||||||
@@ -215,7 +222,8 @@ enum class ChipStateSender(
|
|||||||
controllerSender: MediaTttChipControllerSender,
|
controllerSender: MediaTttChipControllerSender,
|
||||||
routeInfo: MediaRoute2Info,
|
routeInfo: MediaRoute2Info,
|
||||||
undoCallback: IUndoMediaTransferCallback?,
|
undoCallback: IUndoMediaTransferCallback?,
|
||||||
uiEventLogger: MediaTttSenderUiEventLogger
|
uiEventLogger: MediaTttSenderUiEventLogger,
|
||||||
|
falsingManager: FalsingManager,
|
||||||
): View.OnClickListener? = null
|
): View.OnClickListener? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -22,25 +22,30 @@ import android.media.MediaRoute2Info
|
|||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.view.accessibility.AccessibilityManager
|
import android.view.accessibility.AccessibilityManager
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||||
|
import com.android.systemui.Gefingerpoken
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.animation.Interpolators
|
import com.android.systemui.animation.Interpolators
|
||||||
import com.android.systemui.animation.ViewHierarchyAnimator
|
import com.android.systemui.animation.ViewHierarchyAnimator
|
||||||
|
import com.android.systemui.classifier.FalsingCollector
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.dagger.qualifiers.Main
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
||||||
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
|
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.CommandQueue
|
||||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||||
import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
|
import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
|
||||||
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
|
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
|
||||||
import com.android.systemui.temporarydisplay.TemporaryViewInfo
|
import com.android.systemui.temporarydisplay.TemporaryViewInfo
|
||||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||||
|
import dagger.Lazy
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +62,11 @@ class MediaTttChipControllerSender @Inject constructor(
|
|||||||
accessibilityManager: AccessibilityManager,
|
accessibilityManager: AccessibilityManager,
|
||||||
configurationController: ConfigurationController,
|
configurationController: ConfigurationController,
|
||||||
powerManager: PowerManager,
|
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>(
|
) : TemporaryViewDisplayController<ChipSenderInfo, MediaTttLogger>(
|
||||||
context,
|
context,
|
||||||
logger,
|
logger,
|
||||||
@@ -70,6 +79,9 @@ class MediaTttChipControllerSender @Inject constructor(
|
|||||||
MediaTttUtils.WINDOW_TITLE,
|
MediaTttUtils.WINDOW_TITLE,
|
||||||
MediaTttUtils.WAKE_REASON,
|
MediaTttUtils.WAKE_REASON,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private lateinit var parent: MediaTttChipRootView
|
||||||
|
|
||||||
override val windowLayoutParams = commonWindowLayoutParams.apply {
|
override val windowLayoutParams = commonWindowLayoutParams.apply {
|
||||||
gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
|
gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
|
||||||
}
|
}
|
||||||
@@ -120,6 +132,15 @@ class MediaTttChipControllerSender @Inject constructor(
|
|||||||
|
|
||||||
val chipState = newInfo.state
|
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
|
// App icon
|
||||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
|
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
|
||||||
context, newInfo.routeInfo.clientPackageName, logger
|
context, newInfo.routeInfo.clientPackageName, logger
|
||||||
@@ -142,7 +163,11 @@ class MediaTttChipControllerSender @Inject constructor(
|
|||||||
// Undo
|
// Undo
|
||||||
val undoView = currentView.requireViewById<View>(R.id.undo)
|
val undoView = currentView.requireViewById<View>(R.id.undo)
|
||||||
val undoClickListener = chipState.undoClickListener(
|
val undoClickListener = chipState.undoClickListener(
|
||||||
this, newInfo.routeInfo, newInfo.undoCallback, uiEventLogger
|
this,
|
||||||
|
newInfo.routeInfo,
|
||||||
|
newInfo.undoCallback,
|
||||||
|
uiEventLogger,
|
||||||
|
falsingManager.get(),
|
||||||
)
|
)
|
||||||
undoView.setOnClickListener(undoClickListener)
|
undoView.setOnClickListener(undoClickListener)
|
||||||
undoView.visibility = (undoClickListener != null).visibleIfTrue()
|
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.internal.statusbar.IUndoMediaTransferCallback
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.classifier.FalsingCollector
|
||||||
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
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.CommandQueue
|
||||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor
|
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.mockito.eq
|
||||||
import com.android.systemui.util.time.FakeSystemClock
|
import com.android.systemui.util.time.FakeSystemClock
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import dagger.Lazy
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.ArgumentCaptor
|
import org.mockito.ArgumentCaptor
|
||||||
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.never
|
import org.mockito.Mockito.never
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
@@ -75,6 +79,14 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
|||||||
private lateinit var windowManager: WindowManager
|
private lateinit var windowManager: WindowManager
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var commandQueue: CommandQueue
|
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 commandQueueCallback: CommandQueue.Callbacks
|
||||||
private lateinit var fakeAppIconDrawable: Drawable
|
private lateinit var fakeAppIconDrawable: Drawable
|
||||||
private lateinit var fakeClock: FakeSystemClock
|
private lateinit var fakeClock: FakeSystemClock
|
||||||
@@ -101,6 +113,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
|||||||
senderUiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
|
senderUiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
|
||||||
|
|
||||||
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
|
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
|
||||||
|
whenever(lazyFalsingManager.get()).thenReturn(falsingManager)
|
||||||
|
whenever(lazyFalsingCollector.get()).thenReturn(falsingCollector)
|
||||||
|
|
||||||
controllerSender = MediaTttChipControllerSender(
|
controllerSender = MediaTttChipControllerSender(
|
||||||
commandQueue,
|
commandQueue,
|
||||||
@@ -111,7 +125,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
|||||||
accessibilityManager,
|
accessibilityManager,
|
||||||
configurationController,
|
configurationController,
|
||||||
powerManager,
|
powerManager,
|
||||||
senderUiEventLogger
|
senderUiEventLogger,
|
||||||
|
lazyFalsingManager,
|
||||||
|
lazyFalsingCollector
|
||||||
)
|
)
|
||||||
|
|
||||||
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
||||||
@@ -416,6 +432,38 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
|||||||
assertThat(undoCallbackCalled).isTrue()
|
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
|
@Test
|
||||||
fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() {
|
fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() {
|
||||||
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
|
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
|
||||||
|
|||||||
Reference in New Issue
Block a user