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

Commit a483cd32 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[Flexiglass] move getWallpaperColor to Viewbinder" into main

parents da34eb9c dae5dcfe
Loading
Loading
Loading
Loading
+27 −14
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import com.android.systemui.media.controls.ui.viewmodel.MediaOutputSwitcherViewM
import com.android.systemui.media.controls.ui.viewmodel.MediaPlayerViewModel
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
@@ -66,6 +67,8 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

private const val TAG = "MediaControlViewBinder"

object MediaControlViewBinder {

    fun bind(
@@ -234,9 +237,6 @@ object MediaControlViewBinder {
                }
            }
            setDismissible(model.isDismissEnabled)
            setTextPrimaryColor(model.textPrimaryColor)
            setAccentPrimaryColor(model.accentPrimaryColor)
            setSurfaceColor(model.surfaceColor)
        }
    }

@@ -423,22 +423,37 @@ object MediaControlViewBinder {
        val width = viewController.widthInSceneContainerPx
        val height = viewController.heightInSceneContainerPx
        withContext(backgroundDispatcher) {
            val wallpaperColors =
                MediaArtworkHelper.getWallpaperColor(
                    viewHolder.albumView.context,
                    backgroundDispatcher,
                    viewModel.backgroundCover,
                    TAG,
                )
            val isArtworkBound = wallpaperColors != null
            val scheme =
                wallpaperColors?.let { ColorScheme(it, true, Style.CONTENT) }
                    ?: let {
                        if (viewModel.launcherIcon is Icon.Loaded) {
                            MediaArtworkHelper.getColorScheme(viewModel.launcherIcon.drawable, TAG)
                        } else {
                            null
                        }
                    }
            val artwork =
                if (viewModel.shouldAddGradient) {
                wallpaperColors?.let {
                    addGradientToPlayerAlbum(
                        viewHolder.albumView.context,
                        viewModel.backgroundCover!!,
                        viewModel.colorScheme,
                        scheme!!,
                        width,
                        height,
                    )
                } else {
                    ColorDrawable(Color.TRANSPARENT)
                }
                } ?: ColorDrawable(Color.TRANSPARENT)
            withContext(mainDispatcher) {
                // Transition Colors to current color scheme
                val colorSchemeChanged =
                    viewController.colorSchemeTransition.updateColorScheme(viewModel.colorScheme)
                    viewController.colorSchemeTransition.updateColorScheme(scheme)
                val albumView = viewHolder.albumView

                // Set up width of album view constraint.
@@ -449,7 +464,7 @@ object MediaControlViewBinder {
                if (
                    updateBackground ||
                        colorSchemeChanged ||
                        (!viewController.isArtworkBound && viewModel.shouldAddGradient)
                        (!viewController.isArtworkBound && isArtworkBound)
                ) {
                    viewController.prevArtwork?.let {
                        // Since we throw away the last transition, this will pop if your
@@ -464,12 +479,10 @@ object MediaControlViewBinder {
                        transitionDrawable.isCrossFadeEnabled = true

                        albumView.setImageDrawable(transitionDrawable)
                        transitionDrawable.startTransition(
                            if (viewModel.shouldAddGradient) 333 else 80
                        )
                        transitionDrawable.startTransition(if (isArtworkBound) 333 else 80)
                    } ?: albumView.setImageDrawable(artwork)
                }
                viewController.isArtworkBound = viewModel.shouldAddGradient
                viewController.isArtworkBound = isArtworkBound
                viewController.prevArtwork = artwork

                if (viewModel.useGrayColorFilter) {
+197 −29
Original line number Diff line number Diff line
@@ -16,13 +16,24 @@

package com.android.systemui.media.controls.ui.binder

import android.app.WallpaperColors
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
import android.os.Trace
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
@@ -30,17 +41,29 @@ import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.animation.Expandable
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
import com.android.systemui.media.controls.ui.animation.textSecondaryFromScheme
import com.android.systemui.media.controls.ui.controller.MediaViewController
import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.ui.viewmodel.MediaRecViewModel
import com.android.systemui.media.controls.ui.viewmodel.MediaRecommendationsViewModel
import com.android.systemui.media.controls.ui.viewmodel.MediaRecsCardViewModel
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.util.animation.TransitionLayout
import kotlin.math.min
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

private const val TAG = "MediaRecommendationsViewBinder"
private const val MEDIA_REC_SCRIM_START_ALPHA = 0.15f
private const val MEDIA_REC_SCRIM_END_ALPHA = 1.0f

object MediaRecommendationsViewBinder {

@@ -50,6 +73,8 @@ object MediaRecommendationsViewBinder {
        viewModel: MediaRecommendationsViewModel,
        mediaViewController: MediaViewController,
        falsingManager: FalsingManager,
        backgroundDispatcher: CoroutineDispatcher,
        mainDispatcher: CoroutineDispatcher,
    ) {
        mediaViewController.recsConfigurationChangeListener = this::updateRecommendationsVisibility
        val cardView = viewHolder.recommendations
@@ -59,7 +84,14 @@ object MediaRecommendationsViewBinder {
                    launch {
                        viewModel.mediaRecsCard.collectLatest { viewModel ->
                            viewModel?.let {
                                bindRecsCard(viewHolder, it, mediaViewController, falsingManager)
                                bindRecsCard(
                                    viewHolder,
                                    it,
                                    mediaViewController,
                                    falsingManager,
                                    backgroundDispatcher,
                                    mainDispatcher,
                                )
                            }
                        }
                    }
@@ -68,19 +100,19 @@ object MediaRecommendationsViewBinder {
        }
    }

    fun bindRecsCard(
    suspend fun bindRecsCard(
        viewHolder: RecommendationViewHolder,
        viewModel: MediaRecsCardViewModel,
        viewController: MediaViewController,
        falsingManager: FalsingManager,
        backgroundDispatcher: CoroutineDispatcher,
        mainDispatcher: CoroutineDispatcher,
    ) {
        // Set up media control location and its listener.
        viewModel.onLocationChanged(viewController.currentEndLocation)
        viewController.locationChangeListener = viewModel.onLocationChanged

        // Bind main card.
        viewHolder.cardTitle.setTextColor(viewModel.cardTitleColor)
        viewHolder.recommendations.backgroundTintList = ColorStateList.valueOf(viewModel.cardColor)
        viewHolder.recommendations.contentDescription =
            viewModel.contentDescription.invoke(viewController.isGutsVisible)

@@ -100,8 +132,17 @@ object MediaRecommendationsViewBinder {
            return@setOnLongClickListener true
        }

        // Bind colors
        val appIcon = viewModel.mediaRecs.first().appIcon
        fetchAndUpdateColors(viewHolder, appIcon, backgroundDispatcher, mainDispatcher)
        // Bind all recommendations.
        bindRecommendationsList(viewHolder, viewModel.mediaRecs, falsingManager)
        bindRecommendationsList(
            viewHolder,
            viewModel.mediaRecs,
            falsingManager,
            backgroundDispatcher,
            mainDispatcher,
        )
        updateRecommendationsVisibility(viewController, viewHolder.recommendations)

        // Set visibility of recommendations.
@@ -153,26 +194,21 @@ object MediaRecommendationsViewBinder {
        }

        gutsViewHolder.setDismissible(gutsViewModel.isDismissEnabled)
        gutsViewHolder.setTextPrimaryColor(gutsViewModel.textPrimaryColor)
        gutsViewHolder.setAccentPrimaryColor(gutsViewModel.accentPrimaryColor)
        gutsViewHolder.setSurfaceColor(gutsViewModel.surfaceColor)
    }

    private fun bindRecommendationsList(
    private suspend fun bindRecommendationsList(
        viewHolder: RecommendationViewHolder,
        mediaRecs: List<MediaRecViewModel>,
        falsingManager: FalsingManager
        falsingManager: FalsingManager,
        backgroundDispatcher: CoroutineDispatcher,
        mainDispatcher: CoroutineDispatcher,
    ) {
        mediaRecs.forEachIndexed { index, mediaRecViewModel ->
            if (index >= NUM_REQUIRED_RECOMMENDATIONS) return@forEachIndexed

            val appIconView = viewHolder.mediaAppIcons[index]
            appIconView.clearColorFilter()
            if (mediaRecViewModel.appIcon != null) {
            appIconView.setImageDrawable(mediaRecViewModel.appIcon)
            } else {
                appIconView.setImageResource(R.drawable.ic_music_note)
            }

            val mediaCoverContainer = viewHolder.mediaCoverContainers[index]
            mediaCoverContainer.setOnClickListener {
@@ -187,29 +223,24 @@ object MediaRecommendationsViewBinder {
            }

            val mediaCover = viewHolder.mediaCoverItems[index]
            val width: Int =
                mediaCover.context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_width)
            val height: Int =
                mediaCover.context.resources.getDimensionPixelSize(
                    R.dimen.qs_media_rec_album_height_expanded
            bindRecommendationArtwork(
                mediaCover.context,
                viewHolder,
                mediaRecViewModel,
                index,
                backgroundDispatcher,
                mainDispatcher,
            )
            val coverMatrix = Matrix(mediaCover.imageMatrix)
            coverMatrix.postScale(1.25f, 1.25f, 0.5f * width, 0.5f * height)
            mediaCover.imageMatrix = coverMatrix
            mediaCover.setImageDrawable(mediaRecViewModel.albumIcon)
            mediaCover.contentDescription = mediaRecViewModel.contentDescription

            val title = viewHolder.mediaTitles[index]
            title.text = mediaRecViewModel.title
            title.setTextColor(ColorStateList.valueOf(mediaRecViewModel.titleColor))

            val subtitle = viewHolder.mediaSubtitles[index]
            subtitle.text = mediaRecViewModel.subtitle
            subtitle.setTextColor(ColorStateList.valueOf(mediaRecViewModel.subtitleColor))

            val progressBar = viewHolder.mediaProgressBars[index]
            progressBar.progress = mediaRecViewModel.progress
            progressBar.progressTintList = ColorStateList.valueOf(mediaRecViewModel.progressColor)
            if (mediaRecViewModel.progress == 0) {
                progressBar.visibility = View.GONE
            }
@@ -291,11 +322,148 @@ object MediaRecommendationsViewBinder {
                    TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_DIP,
                            displayAvailableDpWidth.toFloat(),
                            res.displayMetrics
                            res.displayMetrics,
                        )
                        .toInt()
                displayAvailableWidth / recCoverWidth
            }
        return min(fittedNum.toDouble(), NUM_REQUIRED_RECOMMENDATIONS.toDouble()).toInt()
    }

    private suspend fun bindRecommendationArtwork(
        context: Context,
        viewHolder: RecommendationViewHolder,
        viewModel: MediaRecViewModel,
        index: Int,
        backgroundDispatcher: CoroutineDispatcher,
        mainDispatcher: CoroutineDispatcher,
    ) {
        val traceCookie = viewHolder.hashCode()
        val traceName = "MediaRecommendationsViewBinder#bindRecommendationArtwork"
        Trace.beginAsyncSection(traceName, traceCookie)

        // Capture width & height from views in foreground for artwork scaling in background
        val width = context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_width)
        val height =
            context.resources.getDimensionPixelSize(R.dimen.qs_media_rec_album_height_expanded)

        withContext(backgroundDispatcher) {
            val artwork =
                getRecCoverBackground(
                    context,
                    viewModel.albumIcon,
                    width,
                    height,
                    backgroundDispatcher,
                )
            withContext(mainDispatcher) {
                val mediaCover = viewHolder.mediaCoverItems[index]
                val coverMatrix = Matrix(mediaCover.imageMatrix)
                coverMatrix.postScale(1.25f, 1.25f, 0.5f * width, 0.5f * height)
                mediaCover.imageMatrix = coverMatrix
                mediaCover.setImageDrawable(artwork)
            }
        }
    }

    /** Returns the recommendation album cover of [width]x[height] size. */
    private suspend fun getRecCoverBackground(
        context: Context,
        icon: Icon?,
        width: Int,
        height: Int,
        backgroundDispatcher: CoroutineDispatcher,
    ): Drawable =
        withContext(backgroundDispatcher) {
            return@withContext MediaArtworkHelper.getWallpaperColor(
                    context,
                    backgroundDispatcher,
                    icon,
                    TAG,
                )
                ?.let { wallpaperColors ->
                    addGradientToRecommendationAlbum(
                        context,
                        icon!!,
                        ColorScheme(wallpaperColors, true, Style.CONTENT),
                        width,
                        height,
                    )
                } ?: ColorDrawable(Color.TRANSPARENT)
        }

    private fun addGradientToRecommendationAlbum(
        context: Context,
        artworkIcon: Icon,
        mutableColorScheme: ColorScheme,
        width: Int,
        height: Int,
    ): LayerDrawable {
        // First try scaling rec card using bitmap drawable.
        // If returns null, set drawable bounds.
        val albumArt =
            getScaledRecommendationCover(context, artworkIcon, width, height)
                ?: MediaArtworkHelper.getScaledBackground(context, artworkIcon, width, height)
        val gradient =
            AppCompatResources.getDrawable(context, R.drawable.qs_media_rec_scrim)?.mutate()
                as GradientDrawable
        return MediaArtworkHelper.setUpGradientColorOnDrawable(
            albumArt,
            gradient,
            mutableColorScheme,
            MEDIA_REC_SCRIM_START_ALPHA,
            MEDIA_REC_SCRIM_END_ALPHA,
        )
    }

    /** Returns a [Drawable] of a given [artworkIcon] scaled to [width]x[height] size, . */
    private fun getScaledRecommendationCover(
        context: Context,
        artworkIcon: Icon,
        width: Int,
        height: Int,
    ): Drawable? {
        check(width > 0) { "Width must be a positive number but was $width" }
        check(height > 0) { "Height must be a positive number but was $height" }

        return if (
            artworkIcon.type == Icon.TYPE_BITMAP || artworkIcon.type == Icon.TYPE_ADAPTIVE_BITMAP
        ) {
            artworkIcon.bitmap?.let {
                val bitmap = Bitmap.createScaledBitmap(it, width, height, false)
                BitmapDrawable(context.resources, bitmap)
            }
        } else {
            null
        }
    }

    private suspend fun fetchAndUpdateColors(
        viewHolder: RecommendationViewHolder,
        appIcon: Drawable,
        backgroundDispatcher: CoroutineDispatcher,
        mainDispatcher: CoroutineDispatcher,
    ) =
        withContext(backgroundDispatcher) {
            val colorScheme =
                ColorScheme(WallpaperColors.fromDrawable(appIcon), /* darkTheme= */ true)
            withContext(mainDispatcher) {
                val backgroundColor = surfaceFromScheme(colorScheme)
                val textPrimaryColor = textPrimaryFromScheme(colorScheme)
                val textSecondaryColor = textSecondaryFromScheme(colorScheme)

                viewHolder.cardTitle.setTextColor(textPrimaryColor)
                viewHolder.recommendations.setBackgroundTintList(
                    ColorStateList.valueOf(backgroundColor)
                )

                viewHolder.mediaTitles.forEach { it.setTextColor(textPrimaryColor) }
                viewHolder.mediaSubtitles.forEach { it.setTextColor(textSecondaryColor) }
                viewHolder.mediaProgressBars.forEach {
                    it.progressTintList = ColorStateList.valueOf(textPrimaryColor)
                }

                viewHolder.gutsViewHolder.setColors(colorScheme)
            }
        }
}
+2 −0
Original line number Diff line number Diff line
@@ -768,6 +768,8 @@ constructor(
                    commonViewModel.recsViewModel,
                    viewController,
                    falsingManager,
                    backgroundDispatcher,
                    mainDispatcher,
                )
                mediaContent.addView(viewHolder.recommendations, position)
                controllerById[commonViewModel.key] = viewController
+4 −11
Original line number Diff line number Diff line
@@ -87,26 +87,19 @@ object MediaArtworkHelper {
        gradient: GradientDrawable,
        colorScheme: ColorScheme,
        startAlpha: Float,
        endAlpha: Float
        endAlpha: Float,
    ): LayerDrawable {
        gradient.colors =
            intArrayOf(
                getColorWithAlpha(backgroundStartFromScheme(colorScheme), startAlpha),
                getColorWithAlpha(backgroundEndFromScheme(colorScheme), endAlpha)
                getColorWithAlpha(backgroundEndFromScheme(colorScheme), endAlpha),
            )
        return LayerDrawable(arrayOf(albumArt, gradient))
    }

    /** Returns [ColorScheme] of media app given its [packageName]. */
    fun getColorScheme(
        applicationContext: Context,
        packageName: String,
        tag: String,
        style: Style = Style.TONAL_SPOT
    ): ColorScheme? {
    /** Returns [ColorScheme] of media app given its [icon]. */
    fun getColorScheme(icon: Drawable, tag: String, style: Style = Style.TONAL_SPOT): ColorScheme? {
        return try {
            // Set up media source app's logo.
            val icon = applicationContext.packageManager.getApplicationIcon(packageName)
            ColorScheme(WallpaperColors.fromDrawable(icon), true, style)
        } catch (e: PackageManager.NameNotFoundException) {
            Log.w(tag, "Fail to get media app info", e)
+0 −4
Original line number Diff line number Diff line
@@ -16,15 +16,11 @@

package com.android.systemui.media.controls.ui.viewmodel

import android.annotation.ColorInt
import android.graphics.drawable.Drawable

/** Models UI state for media guts menu */
data class GutsViewModel(
    val gutsText: CharSequence,
    @ColorInt val textPrimaryColor: Int,
    @ColorInt val accentPrimaryColor: Int,
    @ColorInt val surfaceColor: Int,
    val isDismissEnabled: Boolean = true,
    val onDismissClicked: () -> Unit,
    val cancelTextBackground: Drawable?,
Loading