diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 08138e4f5a4f6..6a098f5d473da 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1246,6 +1246,15 @@
32dp
+
+ 200dp
+
+ 300dp
+
100
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 94a4fd4bdd606..90c2ab2c11a59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -666,6 +666,9 @@ public class NotificationPanelViewController extends PanelViewController {
private Optional
mNotificationPanelUnfoldAnimationController;
+ /** The drag distance required to fully expand the split shade. */
+ private int mSplitShadeFullTransitionDistance;
+
private final NotificationListContainer mNotificationListContainer;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@@ -1165,6 +1168,9 @@ public class NotificationPanelViewController extends PanelViewController {
if (splitShadeChanged) {
onSplitShadeEnabledChanged();
}
+
+ mSplitShadeFullTransitionDistance =
+ mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance);
}
private void onSplitShadeEnabledChanged() {
@@ -3054,7 +3060,16 @@ public class NotificationPanelViewController extends PanelViewController {
int maxHeight;
if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
|| mPulsing || mSplitShadeEnabled) {
- maxHeight = calculatePanelHeightQsExpanded();
+ if (mSplitShadeEnabled && mBarState == SHADE) {
+ // Max panel height is used to calculate the fraction of the shade expansion.
+ // Traditionally the value is based on the number of notifications.
+ // On split-shade, we want the required distance to be a specific and constant
+ // value, to make sure the expansion motion has the expected speed.
+ // We also only want this on non-lockscreen for now.
+ maxHeight = mSplitShadeFullTransitionDistance;
+ } else {
+ maxHeight = calculatePanelHeightQsExpanded();
+ }
} else {
maxHeight = calculatePanelHeightShade();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 5e7dc115d7f9d..0cf9a530afcb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -55,7 +55,6 @@ import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.statusbar.notification.stack.ViewState;
-import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.AlarmTimeout;
@@ -265,7 +264,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager,
ConfigurationController configurationController, @Main Executor mainExecutor,
ScreenOffAnimationController screenOffAnimationController,
- PanelExpansionStateManager panelExpansionStateManager,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
mScrimStateListener = lightBarController::setScrimState;
@@ -305,10 +303,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
ScrimController.this.onThemeChanged();
}
});
- panelExpansionStateManager.addExpansionListener(
- event -> setRawPanelExpansionFraction(event.getFraction())
- );
-
mColors = new GradientColors();
}
@@ -553,13 +547,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
*
* The expansion fraction is tied to the scrim opacity.
*
- * See {@link PanelExpansionListener#onPanelExpansionChanged}.
+ * See {@link ScrimShadeTransitionController#onPanelExpansionChanged}.
*
* @param rawPanelExpansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
*/
- @VisibleForTesting
- void setRawPanelExpansionFraction(
- @FloatRange(from = 0.0, to = 1.0) float rawPanelExpansionFraction) {
+ public void setRawPanelExpansionFraction(
+ @FloatRange(from = 0.0, to = 1.0) float rawPanelExpansionFraction) {
if (isNaN(rawPanelExpansionFraction)) {
throw new IllegalArgumentException("rawPanelExpansionFraction should not be NaN");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ScrimShadeTransitionController.kt
new file mode 100644
index 0000000000000..1740bcbbefc8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ScrimShadeTransitionController.kt
@@ -0,0 +1,78 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import android.content.res.Configuration
+import android.content.res.Resources
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.LargeScreenUtils
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/** Controls the scrim properties during the shade expansion transition on non-lockscreen. */
+@SysUISingleton
+class ScrimShadeTransitionController
+@Inject
+constructor(
+ configurationController: ConfigurationController,
+ dumpManager: DumpManager,
+ private val scrimController: ScrimController,
+ @Main private val resources: Resources
+) {
+
+ private var inSplitShade = false
+ private var splitShadeScrimTransitionDistance = 0
+ private var lastExpansionFraction: Float = 0f
+ private var lastExpansionEvent: PanelExpansionChangeEvent? = null
+
+ init {
+ updateResources()
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ }
+ })
+ dumpManager
+ .registerDumpable(ScrimShadeTransitionController::class.java.simpleName, this::dump)
+ }
+
+ private fun updateResources() {
+ inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(resources)
+ splitShadeScrimTransitionDistance =
+ resources.getDimensionPixelSize(R.dimen.split_shade_scrim_transition_distance)
+ }
+
+ fun onPanelExpansionChanged(panelExpansionChangeEvent: PanelExpansionChangeEvent) {
+ val expansionFraction = calculateScrimExpansionFraction(panelExpansionChangeEvent)
+ scrimController.setRawPanelExpansionFraction(expansionFraction)
+ lastExpansionFraction = expansionFraction
+ lastExpansionEvent = panelExpansionChangeEvent
+ }
+
+ private fun calculateScrimExpansionFraction(expansionEvent: PanelExpansionChangeEvent): Float {
+ return if (inSplitShade) {
+ expansionEvent.dragDownPxAmount / splitShadeScrimTransitionDistance
+ } else {
+ expansionEvent.fraction
+ }
+ }
+
+ private fun dump(printWriter: PrintWriter, args: Array) {
+ printWriter.println(
+ """
+ ScrimShadeTransitionController:
+ Resources:
+ inSplitShade: $inSplitShade
+ splitShadeScrimTransitionDistance: $splitShadeScrimTransitionDistance
+ State:
+ lastExpansionFraction: $lastExpansionFraction
+ lastExpansionEvent: $lastExpansionEvent
+ """.trimIndent()
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt
index 2762b9a38e922..4b4f1e06d980b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt
@@ -22,7 +22,8 @@ constructor(
panelExpansionStateManager: PanelExpansionStateManager,
private val context: Context,
private val splitShadeOverScrollerFactory: SplitShadeOverScroller.Factory,
- private val noOpOverScroller: NoOpOverScroller
+ private val noOpOverScroller: NoOpOverScroller,
+ private val scrimShadeTransitionController: ScrimShadeTransitionController
) {
lateinit var notificationPanelViewController: NotificationPanelViewController
@@ -64,6 +65,7 @@ constructor(
private fun onPanelExpansionChanged(event: PanelExpansionChangeEvent) {
shadeOverScroller.onDragDownAmountChanged(event.dragDownPxAmount)
+ scrimShadeTransitionController.onPanelExpansionChanged(event)
}
private fun propertiesInitialized() =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt
index 71050f2e7c675..d9ccc7ff23699 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt
@@ -129,7 +129,8 @@ constructor(
previousOverscrollAmount: $previousOverscrollAmount
dragDownAmount: $dragDownAmount
panelState: $panelState
- """.trimIndent())
+ """.trimIndent()
+ )
}
@AssistedFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 54bfd55e7b3a1..79c7e55d4d096 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -60,6 +60,7 @@ import android.view.View;
import android.view.ViewParent;
import android.view.ViewPropertyAnimator;
import android.view.ViewStub;
+import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -349,6 +350,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private View mQsHeader;
@Mock
private ViewParent mViewParent;
+ @Mock
+ private ViewTreeObserver mViewTreeObserver;
private NotificationPanelViewController.PanelEventsEmitter mPanelEventsEmitter;
private Optional mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
@@ -474,6 +477,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
return null;
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
+ when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
when(mView.getParent()).thenReturn(mViewParent);
when(mQs.getHeader()).thenReturn(mQsHeader);
@@ -1296,6 +1300,43 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
false/*goingToFullShade*/, SHADE/*oldStatusBarState*/);
}
+ @Test
+ public void getMaxPanelHeight_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() {
+ int splitShadeFullTransitionDistance = 123456;
+ enableSplitShade(true);
+ setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
+ mNotificationPanelViewController.expandWithQs();
+
+ int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
+
+ assertThat(maxPanelHeight).isEqualTo(splitShadeFullTransitionDistance);
+ }
+
+ @Test
+ public void getMaxPanelHeight_expandingSplitShade_keyguard_returnsNonSplitShadeValue() {
+ mStatusBarStateController.setState(KEYGUARD);
+ int splitShadeFullTransitionDistance = 123456;
+ enableSplitShade(true);
+ setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
+ mNotificationPanelViewController.expandWithQs();
+
+ int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
+
+ assertThat(maxPanelHeight).isNotEqualTo(splitShadeFullTransitionDistance);
+ }
+
+ @Test
+ public void getMaxPanelHeight_expanding_notSplitShade_returnsNonSplitShadeValue() {
+ int splitShadeFullTransitionDistance = 123456;
+ enableSplitShade(false);
+ setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
+ mNotificationPanelViewController.expandWithQs();
+
+ int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
+
+ assertThat(maxPanelHeight).isNotEqualTo(splitShadeFullTransitionDistance);
+ }
+
private static MotionEvent createMotionEvent(int x, int y, int action) {
return MotionEvent.obtain(
/* downTime= */ 0, /* eventTime= */ 0, action, x, y, /* metaState= */ 0);
@@ -1355,4 +1396,10 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private void onTouchEvent(MotionEvent ev) {
mTouchHandler.onTouch(mView, ev);
}
+
+ private void setSplitShadeFullTransitionDistance(int splitShadeFullTransitionDistance) {
+ when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance))
+ .thenReturn(splitShadeFullTransitionDistance);
+ mNotificationPanelViewController.updateResources();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 32df2d790bd19..837b0453bc9a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -59,7 +59,6 @@ import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.scrim.ScrimView;
-import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -122,8 +121,6 @@ public class ScrimControllerTest extends SysuiTestCase {
// TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
// event-dispatch-on-registration pattern caused some of these unit tests to fail.)
@Mock
- private PanelExpansionStateManager mPanelExpansionStateManager;
- @Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private static class AnimatorListener implements Animator.AnimatorListener {
@@ -237,7 +234,6 @@ public class ScrimControllerTest extends SysuiTestCase {
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
- mPanelExpansionStateManager,
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
@@ -860,7 +856,6 @@ public class ScrimControllerTest extends SysuiTestCase {
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
- mPanelExpansionStateManager,
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ScrimShadeTransitionControllerTest.kt
new file mode 100644
index 0000000000000..b24b348ac3161
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ScrimShadeTransitionControllerTest.kt
@@ -0,0 +1,69 @@
+package com.android.systemui.statusbar.phone.shade.transition
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ScrimShadeTransitionControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var scrimController: ScrimController
+ @Mock private lateinit var dumpManager: DumpManager
+ private val configurationController = FakeConfigurationController()
+
+ private lateinit var controller: ScrimShadeTransitionController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ context.ensureTestableResources()
+ controller =
+ ScrimShadeTransitionController(
+ configurationController, dumpManager, scrimController, context.resources
+ )
+ }
+ @Test
+ fun onPanelExpansionChanged_inSingleShade_setsFractionEqualToEventFraction() {
+ setSplitShadeEnabled(false)
+
+ controller.onPanelExpansionChanged(EXPANSION_EVENT)
+
+ verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_setsFractionBasedOnDragDownAmount() {
+ val scrimShadeTransitionDistance =
+ context.resources.getDimensionPixelSize(R.dimen.split_shade_scrim_transition_distance)
+ setSplitShadeEnabled(true)
+
+ controller.onPanelExpansionChanged(EXPANSION_EVENT)
+
+ val expectedFraction = EXPANSION_EVENT.dragDownPxAmount / scrimShadeTransitionDistance
+ verify(scrimController).setRawPanelExpansionFraction(expectedFraction)
+ }
+
+ private fun setSplitShadeEnabled(enabled: Boolean) {
+ overrideResource(R.bool.config_use_split_notification_shade, enabled)
+ configurationController.notifyConfigurationChanged()
+ }
+
+ companion object {
+ val EXPANSION_EVENT =
+ PanelExpansionChangeEvent(
+ fraction = 0.5f, expanded = true, tracking = true, dragDownPxAmount = 10f
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt
index 39d33e86925ea..c78a2a720d408 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt
@@ -28,6 +28,7 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
@Mock private lateinit var qs: QS
@Mock private lateinit var noOpOverScroller: NoOpOverScroller
@Mock private lateinit var splitShadeOverScroller: SplitShadeOverScroller
+ @Mock private lateinit var scrimShadeTransitionController: ScrimShadeTransitionController
private lateinit var controller: ShadeTransitionController
@@ -44,7 +45,9 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
panelExpansionStateManager,
context,
splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
- noOpOverScroller)
+ noOpOverScroller,
+ scrimShadeTransitionController
+ )
// Resetting as they are notified upon initialization.
reset(noOpOverScroller, splitShadeOverScroller)
@@ -109,7 +112,8 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
fraction = 0.5f,
expanded = true,
tracking = true,
- dragDownPxAmount = DEFAULT_DRAG_DOWN_AMOUNT)
+ dragDownPxAmount = DEFAULT_DRAG_DOWN_AMOUNT
+ )
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt
index 219737d1dfb4f..4456207c1184b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt
@@ -1,6 +1,5 @@
package com.android.systemui.statusbar.phone.shade.transition
-import org.mockito.Mockito.`when` as whenever
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -21,6 +20,7 @@ import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -43,7 +43,8 @@ class SplitShadeOverScrollerTest : SysuiTestCase() {
whenever(nsslController.height).thenReturn(1000)
overScroller =
SplitShadeOverScroller(
- configurationController, dumpManager, context, scrimController, qs, nsslController)
+ configurationController, dumpManager, context, scrimController, qs, nsslController
+ )
}
@Test