Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a88d27a authored by Caitlin Cassidy's avatar Caitlin Cassidy Committed by Android (Google) Code Review
Browse files

Merge "[Media TTT] Update the receiver view controller and the command line...

Merge "[Media TTT] Update the receiver view controller and the command line helper to handle the receiver @SystemApi states."
parents a3f857f1 ff004423
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -115,12 +115,14 @@ public interface MediaModule {
    @SysUISingleton
    static Optional<MediaTttChipControllerReceiver> 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. */
+29 −40
Original line number Diff line number Diff line
@@ -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<String>) {
            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
                    )
        }
        override fun help(pw: PrintWriter) {
            pw.println("Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_RECEIVER_TAG")
                FAR_FROM_SENDER_STATE ->
                    statusBarManager.updateMediaTapToTransferReceiverDisplay(
                        StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
                        routeInfo
                    )
                else ->
                    pw.println("Invalid command name $commandName")
            }
        }

    /** A command to REMOVE the media ttt chip on the RECEIVER device. */
    inner class RemoveChipCommandReceiver : Command {
        override fun execute(pw: PrintWriter, args: List<String>) {
            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 <chipState>")
        }
    }
}
@@ -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")
+42 −0
Original line number Diff line number Diff line
@@ -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<ChipStateReceiver>(
    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"
+18 −25
Original line number Diff line number Diff line
@@ -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
        // 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
    fun constructor_receiverCommandAlreadyRegistered() {
        // Since creating the chip controller should automatically register the receiver 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<String> =
        arrayOf(SENDER_COMMAND, DEVICE_NAME, displayState)

    private fun getReceiverCommand(displayState: String): Array<String> =
        arrayOf(RECEIVER_COMMAND, displayState)

    class EmptyCommand : Command {
        override fun execute(pw: PrintWriter, args: List<String>) {
        }
+56 −3
Original line number Diff line number Diff line
@@ -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<ImageView>(R.id.app_icon)
}

private const val ROUTE_NAME = "Test name"
private val routeInfo = MediaRoute2Info.Builder("id", ROUTE_NAME)
    .addFeature("feature")
    .build()