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

Commit 63cc7448 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[Media] Background loading improvements.

Changes the background to a snapshot state. This simplifies the compose
logic so it doesn't need to kick off a coroutine to load the background
and can more easily watch for new background images as they come in.

Also adds a crossfade effect between the unloaded/old and new art as it
comes in.

Bug: 397989775
Test: manually verified in the compose gallery app
Flag: EXEMPT the code is not yet used
Change-Id: Ieacc6e2f2656485fa2dbeee9cad77450a0384546
parent f3490d1e
Loading
Loading
Loading
Loading
+0 −9
Original line number Original line Diff line number Diff line
@@ -16,7 +16,6 @@


package com.android.systemui.media.remedia.domain.interactor
package com.android.systemui.media.remedia.domain.interactor


import android.graphics.Bitmap
import com.android.systemui.media.remedia.domain.model.MediaSessionModel
import com.android.systemui.media.remedia.domain.model.MediaSessionModel


/**
/**
@@ -28,14 +27,6 @@ interface MediaInteractor {
    /** The list of sessions. Needs to be backed by a compose snapshot state. */
    /** The list of sessions. Needs to be backed by a compose snapshot state. */
    val sessions: List<MediaSessionModel>
    val sessions: List<MediaSessionModel>


    /**
     * Loads, on a background thread, the media art for a session with the given [sessionToken].
     *
     * This will be called on the main thread. It's the responsibility of the implementation to move
     * its work off the main thread.
     */
    suspend fun loadArt(sessionToken: Any): Bitmap

    /** Seek to [to], in milliseconds on the media session with the given [sessionKey]. */
    /** Seek to [to], in milliseconds on the media session with the given [sessionKey]. */
    fun seek(sessionKey: Any, to: Long)
    fun seek(sessionKey: Any, to: Long)


+3 −0
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.media.remedia.domain.model
package com.android.systemui.media.remedia.domain.model


import androidx.compose.runtime.Stable
import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.ImageBitmap
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.media.remedia.shared.model.MediaCardActionButtonLayout
import com.android.systemui.media.remedia.shared.model.MediaCardActionButtonLayout
import com.android.systemui.media.remedia.shared.model.MediaSessionState
import com.android.systemui.media.remedia.shared.model.MediaSessionState
@@ -31,6 +32,8 @@ interface MediaSessionModel {


    val appIcon: Icon
    val appIcon: Icon


    val background: ImageBitmap?

    val title: String
    val title: String


    val subtitle: String
    val subtitle: String
+28 −38
Original line number Original line Diff line number Diff line
@@ -46,6 +46,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.padding
@@ -68,9 +69,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clip
@@ -257,7 +256,7 @@ private fun Card(


    Box(modifier) {
    Box(modifier) {
        if (stlState.currentScene != Media.Scenes.Compact) {
        if (stlState.currentScene != Media.Scenes.Compact) {
            CardBackground(imageLoader = viewModel.artLoader, modifier = Modifier.matchParentSize())
            CardBackground(image = viewModel.background, modifier = Modifier.matchParentSize())
        }
        }


        key(stlState) {
        key(stlState) {
@@ -541,18 +540,18 @@ private fun ContentScope.CompactCardForeground(


/** Renders the background of a card, loading the artwork and showing an overlay on top of it. */
/** Renders the background of a card, loading the artwork and showing an overlay on top of it. */
@Composable
@Composable
private fun CardBackground(imageLoader: suspend () -> ImageBitmap, modifier: Modifier = Modifier) {
private fun CardBackground(image: ImageBitmap?, modifier: Modifier = Modifier) {
    var image: ImageBitmap? by remember { mutableStateOf(null) }
    Crossfade(targetState = image, modifier = modifier) { imageOrNull ->
    LaunchedEffect(imageLoader) {
        if (imageOrNull != null) {
        image = null
            // Loaded art.
        image = imageLoader()
    }

            val gradientBaseColor = MaterialTheme.colorScheme.onSurface
            val gradientBaseColor = MaterialTheme.colorScheme.onSurface
    Box(
            Image(
                bitmap = imageOrNull,
                contentDescription = null,
                contentScale = ContentScale.Crop,
                modifier =
                modifier =
            modifier.drawWithContent {
                    Modifier.fillMaxSize().drawWithContent {
                // Draw the content of the box (loaded art or placeholder).
                        // Draw the content (loaded art).
                        drawContent()
                        drawContent()


                        if (image != null) {
                        if (image != null) {
@@ -567,20 +566,11 @@ private fun CardBackground(imageLoader: suspend () -> ImageBitmap, modifier: Mod
                                    )
                                    )
                            )
                            )
                        }
                        }
            }
                    },
    ) {
        image?.let { loadedImage ->
            // Loaded art.
            Image(
                bitmap = loadedImage,
                contentDescription = null,
                contentScale = ContentScale.Crop,
                modifier = Modifier.matchParentSize(),
            )
            )
        }
        } else {
            ?: run {
            // Placeholder.
            // Placeholder.
                Box(Modifier.background(MaterialTheme.colorScheme.onSurface).matchParentSize())
            Box(Modifier.background(MaterialTheme.colorScheme.onSurface).fillMaxSize())
        }
        }
    }
    }
}
}
+1 −6
Original line number Original line Diff line number Diff line
@@ -32,12 +32,7 @@ interface MediaCardViewModel {


    val icon: Icon
    val icon: Icon


    /**
    val background: ImageBitmap?
     * A callback to load the artwork for the media shown on this card. This callback will be
     * invoked on the main thread, it's up to the implementation to move the loading off the main
     * thread.
     */
    val artLoader: suspend () -> ImageBitmap


    val title: String
    val title: String


+4 −2
Original line number Original line Diff line number Diff line
@@ -23,7 +23,7 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.ImageBitmap
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.ExclusiveActivatable
@@ -63,7 +63,9 @@ constructor(
            object : MediaCardViewModel {
            object : MediaCardViewModel {
                override val key = session.key
                override val key = session.key
                override val icon = session.appIcon
                override val icon = session.appIcon
                override val artLoader = suspend { interactor.loadArt(session.key).asImageBitmap() }
                override val background: ImageBitmap?
                    get() = session.background

                override val title = session.title
                override val title = session.title
                override val subtitle = session.subtitle
                override val subtitle = session.subtitle
                override val actionButtonLayout = session.actionButtonLayout
                override val actionButtonLayout = session.actionButtonLayout