Merge "Clip UMO in overlay and animate FooterActions" into tm-dev

This commit is contained in:
Fabian Kozynski
2022-03-22 14:32:10 +00:00
committed by Android (Google) Code Review
9 changed files with 136 additions and 36 deletions

View File

@@ -98,6 +98,10 @@ class MediaHierarchyManager @Inject constructor(
private var currentBounds = Rect()
private var animationStartBounds: Rect = Rect()
private var animationStartClipping = Rect()
private var currentClipping = Rect()
private var targetClipping = Rect()
/**
* The cross fade progress at the start of the animation. 0.5f means it's just switching between
* the start and the end location and the content is fully faded, while 0.75f means that we're
@@ -144,7 +148,8 @@ class MediaHierarchyManager @Inject constructor(
}
interpolateBounds(animationStartBounds, targetBounds, boundsProgress,
result = currentBounds)
applyState(currentBounds, currentAlpha)
resolveClipping(currentClipping)
applyState(currentBounds, currentAlpha, clipBounds = currentClipping)
}
addListener(object : AnimatorListenerAdapter() {
private var cancelled: Boolean = false
@@ -169,6 +174,12 @@ class MediaHierarchyManager @Inject constructor(
})
}
private fun resolveClipping(result: Rect) {
if (animationStartClipping.isEmpty) result.set(targetClipping)
else if (targetClipping.isEmpty) result.set(animationStartClipping)
else result.setIntersect(animationStartClipping, targetClipping)
}
private val mediaHosts = arrayOfNulls<MediaHost>(LOCATION_DREAM_OVERLAY + 1)
/**
* The last location where this view was at before going to the desired location. This is
@@ -629,10 +640,12 @@ class MediaHierarchyManager @Inject constructor(
// We also go in here in case the view was detached, since the bounds wouldn't
// be correct anymore
animationStartBounds.set(currentBounds)
animationStartClipping.set(currentClipping)
} else {
// otherwise, let's take the freshest state, since the current one could
// be outdated
animationStartBounds.set(previousHost.currentBounds)
animationStartClipping.set(previousHost.currentClipping)
}
val transformationType = calculateTransformationType()
var needsCrossFade = transformationType == TRANSFORMATION_TYPE_FADE
@@ -745,7 +758,7 @@ class MediaHierarchyManager @Inject constructor(
// Let's immediately apply the target state (which is interpolated) if there is
// no animation running. Otherwise the animation update will already update
// the location
applyState(targetBounds, carouselAlpha)
applyState(targetBounds, carouselAlpha, clipBounds = targetClipping)
}
}
@@ -769,9 +782,11 @@ class MediaHierarchyManager @Inject constructor(
val newBounds = endHost.currentBounds
val previousBounds = starthost.currentBounds
targetBounds = interpolateBounds(previousBounds, newBounds, progress)
targetClipping = endHost.currentClipping
} else if (endHost != null) {
val bounds = endHost.currentBounds
targetBounds.set(bounds)
targetClipping = endHost.currentClipping
}
}
@@ -879,8 +894,14 @@ class MediaHierarchyManager @Inject constructor(
/**
* Apply the current state to the view, updating it's bounds and desired state
*/
private fun applyState(bounds: Rect, alpha: Float, immediately: Boolean = false) {
private fun applyState(
bounds: Rect,
alpha: Float,
immediately: Boolean = false,
clipBounds: Rect = EMPTY_RECT
) {
currentBounds.set(bounds)
currentClipping = clipBounds
carouselAlpha = if (isCurrentlyFading()) alpha else 1.0f
val onlyUseEndState = !isCurrentlyInGuidedTransformation() || isCurrentlyFading()
val startLocation = if (onlyUseEndState) -1 else previousLocation
@@ -889,6 +910,10 @@ class MediaHierarchyManager @Inject constructor(
mediaCarouselController.setCurrentState(startLocation, endLocation, progress, immediately)
updateHostAttachment()
if (currentAttachmentLocation == IN_OVERLAY) {
// Setting the clipping on the hierarchy of `mediaFrame` does not work
if (!currentClipping.isEmpty) {
currentBounds.intersect(currentClipping)
}
mediaFrame.setLeftTopRightBottom(
currentBounds.left,
currentBounds.top,
@@ -1113,6 +1138,7 @@ class MediaHierarchyManager @Inject constructor(
const val TRANSFORMATION_TYPE_FADE = 1
}
}
private val EMPTY_RECT = Rect()
@IntDef(prefix = ["TRANSFORMATION_TYPE_"], value = [
MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION,

View File

@@ -55,6 +55,13 @@ class MediaHost constructor(
return field
}
/**
* Set the clipping that this host should use, based on its parent's bounds.
*
* Use [Rect.set].
*/
val currentClipping = Rect()
private val listener = object : MediaDataManager.Listener {
override fun onMediaDataLoaded(
key: String,

View File

@@ -247,18 +247,7 @@ internal class FooterActionsController @Inject constructor(
}
fun setExpansion(headerExpansionFraction: Float) {
if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
if (headerExpansionFraction != lastExpansion) {
if (headerExpansionFraction >= 1f) {
mView.animate().alpha(1f).setDuration(500L).start()
} else if (lastExpansion >= 1f && headerExpansionFraction < 1f) {
mView.animate().alpha(0f).setDuration(250L).start()
}
lastExpansion = headerExpansionFraction
}
} else {
alphaAnimator.setPosition(headerExpansionFraction)
}
alphaAnimator.setPosition(headerExpansionFraction)
}
fun setKeyguardShowing(showing: Boolean) {

View File

@@ -139,13 +139,17 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
}
void updateResources(QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController) {
QuickStatusBarHeaderController quickStatusBarHeaderController,
boolean newFooter) {
int bottomPadding = 0;
if (newFooter) {
bottomPadding = getResources().getDimensionPixelSize(R.dimen.qs_panel_padding_bottom);
}
mQSPanelContainer.setPaddingRelative(
mQSPanelContainer.getPaddingStart(),
Utils.getQsHeaderSystemIconsAreaHeight(mContext),
mQSPanelContainer.getPaddingEnd(),
mQSPanelContainer.getPaddingBottom()
);
bottomPadding);
int sideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
int padding = getResources().getDimensionPixelSize(

View File

@@ -18,6 +18,8 @@ package com.android.systemui.qs;
import android.content.res.Configuration;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
@@ -30,23 +32,26 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
private final QSPanelController mQsPanelController;
private final QuickStatusBarHeaderController mQuickStatusBarHeaderController;
private final ConfigurationController mConfigurationController;
private final boolean mNewFooter;
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@Override
public void onConfigChanged(Configuration newConfig) {
mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController);
mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController, mNewFooter);
}
};
@Inject
QSContainerImplController(QSContainerImpl view, QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
ConfigurationController configurationController) {
ConfigurationController configurationController,
FeatureFlags featureFlags) {
super(view);
mQsPanelController = qsPanelController;
mQuickStatusBarHeaderController = quickStatusBarHeaderController;
mConfigurationController = configurationController;
mNewFooter = featureFlags.isEnabled(Flags.NEW_FOOTER);
}
@Override
@@ -60,7 +65,7 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
@Override
protected void onViewAttached() {
mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController);
mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController, mNewFooter);
mConfigurationController.addCallback(mConfigurationListener);
}

View File

@@ -599,6 +599,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mQSPanelScrollView.getHeight());
}
mQSPanelScrollView.setClipBounds(mQsBounds);
mQsMediaHost.getCurrentClipping().set(0, 0, getView().getMeasuredWidth(),
mQSPanelScrollView.getMeasuredHeight() - mQSPanelScrollView.getPaddingBottom());
}
private void updateMediaPositions() {

View File

@@ -374,7 +374,7 @@ public class QSPanel extends LinearLayout implements Tunable {
setPaddingRelative(getPaddingStart(),
paddingTop,
getPaddingEnd(),
mUseNewFooter ? res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom) : 0);
getPaddingEnd());
}
void addOnConfigurationChangedListener(OnConfigurationChangedListener listener) {

View File

@@ -0,0 +1,80 @@
package com.android.systemui.qs
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.customize.QSCustomizer
import com.android.systemui.util.mockito.eq
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
@SmallTest
class QSContainerImplTest : SysuiTestCase() {
@Mock
private lateinit var quickStatusBarHeader: QuickStatusBarHeader
@Mock
private lateinit var qsCustomizer: QSCustomizer
@Mock
private lateinit var qsPanelContainer: NonInterceptingScrollView
@Mock
private lateinit var qsPanelController: QSPanelController
@Mock
private lateinit var quickStatusBarHeaderController: QuickStatusBarHeaderController
private lateinit var qsContainer: QSContainerImpl
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
qsContainer = QSContainerImpl(mContext, null)
setUpMockView(quickStatusBarHeader, R.id.header)
setUpMockView(qsCustomizer, R.id.qs_customize)
setUpMockView(qsPanelContainer, R.id.expanded_qs_scroll_view)
qsContainer.onFinishInflate()
}
private fun setUpMockView(view: View, id: Int) {
whenever(view.findViewById<View>(id)).thenReturn(view)
whenever(view.layoutParams).thenReturn(FrameLayout.LayoutParams(0, 0))
qsContainer.addView(view)
}
@Test
fun testContainerBottomPadding() {
qsContainer.updateResources(
qsPanelController,
quickStatusBarHeaderController,
/* newFooter */ false
)
verify(qsPanelContainer).setPaddingRelative(anyInt(), anyInt(), anyInt(), eq(0))
qsContainer.updateResources(
qsPanelController,
quickStatusBarHeaderController,
/* newFooter */ true
)
verify(qsPanelContainer)
.setPaddingRelative(
anyInt(),
anyInt(),
anyInt(),
eq(mContext.resources.getDimensionPixelSize(R.dimen.new_footer_height))
)
}
}

View File

@@ -137,20 +137,6 @@ class QSPanelTest : SysuiTestCase() {
assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(-1)
}
@Test
fun testBottomPadding() {
mQsPanel.setUseNewFooter(false)
mQsPanel.updatePadding()
assertThat(mQsPanel.paddingBottom).isEqualTo(0)
mQsPanel.setUseNewFooter(true)
mQsPanel.updatePadding()
assertThat(mQsPanel.paddingBottom)
.isEqualTo(mContext.resources.getDimensionPixelSize(R.dimen.new_footer_height))
}
private fun getNewOrientationConfig(@Configuration.Orientation newOrientation: Int) =
context.resources.configuration.apply { orientation = newOrientation }
}