From e0394fb27b11cf3451d873222709ccba6477abfa Mon Sep 17 00:00:00 2001 From: Caitlin Cassidy Date: Thu, 10 Feb 2022 20:20:37 +0000 Subject: [PATCH 1/2] [Media TTT] Stop chip from crashing on older devices (e.g. Pixel 4a), un-ignore the tests now that it doesn't crash, and rehab the tests a bit. Fixes: 216286227 Test: media.taptotransfer tests Test: verified chip displays on Pixel 4a correctly Change-Id: I9ce20da5be2973f303884be28dec91dcc5f3a4e5 --- packages/SystemUI/res/values/colors.xml | 1 + .../MediaTttCommandLineHelperTest.kt | 2 - .../MediaTttChipControllerCommonTest.kt | 13 ++-- .../MediaTttChipControllerReceiverTest.kt | 33 ++++++--- .../MediaTttChipControllerSenderTest.kt | 71 +++++++++++-------- 5 files changed, 73 insertions(+), 47 deletions(-) diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index faf518e73e6d7..cbda439e5cb2d 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -155,6 +155,7 @@ #202124 #F28B82 + #EA4335 #C5221F #8AB4F8 diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt index 14afecebd1f63..794bc09715af3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt @@ -39,7 +39,6 @@ import com.android.systemui.util.mockito.nullable import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.mockito.Mock import org.mockito.Mockito.verify @@ -49,7 +48,6 @@ import java.io.StringWriter import java.util.concurrent.Executor @SmallTest -@Ignore("b/216286227") class MediaTttCommandLineHelperTest : SysuiTestCase() { private val inlineExecutor = Executor { command -> command.run() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt index f05d621eef3af..9da405c56e2dc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.media.taptotransfer.common import android.content.Context import android.graphics.drawable.Drawable -import android.graphics.drawable.Icon import android.view.View import android.view.ViewGroup import android.view.WindowManager @@ -29,7 +28,6 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.Mock @@ -39,7 +37,6 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest -@Ignore("b/216286227") class MediaTttChipControllerCommonTest : SysuiTestCase() { private lateinit var controllerCommon: MediaTttChipControllerCommon @@ -50,7 +47,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context) + appIconDrawable = context.getDrawable(R.drawable.ic_cake)!! controllerCommon = TestControllerCommon(context, windowManager) } @@ -93,10 +90,10 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { controllerCommon.displayChip(getState()) val chipView = getChipView() - val state = MediaTttChipState(PACKAGE_NAME) + val state = TestChipState(PACKAGE_NAME) controllerCommon.setIcon(state, chipView) - assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context)) + assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable) assertThat(chipView.getAppIconView().contentDescription) .isEqualTo(state.getAppName(context)) } @@ -120,6 +117,10 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) { } } + + inner class TestChipState(appPackageName: String?) : MediaTttChipState(appPackageName) { + override fun getAppIcon(context: Context) = appIconDrawable + } } private const val PACKAGE_NAME = "com.android.systemui" diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt index 44f691c3f6982..4cbf749d20320 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt @@ -17,7 +17,9 @@ package com.android.systemui.media.taptotransfer.receiver import android.app.StatusBarManager -import android.graphics.drawable.Icon +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.graphics.drawable.Drawable import android.media.MediaRoute2Info import android.os.Handler import android.view.View @@ -29,30 +31,44 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq import com.google.common.truth.Truth.assertThat import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@Ignore("b/216286227") class MediaTttChipControllerReceiverTest : SysuiTestCase() { private lateinit var controllerReceiver: MediaTttChipControllerReceiver + @Mock + private lateinit var packageManager: PackageManager + @Mock + private lateinit var applicationInfo: ApplicationInfo @Mock private lateinit var windowManager: WindowManager @Mock private lateinit var commandQueue: CommandQueue private lateinit var commandQueueCallback: CommandQueue.Callbacks + private lateinit var fakeAppIconDrawable: Drawable @Before fun setUp() { MockitoAnnotations.initMocks(this) + + fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!! + whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable) + whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME) + whenever(packageManager.getApplicationInfo( + eq(PACKAGE_NAME), any() + )).thenReturn(applicationInfo) + context.setMockPackageManager(packageManager) + controllerReceiver = MediaTttChipControllerReceiver( commandQueue, context, windowManager, Handler.getMain()) @@ -113,13 +129,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { controllerReceiver.displayChip(state) - assertThat(getChipView().getAppIconView().drawable).isEqualTo(state.getAppIcon(context)) - + assertThat(getChipView().getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) } @Test fun displayChip_hasAppIconDrawable_iconIsDrawable() { - val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context) + val drawable = context.getDrawable(R.drawable.ic_alarm)!! val state = ChipStateReceiver(PACKAGE_NAME, drawable, "appName") controllerReceiver.displayChip(state) @@ -133,13 +148,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { controllerReceiver.displayChip(state) - assertThat(getChipView().getAppIconView().contentDescription) - .isEqualTo(state.getAppName(context)) + assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(APP_NAME) } @Test fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() { - val appName = "FakeAppName" + val appName = "Override App Name" val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName) controllerReceiver.displayChip(state) @@ -156,6 +170,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { private fun ViewGroup.getAppIconView() = this.requireViewById(R.id.app_icon) } +private const val APP_NAME = "Fake app name" private const val PACKAGE_NAME = "com.android.systemui" private val routeInfo = MediaRoute2Info.Builder("id", "Test route name") diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt index dc39893d421b2..7cfea2455d24a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt @@ -17,6 +17,9 @@ package com.android.systemui.media.taptotransfer.sender import android.app.StatusBarManager +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.graphics.drawable.Drawable import android.media.MediaRoute2Info import android.view.View import android.view.WindowManager @@ -29,30 +32,44 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq import com.google.common.truth.Truth.assertThat import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest -@Ignore("b/216286227") class MediaTttChipControllerSenderTest : SysuiTestCase() { private lateinit var controllerSender: MediaTttChipControllerSender + @Mock + private lateinit var packageManager: PackageManager + @Mock + private lateinit var applicationInfo: ApplicationInfo @Mock private lateinit var windowManager: WindowManager @Mock private lateinit var commandQueue: CommandQueue private lateinit var commandQueueCallback: CommandQueue.Callbacks + private lateinit var fakeAppIconDrawable: Drawable @Before fun setUp() { MockitoAnnotations.initMocks(this) + + fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!! + whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME) + whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable) + whenever(packageManager.getApplicationInfo( + eq(PACKAGE_NAME), any() + )).thenReturn(applicationInfo) + context.setMockPackageManager(packageManager) + controllerSender = MediaTttChipControllerSender(commandQueue, context, windowManager) val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) @@ -192,9 +209,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { controllerSender.displayChip(state) val chipView = getChipView() - assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context)) - assertThat(chipView.getAppIconView().contentDescription) - .isEqualTo(state.getAppName(context)) + assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) + assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) @@ -207,9 +223,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { controllerSender.displayChip(state) val chipView = getChipView() - assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context)) - assertThat(chipView.getAppIconView().contentDescription) - .isEqualTo(state.getAppName(context)) + assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) + assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) @@ -222,9 +237,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { controllerSender.displayChip(state) val chipView = getChipView() - assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context)) - assertThat(chipView.getAppIconView().contentDescription) - .isEqualTo(state.getAppName(context)) + assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) + assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) @@ -237,9 +251,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { controllerSender.displayChip(state) val chipView = getChipView() - assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context)) - assertThat(chipView.getAppIconView().contentDescription) - .isEqualTo(state.getAppName(context)) + assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) + assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) @@ -252,9 +265,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { controllerSender.displayChip(state) val chipView = getChipView() - assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context)) - assertThat(chipView.getAppIconView().contentDescription) - .isEqualTo(state.getAppName(context)) + assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) + assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE) @@ -314,9 +326,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { controllerSender.displayChip(state) val chipView = getChipView() - assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context)) - assertThat(chipView.getAppIconView().contentDescription) - .isEqualTo(state.getAppName(context)) + assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) + assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE) @@ -376,9 +387,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { controllerSender.displayChip(state) val chipView = getChipView() - assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context)) - assertThat(chipView.getAppIconView().contentDescription) - .isEqualTo(state.getAppName(context)) + assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) + assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) @@ -451,15 +461,15 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { /** Helper method providing default parameters to not clutter up the tests. */ private fun almostCloseToStartCast() = - AlmostCloseToStartCast(PACKAGE_NAME, DEVICE_NAME) + AlmostCloseToStartCast(PACKAGE_NAME, OTHER_DEVICE_NAME) /** Helper method providing default parameters to not clutter up the tests. */ private fun almostCloseToEndCast() = - AlmostCloseToEndCast(PACKAGE_NAME, DEVICE_NAME) + AlmostCloseToEndCast(PACKAGE_NAME, OTHER_DEVICE_NAME) /** Helper method providing default parameters to not clutter up the tests. */ private fun transferToReceiverTriggered() = - TransferToReceiverTriggered(PACKAGE_NAME, DEVICE_NAME) + TransferToReceiverTriggered(PACKAGE_NAME, OTHER_DEVICE_NAME) /** Helper method providing default parameters to not clutter up the tests. */ private fun transferToThisDeviceTriggered() = @@ -468,23 +478,24 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { /** Helper method providing default parameters to not clutter up the tests. */ private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) = TransferToReceiverSucceeded( - PACKAGE_NAME, DEVICE_NAME, undoCallback + PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback ) /** Helper method providing default parameters to not clutter up the tests. */ private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) = TransferToThisDeviceSucceeded( - PACKAGE_NAME, DEVICE_NAME, undoCallback + PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback ) /** Helper method providing default parameters to not clutter up the tests. */ private fun transferFailed() = TransferFailed(PACKAGE_NAME) } -private const val DEVICE_NAME = "My Tablet" +private const val APP_NAME = "Fake app name" +private const val OTHER_DEVICE_NAME = "My Tablet" private const val PACKAGE_NAME = "com.android.systemui" -private val routeInfo = MediaRoute2Info.Builder("id", "Test Name") +private val routeInfo = MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME) .addFeature("feature") .setPackageName(PACKAGE_NAME) .build() From 634d0eee2fa3b12caf74e546a19a45306f360e89 Mon Sep 17 00:00:00 2001 From: Caitlin Cassidy Date: Thu, 10 Feb 2022 21:25:42 +0000 Subject: [PATCH 2/2] [Media TTT] Timeout the chip after 3 seconds. (Also updates the media dagger module to use Lazys so we don't have to update the module each time a constructor changes.) Bug: 214274529 Bug: 211487971 Test: manual: verified chip disappears after 3 seconds Test: atest MediaTttChipControllerCommonTest Change-Id: Ib9425d93e28aa5f6b9071ac10e864114363283f4 --- .../systemui/media/dagger/MediaModule.java | 35 +++------ .../common/MediaTttChipControllerCommon.kt | 14 +++- .../MediaTttChipControllerReceiver.kt | 6 +- .../sender/MediaTttChipControllerSender.kt | 5 +- .../MediaTttChipControllerCommonTest.kt | 71 +++++++++++++++++-- .../MediaTttChipControllerReceiverTest.kt | 9 ++- .../MediaTttChipControllerSenderTest.kt | 6 +- 7 files changed, 108 insertions(+), 38 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java index 2dff9472be688..c3b4354ebabe3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -16,12 +16,7 @@ package com.android.systemui.media.dagger; -import android.content.Context; -import android.os.Handler; -import android.view.WindowManager; - import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaFlags; import com.android.systemui.media.MediaHierarchyManager; @@ -34,11 +29,8 @@ import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.media.taptotransfer.MediaTttFlags; import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver; import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender; -import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.commandline.CommandRegistry; import java.util.Optional; -import java.util.concurrent.Executor; import javax.inject.Named; @@ -101,13 +93,11 @@ public interface MediaModule { @SysUISingleton static Optional providesMediaTttChipControllerSender( MediaTttFlags mediaTttFlags, - CommandQueue commandQueue, - Context context, - WindowManager windowManager) { + Lazy controllerSenderLazy) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of(new MediaTttChipControllerSender(commandQueue, context, windowManager)); + return Optional.of(controllerSenderLazy.get()); } /** */ @@ -115,16 +105,11 @@ public interface MediaModule { @SysUISingleton static Optional providesMediaTttChipControllerReceiver( MediaTttFlags mediaTttFlags, - CommandQueue commandQueue, - Context context, - WindowManager windowManager, - @Main Handler mainHandler) { + Lazy controllerReceiverLazy) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of( - new MediaTttChipControllerReceiver( - commandQueue, context, windowManager, mainHandler)); + return Optional.of(controllerReceiverLazy.get()); } /** */ @@ -132,14 +117,11 @@ public interface MediaModule { @SysUISingleton static Optional providesMediaTttCommandLineHelper( MediaTttFlags mediaTttFlags, - CommandRegistry commandRegistry, - Context context, - @Main Executor mainExecutor) { + Lazy helperLazy) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of( - new MediaTttCommandLineHelper(commandRegistry, context, mainExecutor)); + return Optional.of(helperLazy.get()); } /** */ @@ -147,13 +129,12 @@ public interface MediaModule { @SysUISingleton static Optional providesMediaMuteAwaitConnectionCli( MediaFlags mediaFlags, - CommandRegistry commandRegistry, - Context context + Lazy muteAwaitConnectionCliLazy ) { if (!mediaFlags.areMuteAwaitConnectionsEnabled()) { return Optional.empty(); } - return Optional.of(new MediaMuteAwaitConnectionCli(commandRegistry, context)); + return Optional.of(muteAwaitConnectionCliLazy.get()); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt index 4993105fa681d..ee2fba09358b8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt @@ -25,8 +25,11 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.WindowManager +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.util.concurrency.DelayableExecutor /** * A superclass controller that provides common functionality for showing chips on the sender device @@ -38,6 +41,7 @@ import com.android.systemui.R abstract class MediaTttChipControllerCommon( internal val context: Context, private val windowManager: WindowManager, + @Main private val mainExecutor: DelayableExecutor, @LayoutRes private val chipLayoutRes: Int ) { /** The window layout parameters we'll use when attaching the view to a window. */ @@ -56,6 +60,9 @@ abstract class MediaTttChipControllerCommon( /** The chip view currently being displayed. Null if the chip is not being displayed. */ var chipView: ViewGroup? = null + /** A [Runnable] that, when run, will cancel the pending timeout of the chip. */ + var cancelChipViewTimeout: Runnable? = null + /** * Displays the chip with the current state. * @@ -77,8 +84,11 @@ abstract class MediaTttChipControllerCommon( if (oldChipView == null) { windowManager.addView(chipView, windowLayoutParams) } - } + // Cancel and re-set the chip timeout each time we get a new state. + cancelChipViewTimeout?.run() + cancelChipViewTimeout = mainExecutor.executeDelayed(this::removeChip, TIMEOUT_MILLIS) + } /** Hides the chip. */ fun removeChip() { @@ -118,3 +128,5 @@ abstract class MediaTttChipControllerCommon( // Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and // UpdateMediaTapToTransferReceiverDisplayTest private const val WINDOW_TITLE = "Media Transfer Chip View" +@VisibleForTesting +const val TIMEOUT_MILLIS = 3000L diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 18623c0686fde..214a8888e0b5b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -18,7 +18,6 @@ package com.android.systemui.media.taptotransfer.receiver import android.app.StatusBarManager import android.content.Context -import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.media.MediaRoute2Info import android.os.Handler @@ -27,10 +26,10 @@ import android.view.ViewGroup import android.view.WindowManager import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background 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.util.concurrency.DelayableExecutor import javax.inject.Inject /** @@ -43,9 +42,10 @@ class MediaTttChipControllerReceiver @Inject constructor( commandQueue: CommandQueue, context: Context, windowManager: WindowManager, + mainExecutor: DelayableExecutor, @Main private val mainHandler: Handler, ) : MediaTttChipControllerCommon( - context, windowManager, R.layout.media_ttt_chip_receiver + context, windowManager, mainExecutor, R.layout.media_ttt_chip_receiver ) { private val commandQueueCallbacks = object : CommandQueue.Callbacks { override fun updateMediaTapToTransferReceiverDisplay( diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt index da767ea900557..482e604a06351 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt @@ -27,8 +27,10 @@ import android.widget.TextView import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.systemui.R 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.util.concurrency.DelayableExecutor import javax.inject.Inject /** @@ -40,8 +42,9 @@ class MediaTttChipControllerSender @Inject constructor( commandQueue: CommandQueue, context: Context, windowManager: WindowManager, + @Main private val mainExecutor: DelayableExecutor, ) : MediaTttChipControllerCommon( - context, windowManager, R.layout.media_ttt_chip + context, windowManager, mainExecutor, R.layout.media_ttt_chip ) { private val commandQueueCallbacks = object : CommandQueue.Callbacks { override fun updateMediaTapToTransferSenderDisplay( diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt index 9da405c56e2dc..ea0a5a42ad211 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt @@ -25,7 +25,11 @@ import android.widget.ImageView 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.util.concurrency.DelayableExecutor +import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test @@ -40,6 +44,9 @@ import org.mockito.MockitoAnnotations class MediaTttChipControllerCommonTest : SysuiTestCase() { private lateinit var controllerCommon: MediaTttChipControllerCommon + private lateinit var fakeClock: FakeSystemClock + private lateinit var fakeExecutor: FakeExecutor + private lateinit var appIconDrawable: Drawable @Mock private lateinit var windowManager: WindowManager @@ -48,7 +55,10 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) appIconDrawable = context.getDrawable(R.drawable.ic_cake)!! - controllerCommon = TestControllerCommon(context, windowManager) + fakeClock = FakeSystemClock() + fakeExecutor = FakeExecutor(fakeClock) + + controllerCommon = TestControllerCommon(context, windowManager, fakeExecutor) } @Test @@ -67,6 +77,58 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { verify(windowManager, never()).addView(any(), any()) } + @Test + fun displayChip_chipDoesNotDisappearsBeforeTimeout() { + controllerCommon.displayChip(getState()) + reset(windowManager) + + fakeClock.advanceTime(TIMEOUT_MILLIS - 1) + + verify(windowManager, never()).removeView(any()) + } + + @Test + fun displayChip_chipDisappearsAfterTimeout() { + controllerCommon.displayChip(getState()) + reset(windowManager) + + fakeClock.advanceTime(TIMEOUT_MILLIS + 1) + + verify(windowManager).removeView(any()) + } + + @Test + fun displayChip_calledAgainBeforeTimeout_timeoutReset() { + // First, display the chip + controllerCommon.displayChip(getState()) + + // After some time, re-display the chip + val waitTime = 1000L + fakeClock.advanceTime(waitTime) + controllerCommon.displayChip(getState()) + + // Wait until the timeout for the first display would've happened + fakeClock.advanceTime(TIMEOUT_MILLIS - waitTime + 1) + + // Verify we didn't hide the chip + verify(windowManager, never()).removeView(any()) + } + + @Test + fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() { + // First, display the chip + controllerCommon.displayChip(getState()) + + // After some time, re-display the chip + fakeClock.advanceTime(1000L) + controllerCommon.displayChip(getState()) + + // Ensure we still hide the chip eventually + fakeClock.advanceTime(TIMEOUT_MILLIS + 1) + + verify(windowManager).removeView(any()) + } + @Test fun removeChip_chipRemoved() { // First, add the chip @@ -110,9 +172,10 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { inner class TestControllerCommon( context: Context, - windowManager: WindowManager - ) : MediaTttChipControllerCommon( - context, windowManager, R.layout.media_ttt_chip + windowManager: WindowManager, + @Main mainExecutor: DelayableExecutor, + ) : MediaTttChipControllerCommon( + context, windowManager, mainExecutor, R.layout.media_ttt_chip ) { override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) { } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt index 4cbf749d20320..117a6c8985108 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt @@ -30,8 +30,10 @@ 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.util.concurrency.FakeExecutor 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 org.junit.Before import org.junit.Test @@ -70,7 +72,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { context.setMockPackageManager(packageManager) controllerReceiver = MediaTttChipControllerReceiver( - commandQueue, context, windowManager, Handler.getMain()) + commandQueue, + context, + windowManager, + FakeExecutor(FakeSystemClock()), + Handler.getMain() + ) val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) verify(commandQueue).addCallback(callbackCaptor.capture()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt index 7cfea2455d24a..b440064298281 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt @@ -31,8 +31,10 @@ 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.util.concurrency.FakeExecutor 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 org.junit.Before import org.junit.Test @@ -70,7 +72,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { )).thenReturn(applicationInfo) context.setMockPackageManager(packageManager) - controllerSender = MediaTttChipControllerSender(commandQueue, context, windowManager) + controllerSender = MediaTttChipControllerSender( + commandQueue, context, windowManager, FakeExecutor(FakeSystemClock()) + ) val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) verify(commandQueue).addCallback(callbackCaptor.capture())