From b38287430bf6db360652f19ac08256b0dff9c16d Mon Sep 17 00:00:00 2001 From: Caitlin Cassidy Date: Tue, 30 Nov 2021 20:10:33 +0000 Subject: [PATCH] [Media TTT] Add three different states for the media TTT chip and allow each of them to be displayed with the correct text via adb. For example, to display the "Move closer to..." chip state for a device named "Tablet", run the adb command `adb shell cmd statusbar media-ttt-chip-add Tablet MOVE_CLOSER_TO_TRANSFER`. App icon, loading icon, and undo button coming in future CLs. Screenshots of the chip with loading and undo are in https://b.corp.google.com/issues/203800327#comment3. Bug: 203800327 Test: `adb shell cmd statusbar media-ttt-chip-add Tablet MOVE_CLOSER_TO_TRANSFER` shows a chip with the text "Move closer to play on Tablet" Test: `adb shell cmd statusbar media-ttt-chip-add Tablet TRANSFER_INITIATED` shows a chip with the text "Playing on Tablet" (will have a loading icon in future) Test: `adb shell cmd statusbar media-ttt-chip-add Tablet TRANSFER_SUCCEEDED` shows a chip with the text "Playing on Tablet" (will have an undo button in future) Test: MediaTttChipControllerTest Change-Id: Icc8f5561fff6d862c4eedaa5b454c35aa827bcaf --- .../drawable/media_ttt_chip_background.xml | 22 ++++++ .../SystemUI/res/layout/media_ttt_chip.xml | 36 ++++++++++ packages/SystemUI/res/values/dimens.xml | 4 ++ packages/SystemUI/res/values/strings.xml | 8 +++ .../systemui/media/dagger/MediaModule.java | 2 +- .../taptotransfer/MediaTttChipController.kt | 70 ++++++++++++++----- .../MediaTttChipControllerTest.kt | 67 ++++++++++++++++-- 7 files changed, 186 insertions(+), 23 deletions(-) create mode 100644 packages/SystemUI/res/drawable/media_ttt_chip_background.xml create mode 100644 packages/SystemUI/res/layout/media_ttt_chip.xml 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"