diff --git a/packages/SystemUI/res/drawable/media_ttt_chip_background.xml b/packages/SystemUI/res/drawable/media_ttt_chip_background.xml new file mode 100644 index 0000000000000..3abf4d72c5386 --- /dev/null +++ b/packages/SystemUI/res/drawable/media_ttt_chip_background.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/packages/SystemUI/res/layout/media_ttt_chip.xml b/packages/SystemUI/res/layout/media_ttt_chip.xml new file mode 100644 index 0000000000000..5b3590bfb0a79 --- /dev/null +++ b/packages/SystemUI/res/layout/media_ttt_chip.xml @@ -0,0 +1,36 @@ + + + + + + diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a437ae6169995..89c9a359af439 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -976,6 +976,10 @@ 4dp 16sp + + 16dp + 16sp + 35dp 15dp diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 300cb2d38dffd..180c8ae982c0d 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2173,6 +2173,14 @@ Play %1$s from %2$s + + + Undo + + Move closer to play on %1$s + + Playing on %1$s + Inactive, check app 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 237d0771bec20..cf679f070c3a1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -83,6 +83,6 @@ public interface MediaModule { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of(new MediaTttChipController(context, commandRegistry, windowManager)); + return Optional.of(new MediaTttChipController(commandRegistry, context, windowManager)); } } 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 85e5b336575b9..9964d881ff925 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt @@ -19,9 +19,13 @@ package com.android.systemui.media.taptotransfer import android.content.Context import android.graphics.PixelFormat import android.view.Gravity +import android.view.LayoutInflater import android.view.WindowManager +import android.widget.LinearLayout import android.widget.TextView +import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting +import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry @@ -36,8 +40,8 @@ import javax.inject.Inject */ @SysUISingleton class MediaTttChipController @Inject constructor( - context: Context, commandRegistry: CommandRegistry, + private val context: Context, private val windowManager: WindowManager, ) { init { @@ -46,9 +50,9 @@ class MediaTttChipController @Inject constructor( } private val windowLayoutParams = WindowManager.LayoutParams().apply { - width = WindowManager.LayoutParams.MATCH_PARENT - height = WindowManager.LayoutParams.MATCH_PARENT - gravity = Gravity.CENTER_HORIZONTAL + width = WindowManager.LayoutParams.WRAP_CONTENT + height = WindowManager.LayoutParams.WRAP_CONTENT + gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY title = "Media Tap-To-Transfer Chip View" flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE @@ -57,29 +61,61 @@ class MediaTttChipController @Inject constructor( setTrustedOverlay() } - // TODO(b/203800327): Create a layout that matches UX. - private val chipView: TextView = TextView(context).apply { - text = "Media Tap-To-Transfer Chip" - } + /** The chip view currently being displayed. Null if the chip is not being displayed. */ + private var chipView: LinearLayout? = null - private var chipDisplaying: Boolean = false + private fun displayChip(chipType: ChipType, otherDeviceName: String) { + val oldChipView = chipView + if (chipView == null) { + chipView = LayoutInflater + .from(context) + .inflate(R.layout.media_ttt_chip, null) as LinearLayout + } + val currentChipView = chipView!! - private fun addChip() { - if (chipDisplaying) { return } - windowManager.addView(chipView, windowLayoutParams) - chipDisplaying = true + // Text + currentChipView.requireViewById(R.id.text).apply { + text = context.getString(chipType.chipText, otherDeviceName) + } + + if (oldChipView == null) { + windowManager.addView(chipView, windowLayoutParams) + } } private fun removeChip() { - if (!chipDisplaying) { return } + if (chipView == null) { return } windowManager.removeView(chipView) - chipDisplaying = false + chipView = null + } + + @VisibleForTesting + enum class ChipType( + @StringRes internal val chipText: Int + ) { + MOVE_CLOSER_TO_TRANSFER(R.string.media_move_closer_to_transfer), + TRANSFER_INITIATED(R.string.media_transfer_playing), + TRANSFER_SUCCEEDED(R.string.media_transfer_playing), } inner class AddChipCommand : Command { - override fun execute(pw: PrintWriter, args: List) = addChip() + override fun execute(pw: PrintWriter, args: List) { + val chipTypeArg = args[1] + ChipType.values().forEach { + if (it.name == chipTypeArg) { + displayChip(it, otherDeviceName = args[0]) + return + } + } + + pw.println("Chip type must be one of " + + ChipType.values().map { it.name }.reduce { acc, s -> "$acc, $s" }) + } + override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG") + pw.println( + "Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG " + ) } } 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 efb493123a338..9438ee3dace2b 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,14 +16,20 @@ package com.android.systemui.media.taptotransfer +import android.view.View import android.view.WindowManager +import android.widget.LinearLayout +import android.widget.TextView import androidx.test.filters.SmallTest +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.util.mockito.any +import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test +import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.reset @@ -48,7 +54,7 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - mediaTttChipController = MediaTttChipController(context, commandRegistry, windowManager) + mediaTttChipController = MediaTttChipController(commandRegistry, context, windowManager) } @Test(expected = IllegalStateException::class) @@ -71,24 +77,24 @@ class MediaTttChipControllerTest : SysuiTestCase() { @Test fun addChipCommand_chipAdded() { - commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG)) + commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) verify(windowManager).addView(any(), any()) } @Test fun addChipCommand_twice_chipNotAddedTwice() { - commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG)) + commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) reset(windowManager) - commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG)) + commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) verify(windowManager, never()).addView(any(), any()) } @Test fun removeChipCommand_chipRemoved() { // First, add the chip - commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG)) + commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) // Then, remove it commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.REMOVE_CHIP_COMMAND_TAG)) @@ -103,6 +109,55 @@ class MediaTttChipControllerTest : SysuiTestCase() { verify(windowManager, never()).removeView(any()) } + @Test + fun moveCloserToTransfer_chipTextContainsDeviceName() { + commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand()) + + assertThat(getChipText()).contains(DEVICE_NAME) + } + + @Test + fun transferInitiated_chipTextContainsDeviceName() { + commandRegistry.onShellCommand(pw, getTransferInitiatedCommand()) + + assertThat(getChipText()).contains(DEVICE_NAME) + } + + @Test + fun transferSucceeded_chipTextContainsDeviceName() { + commandRegistry.onShellCommand(pw, getTransferSucceededCommand()) + + assertThat(getChipText()).contains(DEVICE_NAME) + } + + private fun getMoveCloserToTransferCommand(): Array = + arrayOf( + MediaTttChipController.ADD_CHIP_COMMAND_TAG, + DEVICE_NAME, + MediaTttChipController.ChipType.MOVE_CLOSER_TO_TRANSFER.name + ) + + private fun getTransferInitiatedCommand(): Array = + arrayOf( + MediaTttChipController.ADD_CHIP_COMMAND_TAG, + DEVICE_NAME, + MediaTttChipController.ChipType.TRANSFER_INITIATED.name + ) + + private fun getTransferSucceededCommand(): Array = + arrayOf( + MediaTttChipController.ADD_CHIP_COMMAND_TAG, + DEVICE_NAME, + MediaTttChipController.ChipType.TRANSFER_SUCCEEDED.name + ) + + private fun getChipText(): String { + val viewCaptor = ArgumentCaptor.forClass(View::class.java) + verify(windowManager).addView(viewCaptor.capture(), any()) + val chipView = viewCaptor.value as LinearLayout + return (chipView.requireViewById(R.id.text) as TextView).text as String + } + class EmptyCommand : Command { override fun execute(pw: PrintWriter, args: List) { } @@ -111,3 +166,5 @@ class MediaTttChipControllerTest : SysuiTestCase() { } } } + +private const val DEVICE_NAME = "My Tablet"