diff options
| author | 2022-03-14 15:02:03 +0000 | |
|---|---|---|
| committer | 2022-03-14 15:02:03 +0000 | |
| commit | 362dcaab4cbc288650443a4de53331ebdbf0e7a2 (patch) | |
| tree | 9392e853ffe7e461cf0ae1a873d54657b4ed44fd | |
| parent | be26fbdfdc5b2d09fd87795c958c2699ad89c699 (diff) | |
| parent | 3f2eea4703f6ef26ab039c884dca54f76ee1fadb (diff) | |
Merge "Make media squishy for portrait shade open and close" into tm-dev
7 files changed, 128 insertions, 43 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index 0a4b68b501f7..eb209f723cb5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -192,6 +192,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)) { @@ -242,6 +250,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 @@ -260,6 +269,9 @@ class MediaHost constructor( if (expansion != other.expansion) { return false } + if (squishFraction != other.squishFraction) { + return false + } if (showsOnlyActiveMedia != other.showsOnlyActiveMedia) { return false } @@ -278,6 +290,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 @@ -318,6 +331,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? */ var showsOnlyActiveMedia: Boolean diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt index 591aad1014bd..a60016b23a7c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt @@ -18,13 +18,11 @@ 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.statusbar.policy.ConfigurationController -import com.android.systemui.util.animation.MeasurementOutput -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.animation.* import javax.inject.Inject /** @@ -270,7 +268,6 @@ class MediaViewController @Inject constructor( TYPE.PLAYER_SESSION -> PlayerSessionViewHolder.gutsIds TYPE.RECOMMENDATION -> RecommendationViewHolder.gutsIds } - controlsIds.forEach { id -> viewState.widgetStates.get(id)?.let { state -> // Make sure to use the unmodified state if guts are not visible. @@ -282,59 +279,79 @@ class MediaViewController @Inject constructor( viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f viewState.widgetStates.get(id)?.gone = !isGutsVisible } - if (shouldHideGutsSettings) { viewState.widgetStates.get(R.id.settings)?.gone = true } } /** + * Apply squishFraction to a copy of viewState such that the cached version is untouched. + */ + private fun squishViewState(viewState: TransitionViewState, + squishFraction: Float): TransitionViewState { + val squishedViewState = viewState.copy() + squishedViewState.height = (squishedViewState.height * squishFraction).toInt() + val albumArtViewState = viewState.widgetStates.get(R.id.album_art) + if (albumArtViewState != null) { + albumArtViewState.height = squishedViewState.height + } + 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 + public fun obtainViewState(state: MediaHostState?): TransitionViewState? { if (state == null || state.measurementInput == null) { return null } // Only a subset of the state is relevant to get a valid viewState. Let's get the cachekey var cacheKey = getKey(state, isGutsVisible, tmpKey) 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) { - // Let's create a new measurement - if (state.expansion == 0.0f || state.expansion == 1.0f) { - result = transitionLayout!!.calculateViewState( - state.measurementInput!!, - constraintSetForExpansion(state.expansion), - TransitionViewState()) - - setGutsViewState(result) - // We don't want to cache interpolated or null states as this could quickly fill up - // our cache. We only cache the start and the end states since the interpolation - // is cheap - viewStates[cacheKey] = result - } else { - // This is an interpolated state - val startState = state.copy().also { it.expansion = 0.0f } - - // Given that we have a measurement and a view, let's get (guaranteed) viewstates - // from the start and end state and interpolate them - val startViewState = obtainViewState(startState) as TransitionViewState - val endState = state.copy().also { it.expansion = 1.0f } - val endViewState = obtainViewState(endState) as TransitionViewState - result = layoutController.getInterpolatedState( - startViewState, - endViewState, - state.expansion) - } + if (transitionLayout == null) { + return null + } + // Not cached. Let's create a new measurement + if (state.expansion == 0.0f || state.expansion == 1.0f) { + result = transitionLayout!!.calculateViewState( + state.measurementInput!!, + constraintSetForExpansion(state.expansion), + TransitionViewState()) + // We don't want to cache interpolated or null states as this could quickly fill up + // our cache. We only cache the start and the end states since the interpolation + // is cheap + setGutsViewState(result) + viewStates[cacheKey] = result } else { - result = null + // This is an interpolated state + val startState = state.copy().also { it.expansion = 0.0f } + + // Given that we have a measurement and a view, let's get (guaranteed) viewstates + // from the start and end state and interpolate them + val startViewState = obtainViewState(startState) as TransitionViewState + val endState = state.copy().also { it.expansion = 1.0f } + + val endViewState = obtainViewState(endState) as TransitionViewState + result = layoutController.getInterpolatedState( + startViewState, + endViewState, + state.expansion) + } + if (state.squishFraction < 1f) { + return squishViewState(result, state.squishFraction); } return result } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 3ef72202a591..fe8c309ad2f3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -573,6 +573,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca if (mQSAnimator != null) { mQSAnimator.setPosition(expansion); } + mQqsMediaHost.setSquishFraction(mSquishinessFraction); updateMediaPositions(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 5b9dbd0f3361..be1aa10c5f80 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -1332,12 +1332,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ @ShadeViewRefactor(RefactorComponent.COORDINATOR) public void setExpandedHeight(float height) { - final float shadeBottom = getHeight() - getEmptyBottomMargin(); final boolean skipHeightUpdate = shouldSkipHeightUpdate(); - if (!skipHeightUpdate) { - final float expansionFraction = MathUtils.saturate(height / shadeBottom); - mAmbientState.setExpansionFraction(expansionFraction); - } updateStackPosition(); if (!skipHeightUpdate) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index cbdf5c8017c6..1891ab017ef8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -3009,9 +3009,7 @@ public class NotificationPanelViewController extends PanelViewController { } private int calculatePanelHeightShade() { - int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin(); - int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin; - + final int maxHeight = mNotificationStackScrollLayoutController.getHeight(); if (mBarState == KEYGUARD) { int minKeyguardPanelBottom = mClockPositionAlgorithm.getLockscreenStatusViewHeight() + mNotificationStackScrollLayoutController.getIntrinsicContentHeight(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 942aab58763e..24f5ff86a794 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -796,6 +796,7 @@ public abstract class PanelViewController { } mExpandedFraction = Math.min(1f, maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight); + mAmbientState.setExpansionFraction(mExpandedFraction); onHeightUpdated(mExpandedHeight); updatePanelExpansionAndVisibility(); }); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt new file mode 100644 index 000000000000..b7d5ba170e6d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt @@ -0,0 +1,55 @@ +package com.android.systemui.media + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.animation.MeasurementInput +import com.android.systemui.util.animation.TransitionLayout +import com.android.systemui.util.animation.TransitionViewState +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Tests for {@link MediaViewController}. + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class MediaViewControllerTest : SysuiTestCase() { + private val configurationController = + com.android.systemui.statusbar.phone.ConfigurationControllerImpl(context) + private val mediaHostStatesManager = MediaHostStatesManager() + private val mediaViewController = + MediaViewController(context, configurationController, mediaHostStatesManager) + private val mediaHostStateHolder = MediaHost.MediaHostStateHolder() + private var transitionLayout = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0) + + @Before + fun setUp() { + mediaViewController.attach(transitionLayout, MediaViewController.TYPE.PLAYER) + } + + @Test + fun testObtainViewState_applySquishFraction_toTransitionViewState_height() { + transitionLayout.measureState = TransitionViewState().apply { + this.height = 100 + } + mediaHostStateHolder.expansion = 1f + val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) + val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) + mediaHostStateHolder.measurementInput = + MeasurementInput(widthMeasureSpec, heightMeasureSpec) + + // Test no squish + mediaHostStateHolder.squishFraction = 1f + assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 100) + + // Test half squish + mediaHostStateHolder.squishFraction = 0.5f + assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 50) + } +}
\ No newline at end of file |