summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Fabian Kozynski <kozynski@google.com> 2022-09-06 15:46:53 -0400
committer Fabian Kozynski <kozynski@google.com> 2022-09-13 10:19:59 -0400
commitc30c4e1412eef93563bef43b456427751af417cf (patch)
treec89186660897ad1cac800f834ffb42fd2b6e092a
parent9fefe95ba6f566c5993ed84d40e10be533c4cb2e (diff)
Fade out/in header with customizer in/out
As the QSPanel doesn't reach the top of the screen, the LargeScreenShadeHeader peeks out from behind the Edit screen in certain configurations. In order to hide it, we fade it out/in together with the circular reveal. Test: manual Test: atest NotificationQSContainerControllerTest Test: atest LargeScreenShadeHeaderControllerTest Test: atest LargeScreenShadeHeaderControllerCombinedTest Fixes: 244455245 Change-Id: Ie8b8fb07b28e18b9620a948fbb6589394c4224d6
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt33
8 files changed, 130 insertions, 16 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
index 8bf982d360a1..9c7fbe8842bc 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
@@ -3,7 +3,9 @@ package com.android.systemui.plugins.qs
interface QSContainerController {
fun setCustomizerAnimating(animating: Boolean)
- fun setCustomizerShowing(showing: Boolean)
+ fun setCustomizerShowing(showing: Boolean) = setCustomizerShowing(showing, 0L)
+
+ fun setCustomizerShowing(showing: Boolean, animationDuration: Long)
fun setDetailShowing(showing: Boolean)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index b02efba93161..de11d567d858 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -39,8 +39,15 @@ public class QSDetailClipper {
mBackground = (TransitionDrawable) detail.getBackground();
}
- public void animateCircularClip(int x, int y, boolean in, AnimatorListener listener) {
- updateCircularClip(true /* animate */, x, y, in, listener);
+ /**
+ * @param x x position where animation should originate
+ * @param y y position where animation should originate
+ * @param in whether animating in or out
+ * @param listener Animation listener. Called whether or not {@code animate} is true.
+ * @return the duration of the circular animator
+ */
+ public long animateCircularClip(int x, int y, boolean in, AnimatorListener listener) {
+ return updateCircularClip(true /* animate */, x, y, in, listener);
}
/**
@@ -50,8 +57,9 @@ public class QSDetailClipper {
* @param y y position where animation should originate
* @param in whether animating in or out
* @param listener Animation listener. Called whether or not {@code animate} is true.
+ * @return the duration of the circular animator
*/
- public void updateCircularClip(boolean animate, int x, int y, boolean in,
+ public long updateCircularClip(boolean animate, int x, int y, boolean in,
AnimatorListener listener) {
if (mAnimator != null) {
mAnimator.cancel();
@@ -87,6 +95,7 @@ public class QSDetailClipper {
mAnimator.addListener(mGoneOnEnd);
}
mAnimator.start();
+ return mAnimator.getDuration();
}
private final Runnable mReverseBackground = new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 8ad011904d3d..cf10c7940871 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -125,9 +125,10 @@ public class QSCustomizer extends LinearLayout {
isShown = true;
mOpening = true;
setVisibility(View.VISIBLE);
- mClipper.animateCircularClip(mX, mY, true, new ExpandAnimatorListener(tileAdapter));
+ long duration = mClipper.animateCircularClip(
+ mX, mY, true, new ExpandAnimatorListener(tileAdapter));
mQsContainerController.setCustomizerAnimating(true);
- mQsContainerController.setCustomizerShowing(true);
+ mQsContainerController.setCustomizerShowing(true, duration);
}
}
@@ -153,13 +154,14 @@ public class QSCustomizer extends LinearLayout {
// Make sure we're not opening (because we're closing). Nobody can think we are
// customizing after the next two lines.
mOpening = false;
+ long duration = 0;
if (animate) {
- mClipper.animateCircularClip(mX, mY, false, mCollapseAnimationListener);
+ duration = mClipper.animateCircularClip(mX, mY, false, mCollapseAnimationListener);
} else {
setVisibility(View.GONE);
}
mQsContainerController.setCustomizerAnimating(animate);
- mQsContainerController.setCustomizerShowing(false);
+ mQsContainerController.setCustomizerShowing(false, duration);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 6b32daff0ab1..fe40d4cbe23a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -30,6 +30,7 @@ import androidx.constraintlayout.motion.widget.MotionLayout
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -310,6 +311,14 @@ class LargeScreenShadeHeaderController @Inject constructor(
updateVisibility()
}
+ fun startCustomizingAnimation(show: Boolean, duration: Long) {
+ header.animate()
+ .setDuration(duration)
+ .alpha(if (show) 0f else 1f)
+ .setInterpolator(if (show) Interpolators.ALPHA_OUT else Interpolators.ALPHA_IN)
+ .start()
+ }
+
private fun loadConstraints() {
if (header is MotionLayout) {
// Use resources.getXml instead of passing the resource id due to bug b/205018300
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 2a467763951c..d6f0de83ecc1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -35,6 +35,7 @@ class NotificationsQSContainerController @Inject constructor(
view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController,
private val overviewProxyService: OverviewProxyService,
+ private val largeScreenShadeHeaderController: LargeScreenShadeHeaderController,
private val featureFlags: FeatureFlags,
@Main private val delayableExecutor: DelayableExecutor
) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
@@ -156,9 +157,12 @@ class NotificationsQSContainerController @Inject constructor(
}
}
- override fun setCustomizerShowing(showing: Boolean) {
- isQSCustomizing = showing
- updateBottomSpacing()
+ override fun setCustomizerShowing(showing: Boolean, animationDuration: Long) {
+ if (showing != isQSCustomizing) {
+ isQSCustomizing = showing
+ largeScreenShadeHeaderController.startCustomizingAnimation(showing, animationDuration)
+ updateBottomSpacing()
+ }
}
override fun setDetailShowing(showing: Boolean) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index e85ffb68de54..c4485389d646 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -23,6 +23,7 @@ import android.graphics.Rect
import android.testing.AndroidTestingRunner
import android.view.DisplayCutout
import android.view.View
+import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import android.widget.TextView
import androidx.constraintlayout.motion.widget.MotionLayout
@@ -30,6 +31,7 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -64,6 +66,7 @@ import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.anyInt
@@ -614,6 +617,34 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
)
}
+ @Test
+ fun animateOutOnStartCustomizing() {
+ val animator = Mockito.mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ val duration = 1000L
+ whenever(view.animate()).thenReturn(animator)
+
+ controller.startCustomizingAnimation(show = true, duration)
+
+ verify(animator).setDuration(duration)
+ verify(animator).alpha(0f)
+ verify(animator).setInterpolator(Interpolators.ALPHA_OUT)
+ verify(animator).start()
+ }
+
+ @Test
+ fun animateInOnEndCustomizing() {
+ val animator = Mockito.mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ val duration = 1000L
+ whenever(view.animate()).thenReturn(animator)
+
+ controller.startCustomizingAnimation(show = false, duration)
+
+ verify(animator).setDuration(duration)
+ verify(animator).alpha(1f)
+ verify(animator).setInterpolator(Interpolators.ALPHA_IN)
+ verify(animator).start()
+ }
+
private fun createWindowInsets(
topCutout: Rect? = Rect()
): WindowInsets {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index 8511443705e4..5ecfc8eb3649 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -4,10 +4,12 @@ import android.app.StatusBarManager
import android.content.Context
import android.testing.AndroidTestingRunner
import android.view.View
+import android.view.ViewPropertyAnimator
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -29,8 +31,10 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when` as whenever
@@ -198,4 +202,32 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
context.getString(com.android.internal.R.string.status_bar_alarm_clock)
)
}
+
+ @Test
+ fun animateOutOnStartCustomizing() {
+ val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ val duration = 1000L
+ whenever(view.animate()).thenReturn(animator)
+
+ mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, duration)
+
+ verify(animator).setDuration(duration)
+ verify(animator).alpha(0f)
+ verify(animator).setInterpolator(Interpolators.ALPHA_OUT)
+ verify(animator).start()
+ }
+
+ @Test
+ fun animateInOnEndCustomizing() {
+ val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF)
+ val duration = 1000L
+ whenever(view.animate()).thenReturn(animator)
+
+ mLargeScreenShadeHeaderController.startCustomizingAnimation(show = false, duration)
+
+ verify(animator).setDuration(duration)
+ verify(animator).alpha(1f)
+ verify(animator).setInterpolator(Interpolators.ALPHA_IN)
+ verify(animator).start()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
index 0c6a6a98052f..12ef036d89d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
@@ -20,6 +20,7 @@ import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -33,10 +34,10 @@ import org.mockito.Mockito.doNothing
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -63,6 +64,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
@Mock
private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer
@Mock
+ private lateinit var largeScreenShadeHeaderController: LargeScreenShadeHeaderController
+ @Mock
private lateinit var featureFlags: FeatureFlags
@Captor
lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
@@ -92,6 +95,7 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
notificationsQSContainer,
navigationModeController,
overviewProxyService,
+ largeScreenShadeHeaderController,
featureFlags,
delayableExecutor
)
@@ -371,8 +375,14 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
container.removeAllViews()
container.addView(newViewWithId(1))
container.addView(newViewWithId(View.NO_ID))
- val controller = NotificationsQSContainerController(container, navigationModeController,
- overviewProxyService, featureFlags, delayableExecutor)
+ val controller = NotificationsQSContainerController(
+ container,
+ navigationModeController,
+ overviewProxyService,
+ largeScreenShadeHeaderController,
+ featureFlags,
+ delayableExecutor
+ )
controller.updateConstraints()
assertThat(container.getChildAt(0).id).isEqualTo(1)
@@ -397,6 +407,21 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
verify(notificationsQSContainer).setQSContainerPaddingBottom(STABLE_INSET_BOTTOM)
}
+ @Test
+ fun testStartCustomizingWithDuration() {
+ controller.setCustomizerShowing(true, 100L)
+ verify(largeScreenShadeHeaderController).startCustomizingAnimation(true, 100L)
+ }
+
+ @Test
+ fun testEndCustomizingWithDuration() {
+ controller.setCustomizerShowing(true, 0L) // Only tracks changes
+ reset(largeScreenShadeHeaderController)
+
+ controller.setCustomizerShowing(false, 100L)
+ verify(largeScreenShadeHeaderController).startCustomizingAnimation(false, 100L)
+ }
+
private fun disableSplitShade() {
setSplitShadeEnabled(false)
}