From e087e12997ae4b4d0d22c4aad0824b56d66d8d37 Mon Sep 17 00:00:00 2001 From: Caitlin Cassidy Date: Thu, 16 Dec 2021 17:26:41 +0000 Subject: [PATCH] [Media TTT] Add the app icon to the view. Fixes: 209796699 Test: manual (icon shows on the command `adb shell cmd statusbar media-ttt-chip-add Tablet ` Test: MediaTttChipControllerTest Change-Id: I59f243ed28521ce7ce0c07a0e92a5ac4e37c6599 --- .../SystemUI/res/layout/media_ttt_chip.xml | 11 ++- packages/SystemUI/res/values/dimens.xml | 3 +- .../systemui/media/dagger/MediaModule.java | 3 +- .../taptotransfer/MediaTttChipController.kt | 12 ++- .../media/taptotransfer/MediaTttChipState.kt | 21 +++-- .../MediaTttCommandLineHelper.kt | 15 +++- .../MediaTttChipControllerTest.kt | 83 +++++++++++++------ .../MediaTttCommandLineHelperTest.kt | 2 +- 8 files changed, 107 insertions(+), 43 deletions(-) diff --git a/packages/SystemUI/res/layout/media_ttt_chip.xml b/packages/SystemUI/res/layout/media_ttt_chip.xml index 6fbc41c7e20d8..2d082dc7d5e2e 100644 --- a/packages/SystemUI/res/layout/media_ttt_chip.xml +++ b/packages/SystemUI/res/layout/media_ttt_chip.xml @@ -26,6 +26,13 @@ android:gravity="center_vertical" > + + 16dp 16sp - 16dp + 24dp + 20dp 8dp -8dp 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 1174fa8e40612..a398a7fa45750 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -99,12 +99,13 @@ public interface MediaModule { static Optional providesMediaTttCommandLineHelper( MediaTttFlags mediaTttFlags, CommandRegistry commandRegistry, + Context context, MediaTttChipController mediaTttChipController, @Main DelayableExecutor mainExecutor) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } return Optional.of(new MediaTttCommandLineHelper( - commandRegistry, mediaTttChipController, mainExecutor)); + commandRegistry, context, mediaTttChipController, mainExecutor)); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt index baa469d43e227..2b55d634b382f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt @@ -25,6 +25,7 @@ import android.view.View import android.view.WindowManager import android.widget.LinearLayout import android.widget.TextView +import com.android.internal.widget.CachingIconView import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -74,6 +75,11 @@ class MediaTttChipController @Inject constructor( } val currentChipView = chipView!! + // App icon + currentChipView.findViewById(R.id.app_icon).apply { + this.setImageDrawable(chipState.appIconDrawable) + } + // Text currentChipView.requireViewById(R.id.text).apply { text = context.getString(chipState.chipText, chipState.otherDeviceName) @@ -128,7 +134,11 @@ class MediaTttChipController @Inject constructor( val undoRunnable = chipState.future.get(TRANSFER_TIMEOUT_SECONDS, TimeUnit.SECONDS) // Make UI changes on the main thread mainExecutor.execute { - displayChip(TransferSucceeded(chipState.otherDeviceName, undoRunnable)) + displayChip( + TransferSucceeded( + chipState.otherDeviceName, chipState.appIconDrawable, undoRunnable + ) + ) } } catch (ex: Exception) { // TODO(b/203800327): Maybe show a failure chip here if UX decides we need one. diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt index 1f308a9a6cd9c..62e3b1ac8d5ce 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt @@ -16,6 +16,7 @@ package com.android.systemui.media.taptotransfer +import android.graphics.drawable.Drawable import androidx.annotation.StringRes import com.android.systemui.R import java.util.concurrent.Future @@ -26,12 +27,15 @@ import java.util.concurrent.Future * * This is a sealed class where each subclass represents a specific chip state. Each subclass can * contain additional information that is necessary for only that state. + * + * @property chipText a string resource for the text that the chip should display. + * @property otherDeviceName the name of the other device involved in the transfer. + * @property appIconDrawable a drawable representing the icon of the app playing the media. */ sealed class MediaTttChipState( - /** A string resource for the text that the chip should display. */ @StringRes internal val chipText: Int, - /** The name of the other device involved in the transfer. */ - internal val otherDeviceName: String + internal val otherDeviceName: String, + internal val appIconDrawable: Drawable, ) /** @@ -39,8 +43,9 @@ sealed class MediaTttChipState( * The chip will instruct the user to move closer in order to initiate the transfer. */ class MoveCloserToTransfer( - otherDeviceName: String -) : MediaTttChipState(R.string.media_move_closer_to_transfer, otherDeviceName) + otherDeviceName: String, + appIconDrawable: Drawable +) : MediaTttChipState(R.string.media_move_closer_to_transfer, otherDeviceName, appIconDrawable) /** * A state representing that a transfer has been initiated (but not completed). @@ -52,8 +57,9 @@ class MoveCloserToTransfer( */ class TransferInitiated( otherDeviceName: String, + appIconDrawable: Drawable, val future: Future -) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName) +) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName, appIconDrawable) /** * A state representing that a transfer has been successfully completed. @@ -63,5 +69,6 @@ class TransferInitiated( */ class TransferSucceeded( otherDeviceName: String, + appIconDrawable: Drawable, val undoRunnable: Runnable? = null -) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName) +) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName, appIconDrawable) diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt index 663037cc850fa..74983e5bfe03c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt @@ -16,8 +16,11 @@ package com.android.systemui.media.taptotransfer +import android.content.Context +import android.graphics.drawable.Icon import android.util.Log import androidx.annotation.VisibleForTesting +import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.commandline.Command @@ -34,9 +37,13 @@ import javax.inject.Inject @SysUISingleton class MediaTttCommandLineHelper @Inject constructor( commandRegistry: CommandRegistry, + context: Context, private val mediaTttChipController: MediaTttChipController, @Main private val mainExecutor: DelayableExecutor, ) { + private val appIconDrawable = + Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context) + init { commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() } commandRegistry.registerCommand(REMOVE_CHIP_COMMAND_TAG) { RemoveChipCommand() } @@ -47,19 +54,21 @@ class MediaTttCommandLineHelper @Inject constructor( val otherDeviceName = args[0] when (args[1]) { MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> { - mediaTttChipController.displayChip(MoveCloserToTransfer(otherDeviceName)) + mediaTttChipController.displayChip( + MoveCloserToTransfer(otherDeviceName, appIconDrawable) + ) } TRANSFER_INITIATED_COMMAND_NAME -> { val futureTask = FutureTask { fakeUndoRunnable } mediaTttChipController.displayChip( - TransferInitiated(otherDeviceName, futureTask) + TransferInitiated(otherDeviceName, appIconDrawable, futureTask) ) mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME) } TRANSFER_SUCCEEDED_COMMAND_NAME -> { mediaTttChipController.displayChip( - TransferSucceeded(otherDeviceName, fakeUndoRunnable) + TransferSucceeded(otherDeviceName, appIconDrawable, fakeUndoRunnable) ) } else -> { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt index 87499172fc46e..de6525a2e750b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt @@ -16,8 +16,11 @@ package com.android.systemui.media.taptotransfer +import android.graphics.drawable.Drawable +import android.graphics.drawable.Icon import android.view.View import android.view.WindowManager +import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.test.filters.SmallTest @@ -36,10 +39,11 @@ import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import java.util.concurrent.Future @SmallTest class MediaTttChipControllerTest : SysuiTestCase() { - + private lateinit var appIconDrawable: Drawable private lateinit var fakeMainClock: FakeSystemClock private lateinit var fakeMainExecutor: FakeExecutor private lateinit var fakeBackgroundClock: FakeSystemClock @@ -53,6 +57,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) + appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context) fakeMainClock = FakeSystemClock() fakeMainExecutor = FakeExecutor(fakeMainClock) fakeBackgroundClock = FakeSystemClock() @@ -64,24 +69,24 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun displayChip_chipAdded() { - mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) + mediaTttChipController.displayChip(moveCloserToTransfer()) verify(windowManager).addView(any(), any()) } @Test fun displayChip_twice_chipNotAddedTwice() { - mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) + mediaTttChipController.displayChip(moveCloserToTransfer()) reset(windowManager) - mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) + mediaTttChipController.displayChip(moveCloserToTransfer()) verify(windowManager, never()).addView(any(), any()) } @Test fun removeChip_chipRemoved() { // First, add the chip - mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) + mediaTttChipController.displayChip(moveCloserToTransfer()) // Then, remove it mediaTttChipController.removeChip() @@ -97,24 +102,26 @@ class MediaTttChipControllerTest : SysuiTestCase() { } @Test - fun moveCloserToTransfer_chipTextContainsDeviceName_noLoadingIcon_noUndo() { - mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) + fun moveCloserToTransfer_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() { + mediaTttChipController.displayChip(moveCloserToTransfer()) val chipView = getChipView() + assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable) assertThat(chipView.getChipText()).contains(DEVICE_NAME) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) } @Test - fun transferInitiated_futureNotResolvedYet_loadingIcon_noUndo() { + fun transferInitiated_futureNotResolvedYet_appIcon_loadingIcon_noUndo() { val future: SettableFuture = SettableFuture.create() - mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future)) + mediaTttChipController.displayChip(transferInitiated(future)) // Don't resolve the future in any way and don't run our executors // Assert we're still in the loading state val chipView = getChipView() + assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable) assertThat(chipView.getChipText()).contains(DEVICE_NAME) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) @@ -125,7 +132,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { val future: SettableFuture = SettableFuture.create() val undoRunnable = Runnable { } - mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future)) + mediaTttChipController.displayChip(transferInitiated(future)) future.set(undoRunnable) fakeBackgroundExecutor.advanceClockToLast() @@ -146,7 +153,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { fun transferInitiated_futureCancelled_chipRemoved() { val future: SettableFuture = SettableFuture.create() - mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future)) + mediaTttChipController.displayChip(transferInitiated(future)) future.cancel(true) fakeBackgroundExecutor.advanceClockToLast() @@ -163,7 +170,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun transferInitiated_futureNotResolvedAfterTimeout_chipRemoved() { val future: SettableFuture = SettableFuture.create() - mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future)) + mediaTttChipController.displayChip(transferInitiated(future)) // We won't set anything on the future, but we will still run the executors so that we're // waiting on the future resolving. If we have a bug in our code, then this test will time @@ -180,22 +187,28 @@ class MediaTttChipControllerTest : SysuiTestCase() { } @Test - fun transferSucceededNullUndoRunnable_chipTextContainsDeviceName_noLoadingIcon_noUndo() { - mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME, undoRunnable = null)) + fun transferSucceeded_appIcon_chipTextContainsDeviceName_noLoadingIcon() { + mediaTttChipController.displayChip(transferSucceeded()) val chipView = getChipView() + assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable) assertThat(chipView.getChipText()).contains(DEVICE_NAME) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) + } + + @Test + fun transferSucceededNullUndoRunnable_noUndo() { + mediaTttChipController.displayChip(transferSucceeded(undoRunnable = null)) + + val chipView = getChipView() assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) } @Test - fun transferSucceededWithUndoRunnable_chipTextContainsDeviceName_noLoadingIcon_undoWithClick() { - mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME) { }) + fun transferSucceededWithUndoRunnable_undoWithClick() { + mediaTttChipController.displayChip(transferSucceeded { }) val chipView = getChipView() - assertThat(chipView.getChipText()).contains(DEVICE_NAME) - assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE) assertThat(chipView.getUndoButton().hasOnClickListeners()).isTrue() } @@ -205,7 +218,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { var runnableRun = false val runnable = Runnable { runnableRun = true } - mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME, runnable)) + mediaTttChipController.displayChip(transferSucceeded(undoRunnable = runnable)) getChipView().getUndoButton().performClick() assertThat(runnableRun).isTrue() @@ -213,36 +226,39 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() { - mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) - mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, TEST_FUTURE)) + mediaTttChipController.displayChip(moveCloserToTransfer()) + mediaTttChipController.displayChip(transferInitiated()) assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE) } @Test fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() { - mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, TEST_FUTURE)) - mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME)) + mediaTttChipController.displayChip(transferInitiated()) + mediaTttChipController.displayChip(transferSucceeded()) assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE) } @Test fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() { - mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, TEST_FUTURE)) - mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME) { }) + mediaTttChipController.displayChip(transferInitiated()) + mediaTttChipController.displayChip(transferSucceeded { }) assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.VISIBLE) } @Test fun changeFromTransferSucceededToMoveCloser_undoButtonDisappears() { - mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME)) - mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME)) + mediaTttChipController.displayChip(transferSucceeded()) + mediaTttChipController.displayChip(moveCloserToTransfer()) assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE) } + private fun LinearLayout.getAppIconDrawable(): Drawable = + (this.requireViewById(R.id.app_icon)).drawable + private fun LinearLayout.getChipText(): String = (this.requireViewById(R.id.text)).text as String @@ -256,6 +272,19 @@ class MediaTttChipControllerTest : SysuiTestCase() { verify(windowManager).addView(viewCaptor.capture(), any()) return viewCaptor.value as LinearLayout } + + /** Helper method providing default parameters to not clutter up the tests. */ + private fun moveCloserToTransfer() = MoveCloserToTransfer(DEVICE_NAME, appIconDrawable) + + /** Helper method providing default parameters to not clutter up the tests. */ + private fun transferInitiated( + future: Future = TEST_FUTURE + ) = TransferInitiated(DEVICE_NAME, appIconDrawable, future) + + /** Helper method providing default parameters to not clutter up the tests. */ + private fun transferSucceeded( + undoRunnable: Runnable? = null + ) = TransferSucceeded(DEVICE_NAME, appIconDrawable, undoRunnable) } private const val DEVICE_NAME = "My Tablet" 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 9b2d3eb17568c..91b529875654e 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 @@ -49,7 +49,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) mediaTttCommandLineHelper = MediaTttCommandLineHelper( - commandRegistry, mediaTttChipController, FakeExecutor(FakeSystemClock()) + commandRegistry, context, mediaTttChipController, FakeExecutor(FakeSystemClock()) ) }