diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3a9159094433b..078356f4ef122 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2192,7 +2192,9 @@
Move closer to %1$s to play here
- Playing on %1$s
+ Playing on %1$s
+
+ Playing on this phone
Something went wrong
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
index 8db3e9d135a06..eb026e2187c46 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
@@ -72,6 +72,18 @@ interface IDeviceSenderCallback {
oneway void transferToReceiverTriggered(
in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+ /**
+ * Invoke to notify System UI that a media transfer from the receiver and back to this device
+ * (the sender) has been started.
+ *
+ * Important notes:
+ * - This callback is for *ending* a cast. It should be used when media is currently being
+ * played on the receiver device and the media has started being transferred to play locally
+ * instead.
+ */
+ oneway void transferToThisDeviceTriggered(
+ in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+
/**
* Invoke to notify System UI that the attempted transfer has failed.
*
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 e8a847f6ae418..5ce9d56c15375 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -36,6 +36,7 @@ import com.android.systemui.media.taptotransfer.sender.MoveCloserToEndCast
import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast
import com.android.systemui.media.taptotransfer.sender.TransferFailed
import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered
+import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered
import com.android.systemui.media.taptotransfer.sender.TransferSucceeded
import com.android.systemui.shared.mediattt.DeviceInfo
import com.android.systemui.shared.mediattt.IDeviceSenderCallback
@@ -98,6 +99,11 @@ class MediaTttCommandLineHelper @Inject constructor(
senderCallback.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
}
}
+ TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME -> {
+ runOnService { senderCallback ->
+ senderCallback.transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo)
+ }
+ }
// TODO(b/203800643): Migrate this command to invoke the service instead of the
// controller.
TRANSFER_SUCCEEDED_COMMAND_NAME -> {
@@ -120,6 +126,7 @@ class MediaTttCommandLineHelper @Inject constructor(
"$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " +
"$MOVE_CLOSER_TO_END_CAST_COMMAND_NAME, " +
"$TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME, " +
+ "$TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME, " +
"$TRANSFER_SUCCEEDED_COMMAND_NAME, " +
TRANSFER_FAILED_COMMAND_NAME
)
@@ -231,6 +238,9 @@ val MOVE_CLOSER_TO_END_CAST_COMMAND_NAME = MoveCloserToEndCast::class.simpleName
@VisibleForTesting
val TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME = TransferToReceiverTriggered::class.simpleName!!
@VisibleForTesting
+val TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME =
+ TransferToThisDeviceTriggered::class.simpleName!!
+@VisibleForTesting
val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
@VisibleForTesting
val TRANSFER_FAILED_COMMAND_NAME = TransferFailed::class.simpleName!!
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index 0f2715c4a6e35..abfd9d3e498af 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -34,6 +34,9 @@ sealed class ChipStateSender(
) : MediaTttChipState(appIconDrawable, appIconContentDescription) {
/** Returns a fully-formed string with the text that the chip should display. */
abstract fun getChipTextString(context: Context): String
+
+ /** Returns true if the loading icon should be displayed and false otherwise. */
+ abstract fun showLoading(): Boolean
}
/**
@@ -51,6 +54,8 @@ class MoveCloserToStartCast(
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_move_closer_to_start_cast, otherDeviceName)
}
+
+ override fun showLoading() = false
}
/**
@@ -68,6 +73,8 @@ class MoveCloserToEndCast(
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_move_closer_to_end_cast, otherDeviceName)
}
+
+ override fun showLoading() = false
}
/**
@@ -82,8 +89,25 @@ class TransferToReceiverTriggered(
private val otherDeviceName: String
) : ChipStateSender(appIconDrawable, appIconContentDescription) {
override fun getChipTextString(context: Context): String {
- return context.getString(R.string.media_transfer_playing, otherDeviceName)
+ return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName)
}
+
+ override fun showLoading() = true
+}
+
+/**
+ * A state representing that a transfer from the receiver device and back to this device (the
+ * sender) has been initiated (but not completed).
+ */
+class TransferToThisDeviceTriggered(
+ appIconDrawable: Drawable,
+ appIconContentDescription: String
+) : ChipStateSender(appIconDrawable, appIconContentDescription) {
+ override fun getChipTextString(context: Context): String {
+ return context.getString(R.string.media_transfer_playing_this_device)
+ }
+
+ override fun showLoading() = true
}
/**
@@ -100,8 +124,10 @@ class TransferSucceeded(
val undoRunnable: Runnable? = null
) : ChipStateSender(appIconDrawable, appIconContentDescription) {
override fun getChipTextString(context: Context): String {
- return context.getString(R.string.media_transfer_playing, otherDeviceName)
+ return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName)
}
+
+ override fun showLoading() = false
}
/** A state representing that a transfer has failed. */
@@ -112,4 +138,6 @@ class TransferFailed(
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_transfer_failed)
}
+
+ override fun showLoading() = false
}
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 88264b6e669ee..5cfd674b21cb7 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
@@ -49,9 +49,8 @@ class MediaTttChipControllerSender @Inject constructor(
}
// Loading
- val showLoading = chipState is TransferToReceiverTriggered
currentChipView.requireViewById(R.id.loading).visibility =
- if (showLoading) { View.VISIBLE } else { View.GONE }
+ if (chipState.showLoading()) { View.VISIBLE } else { View.GONE }
// Undo
val undoClickListener: View.OnClickListener? =
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
index cbf646209b007..56f33a8dcf755 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
@@ -61,6 +61,12 @@ class MediaTttSenderService @Inject constructor(
) {
this@MediaTttSenderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
}
+
+ override fun transferToThisDeviceTriggered(
+ mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+ ) {
+ this@MediaTttSenderService.transferToThisDeviceTriggered(mediaInfo)
+ }
}
// TODO(b/203800643): Use the app icon from the media info instead of a fake one.
@@ -109,4 +115,12 @@ class MediaTttSenderService @Inject constructor(
)
controller.displayChip(chipState)
}
+
+ private fun transferToThisDeviceTriggered(mediaInfo: MediaRoute2Info) {
+ val chipState = TransferToThisDeviceTriggered(
+ appIconDrawable = fakeAppIconDrawable,
+ appIconContentDescription = mediaInfo.name.toString()
+ )
+ controller.displayChip(chipState)
+ }
}
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 cf5d47747e4c4..6275c240921a1 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
@@ -145,6 +145,14 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
}
+ @Test
+ fun sender_transferToThisDeviceTriggered_chipDisplayWithCorrectState() {
+ commandRegistry.onShellCommand(pw, getTransferToThisDeviceTriggeredCommand())
+
+ assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
+ verify(mediaSenderService).transferToThisDeviceTriggered(any(), any())
+ }
+
@Test
fun sender_transferSucceeded_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
@@ -202,6 +210,13 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME
)
+ private fun getTransferToThisDeviceTriggeredCommand(): Array =
+ arrayOf(
+ ADD_CHIP_COMMAND_SENDER_TAG,
+ DEVICE_NAME,
+ TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME
+ )
+
private fun getTransferSucceededCommand(): Array =
arrayOf(
ADD_CHIP_COMMAND_SENDER_TAG,
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 b6c1834e68f1a..703f6b0d57458 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
@@ -93,6 +93,20 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
+ @Test
+ fun transferToThisDeviceTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
+ val state = transferToThisDeviceTriggered()
+ controllerSender.displayChip(state)
+
+ val chipView = getChipView()
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
+ assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
+ }
+
@Test
fun transferSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
val state = transferSucceeded()
@@ -218,6 +232,10 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
private fun transferToReceiverTriggered() =
TransferToReceiverTriggered(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
+ /** Helper method providing default parameters to not clutter up the tests. */
+ private fun transferToThisDeviceTriggered() =
+ TransferToThisDeviceTriggered(appIconDrawable, APP_ICON_CONTENT_DESC)
+
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferSucceeded(
undoRunnable: Runnable? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
index 8a491c220592b..1abf49f2c4bf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
@@ -59,6 +59,13 @@ class MediaTttSenderServiceTest : SysuiTestCase() {
assertThat(chipState.getChipTextString(context)).contains(name)
}
+ @Test
+ fun transferToThisDeviceTriggered_controllerTriggeredWithCorrectState() {
+ callback.transferToThisDeviceTriggered(mediaInfo, DeviceInfo("Fake name"))
+
+ verify(controller).displayChip(any())
+ }
+
@Test
fun transferToReceiverTriggered_controllerTriggeredWithCorrectState() {
val name = "Fake name"