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

Commit 37a5bf62 authored by Sherry Zhou's avatar Sherry Zhou Committed by Automerger Merge Worker
Browse files

Merge "Fix widgets in recommendation card in UMO are clipped" into tm-qpr-dev am: 4bcdc7b0

parents 37e4a318 4bcdc7b0
Loading
Loading
Loading
Loading
+24 −23
Original line number Diff line number Diff line
@@ -94,7 +94,7 @@ constructor(
    private var currentCarouselWidth: Int = 0

    /** The current height of the carousel */
    private var currentCarouselHeight: Int = 0
    @VisibleForTesting var currentCarouselHeight: Int = 0

    /** Are we currently showing only active players */
    private var currentlyShowingOnlyActive: Boolean = false
@@ -128,14 +128,14 @@ constructor(
    /** The measured height of the carousel */
    private var carouselMeasureHeight: Int = 0
    private var desiredHostState: MediaHostState? = null
    private val mediaCarousel: MediaScrollView
    @VisibleForTesting var mediaCarousel: MediaScrollView
    val mediaCarouselScrollHandler: MediaCarouselScrollHandler
    val mediaFrame: ViewGroup
    @VisibleForTesting
    lateinit var settingsButton: View
        private set
    private val mediaContent: ViewGroup
    @VisibleForTesting val pageIndicator: PageIndicator
    @VisibleForTesting var pageIndicator: PageIndicator
    private val visualStabilityCallback: OnReorderingAllowedListener
    private var needsReordering: Boolean = false
    private var keysNeedRemoval = mutableSetOf<String>()
@@ -160,25 +160,20 @@ 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,

        fun calculateAlpha(
            squishinessFraction: Float,
            startPosition: Float,
            endPosition: Float
        ): Float {
            val transformFraction =
                MathUtils.constrain(
                    (squishinessFraction - startPosition) / (endPosition - startPosition),
                    0F,
                    1F
                )
            return TRANSFORM_BEZIER.getInterpolation(transformFraction)
        }
    }

@@ -813,7 +808,12 @@ constructor(
        val squishFraction = hostStates[currentEndLocation]?.squishFraction ?: 1.0F
        val endAlpha =
            (if (endIsVisible) 1.0f else 0.0f) *
                calculateAlpha(squishFraction, PAGINATION_DELAY, DURATION)
                calculateAlpha(
                    squishFraction,
                    (pageIndicator.translationY + pageIndicator.height) /
                        mediaCarousel.measuredHeight,
                    1F
                )
        var alpha = 1.0f
        if (!endIsVisible || !startIsVisible) {
            var progress = currentTransitionProgress
@@ -839,7 +839,8 @@ constructor(
        pageIndicator.translationX = translationX + mediaCarouselScrollHandler.contentTranslation
        val layoutParams = pageIndicator.layoutParams as ViewGroup.MarginLayoutParams
        pageIndicator.translationY =
            (currentCarouselHeight - pageIndicator.height - layoutParams.bottomMargin).toFloat()
            (mediaCarousel.measuredHeight - pageIndicator.height - layoutParams.bottomMargin)
                .toFloat()
    }

    /** Update the dimension of this carousel. */
+96 −30
Original line number Diff line number Diff line
@@ -24,11 +24,6 @@ import com.android.systemui.R
import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.CONTROLS_DELAY
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.DETAILS_DELAY
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.DURATION
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.MEDIACONTAINERS_DELAY
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.MEDIATITLES_DELAY
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.calculateAlpha
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.animation.MeasurementOutput
@@ -36,6 +31,8 @@ import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionLayoutController
import com.android.systemui.util.animation.TransitionViewState
import com.android.systemui.util.traceSection
import java.lang.Float.max
import java.lang.Float.min
import javax.inject.Inject

/**
@@ -304,39 +301,106 @@ constructor(
        val squishedViewState = viewState.copy()
        val squishedHeight = (squishedViewState.measureHeight * squishFraction).toInt()
        squishedViewState.height = squishedHeight
        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)
            }
        }

        // We are not overriding the squishedViewStates height but only the children to avoid
        // them remeasuring the whole view. Instead it just remains as the original size
        backgroundIds.forEach { id ->
            squishedViewState.widgetStates.get(id)?.let { state ->
                state.height = squishedHeight
            }
            squishedViewState.widgetStates.get(id)?.let { state -> state.height = squishedHeight }
        }

        RecommendationViewHolder.mediaContainersIds.forEach { id ->
            squishedViewState.widgetStates.get(id)?.let { state ->
                state.alpha = calculateAlpha(squishFraction, MEDIACONTAINERS_DELAY, DURATION)
            }
        // media player
        val controlsTop =
            calculateWidgetGroupAlphaForSquishiness(
                controlIds,
                squishedViewState.measureHeight.toFloat(),
                squishedViewState,
                squishFraction
            )
        calculateWidgetGroupAlphaForSquishiness(
            detailIds,
            controlsTop,
            squishedViewState,
            squishFraction
        )
        // recommendation card
        val titlesTop =
            calculateWidgetGroupAlphaForSquishiness(
                RecommendationViewHolder.mediaTitlesAndSubtitlesIds,
                squishedViewState.measureHeight.toFloat(),
                squishedViewState,
                squishFraction
            )
        calculateWidgetGroupAlphaForSquishiness(
            RecommendationViewHolder.mediaContainersIds,
            titlesTop,
            squishedViewState,
            squishFraction
        )
        return squishedViewState
    }

        RecommendationViewHolder.mediaTitlesAndSubtitlesIds.forEach { id ->
    /**
     * This function is to make each widget in UMO disappear before being clipped by squished UMO
     *
     * The general rule is that widgets in UMO has been divided into several groups, and widgets in
     * one group have the same alpha during squishing It will change from alpha 0.0 when the visible
     * bottom of UMO reach the bottom of this group It will change to alpha 1.0 when the visible
     * bottom of UMO reach the top of the group below e.g.Album title, artist title and play-pause
     * button will change alpha together.
     * ```
     *     And their alpha becomes 1.0 when the visible bottom of UMO reach the top of controls,
     *     including progress bar, next button, previous button
     * ```
     * widgetGroupIds: a group of widgets have same state during UMO is squished,
     * ```
     *     e.g. Album title, artist title and play-pause button
     * ```
     * groupEndPosition: the height of UMO, when the height reaches this value,
     * ```
     *     widgets in this group should have 1.0 as alpha
     *     e.g., the group of album title, artist title and play-pause button will become fully
     *         visible when the height of UMO reaches the top of controls group
     *         (progress bar, previous button and next button)
     * ```
     * squishedViewState: hold the widgetState of each widget, which will be modified
     * squishFraction: the squishFraction of UMO
     */
    private fun calculateWidgetGroupAlphaForSquishiness(
        widgetGroupIds: Set<Int>,
        groupEndPosition: Float,
        squishedViewState: TransitionViewState,
        squishFraction: Float
    ): Float {
        val nonsquishedHeight = squishedViewState.measureHeight
        var groupTop = squishedViewState.measureHeight.toFloat()
        var groupBottom = 0F
        widgetGroupIds.forEach { id ->
            squishedViewState.widgetStates.get(id)?.let { state ->
                groupTop = min(groupTop, state.y)
                groupBottom = max(groupBottom, state.y + state.height)
            }
        }
        // startPosition means to the height of squished UMO where the widget alpha should start
        // changing from 0.0
        // generally, it equals to the bottom of widgets, so that we can meet the requirement that
        // widget should not go beyond the bounds of background
        // endPosition means to the height of squished UMO where the widget alpha should finish
        // changing alpha to 1.0
        var startPosition = groupBottom
        val endPosition = groupEndPosition
        if (startPosition == endPosition) {
            startPosition = (endPosition - 0.2 * (groupBottom - groupTop)).toFloat()
        }
        widgetGroupIds.forEach { id ->
            squishedViewState.widgetStates.get(id)?.let { state ->
                state.alpha = calculateAlpha(squishFraction, MEDIATITLES_DELAY, DURATION)
                state.alpha =
                    calculateAlpha(
                        squishFraction,
                        startPosition / nonsquishedHeight,
                        endPosition / nonsquishedHeight
                    )
            }
        }

        return squishedViewState
        return groupTop // used for the widget group above this group
    }

    /**
@@ -544,11 +608,13 @@ constructor(
        overrideSize?.let {
            // To be safe we're using a maximum here. The override size should always be set
            // properly though.
            if (result.measureHeight != it.measuredHeight
                    || result.measureWidth != it.measuredWidth) {
            if (
                result.measureHeight != it.measuredHeight || result.measureWidth != it.measuredWidth
            ) {
                result.measureHeight = Math.max(it.measuredHeight, result.measureHeight)
                result.measureWidth = Math.max(it.measuredWidth, result.measureWidth)
                // The measureHeight and the shown height should both be set to the overridden height
                // The measureHeight and the shown height should both be set to the overridden
                // height
                result.height = result.measureHeight
                result.width = result.measureWidth
                // Make sure all background views are also resized such that their size is correct
+14 −16
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.app.PendingIntent
import android.content.res.Configuration
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.util.MathUtils.abs
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
@@ -31,14 +32,11 @@ import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.ANIMATION_BASE_DURATION
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.DURATION
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.PAGINATION_DELAY
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.TRANSFORM_BEZIER
import com.android.systemui.media.controls.ui.MediaHierarchyManager.Companion.LOCATION_QS
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -56,6 +54,7 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.floatThat
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -86,6 +85,8 @@ class MediaCarouselControllerTest : SysuiTestCase() {
    @Mock lateinit var debugLogger: MediaCarouselControllerLogger
    @Mock lateinit var mediaViewController: MediaViewController
    @Mock lateinit var smartspaceMediaData: SmartspaceMediaData
    @Mock lateinit var mediaCarousel: MediaScrollView
    @Mock lateinit var pageIndicator: PageIndicator
    @Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
    @Captor
    lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
@@ -647,25 +648,22 @@ class MediaCarouselControllerTest : SysuiTestCase() {
    @Test
    fun testSetCurrentState_UpdatePageIndicatorAlphaWhenSquish() {
        val delta = 0.0001F
        val paginationSquishMiddle =
            TRANSFORM_BEZIER.getInterpolation(
                (PAGINATION_DELAY + DURATION / 2) / ANIMATION_BASE_DURATION
            )
        val paginationSquishEnd =
            TRANSFORM_BEZIER.getInterpolation(
                (PAGINATION_DELAY + DURATION) / ANIMATION_BASE_DURATION
            )
        mediaCarouselController.mediaCarousel = mediaCarousel
        mediaCarouselController.pageIndicator = pageIndicator
        whenever(mediaCarousel.measuredHeight).thenReturn(100)
        whenever(pageIndicator.translationY).thenReturn(80F)
        whenever(pageIndicator.height).thenReturn(10)
        whenever(mediaHostStatesManager.mediaHostStates)
            .thenReturn(mutableMapOf(LOCATION_QS to mediaHostState))
        whenever(mediaHostState.visible).thenReturn(true)
        mediaCarouselController.currentEndLocation = LOCATION_QS
        whenever(mediaHostState.squishFraction).thenReturn(paginationSquishMiddle)
        whenever(mediaHostState.squishFraction).thenReturn(0.938F)
        mediaCarouselController.updatePageIndicatorAlpha()
        assertEquals(mediaCarouselController.pageIndicator.alpha, 0.5F, delta)
        verify(pageIndicator).alpha = floatThat { abs(it - 0.5F) < delta }

        whenever(mediaHostState.squishFraction).thenReturn(paginationSquishEnd)
        whenever(mediaHostState.squishFraction).thenReturn(1.0F)
        mediaCarouselController.updatePageIndicatorAlpha()
        assertEquals(mediaCarouselController.pageIndicator.alpha, 1.0F, delta)
        verify(pageIndicator).alpha = floatThat { abs(it - 1.0F) < delta }
    }

    @Ignore("b/253229241")
+40 −56
Original line number Diff line number Diff line
@@ -22,13 +22,6 @@ import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.ANIMATION_BASE_DURATION
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.CONTROLS_DELAY
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.DETAILS_DELAY
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.DURATION
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.MEDIACONTAINERS_DELAY
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.MEDIATITLES_DELAY
import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.TRANSFORM_BEZIER
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionViewState
@@ -60,9 +53,10 @@ class MediaViewControllerTest : SysuiTestCase() {
    @Mock private lateinit var controlWidgetState: WidgetState
    @Mock private lateinit var bgWidgetState: WidgetState
    @Mock private lateinit var mediaTitleWidgetState: WidgetState
    @Mock private lateinit var mediaSubTitleWidgetState: WidgetState
    @Mock private lateinit var mediaContainerWidgetState: WidgetState

    val delta = 0.0001F
    val delta = 0.1F

    private lateinit var mediaViewController: MediaViewController

@@ -76,7 +70,8 @@ class MediaViewControllerTest : SysuiTestCase() {
    @Test
    fun testObtainViewState_applySquishFraction_toPlayerTransitionViewState_height() {
        mediaViewController.attach(player, MediaViewController.TYPE.PLAYER)
        player.measureState = TransitionViewState().apply {
        player.measureState =
            TransitionViewState().apply {
                this.height = 100
                this.measureHeight = 100
            }
@@ -128,29 +123,21 @@ class MediaViewControllerTest : SysuiTestCase() {
                    R.id.header_artist to detailWidgetState
                )
            )

        val detailSquishMiddle =
            TRANSFORM_BEZIER.getInterpolation(
                (DETAILS_DELAY + DURATION / 2) / ANIMATION_BASE_DURATION
            )
        mediaViewController.squishViewState(mockViewState, detailSquishMiddle)
        whenever(mockCopiedState.measureHeight).thenReturn(200)
        // detail widgets occupy [90, 100]
        whenever(detailWidgetState.y).thenReturn(90F)
        whenever(detailWidgetState.height).thenReturn(10)
        // control widgets occupy [150, 170]
        whenever(controlWidgetState.y).thenReturn(150F)
        whenever(controlWidgetState.height).thenReturn(20)
        // in current beizer, when the progress reach 0.38, the result will be 0.5
        mediaViewController.squishViewState(mockViewState, 119F / 200F)
        verify(detailWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }

        val detailSquishEnd =
            TRANSFORM_BEZIER.getInterpolation((DETAILS_DELAY + DURATION) / ANIMATION_BASE_DURATION)
        mediaViewController.squishViewState(mockViewState, detailSquishEnd)
        mediaViewController.squishViewState(mockViewState, 150F / 200F)
        verify(detailWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }

        val controlSquishMiddle =
            TRANSFORM_BEZIER.getInterpolation(
                (CONTROLS_DELAY + DURATION / 2) / ANIMATION_BASE_DURATION
            )
        mediaViewController.squishViewState(mockViewState, controlSquishMiddle)
        mediaViewController.squishViewState(mockViewState, 181.4F / 200F)
        verify(controlWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }

        val controlSquishEnd =
            TRANSFORM_BEZIER.getInterpolation((CONTROLS_DELAY + DURATION) / ANIMATION_BASE_DURATION)
        mediaViewController.squishViewState(mockViewState, controlSquishEnd)
        mediaViewController.squishViewState(mockViewState, 200F / 200F)
        verify(controlWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
    }

@@ -161,36 +148,33 @@ class MediaViewControllerTest : SysuiTestCase() {
            .thenReturn(
                mutableMapOf(
                    R.id.media_title1 to mediaTitleWidgetState,
                    R.id.media_subtitle1 to mediaSubTitleWidgetState,
                    R.id.media_cover1_container to mediaContainerWidgetState
                )
            )

        val containerSquishMiddle =
            TRANSFORM_BEZIER.getInterpolation(
                (MEDIACONTAINERS_DELAY + DURATION / 2) / ANIMATION_BASE_DURATION
            )
        mediaViewController.squishViewState(mockViewState, containerSquishMiddle)
        whenever(mockCopiedState.measureHeight).thenReturn(360)
        // media container widgets occupy [20, 300]
        whenever(mediaContainerWidgetState.y).thenReturn(20F)
        whenever(mediaContainerWidgetState.height).thenReturn(280)
        // media title widgets occupy [320, 330]
        whenever(mediaTitleWidgetState.y).thenReturn(320F)
        whenever(mediaTitleWidgetState.height).thenReturn(10)
        // media subtitle widgets occupy [340, 350]
        whenever(mediaSubTitleWidgetState.y).thenReturn(340F)
        whenever(mediaSubTitleWidgetState.height).thenReturn(10)

        // in current beizer, when the progress reach 0.38, the result will be 0.5
        mediaViewController.squishViewState(mockViewState, 307.6F / 360F)
        verify(mediaContainerWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }

        val containerSquishEnd =
            TRANSFORM_BEZIER.getInterpolation(
                (MEDIACONTAINERS_DELAY + DURATION) / ANIMATION_BASE_DURATION
            )
        mediaViewController.squishViewState(mockViewState, containerSquishEnd)
        mediaViewController.squishViewState(mockViewState, 320F / 360F)
        verify(mediaContainerWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }

        val titleSquishMiddle =
            TRANSFORM_BEZIER.getInterpolation(
                (MEDIATITLES_DELAY + DURATION / 2) / ANIMATION_BASE_DURATION
            )
        mediaViewController.squishViewState(mockViewState, titleSquishMiddle)
        // media title and media subtitle are in same widget group, should be calculate together and
        // have same alpha
        mediaViewController.squishViewState(mockViewState, 353.8F / 360F)
        verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }

        val titleSquishEnd =
            TRANSFORM_BEZIER.getInterpolation(
                (MEDIATITLES_DELAY + DURATION) / ANIMATION_BASE_DURATION
            )
        mediaViewController.squishViewState(mockViewState, titleSquishEnd)
        verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 0.5F) < delta }
        mediaViewController.squishViewState(mockViewState, 360F / 360F)
        verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
        verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
    }
}