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

Commit 6eadd0ed authored by Sherry Zhou's avatar Sherry Zhou
Browse files

Make UMO squishy and add motion to widgets only for split shade

Test: atest MediaCarouselControllerTest and MediaViewControllerTest

Bug: 227478127

Change-Id: I4eae9413ae89950e1589487b2ff7c38e4ea8fa58
parent f12d6c28
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -16,6 +16,6 @@
  -->

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <solid android:color="@android:color/white" />
    <solid android:color="@android:color/transparent" />
    <corners android:radius="@dimen/qs_media_album_radius" />
</shape>
 No newline at end of file
+34 −4
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ import android.util.MathUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.PathInterpolator
import android.widget.LinearLayout
import androidx.annotation.VisibleForTesting
import com.android.internal.logging.InstanceId
@@ -95,7 +96,8 @@ class MediaCarouselController @Inject constructor(
     * finished
     */
    @MediaLocation
    private var currentEndLocation: Int = -1
    @VisibleForTesting
    var currentEndLocation: Int = -1

    /**
     * The ending location of the view where it ends when all animations and transitions have
@@ -126,7 +128,8 @@ class MediaCarouselController @Inject constructor(
    lateinit var settingsButton: View
        private set
    private val mediaContent: ViewGroup
    private val pageIndicator: PageIndicator
    @VisibleForTesting
    val pageIndicator: PageIndicator
    private val visualStabilityCallback: OnReorderingAllowedListener
    private var needsReordering: Boolean = false
    private var keysNeedRemoval = mutableSetOf<String>()
@@ -149,6 +152,27 @@ class MediaCarouselController @Inject constructor(
                }
            }
        }

    companion object {
        const val ANIMATION_BASE_DURATION = 2200f
        const val DURATION = 167f
        const val DETAILS_DELAY = 1067f
        const val CONTROLS_DELAY = 1400f
        const val PAGINATION_DELAY = 1900f
        const val MEDIATITLES_DELAY = 1000f
        const val MEDIACONTAINERS_DELAY = 967f
        val TRANSFORM_BEZIER = PathInterpolator (0.68F, 0F, 0F, 1F)
        val REVERSE_BEZIER = PathInterpolator (0F, 0.68F, 1F, 0F)

        fun calculateAlpha(squishinessFraction: Float, delay: Float, duration: Float): Float {
            val transformStartFraction = delay / ANIMATION_BASE_DURATION
            val transformDurationFraction = duration / ANIMATION_BASE_DURATION
            val squishinessToTime = REVERSE_BEZIER.getInterpolation(squishinessFraction)
            return MathUtils.constrain((squishinessToTime - transformStartFraction) /
                    transformDurationFraction, 0F, 1F)
        }
    }

    private val configListener = object : ConfigurationController.ConfigurationListener {
        override fun onDensityOrFontScaleChanged() {
            // System font changes should only happen when UMO is offscreen or a flicker may occur
@@ -633,12 +657,17 @@ class MediaCarouselController @Inject constructor(
        }
    }

    private fun updatePageIndicatorAlpha() {
    @VisibleForTesting
    fun updatePageIndicatorAlpha() {
        val hostStates = mediaHostStatesManager.mediaHostStates
        val endIsVisible = hostStates[currentEndLocation]?.visible ?: false
        val startIsVisible = hostStates[currentStartLocation]?.visible ?: false
        val startAlpha = if (startIsVisible) 1.0f else 0.0f
        val endAlpha = if (endIsVisible) 1.0f else 0.0f
        // when squishing in split shade, only use endState, which keeps changing
        // to provide squishFraction
        val squishFraction = hostStates[currentEndLocation]?.squishFraction ?: 1.0F
        val endAlpha = (if (endIsVisible) 1.0f else 0.0f) *
                calculateAlpha(squishFraction, PAGINATION_DELAY, DURATION)
        var alpha = 1.0f
        if (!endIsVisible || !startIsVisible) {
            var progress = currentTransitionProgress
@@ -687,6 +716,7 @@ class MediaCarouselController @Inject constructor(
            mediaCarouselScrollHandler.setCarouselBounds(
                    currentCarouselWidth, currentCarouselHeight)
            updatePageIndicatorLocation()
            updatePageIndicatorAlpha()
        }
    }

+18 −0
Original line number Diff line number Diff line
@@ -203,6 +203,14 @@ class MediaHost constructor(
                }
            }

        override var squishFraction: Float = 1.0f
            set(value) {
                if (!value.equals(field)) {
                    field = value
                    changedListener?.invoke()
                }
            }

        override var showsOnlyActiveMedia: Boolean = false
            set(value) {
                if (!value.equals(field)) {
@@ -253,6 +261,7 @@ class MediaHost constructor(
        override fun copy(): MediaHostState {
            val mediaHostState = MediaHostStateHolder()
            mediaHostState.expansion = expansion
            mediaHostState.squishFraction = squishFraction
            mediaHostState.showsOnlyActiveMedia = showsOnlyActiveMedia
            mediaHostState.measurementInput = measurementInput?.copy()
            mediaHostState.visible = visible
@@ -271,6 +280,9 @@ class MediaHost constructor(
            if (expansion != other.expansion) {
                return false
            }
            if (squishFraction != other.squishFraction) {
                return false
            }
            if (showsOnlyActiveMedia != other.showsOnlyActiveMedia) {
                return false
            }
@@ -289,6 +301,7 @@ class MediaHost constructor(
        override fun hashCode(): Int {
            var result = measurementInput?.hashCode() ?: 0
            result = 31 * result + expansion.hashCode()
            result = 31 * result + squishFraction.hashCode()
            result = 31 * result + falsingProtectionNeeded.hashCode()
            result = 31 * result + showsOnlyActiveMedia.hashCode()
            result = 31 * result + if (visible) 1 else 2
@@ -328,6 +341,11 @@ interface MediaHostState {
     */
    var expansion: Float

    /**
     * Fraction of the height animation.
     */
    var squishFraction: Float

    /**
     * Is this host only showing active media or is it showing all of them including resumption?
     */
+97 −29
Original line number Diff line number Diff line
@@ -18,8 +18,15 @@ package com.android.systemui.media

import android.content.Context
import android.content.res.Configuration
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.R
import com.android.systemui.media.MediaCarouselController.Companion.CONTROLS_DELAY
import com.android.systemui.media.MediaCarouselController.Companion.DETAILS_DELAY
import com.android.systemui.media.MediaCarouselController.Companion.DURATION
import com.android.systemui.media.MediaCarouselController.Companion.MEDIACONTAINERS_DELAY
import com.android.systemui.media.MediaCarouselController.Companion.MEDIATITLES_DELAY
import com.android.systemui.media.MediaCarouselController.Companion.calculateAlpha
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.animation.MeasurementOutput
import com.android.systemui.util.animation.TransitionLayout
@@ -50,6 +57,24 @@ class MediaViewController @Inject constructor(
    companion object {
        @JvmField
        val GUTS_ANIMATION_DURATION = 500L
        val controlIds = setOf(
                R.id.media_progress_bar,
                R.id.actionNext,
                R.id.actionPrev,
                R.id.action0,
                R.id.action1,
                R.id.action2,
                R.id.action3,
                R.id.action4,
                R.id.media_scrubbing_elapsed_time,
                R.id.media_scrubbing_total_time
        )

        val detailIds = setOf(
                R.id.header_title,
                R.id.header_artist,
                R.id.actionPlayPause,
        )
    }

    /**
@@ -57,6 +82,7 @@ class MediaViewController @Inject constructor(
     */
    lateinit var sizeChangedListener: () -> Unit
    private var firstRefresh: Boolean = true
    @VisibleForTesting
    private var transitionLayout: TransitionLayout? = null
    private val layoutController = TransitionLayoutController()
    private var animationDelay: Long = 0
@@ -278,11 +304,48 @@ class MediaViewController @Inject constructor(
        }
    }

    /**
     * Apply squishFraction to a copy of viewState such that the cached version is untouched.
    */
    internal fun squishViewState(
        viewState: TransitionViewState,
        squishFraction: Float
    ): TransitionViewState {
        val squishedViewState = viewState.copy()
        squishedViewState.height = (squishedViewState.height * squishFraction).toInt()
        controlIds.forEach { id ->
            squishedViewState.widgetStates.get(id)?.let { state ->
                state.alpha = calculateAlpha(squishFraction, CONTROLS_DELAY, DURATION)
            }
        }

        detailIds.forEach { id ->
            squishedViewState.widgetStates.get(id)?.let { state ->
                state.alpha = calculateAlpha(squishFraction, DETAILS_DELAY, DURATION)
            }
        }

        RecommendationViewHolder.mediaContainersIds.forEach { id ->
            squishedViewState.widgetStates.get(id)?.let { state ->
                state.alpha = calculateAlpha(squishFraction, MEDIACONTAINERS_DELAY, DURATION)
            }
        }

        RecommendationViewHolder.mediaTitlesAndSubtitlesIds.forEach { id ->
            squishedViewState.widgetStates.get(id)?.let { state ->
                state.alpha = calculateAlpha(squishFraction, MEDIATITLES_DELAY, DURATION)
            }
        }

        return squishedViewState
    }

    /**
     * Obtain a new viewState for a given media state. This usually returns a cached state, but if
     * it's not available, it will recreate one by measuring, which may be expensive.
     */
    private fun obtainViewState(state: MediaHostState?): TransitionViewState? {
     @VisibleForTesting
     fun obtainViewState(state: MediaHostState?): TransitionViewState? {
        if (state == null || state.measurementInput == null) {
            return null
        }
@@ -291,13 +354,18 @@ class MediaViewController @Inject constructor(
        val viewState = viewStates[cacheKey]
        if (viewState != null) {
            // we already have cached this measurement, let's continue
            if (state.squishFraction <= 1f) {
                return squishViewState(viewState, state.squishFraction)
            }
            return viewState
        }
        // Copy the key since this might call recursively into it and we're using tmpKey
        cacheKey = cacheKey.copy()
        val result: TransitionViewState?

        if (transitionLayout != null) {
        if (transitionLayout == null) {
            return null
        }
        // Let's create a new measurement
        if (state.expansion == 0.0f || state.expansion == 1.0f) {
            result = transitionLayout!!.calculateViewState(
@@ -324,8 +392,8 @@ class MediaViewController @Inject constructor(
                    endViewState,
                    state.expansion)
        }
        } else {
            result = null
        if (state.squishFraction <= 1f) {
            return squishViewState(result, state.squishFraction)
        }
        return result
    }
+15 −0
Original line number Diff line number Diff line
@@ -106,5 +106,20 @@ class RecommendationViewHolder private constructor(itemView: View) {
            R.id.media_subtitle2,
            R.id.media_subtitle3
        )

        val mediaTitlesAndSubtitlesIds = setOf(
            R.id.media_title1,
            R.id.media_title2,
            R.id.media_title3,
            R.id.media_subtitle1,
            R.id.media_subtitle2,
            R.id.media_subtitle3
        )

        val mediaContainersIds = setOf(
            R.id.media_cover1_container,
            R.id.media_cover2_container,
            R.id.media_cover3_container
        )
    }
}
Loading