diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 7dc2e19492740..ce3e27c55f3e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -565,6 +565,10 @@ public class AmbientState { } } + public float getDozeAmount() { + return mDozeAmount; + } + /** * Is the device fully awake, which is different from not tark at all when there are pulsing * notifications. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java deleted file mode 100644 index bd5b7d7df5b63..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification.stack; - -import android.animation.AnimatorListenerAdapter; -import android.content.Context; -import android.util.AttributeSet; - -import com.android.systemui.statusbar.notification.row.ExpandableView; - -/** - * Root view to insert Lock screen media controls into the notification stack. - */ -public class MediaContainerView extends ExpandableView { - - public MediaContainerView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public long performRemoveAnimation(long duration, long delay, float translationDirection, - boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable, - AnimatorListenerAdapter animationListener) { - return 0; - } - - @Override - public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear, - Runnable onEnd) { - // No animation, it doesn't need it, this would be local - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt new file mode 100644 index 0000000000000..b8f28b5a60ea6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.stack + +import android.animation.AnimatorListenerAdapter +import android.content.Context +import android.content.res.Configuration +import android.graphics.Canvas +import android.graphics.Path +import android.graphics.RectF +import android.util.AttributeSet +import com.android.systemui.R +import com.android.systemui.statusbar.notification.row.ExpandableView + +/** + * Root view to insert Lock screen media controls into the notification stack. + */ +class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableView(context, attrs) { + + var cornerRadius = 0f + var clipHeight = 0 + var clipRect = RectF() + var clipPath = Path() + + init { + setWillNotDraw(false) // Run onDraw after invalidate. + updateResources() + } + + override fun onConfigurationChanged(newConfig: Configuration?) { + super.onConfigurationChanged(newConfig) + updateResources() + } + + private fun updateResources() { + cornerRadius = context.resources + .getDimensionPixelSize(R.dimen.notification_corner_radius).toFloat() + } + + public override fun updateClipping() { + if (clipHeight != actualHeight) { + clipHeight = actualHeight + } + invalidate() + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + val bounds = canvas.clipBounds + bounds.bottom = clipHeight + clipRect.set(bounds) + + clipPath.reset() + clipPath.addRoundRect(clipRect, cornerRadius, cornerRadius, Path.Direction.CW) + canvas.clipPath(clipPath) + } + + + override fun performRemoveAnimation(duration: Long, delay: Long, translationDirection: Float, + isHeadsUpAnimation: Boolean, endLocation: Float, + onFinishedRunnable: Runnable?, + animationListener: AnimatorListenerAdapter?): Long { + return 0 + } + + override fun performAddAnimation(delay: Long, duration: Long, isHeadsUpAppear: Boolean, + onEnd: Runnable?) { + // No animation, it doesn't need it, this would be local + } +} \ No newline at end of file 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 eff8af0fb4c39..87de4ca38b4e2 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 @@ -1273,8 +1273,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * @param listenerNeedsAnimation does the listener need to animate? */ private void updateStackPosition(boolean listenerNeedsAnimation) { - // Consider interpolating from an mExpansionStartY for use on lockscreen and AOD - float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition + final float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition + mAmbientState.getOverExpansion() - getCurrentOverScrollAmount(false /* top */); final float fraction = mAmbientState.getExpansionFraction(); @@ -1284,15 +1283,31 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mOnStackYChanged.accept(listenerNeedsAnimation); } if (mQsExpansionFraction <= 0) { - final float stackEndHeight = Math.max(0f, - getHeight() - getEmptyBottomMargin() - mTopPadding); - mAmbientState.setStackEndHeight(stackEndHeight); - mAmbientState.setStackHeight( - MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION, - stackEndHeight, fraction)); + final float endHeight = updateStackEndHeight( + getHeight(), getEmptyBottomMargin(), mTopPadding); + updateStackHeight(endHeight, fraction); } } + public float updateStackEndHeight(float height, float bottomMargin, float topPadding) { + final float stackEndHeight = Math.max(0f, height - bottomMargin - topPadding); + mAmbientState.setStackEndHeight(stackEndHeight); + return stackEndHeight; + } + + public void updateStackHeight(float endHeight, float fraction) { + // During the (AOD<=>LS) transition where dozeAmount is changing, + // apply dozeAmount to stack height instead of expansionFraction + // to unfurl notifications on AOD=>LS wakeup (and furl up on LS=>AOD sleep) + final float dozeAmount = mAmbientState.getDozeAmount(); + if (0f < dozeAmount && dozeAmount < 1f) { + fraction = 1f - dozeAmount; + } + mAmbientState.setStackHeight( + MathUtils.lerp(endHeight * StackScrollAlgorithm.START_FRACTION, + endHeight, fraction)); + } + /** * Add a listener when the StackY changes. The argument signifies whether an animation is * needed. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt new file mode 100644 index 0000000000000..0909ff2e59937 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt @@ -0,0 +1,38 @@ +package com.android.systemui.statusbar.notification.stack + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.LayoutInflater +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.R +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Tests for {@link MediaContainView}. + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class MediaContainerViewTest : SysuiTestCase() { + + lateinit var mediaContainerView : MediaContainerView + + @Before + fun setUp() { + mediaContainerView = LayoutInflater.from(context).inflate( + R.layout.keyguard_media_container, null, false) as MediaContainerView + } + + @Test + fun testUpdateClipping_updatesClipHeight() { + assertTrue(mediaContainerView.clipHeight == 0) + + mediaContainerView.actualHeight = 10 + mediaContainerView.updateClipping() + assertTrue(mediaContainerView.clipHeight == 10) + } +} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 46ba097951438..bdcbbbc99ea34 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -27,6 +27,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; @@ -44,6 +45,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.util.MathUtils; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; @@ -164,6 +166,47 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { doNothing().when(mNotificationShelf).setAnimationsEnabled(anyBoolean()); } + @Test + public void testUpdateStackEndHeight_forEndOfStackHeightAnimation() { + final float nsslHeight = 10f; + final float bottomMargin = 1f; + final float topPadding = 1f; + + mStackScroller.updateStackEndHeight(nsslHeight, bottomMargin, topPadding); + final float stackEndHeight = nsslHeight - bottomMargin - topPadding; + assertTrue(mAmbientState.getStackEndHeight() == stackEndHeight); + } + + @Test + public void testUpdateStackHeight_withDozeAmount_whenDozeChanging() { + final float dozeAmount = 0.5f; + mAmbientState.setDozeAmount(dozeAmount); + + final float endHeight = 8f; + final float expansionFraction = 1f; + float expected = MathUtils.lerp( + endHeight * StackScrollAlgorithm.START_FRACTION, + endHeight, dozeAmount); + + mStackScroller.updateStackHeight(endHeight, expansionFraction); + assertTrue(mAmbientState.getStackHeight() == expected); + } + + @Test + public void testUpdateStackHeight_withExpansionAmount_whenDozeNotChanging() { + final float dozeAmount = 1f; + mAmbientState.setDozeAmount(dozeAmount); + + final float endHeight = 8f; + final float expansionFraction = 0.5f; + final float expected = MathUtils.lerp( + endHeight * StackScrollAlgorithm.START_FRACTION, + endHeight, expansionFraction); + + mStackScroller.updateStackHeight(endHeight, expansionFraction); + assertTrue(mAmbientState.getStackHeight() == expected); + } + @Test public void testNotDimmedOnKeyguard() { when(mBarState.getState()).thenReturn(StatusBarState.SHADE);