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())
)
}