[Motion] Split-shade expansion: shorten the required drag down amount to fully expand shade
Before, the drag down amount required to fully expand the shade was the entire height of the screen. Changes: - Changes the required drag amount distance to a static value in resources. Then uses this value in Npvc#calculatePanelHeightQsExpanded. This then is used to compute the expansion fraction, which is used downstream almost everywhere. - For the scrim, instead of using the fraction, we introduce another static drag down amount distance value in resources. This is to make sure the timing looks a little better and avoid having a fully black screen when expanding. Test: Manually Test: Unit tests Bug: 214946307 Bug: 229726547 Fixes: 229997327 Change-Id: I3bb21225860b5d49c1b8f15427c9dd85bdbb8c84
This commit is contained in:
@@ -1246,6 +1246,15 @@
|
||||
<!-- Maximum overshoot for the pulse expansion -->
|
||||
<dimen name="pulse_expansion_max_top_overshoot">32dp</dimen>
|
||||
|
||||
<!-- The drag amount required for the split shade to fully expand. -->
|
||||
<dimen name="split_shade_full_transition_distance">200dp</dimen>
|
||||
<!--
|
||||
The drag amount required for the scrim to fully fade in when expanding the split shade.
|
||||
Currently setting it a little longer than the full shade transition distance, to avoid
|
||||
having a state where the screen is fully black without any content showing.
|
||||
-->
|
||||
<dimen name="split_shade_scrim_transition_distance">300dp</dimen>
|
||||
|
||||
<!-- Alpha in duration in ms for the auth ripple to become fully vislble. If set to 0,
|
||||
it is immediately visible. -->
|
||||
<integer name="auth_ripple_alpha_in_duration">100</integer>
|
||||
|
||||
@@ -666,6 +666,9 @@ public class NotificationPanelViewController extends PanelViewController {
|
||||
private Optional<NotificationPanelUnfoldAnimationController>
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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<String>) {
|
||||
printWriter.println(
|
||||
"""
|
||||
ScrimShadeTransitionController:
|
||||
Resources:
|
||||
inSplitShade: $inSplitShade
|
||||
splitShadeScrimTransitionDistance: $splitShadeScrimTransitionDistance
|
||||
State:
|
||||
lastExpansionFraction: $lastExpansionFraction
|
||||
lastExpansionEvent: $lastExpansionEvent
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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() =
|
||||
|
||||
@@ -129,7 +129,8 @@ constructor(
|
||||
previousOverscrollAmount: $previousOverscrollAmount
|
||||
dragDownAmount: $dragDownAmount
|
||||
panelState: $panelState
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
||||
@@ -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<SysUIUnfoldComponent> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user