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 663877ce13884..f8b34f9769e42 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -115,12 +115,14 @@ public interface MediaModule { @SysUISingleton static Optional providesMediaTttChipControllerReceiver( MediaTttFlags mediaTttFlags, + CommandQueue commandQueue, Context context, WindowManager windowManager) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } - return Optional.of(new MediaTttChipControllerReceiver(context, windowManager)); + return Optional.of( + new MediaTttChipControllerReceiver(commandQueue, context, windowManager)); } /** */ @@ -130,17 +132,12 @@ public interface MediaModule { MediaTttFlags mediaTttFlags, CommandRegistry commandRegistry, Context context, - @Main Executor mainExecutor, - MediaTttChipControllerReceiver mediaTttChipControllerReceiver) { + @Main Executor mainExecutor) { if (!mediaTttFlags.isMediaTttEnabled()) { return Optional.empty(); } return Optional.of( - new MediaTttCommandLineHelper( - commandRegistry, - context, - mainExecutor, - mediaTttChipControllerReceiver)); + new MediaTttCommandLineHelper(commandRegistry, context, mainExecutor)); } /** Inject into NearbyMediaDevicesService. */ 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 1ea21ce7728b6..26f31cd11704f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt @@ -18,16 +18,11 @@ package com.android.systemui.media.taptotransfer import android.app.StatusBarManager import android.content.Context -import android.graphics.Color -import android.graphics.drawable.Icon import android.media.MediaRoute2Info 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.media.taptotransfer.receiver.MediaTttChipControllerReceiver -import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast import com.android.systemui.media.taptotransfer.sender.TransferFailed @@ -49,14 +44,8 @@ import javax.inject.Inject class MediaTttCommandLineHelper @Inject constructor( commandRegistry: CommandRegistry, private val context: Context, - @Main private val mainExecutor: Executor, - private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver, + @Main private val mainExecutor: Executor ) { - private val appIconDrawable = - Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also { - it.setTint(Color.YELLOW) - } - /** * A map from a display state string typed in the command line to the display int it represents. */ @@ -81,10 +70,7 @@ class MediaTttCommandLineHelper @Inject constructor( init { commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() } - commandRegistry.registerCommand( - ADD_CHIP_COMMAND_RECEIVER_TAG) { AddChipCommandReceiver() } - commandRegistry.registerCommand( - REMOVE_CHIP_COMMAND_RECEIVER_TAG) { RemoveChipCommandReceiver() } + commandRegistry.registerCommand(RECEIVER_COMMAND) { ReceiverCommand() } } /** All commands for the sender device. */ @@ -94,10 +80,11 @@ class MediaTttCommandLineHelper @Inject constructor( .addFeature("feature") .build() + val commandName = args[1] @StatusBarManager.MediaTransferSenderState - val displayState = stateStringToStateInt[args[1]] + val displayState = stateStringToStateInt[commandName] if (displayState == null) { - pw.println("Invalid command name") + pw.println("Invalid command name $commandName") return } @@ -145,27 +132,29 @@ class MediaTttCommandLineHelper @Inject constructor( } } - // TODO(b/216318437): Migrate the receiver callbacks to StatusBarManager. - - /** A command to DISPLAY the media ttt chip on the RECEIVER device. */ - inner class AddChipCommandReceiver : Command { + /** All commands for the receiver device. */ + inner class ReceiverCommand : Command { override fun execute(pw: PrintWriter, args: List) { - mediaTttChipControllerReceiver.displayChip( - ChipStateReceiver(appIconDrawable, APP_ICON_CONTENT_DESCRIPTION) - ) + val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE) + as StatusBarManager + when(val commandName = args[0]) { + CLOSE_TO_SENDER_STATE -> + statusBarManager.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, + routeInfo + ) + FAR_FROM_SENDER_STATE -> + statusBarManager.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, + routeInfo + ) + else -> + pw.println("Invalid command name $commandName") + } } - override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_RECEIVER_TAG") - } - } - /** A command to REMOVE the media ttt chip on the RECEIVER device. */ - inner class RemoveChipCommandReceiver : Command { - override fun execute(pw: PrintWriter, args: List) { - mediaTttChipControllerReceiver.removeChip() - } override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_RECEIVER_TAG") + pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND ") } } } @@ -173,13 +162,13 @@ class MediaTttCommandLineHelper @Inject constructor( @VisibleForTesting const val SENDER_COMMAND = "media-ttt-chip-sender" @VisibleForTesting -const val ADD_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-add-receiver" +const val RECEIVER_COMMAND = "media-ttt-chip-receiver" @VisibleForTesting -const val REMOVE_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-remove-receiver" +const val FAR_FROM_RECEIVER_STATE = "FarFromReceiver" @VisibleForTesting -val FAR_FROM_RECEIVER_STATE = "FarFromReceiver" - -private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon" +const val CLOSE_TO_SENDER_STATE = "CloseToSender" +@VisibleForTesting +const val FAR_FROM_SENDER_STATE = "FarFromSender" private const val CLI_TAG = "MediaTransferCli" private val routeInfo = MediaRoute2Info.Builder("id", "Test Name") diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 17809548d1fdc..2d3ca5fdb6b82 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -16,12 +16,18 @@ package com.android.systemui.media.taptotransfer.receiver +import android.app.StatusBarManager import android.content.Context +import android.graphics.Color +import android.graphics.drawable.Icon +import android.media.MediaRoute2Info +import android.util.Log import android.view.ViewGroup import android.view.WindowManager import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon +import com.android.systemui.statusbar.CommandQueue import javax.inject.Inject /** @@ -31,13 +37,49 @@ import javax.inject.Inject */ @SysUISingleton class MediaTttChipControllerReceiver @Inject constructor( + commandQueue: CommandQueue, context: Context, windowManager: WindowManager, ) : MediaTttChipControllerCommon( context, windowManager, R.layout.media_ttt_chip_receiver ) { + // TODO(b/216141279): Use app icon from media route info instead of this fake one. + private val fakeAppIconDrawable = + Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also { + it.setTint(Color.YELLOW) + } + + private val commandQueueCallbacks = object : CommandQueue.Callbacks { + override fun updateMediaTapToTransferReceiverDisplay( + @StatusBarManager.MediaTransferReceiverState displayState: Int, + routeInfo: MediaRoute2Info + ) { + this@MediaTttChipControllerReceiver.updateMediaTapToTransferReceiverDisplay( + displayState, routeInfo + ) + } + } + + init { + commandQueue.addCallback(commandQueueCallbacks) + } + + private fun updateMediaTapToTransferReceiverDisplay( + @StatusBarManager.MediaTransferReceiverState displayState: Int, + routeInfo: MediaRoute2Info + ) { + when(displayState) { + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER -> + displayChip(ChipStateReceiver(fakeAppIconDrawable, routeInfo.name.toString())) + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER -> removeChip() + else -> + Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState") + } + } override fun updateChipView(chipState: ChipStateReceiver, currentChipView: ViewGroup) { setIcon(chipState, currentChipView) } } + +private const val RECEIVER_TAG = "MediaTapToTransferReceiver" 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 c3d8d245baed5..cb05d03026985 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 @@ -21,8 +21,6 @@ import android.content.Context import android.media.MediaRoute2Info import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver -import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast import com.android.systemui.media.taptotransfer.sender.TransferFailed @@ -62,8 +60,6 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { @Mock private lateinit var statusBarManager: StatusBarManager - @Mock - private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver @Before fun setUp() { @@ -74,7 +70,6 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { commandRegistry, context, FakeExecutor(FakeSystemClock()), - mediaTttChipControllerReceiver, ) } @@ -86,21 +81,10 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { } @Test(expected = IllegalStateException::class) - fun constructor_addReceiverCommandAlreadyRegistered() { - // Since creating the chip controller should automatically register the add command, it + fun constructor_receiverCommandAlreadyRegistered() { + // Since creating the chip controller should automatically register the receiver command, it // should throw when registering it again. - commandRegistry.registerCommand( - ADD_CHIP_COMMAND_RECEIVER_TAG - ) { EmptyCommand() } - } - - @Test(expected = IllegalStateException::class) - fun constructor_removeReceiverCommandAlreadyRegistered() { - // Since creating the chip controller should automatically register the remove command, it - // should throw when registering it again. - commandRegistry.registerCommand( - REMOVE_CHIP_COMMAND_RECEIVER_TAG - ) { EmptyCommand() } + commandRegistry.registerCommand(RECEIVER_COMMAND) { EmptyCommand() } } @Test @@ -214,22 +198,31 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() { } @Test - fun receiver_addCommand_chipAdded() { - commandRegistry.onShellCommand(pw, arrayOf(ADD_CHIP_COMMAND_RECEIVER_TAG)) + fun receiver_closeToSender_serviceCallbackCalled() { + commandRegistry.onShellCommand(pw, getReceiverCommand(CLOSE_TO_SENDER_STATE)) - verify(mediaTttChipControllerReceiver).displayChip(any(ChipStateReceiver::class.java)) + verify(statusBarManager).updateMediaTapToTransferReceiverDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER), + any() + ) } @Test - fun receiver_removeCommand_chipRemoved() { - commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_RECEIVER_TAG)) + fun receiver_farFromSender_serviceCallbackCalled() { + commandRegistry.onShellCommand(pw, getReceiverCommand(FAR_FROM_SENDER_STATE)) - verify(mediaTttChipControllerReceiver).removeChip() + verify(statusBarManager).updateMediaTapToTransferReceiverDisplay( + eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER), + any() + ) } private fun getSenderCommand(displayState: String): Array = arrayOf(SENDER_COMMAND, DEVICE_NAME, displayState) + private fun getReceiverCommand(displayState: String): Array = + arrayOf(RECEIVER_COMMAND, displayState) + class EmptyCommand : Command { override fun execute(pw: PrintWriter, args: List) { } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt index 1d1265b16e63d..fce495470ab0e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt @@ -16,7 +16,9 @@ package com.android.systemui.media.taptotransfer.receiver +import android.app.StatusBarManager import android.graphics.drawable.Icon +import android.media.MediaRoute2Info import android.view.View import android.view.ViewGroup import android.view.WindowManager @@ -24,6 +26,7 @@ import android.widget.ImageView import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -31,7 +34,8 @@ import org.junit.Ignore import org.junit.Test import org.mockito.ArgumentCaptor import org.mockito.Mock -import org.mockito.Mockito +import org.mockito.Mockito.never +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @@ -41,11 +45,55 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { @Mock private lateinit var windowManager: WindowManager + @Mock + private lateinit var commandQueue: CommandQueue + private lateinit var commandQueueCallback: CommandQueue.Callbacks @Before fun setUp() { MockitoAnnotations.initMocks(this) - controllerReceiver = MediaTttChipControllerReceiver(context, windowManager) + controllerReceiver = MediaTttChipControllerReceiver(commandQueue, context, windowManager) + + val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) + verify(commandQueue).addCallback(callbackCaptor.capture()) + commandQueueCallback = callbackCaptor.value!! + } + + @Test + fun commandQueueCallback_closeToSender_triggersChip() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, + routeInfo + ) + + assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(ROUTE_NAME) + } + + @Test + fun commandQueueCallback_farFromSender_noChipShown() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, + routeInfo + ) + + verify(windowManager, never()).addView(any(), any()) + } + + @Test + fun commandQueueCallback_closeThenFar_chipShownThenHidden() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, + routeInfo + ) + + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, + routeInfo + ) + + val viewCaptor = ArgumentCaptor.forClass(View::class.java) + verify(windowManager).addView(viewCaptor.capture(), any()) + verify(windowManager).removeView(viewCaptor.value) } @Test @@ -61,9 +109,14 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { private fun getChipView(): ViewGroup { val viewCaptor = ArgumentCaptor.forClass(View::class.java) - Mockito.verify(windowManager).addView(viewCaptor.capture(), any()) + verify(windowManager).addView(viewCaptor.capture(), any()) return viewCaptor.value as ViewGroup } private fun ViewGroup.getAppIconView() = this.requireViewById(R.id.app_icon) } + +private const val ROUTE_NAME = "Test name" +private val routeInfo = MediaRoute2Info.Builder("id", ROUTE_NAME) + .addFeature("feature") + .build()