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

Commit 07d03204 authored by Beth Thibodeau's avatar Beth Thibodeau Committed by Android (Google) Code Review
Browse files

Merge "Update media3 button tests and error handling" into main

parents 793c88f2 34ef2450
Loading
Loading
Loading
Loading
+2 −7
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.media.session.MediaSession
import android.os.Bundle
import android.os.Handler
import android.os.looper
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.media.utils.MediaConstants
import androidx.media3.common.Player
@@ -69,10 +68,9 @@ class Media3ActionFactoryTest : SysuiTestCase() {
    private val testScope = kosmos.testScope
    private val controllerFactory = kosmos.fakeMediaControllerFactory
    private val tokenFactory = kosmos.fakeSessionTokenFactory
    private lateinit var testableLooper: TestableLooper

    private var commandCaptor = argumentCaptor<SessionCommand>()
    private var runnableCaptor = argumentCaptor<Runnable>()
    private val commandCaptor = argumentCaptor<SessionCommand>()
    private val runnableCaptor = argumentCaptor<Runnable>()

    private val legacyToken = MediaSession.Token(1, null)
    private val token = mock<SessionToken>()
@@ -97,8 +95,6 @@ class Media3ActionFactoryTest : SysuiTestCase() {

    @Before
    fun setup() {
        testableLooper = TestableLooper.get(this)

        underTest =
            Media3ActionFactory(
                context,
@@ -246,7 +242,6 @@ class Media3ActionFactoryTest : SysuiTestCase() {
            assertThat(actions.custom0!!.contentDescription).isEqualTo("$CUSTOM_ACTION_NAME 2")
            actions.custom0!!.action!!.run()
            runCurrent()
            testableLooper.processAllMessages()
            verify(media3Controller).sendCustomCommand(commandCaptor.capture(), any<Bundle>())
            assertThat(commandCaptor.lastValue.customAction).isEqualTo("$CUSTOM_ACTION_COMMAND 2")
            verify(media3Controller).release()
+2 −2
Original line number Diff line number Diff line
@@ -82,7 +82,7 @@ class MediaDataLoaderTest : SysuiTestCase() {
    private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
    private val mediaFlags = kosmos.mediaFlags
    private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
    private val media3ActionFactory = kosmos.media3ActionFactory
    private lateinit var media3ActionFactory: Media3ActionFactory
    private val session = MediaSession(context, "MediaDataLoaderTestSession")
    private val metadataBuilder =
        MediaMetadata.Builder().apply {
@@ -94,6 +94,7 @@ class MediaDataLoaderTest : SysuiTestCase() {

    @Before
    fun setUp() {
        media3ActionFactory = kosmos.media3ActionFactory
        mediaControllerFactory.setControllerForToken(session.sessionToken, mediaController)
        whenever(mediaController.sessionToken).thenReturn(session.sessionToken)
        whenever(mediaController.metadata).then { metadataBuilder.build() }
@@ -311,7 +312,6 @@ class MediaDataLoaderTest : SysuiTestCase() {
                    }
                    build()
                }

            val result = underTest.loadMediaData(KEY, mediaNotification)

            assertThat(result).isNotNull()
+45 −32
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import androidx.annotation.WorkerThread
import androidx.media.utils.MediaConstants
import androidx.media3.common.Player
import androidx.media3.session.CommandButton
import androidx.media3.session.MediaController as Media3Controller
import androidx.media3.session.SessionCommand
import androidx.media3.session.SessionToken
import com.android.systemui.dagger.SysUISingleton
@@ -82,10 +83,19 @@ constructor(
        // Build button info
        val buttons = suspendCancellableCoroutine { continuation ->
            // Media3Controller methods must always be called from a specific looper
            handler.post {
            val runnable = Runnable {
                try {
                    val result = getMedia3Actions(packageName, m3controller, token)
                m3controller.release()
                    continuation.resumeWith(Result.success(result))
                } finally {
                    m3controller.release()
                }
            }
            handler.post(runnable)
            continuation.invokeOnCancellation {
                // Ensure controller is released, even if loading was cancelled partway through
                handler.post(m3controller::release)
                handler.removeCallbacks(runnable)
            }
        }
        return buttons
@@ -95,7 +105,7 @@ constructor(
    @WorkerThread
    private fun getMedia3Actions(
        packageName: String,
        m3controller: androidx.media3.session.MediaController,
        m3controller: Media3Controller,
        token: SessionToken,
    ): MediaButton? {
        Assert.isNotMainThread()
@@ -197,7 +207,7 @@ constructor(
     * @return A [MediaAction] representing the first supported command, or null if not supported
     */
    private fun getStandardAction(
        controller: androidx.media3.session.MediaController,
        controller: Media3Controller,
        token: SessionToken,
        vararg commands: @Player.Command Int,
    ): MediaAction? {
@@ -304,6 +314,7 @@ constructor(
        bgScope.launch {
            val controller = controllerFactory.create(token, looper)
            handler.post {
                try {
                    when (command) {
                        Player.COMMAND_PLAY_PAUSE -> {
                            if (controller.isPlaying) controller.pause() else controller.play()
@@ -316,26 +327,28 @@ constructor(
                        Player.COMMAND_SEEK_TO_NEXT -> controller.seekToNext()
                        Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM -> controller.seekToNextMediaItem()
                        Player.COMMAND_INVALID -> {
                        if (
                            customAction != null &&
                                customAction!!.sessionCommand != null &&
                                controller.isSessionCommandAvailable(
                                    customAction!!.sessionCommand!!
                                )
                        ) {
                            if (customAction?.sessionCommand != null) {
                                val sessionCommand = customAction.sessionCommand!!
                                if (controller.isSessionCommandAvailable(sessionCommand)) {
                                    controller.sendCustomCommand(
                                customAction!!.sessionCommand!!,
                                customAction!!.extras,
                                        sessionCommand,
                                        customAction.extras,
                                    )
                                } else {
                                    logger.logMedia3UnsupportedCommand(
                                        "$sessionCommand, action $customAction"
                                    )
                                }
                            } else {
                                logger.logMedia3UnsupportedCommand("$command, action $customAction")
                            }
                        }

                        else -> logger.logMedia3UnsupportedCommand(command.toString())
                    }
                } finally {
                    controller.release()
                }
            }
        }
    }
}
+6 −1
Original line number Diff line number Diff line
@@ -35,7 +35,12 @@ open class MediaControllerFactory @Inject constructor(private val context: Conte
        return MediaController(context, token)
    }

    /** Creates a new [Media3Controller] from a [SessionToken] */
    /**
     * Creates a new [Media3Controller] from the media3 [SessionToken].
     *
     * @param token The token for the session
     * @param looper The looper that will be used for this controller's operations
     */
    open suspend fun create(token: SessionToken, looper: Looper): Media3Controller {
        return Media3Controller.Builder(context, token)
            .setApplicationLooper(looper)
+42 −1
Original line number Diff line number Diff line
@@ -16,7 +16,48 @@

package com.android.systemui.media.controls.domain.pipeline

import android.content.applicationContext
import android.os.Bundle
import android.os.Handler
import android.os.looper
import androidx.media3.session.CommandButton
import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken
import com.android.systemui.Flags
import com.android.systemui.graphics.imageLoader
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.shared.mediaLogger
import com.android.systemui.media.controls.util.fakeMediaControllerFactory
import com.android.systemui.media.controls.util.fakeSessionTokenFactory
import com.google.common.collect.ImmutableList
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

var Kosmos.media3ActionFactory: Media3ActionFactory by Kosmos.Fixture { mock {} }
/**
 * Set up fake [Media3ActionFactory]. Note that tests using this fake will need to be
 * annotated @RunWithLooper
 */
var Kosmos.media3ActionFactory: Media3ActionFactory by
    Kosmos.Fixture {
        if (Flags.mediaControlsButtonMedia3()) {
            val customLayout = ImmutableList.of<CommandButton>()
            val media3Controller =
                mock<MediaController>().also {
                    whenever(it.customLayout).thenReturn(customLayout)
                    whenever(it.sessionExtras).thenReturn(Bundle())
                }
            fakeMediaControllerFactory.setMedia3Controller(media3Controller)
            fakeSessionTokenFactory.setMedia3SessionToken(mock<SessionToken>())
        }
        Media3ActionFactory(
            context = applicationContext,
            imageLoader = imageLoader,
            controllerFactory = fakeMediaControllerFactory,
            tokenFactory = fakeSessionTokenFactory,
            logger = mediaLogger,
            looper = looper,
            handler = Handler(looper),
            bgScope = testScope,
        )
    }