summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSelim Cinek <cinek@google.com>2021-05-11 21:39:20 +0200
committerSelim Cinek <cinek@google.com>2021-05-14 17:20:19 +0200
commit8304b95fade5f99c26db3cbbe30cc84dc6906b2d (patch)
treeedcf02074685b05a6b1f7b9b09377dd3239cf66c
parent91579bde5974a604c7e61892232a648b74a25814 (diff)
Implemented Lockscreen to shade transition
When dragging down on the lockscreen, we now pull down the quick settings while dragging instead of wating for the release Bug: 184946919 Test: atest SystemUITests Change-Id: Ib233282dd7ce4ba63ceab3e1b788aa164e88c8c0
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java22
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java24
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml9
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml54
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml8
-rw-r--r--packages/SystemUI/res/values/dimens.xml20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java48
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt580
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java144
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java404
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java143
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java94
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt225
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java3
36 files changed, 1655 insertions, 550 deletions
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
index 0a052df43ead..044b5ed16b68 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
@@ -16,6 +16,7 @@
package com.android.systemui.animation;
+import android.util.MathUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.BounceInterpolator;
@@ -72,6 +73,27 @@ public class Interpolators {
new PathInterpolator(0.9f, 0f, 0.7f, 1f);
/**
+ * Calculate the amount of overshoot using an exponential falloff function with desired
+ * properties, where the overshoot smoothly transitions at the 1.0f boundary into the
+ * overshoot, retaining its acceleration.
+ *
+ * @param progress a progress value going from 0 to 1
+ * @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max
+ * value of the overall progress will be at 1.1.
+ * @param overshootStart the point in (0,1] where the result should reach 1
+ * @return the interpolated overshoot
+ */
+ public static float getOvershootInterpolation(float progress, float overshootAmount,
+ float overshootStart) {
+ if (overshootAmount == 0.0f || overshootStart == 0.0f) {
+ throw new IllegalArgumentException("Invalid values for overshoot");
+ }
+ float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart;
+ return MathUtils.max(0.0f,
+ (float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f));
+ }
+
+ /**
* Interpolate alpha for notifications background scrim during shade expansion.
* @param fraction Shade expansion fraction
*/
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 4d4c909d2894..98ef9e20ac3b 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -33,7 +33,7 @@ public interface QS extends FragmentBase {
String ACTION = "com.android.systemui.action.PLUGIN_QS";
- int VERSION = 8;
+ int VERSION = 9;
String TAG = "QS";
@@ -50,8 +50,13 @@ public interface QS extends FragmentBase {
void setListening(boolean listening);
boolean isShowingDetail();
void closeDetail();
- default void setShowCollapsedOnKeyguard(boolean showCollapsedOnKeyguard) {}
- void animateHeaderSlidingIn(long delay);
+
+ /**
+ * Set that we're currently pulse expanding
+ *
+ * @param pulseExpanding if we're currently expanding during pulsing
+ */
+ default void setPulseExpanding(boolean pulseExpanding) {}
void animateHeaderSlidingOut();
void setQsExpansion(float qsExpansionFraction, float headerTranslation);
void setHeaderListening(boolean listening);
@@ -79,10 +84,23 @@ public interface QS extends FragmentBase {
void setTranslateWhileExpanding(boolean shouldTranslate);
/**
+ * Set the amount of pixels we have currently dragged down if we're transitioning to the full
+ * shade. 0.0f means we're not transitioning yet.
+ */
+ default void setTransitionToFullShadeAmount(float pxAmount, boolean animated) {}
+
+ /**
* A rounded corner clipping that makes QS feel as if it were behind everything.
*/
void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible);
+ /**
+ * @return if quick settings is fully collapsed currently
+ */
+ default boolean isFullyCollapsed() {
+ return true;
+ }
+
@ProvidesInterface(version = HeightListener.VERSION)
interface HeightListener {
int VERSION = 1;
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 95483f13ec6a..0dc147324008 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -21,8 +21,7 @@
android:id="@+id/keyguard_bottom_area"
android:layout_height="match_parent"
android:layout_width="match_parent"
- android:outlineProvider="none"
- android:elevation="5dp" > <!-- Put it above the status bar header -->
+ android:outlineProvider="none" > <!-- Put it above the status bar header -->
<LinearLayout
android:id="@+id/keyguard_indication_area"
@@ -58,12 +57,6 @@
</LinearLayout>
- <FrameLayout
- android:id="@+id/preview_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- </FrameLayout>
-
<com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/camera_button"
android:layout_height="@dimen/keyguard_affordance_height"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index f4cb3b144239..3543fd11d64a 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -37,6 +37,25 @@
android:layout_height="match_parent"
android:layout_width="match_parent" />
+ <include
+ layout="@layout/keyguard_bottom_area"
+ android:visibility="gone" />
+
+ <ViewStub
+ android:id="@+id/keyguard_user_switcher_stub"
+ android:layout="@layout/keyguard_user_switcher"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent" />
+
+ <include layout="@layout/status_bar_expanded_plugin_frame"/>
+
+ <include layout="@layout/dock_info_bottom_area_overlay" />
+
+ <com.android.keyguard.LockIconView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/lock_icon_view" />
+
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -49,6 +68,18 @@
layout="@layout/keyguard_status_view"
android:visibility="gone"/>
+ <com.android.systemui.scrim.ScrimView
+ android:id="@+id/scrim_notifications"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:importantForAccessibility="no"
+ systemui:ignoreRightInset="true"
+ systemui:layout_constraintStart_toStartOf="parent"
+ systemui:layout_constraintEnd_toEndOf="parent"
+ systemui:layout_constraintTop_toTopOf="parent"
+ systemui:layout_constraintBottom_toBottomOf="parent"
+ />
+
<include layout="@layout/dock_info_overlay" />
<FrameLayout
@@ -101,22 +132,9 @@
</com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
- <include layout="@layout/dock_info_bottom_area_overlay" />
-
- <include
- layout="@layout/keyguard_bottom_area"
- android:visibility="gone" />
-
- <ViewStub
- android:id="@+id/keyguard_user_switcher_stub"
- android:layout="@layout/keyguard_user_switcher"
- android:layout_height="match_parent"
- android:layout_width="match_parent" />
-
- <include layout="@layout/status_bar_expanded_plugin_frame"/>
-
- <com.android.keyguard.LockIconView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/lock_icon_view" />
+ <FrameLayout
+ android:id="@+id/preview_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ </FrameLayout>
</com.android.systemui.statusbar.phone.NotificationPanelView>
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index bea50e87a29a..08284a0a2e3a 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -51,14 +51,6 @@
sysui:ignoreRightInset="true"
/>
- <com.android.systemui.scrim.ScrimView
- android:id="@+id/scrim_notifications"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- sysui:ignoreRightInset="true"
- />
-
<com.android.systemui.statusbar.LightRevealScrim
android:id="@+id/light_reveal_scrim"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 87a669f710db..e7d714e60076 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1409,6 +1409,26 @@
<dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
<dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
+ <!-- Delay after which the media will start transitioning to the full shade on
+ the lockscreen -->
+ <dimen name="lockscreen_shade_media_transition_start_delay">40dp</dimen>
+
+ <!-- Distance that the full shade transition takes in order for qs to fully transition to the
+ shade -->
+ <dimen name="lockscreen_shade_qs_transition_distance">200dp</dimen>
+
+ <!-- Distance that the full shade transition takes in order for scrim to fully transition to
+ the shade (in alpha) -->
+ <dimen name="lockscreen_shade_scrim_transition_distance">80dp</dimen>
+
+ <!-- Extra inset for the notifications when accounting for media during the lockscreen to
+ shade transition to compensate for the disappearing media -->
+ <dimen name="lockscreen_shade_transition_extra_media_inset">-48dp</dimen>
+
+ <!-- Maximum overshoot for the topPadding of notifications when transitioning to the full
+ shade -->
+ <dimen name="lockscreen_shade_max_top_overshoot">32dp</dimen>
+
<dimen name="people_space_widget_radius">28dp</dimen>
<dimen name="people_space_image_radius">20dp</dimen>
<dimen name="people_space_messages_count_radius">12dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 3a3f2fc40b25..388c085fd1a0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -198,6 +198,13 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
/**
+ * @return {@code true} if we are currently animating the screen off from unlock
+ */
+ public boolean isAnimatingScreenOffFromUnlocked() {
+ return mKeyguardVisibilityHelper.isAnimatingScreenOffFromUnlocked();
+ }
+
+ /**
* Set the visibility of the keyguard status view based on some new state.
*/
public void setKeyguardStatusViewVisibility(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 6aca5a702a1a..b6a58dc7cb91 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -22,6 +22,9 @@ import android.view.View;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -37,6 +40,8 @@ public class KeyguardVisibilityHelper {
private final DozeParameters mDozeParameters;
private boolean mKeyguardViewVisibilityAnimating;
private boolean mLastOccludedState = false;
+ private boolean mAnimatingScreenOff;
+ private final AnimationProperties mAnimationProperties = new AnimationProperties();
public KeyguardVisibilityHelper(View view, KeyguardStateController keyguardStateController,
DozeParameters dozeParameters) {
@@ -89,12 +94,21 @@ public class KeyguardVisibilityHelper {
} else if (statusBarState == KEYGUARD) {
if (keyguardFadingAway) {
mKeyguardViewVisibilityAnimating = true;
+ float target = mView.getY() - mView.getHeight() * 0.05f;
+ int delay = 0;
+ int duration = 125;
+ // We animate the Y properly separately using the PropertyAnimator, as the panel
+ // view als needs to update the end position.
+ mAnimationProperties.setDuration(duration).setDelay(delay);
+ PropertyAnimator.cancelAnimation(mView, AnimatableProperty.Y);
+ PropertyAnimator.setProperty(mView, AnimatableProperty.Y, target,
+ mAnimationProperties,
+ true /* animate */);
mView.animate()
.alpha(0)
- .translationYBy(-mView.getHeight() * 0.05f)
.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
- .setDuration(125)
- .setStartDelay(0)
+ .setDuration(duration)
+ .setStartDelay(delay)
.withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
.start();
} else if (mLastOccludedState && !isOccluded) {
@@ -110,20 +124,30 @@ public class KeyguardVisibilityHelper {
.start();
} else if (mDozeParameters.shouldControlUnlockedScreenOff()) {
mKeyguardViewVisibilityAnimating = true;
+ mAnimatingScreenOff = true;
mView.setVisibility(View.VISIBLE);
mView.setAlpha(0f);
+ float currentY = mView.getY();
+ mView.setY(currentY - mView.getHeight() * 0.1f);
+ int duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+ int delay = (int) (duration * .6f);
+ // We animate the Y properly separately using the PropertyAnimator, as the panel
+ // view als needs to update the end position.
+ mAnimationProperties.setDuration(duration).setDelay(delay);
+ PropertyAnimator.cancelAnimation(mView, AnimatableProperty.Y);
+ PropertyAnimator.setProperty(mView, AnimatableProperty.Y, currentY,
+ mAnimationProperties,
+ true /* animate */);
- float curTranslationY = mView.getTranslationY();
- mView.setTranslationY(curTranslationY - mView.getHeight() * 0.1f);
mView.animate()
- .setStartDelay((int) (StackStateAnimator.ANIMATION_DURATION_WAKEUP * .6f))
- .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP)
+ .setStartDelay(delay)
+ .setDuration(duration)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(1f)
- .translationY(curTranslationY)
.withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
.start();
+
} else {
mView.setVisibility(View.VISIBLE);
mView.setAlpha(1f);
@@ -148,5 +172,13 @@ public class KeyguardVisibilityHelper {
private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
mKeyguardViewVisibilityAnimating = false;
+ mAnimatingScreenOff = false;
};
+
+ /**
+ * @return {@code true} if we are currently animating the screen off from unlock
+ */
+ public boolean isAnimatingScreenOffFromUnlocked() {
+ return mAnimatingScreenOff;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index b367bdf08886..7127444befb7 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -145,7 +145,13 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
final boolean hasUdfps = mAuthController.getUdfpsSensorLocation() != null;
mHasUdfpsOrFaceAuthFeatures = hasFaceAuth || hasUdfps;
if (!mHasUdfpsOrFaceAuthFeatures) {
- ((ViewGroup) mView.getParent()).removeView(mView);
+ // Posting since removing a view in the middle of onAttach can lead to a crash in the
+ // iteration loop when the view isn't last
+ mView.setVisibility(View.GONE);
+ mView.post(() -> {
+ mView.setVisibility(View.VISIBLE);
+ ((ViewGroup) mView.getParent()).removeView(mView);
+ });
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index b668e881230d..241c2a939f9a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -64,6 +64,12 @@ class KeyguardMediaController @Inject constructor(
mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN)
}
+ /**
+ * Is the media player visible?
+ */
+ var visible = false
+ private set
+
var visibilityChangedListener: ((Boolean) -> Unit)? = null
/**
@@ -106,11 +112,11 @@ class KeyguardMediaController @Inject constructor(
val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD ||
statusBarStateController.state == StatusBarState.FULLSCREEN_USER_SWITCHER)
// mediaHost.visible required for proper animations handling
- val shouldBeVisible = mediaHost.visible &&
+ visible = mediaHost.visible &&
!bypassController.bypassEnabled &&
keyguardOrUserSwitcher &&
notifLockscreenUserManager.shouldShowLockscreenNotifications()
- if (shouldBeVisible) {
+ if (visible) {
showMediaPlayer()
} else {
hideMediaPlayer()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 60e832ace7b0..73dfe5e68d9a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -26,6 +26,7 @@ import android.util.MathUtils
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
+import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -35,6 +36,7 @@ import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import javax.inject.Inject
@@ -74,6 +76,7 @@ class MediaHierarchyManager @Inject constructor(
private val bypassController: KeyguardBypassController,
private val mediaCarouselController: MediaCarouselController,
private val notifLockscreenUserManager: NotificationLockscreenUserManager,
+ configurationController: ConfigurationController,
wakefulnessLifecycle: WakefulnessLifecycle,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager
) {
@@ -183,6 +186,58 @@ class MediaHierarchyManager @Inject constructor(
}
/**
+ * distance that the full shade transition takes in order for media to fully transition to the
+ * shade
+ */
+ private var distanceForFullShadeTransition = 0
+
+ /**
+ * Delay after which the media will start transitioning to the full shade on the lockscreen.
+ */
+ private var fullShadeTransitionDelay = 0
+
+ /**
+ * The amount of progress we are currently in if we're transitioning to the full shade.
+ * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+ * shade.
+ */
+ private var fullShadeTransitionProgress = 0f
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ if (bypassController.bypassEnabled) {
+ return
+ }
+ updateDesiredLocation()
+ if (value >= 0) {
+ updateTargetState()
+ applyTargetStateIfNotAnimating()
+ }
+ }
+
+ private val isTransitioningToFullShade: Boolean
+ get() = fullShadeTransitionProgress != 0f && !bypassController.bypassEnabled
+
+ /**
+ * Set the amount of pixels we have currently dragged down if we're transitioning to the full
+ * shade. 0.0f means we're not transitioning yet.
+ */
+ fun setTransitionToFullShadeAmount(value: Float) {
+ // If we're transitioning starting on the shade_locked, we don't want any delay and rather
+ // have it aligned with the rest of the animation
+ val delay = if (statusbarState == StatusBarState.KEYGUARD) {
+ fullShadeTransitionDelay
+ } else {
+ 0
+ }
+ val progress = MathUtils.saturate((value - delay) /
+ (distanceForFullShadeTransition - delay))
+ fullShadeTransitionProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(progress)
+ }
+
+ /**
* Is the shade currently collapsing from the expanded qs? If we're on the lockscreen and in qs,
* we wouldn't want to transition in that case.
*/
@@ -242,6 +297,12 @@ class MediaHierarchyManager @Inject constructor(
}
init {
+ updateConfiguration()
+ configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
+ override fun onDensityOrFontScaleChanged() {
+ updateConfiguration()
+ }
+ })
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
override fun onStatePreChange(oldState: Int, newState: Int) {
// We're updating the location before the state change happens, since we want the
@@ -312,6 +373,13 @@ class MediaHierarchyManager @Inject constructor(
})
}
+ private fun updateConfiguration() {
+ distanceForFullShadeTransition = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_qs_transition_distance)
+ fullShadeTransitionDelay = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_media_transition_start_delay)
+ }
+
/**
* Register a media host and create a view can be attached to a view hierarchy
* and where the players will be placed in when the host is the currently desired state.
@@ -546,6 +614,9 @@ class MediaHierarchyManager @Inject constructor(
if (progress >= 0) {
return progress
}
+ if (isTransitioningToFullShade) {
+ return fullShadeTransitionProgress
+ }
return -1.0f
}
@@ -643,6 +714,7 @@ class MediaHierarchyManager @Inject constructor(
val location = when {
qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
+ onLockscreen && isTransitioningToFullShade -> LOCATION_QQS
onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 53b4d5f7d5f8..34c654c9135d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -112,6 +112,17 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
* otherwise.
*/
private boolean mTranslateWhileExpanding;
+ private boolean mPulseExpanding;
+
+ /**
+ * Are we currently transitioning from lockscreen to the full shade?
+ */
+ private boolean mTransitioningToFullShade;
+
+ /**
+ * Whether the next Quick settings
+ */
+ private boolean mAnimateNextQsUpdate;
@Inject
public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
@@ -265,6 +276,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
}
+ @Override
+ public boolean isFullyCollapsed() {
+ return mLastQSExpansion == 0.0f || mLastQSExpansion == -1;
+ }
+
private void setEditLocation(View view) {
View edit = view.findViewById(android.R.id.edit);
int[] loc = edit.getLocationOnScreen();
@@ -335,14 +351,22 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
@Override
- public void setShowCollapsedOnKeyguard(boolean showCollapsedOnKeyguard) {
- if (showCollapsedOnKeyguard != mShowCollapsedOnKeyguard) {
- mShowCollapsedOnKeyguard = showCollapsedOnKeyguard;
+ public void setPulseExpanding(boolean pulseExpanding) {
+ if (pulseExpanding != mPulseExpanding) {
+ mPulseExpanding = pulseExpanding;
+ updateShowCollapsedOnKeyguard();
+ }
+ }
+
+ private void updateShowCollapsedOnKeyguard() {
+ boolean showCollapsed = mPulseExpanding || mTransitioningToFullShade;
+ if (showCollapsed != mShowCollapsedOnKeyguard) {
+ mShowCollapsedOnKeyguard = showCollapsed;
updateQsState();
if (mQSAnimator != null) {
- mQSAnimator.setShowCollapsedOnKeyguard(showCollapsedOnKeyguard);
+ mQSAnimator.setShowCollapsedOnKeyguard(showCollapsed);
}
- if (!showCollapsedOnKeyguard && isKeyguardShowing()) {
+ if (!showCollapsed && isKeyguardShowing()) {
setQsExpansion(mLastQSExpansion, 0);
}
}
@@ -411,13 +435,24 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
@Override
- public void setQsExpansion(float expansion, float headerTranslation) {
- if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
+ public void setTransitionToFullShadeAmount(float pxAmount, boolean animated) {
+ boolean isTransitioningToFullShade = pxAmount > 0;
+ if (isTransitioningToFullShade != mTransitioningToFullShade) {
+ mTransitioningToFullShade = isTransitioningToFullShade;
+ updateShowCollapsedOnKeyguard();
+ setQsExpansion(mLastQSExpansion, mLastHeaderTranslation);
+ }
+ }
+ @Override
+ public void setQsExpansion(float expansion, float proposedTranslation) {
+ if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + proposedTranslation);
+ float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
if (mQSAnimator != null) {
final boolean showQSOnLockscreen = expansion > 0;
final boolean showQSUnlocked = headerTranslation == 0 || !mTranslateWhileExpanding;
- mQSAnimator.startAlphaAnimation(showQSOnLockscreen || showQSUnlocked);
+ mQSAnimator.startAlphaAnimation(showQSOnLockscreen || showQSUnlocked
+ || mTransitioningToFullShade);
}
mContainer.setExpansion(expansion);
final float translationScaleY = (mTranslateWhileExpanding
@@ -542,18 +577,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
@Override
- public void animateHeaderSlidingIn(long delay) {
- if (DEBUG) Log.d(TAG, "animateHeaderSlidingIn");
- // If the QS is already expanded we don't need to slide in the header as it's already
- // visible.
- if (!mQsExpanded && getView().getTranslationY() != 0) {
- mHeaderAnimating = true;
- mDelay = delay;
- getView().getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
- }
- }
-
- @Override
public void animateHeaderSlidingOut() {
if (DEBUG) Log.d(TAG, "animateHeaderSlidingOut");
if (getView().getY() == -mHeader.getHeight()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 6d91ccba0156..12f569ce9fbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -5,35 +5,489 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
+import android.content.res.Configuration
+import android.os.SystemClock
+import android.util.DisplayMetrics
+import android.util.MathUtils
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
+import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent
import com.android.systemui.ExpandHelper
import com.android.systemui.Gefingerpoken
-import com.android.systemui.animation.Interpolators
import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.MediaHierarchyManager
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.stack.AmbientState
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.Utils
+import javax.inject.Inject
+
+private const val SPRING_BACK_ANIMATION_LENGTH_MS = 375L
+private const val RUBBERBAND_FACTOR_STATIC = 0.15f
+private const val RUBBERBAND_FACTOR_EXPANDABLE = 0.5f
+
+/**
+ * A class that controls the lockscreen to shade transition
+ */
+@SysUISingleton
+class LockscreenShadeTransitionController @Inject constructor(
+ private val statusBarStateController: SysuiStatusBarStateController,
+ private val lockscreenGestureLogger: LockscreenGestureLogger,
+ private val keyguardBypassController: KeyguardBypassController,
+ private val lockScreenUserManager: NotificationLockscreenUserManager,
+ private val falsingCollector: FalsingCollector,
+ private val ambientState: AmbientState,
+ private val displayMetrics: DisplayMetrics,
+ private val mediaHierarchyManager: MediaHierarchyManager,
+ private val scrimController: ScrimController,
+ private val featureFlags: FeatureFlags,
+ private val context: Context,
+ configurationController: ConfigurationController,
+ falsingManager: FalsingManager
+) {
+ private var useSplitShade: Boolean = false
+ private lateinit var nsslController: NotificationStackScrollLayoutController
+ lateinit var notificationPanelController: NotificationPanelViewController
+ lateinit var statusbar: StatusBar
+ lateinit var qS: QS
+
+ /**
+ * A handler that handles the next keyguard dismiss animation.
+ */
+ private var animationHandlerOnKeyguardDismiss: ((Long) -> Unit)? = null
+
+ /**
+ * The entry that was just dragged down on.
+ */
+ private var draggedDownEntry: NotificationEntry? = null
+
+ /**
+ * The current animator if any
+ */
+ @VisibleForTesting
+ internal var dragDownAnimator: ValueAnimator? = null
+
+ /**
+ * Distance that the full shade transition takes in order for scrim to fully transition to
+ * the shade (in alpha)
+ */
+ private var scrimTransitionDistance = 0
+
+ /**
+ * Distance that the full transition takes in order for us to fully transition to the shade
+ */
+ private var fullTransitionDistance = 0
+
+ /**
+ * Flag to make sure that the dragDownAmount is applied to the listeners even when in the
+ * locked down shade.
+ */
+ private var forceApplyAmount = false
+
+ /**
+ * A flag to suppress the default animation when unlocking in the locked down shade.
+ */
+ private var nextHideKeyguardNeedsNoAnimation = false
+
+ /**
+ * The touch helper responsible for the drag down animation.
+ */
+ val touchHelper = DragDownHelper(falsingManager, falsingCollector, this, context)
+
+ init {
+ updateResources()
+ configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ touchHelper.updateResources(context)
+ }
+ })
+ }
+
+ private fun updateResources() {
+ scrimTransitionDistance = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_scrim_transition_distance)
+ fullTransitionDistance = context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_qs_transition_distance)
+ useSplitShade = Utils.shouldUseSplitNotificationShade(featureFlags, context.resources)
+ }
+
+ fun setStackScroller(nsslController: NotificationStackScrollLayoutController) {
+ this.nsslController = nsslController
+ touchHelper.host = nsslController.view
+ touchHelper.expandCallback = nsslController.expandHelperCallback
+ }
+
+ /**
+ * Initialize the shelf controller such that clicks on it will expand the shade
+ */
+ fun bindController(notificationShelfController: NotificationShelfController) {
+ // Bind the click listener of the shelf to go to the full shade
+ notificationShelfController.setOnClickListener {
+ if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ statusbar.wakeUpIfDozing(SystemClock.uptimeMillis(), it, "SHADE_CLICK")
+ goToLockedShade(it)
+ }
+ }
+ }
+
+ /**
+ * @return true if the interaction is accepted, false if it should be cancelled
+ */
+ internal fun canDragDown(): Boolean {
+ return (statusBarStateController.state == StatusBarState.KEYGUARD ||
+ nsslController.isInLockedDownShade()) &&
+ qS.isFullyCollapsed
+ }
+
+ /**
+ * Called by the touch helper when when a gesture has completed all the way and released.
+ */
+ internal fun onDraggedDown(startingChild: View?, dragLengthY: Int) {
+ if (canDragDown()) {
+ if (nsslController.isInLockedDownShade()) {
+ statusBarStateController.setLeaveOpenOnKeyguardHide(true)
+ statusbar.dismissKeyguardThenExecute(OnDismissAction {
+ nextHideKeyguardNeedsNoAnimation = true
+ false
+ },
+ null /* cancelRunnable */, false /* afterKeyguardGone */)
+ } else {
+ lockscreenGestureLogger.write(
+ MetricsEvent.ACTION_LS_SHADE,
+ (dragLengthY / displayMetrics.density).toInt(),
+ 0 /* velocityDp */)
+ lockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN)
+ if (!ambientState.isDozing() || startingChild != null) {
+ // go to locked shade while animating the drag down amount from its current
+ // value
+ val animationHandler = { delay: Long ->
+ if (startingChild is ExpandableNotificationRow) {
+ startingChild.onExpandedByGesture(
+ true /* drag down is always an open */)
+ }
+ notificationPanelController.animateToFullShade(delay)
+ notificationPanelController.setTransitionToFullShadeAmount(0f,
+ true /* animated */, delay)
+
+ // Let's reset ourselves, ready for the next animation
+
+ // changing to shade locked will make isInLockDownShade true, so let's
+ // override that
+ forceApplyAmount = true
+ // Reset the behavior. At this point the animation is already started
+ dragDownAmount = 0f
+ forceApplyAmount = false
+ }
+ val cancelRunnable = Runnable { setDragDownAmountAnimated(0f) }
+ goToLockedShadeInternal(startingChild, animationHandler, cancelRunnable)
+ }
+ }
+ } else {
+ setDragDownAmountAnimated(0f)
+ }
+ }
+
+ /**
+ * Called by the touch helper when the drag down was aborted and should be reset.
+ */
+ internal fun onDragDownReset() {
+ nsslController.setDimmed(true /* dimmed */, true /* animated */)
+ nsslController.resetScrollPosition()
+ nsslController.resetCheckSnoozeLeavebehind()
+ setDragDownAmountAnimated(0f)
+ }
+
+ /**
+ * The user has dragged either above or below the threshold which changes the dimmed state.
+ * @param above whether they dragged above it
+ */
+ internal fun onCrossedThreshold(above: Boolean) {
+ nsslController.setDimmed(!above /* dimmed */, true /* animate */)
+ }
+
+ /**
+ * Called by the touch helper when the drag down was started
+ */
+ internal fun onDragDownStarted() {
+ nsslController.cancelLongPress()
+ nsslController.checkSnoozeLeavebehind()
+ dragDownAnimator?.cancel()
+ }
+
+ /**
+ * Do we need a falsing check currently?
+ */
+ internal val isFalsingCheckNeeded: Boolean
+ get() = statusBarStateController.state == StatusBarState.KEYGUARD
+
+ /**
+ * Is dragging down enabled on a given view
+ * @param view The view to check or `null` to check if it's enabled at all
+ */
+ internal fun isDragDownEnabledForView(view: ExpandableView?): Boolean {
+ if (isDragDownAnywhereEnabled) {
+ return true
+ }
+ if (nsslController.isInLockedDownShade()) {
+ if (view == null) {
+ // Dragging down is allowed in general
+ return true
+ }
+ if (view is ExpandableNotificationRow) {
+ // Only drag down on sensitive views, otherwise the ExpandHelper will take this
+ return view.entry.isSensitive
+ }
+ }
+ return false
+ }
+
+ /**
+ * @return if drag down is enabled anywhere, not just on selected views.
+ */
+ internal val isDragDownAnywhereEnabled: Boolean
+ get() = (statusBarStateController.getState() == StatusBarState.KEYGUARD &&
+ !keyguardBypassController.bypassEnabled &&
+ qS.isFullyCollapsed)
+
+ /**
+ * The amount in pixels that the user has dragged down.
+ */
+ internal var dragDownAmount = 0f
+ set(value) {
+ if (field != value || forceApplyAmount) {
+ field = value
+ if (!nsslController.isInLockedDownShade() || forceApplyAmount) {
+ nsslController.setTransitionToFullShadeAmount(field)
+ notificationPanelController.setTransitionToFullShadeAmount(field,
+ false /* animate */, 0 /* delay */)
+ mediaHierarchyManager.setTransitionToFullShadeAmount(field)
+ val scrimProgress = MathUtils.saturate(field / scrimTransitionDistance)
+ scrimController.setTransitionToFullShadeProgress(scrimProgress)
+ // TODO: appear qs also in split shade
+ val qsAmount = if (useSplitShade) 0f else field
+ qS.setTransitionToFullShadeAmount(qsAmount, false /* animate */)
+ }
+ }
+ }
+
+ private fun setDragDownAmountAnimated(
+ target: Float,
+ delay: Long = 0,
+ endlistener: (() -> Unit)? = null
+ ) {
+ val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target)
+ dragDownAnimator.interpolator = Interpolators.FAST_OUT_SLOW_IN
+ dragDownAnimator.duration = SPRING_BACK_ANIMATION_LENGTH_MS
+ dragDownAnimator.addUpdateListener { animation: ValueAnimator ->
+ dragDownAmount = animation.animatedValue as Float
+ }
+ if (delay > 0) {
+ dragDownAnimator.startDelay = delay
+ }
+ if (endlistener != null) {
+ dragDownAnimator.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ endlistener.invoke()
+ }
+ })
+ }
+ dragDownAnimator.start()
+ this.dragDownAnimator = dragDownAnimator
+ }
+
+ /**
+ * Animate appear the drag down amount.
+ */
+ private fun animateAppear(delay: Long = 0) {
+ // changing to shade locked will make isInLockDownShade true, so let's override
+ // that
+ forceApplyAmount = true
+
+ // we set the value initially to 1 pixel, since that will make sure we're
+ // transitioning to the full shade. this is important to avoid flickering,
+ // as the below animation only starts once the shade is unlocked, which can
+ // be a couple of frames later. if we're setting it to 0, it will use the
+ // default inset and therefore flicker
+ dragDownAmount = 1f
+ setDragDownAmountAnimated(fullTransitionDistance.toFloat(), delay = delay) {
+ // End listener:
+ // Reset
+ dragDownAmount = 0f
+ forceApplyAmount = false
+ }
+ }
+
+ /**
+ * Ask this controller to go to the locked shade, changing the state change and doing
+ * an animation, where the qs appears from 0 from the top
+ *
+ * If secure with redaction: Show bouncer, go to unlocked shade.
+ * If secure without redaction or no security: Go to [StatusBarState.SHADE_LOCKED].
+ *
+ * @param expandView The view to expand after going to the shade
+ * @param needsQSAnimation if this needs the quick settings to slide in from the top or if
+ * that's already handled separately
+ */
+ @JvmOverloads
+ fun goToLockedShade(expandedView: View?, needsQSAnimation: Boolean = true) {
+ if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ val animationHandler: ((Long) -> Unit)?
+ if (needsQSAnimation) {
+ // Let's use the default animation
+ animationHandler = null
+ } else {
+ // Let's only animate notifications
+ animationHandler = { delay: Long ->
+ notificationPanelController.animateToFullShade(delay)
+ }
+ }
+ goToLockedShadeInternal(expandedView, animationHandler,
+ cancelAction = null)
+ }
+ }
+
+ /**
+ * If secure with redaction: Show bouncer, go to unlocked shade.
+ *
+ * If secure without redaction or no security: Go to [StatusBarState.SHADE_LOCKED].
+ *
+ * @param expandView The view to expand after going to the shade.
+ * @param animationHandler The handler which performs the go to full shade animation. If null,
+ * the default handler will do the animation, otherwise the caller is
+ * responsible for the animation. The input value is a Long for the
+ * delay for the animation.
+ * @param cancelAction The runnable to invoke when the transition is aborted. This happens if
+ * the user goes to the bouncer and goes back.
+ */
+ private fun goToLockedShadeInternal(
+ expandView: View?,
+ animationHandler: ((Long) -> Unit)? = null,
+ cancelAction: Runnable? = null
+ ) {
+ if (statusbar.isShadeDisabled) {
+ cancelAction?.run()
+ return
+ }
+ var userId: Int = lockScreenUserManager.getCurrentUserId()
+ var entry: NotificationEntry? = null
+ if (expandView is ExpandableNotificationRow) {
+ entry = expandView.entry
+ entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */)
+ // Indicate that the group expansion is changing at this time -- this way the group
+ // and children backgrounds / divider animations will look correct.
+ entry.setGroupExpansionChanging(true)
+ userId = entry.sbn.userId
+ }
+ var fullShadeNeedsBouncer = (!lockScreenUserManager.userAllowsPrivateNotificationsInPublic(
+ lockScreenUserManager.getCurrentUserId()) ||
+ !lockScreenUserManager.shouldShowLockscreenNotifications() ||
+ falsingCollector.shouldEnforceBouncer())
+ if (keyguardBypassController.bypassEnabled) {
+ fullShadeNeedsBouncer = false
+ }
+ if (lockScreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
+ statusBarStateController.setLeaveOpenOnKeyguardHide(true)
+ var onDismissAction: OnDismissAction? = null
+ if (animationHandler != null) {
+ onDismissAction = OnDismissAction {
+ // We're waiting on keyguard to hide before triggering the action,
+ // as that will make the animation work properly
+ animationHandlerOnKeyguardDismiss = animationHandler
+ false
+ }
+ }
+ val cancelHandler = Runnable {
+ draggedDownEntry?.apply {
+ setUserLocked(false)
+ notifyHeightChanged(false /* needsAnimation */)
+ draggedDownEntry = null
+ }
+ cancelAction?.run()
+ }
+ statusbar.showBouncerWithDimissAndCancelIfKeyguard(onDismissAction, cancelHandler)
+ draggedDownEntry = entry
+ } else {
+ statusBarStateController.setState(StatusBarState.SHADE_LOCKED)
+ // This call needs to be after updating the shade state since otherwise
+ // the scrimstate resets too early
+ if (animationHandler != null) {
+ animationHandler.invoke(0 /* delay */)
+ } else {
+ performDefaultGoToFullShadeAnimation(0)
+ }
+ }
+ }
+
+ /**
+ * Notify this handler that the keyguard was just dismissed and that a animation to
+ * the full shade should happen.
+ */
+ fun onHideKeyguard(delay: Long) {
+ if (animationHandlerOnKeyguardDismiss != null) {
+ animationHandlerOnKeyguardDismiss!!.invoke(delay)
+ animationHandlerOnKeyguardDismiss = null
+ } else {
+ if (nextHideKeyguardNeedsNoAnimation) {
+ nextHideKeyguardNeedsNoAnimation = false
+ } else {
+ performDefaultGoToFullShadeAnimation(delay)
+ }
+ }
+ draggedDownEntry?.apply {
+ setUserLocked(false)
+ draggedDownEntry = null
+ }
+ }
+
+ /**
+ * Perform the default appear animation when going to the full shade. This is called when
+ * not triggered by gestures, e.g. when clicking on the shelf or expand button.
+ */
+ private fun performDefaultGoToFullShadeAnimation(delay: Long) {
+ notificationPanelController.animateToFullShade(delay)
+ animateAppear(delay)
+ }
+}
/**
* A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand
* the notification where the drag started.
*/
-class DragDownHelper(private val falsingManager: FalsingManager,
- private val expandCallback: ExpandHelper.Callback,
- private val dragDownCallback: DragDownCallback,
- private val falsingCollector: FalsingCollector,
- private val host: View,
- context: Context): Gefingerpoken {
-
- private val minDragDistance: Int
+class DragDownHelper(
+ private val falsingManager: FalsingManager,
+ private val falsingCollector: FalsingCollector,
+ private val dragDownCallback: LockscreenShadeTransitionController,
+ context: Context
+) : Gefingerpoken {
+
+ private var dragDownAmountOnStart = 0.0f
+ lateinit var expandCallback: ExpandHelper.Callback
+ lateinit var host: View
+
+ private var minDragDistance = 0
private var initialTouchX = 0f
private var initialTouchY = 0f
- private val touchSlop: Float
- private val slopMultiplier: Float
+ private var touchSlop = 0f
+ private var slopMultiplier = 0f
private val temp2 = IntArray(2)
private var draggedFarEnough = false
private var startingChild: ExpandableView? = null
@@ -41,7 +495,23 @@ class DragDownHelper(private val falsingManager: FalsingManager,
var isDraggingDown = false
private set
+ private val isFalseTouch: Boolean
+ get() {
+ return if (!dragDownCallback.isFalsingCheckNeeded) {
+ false
+ } else {
+ falsingManager.isFalseTouch(Classifier.NOTIFICATION_DRAG_DOWN) || !draggedFarEnough
+ }
+ }
+
+ val isDragDownEnabled: Boolean
+ get() = dragDownCallback.isDragDownEnabledForView(null)
+
init {
+ updateResources(context)
+ }
+
+ fun updateResources(context: Context) {
minDragDistance = context.resources.getDimensionPixelSize(
R.dimen.keyguard_drag_down_min_distance)
val configuration = ViewConfiguration.get(context)
@@ -74,7 +544,8 @@ class DragDownHelper(private val falsingManager: FalsingManager,
captureStartingChild(initialTouchX, initialTouchY)
initialTouchY = y
initialTouchX = x
- dragDownCallback.onTouchSlopExceeded()
+ dragDownCallback.onDragDownStarted()
+ dragDownAmountOnStart = dragDownCallback.dragDownAmount
return startingChild != null || dragDownCallback.isDragDownAnywhereEnabled
}
}
@@ -92,10 +563,9 @@ class DragDownHelper(private val falsingManager: FalsingManager,
MotionEvent.ACTION_MOVE -> {
lastHeight = y - initialTouchY
captureStartingChild(initialTouchX, initialTouchY)
+ dragDownCallback.dragDownAmount = lastHeight + dragDownAmountOnStart
if (startingChild != null) {
handleExpansion(lastHeight, startingChild!!)
- } else {
- dragDownCallback.setEmptyDragAmount(lastHeight)
}
if (lastHeight > minDragDistance) {
if (!draggedFarEnough) {
@@ -110,12 +580,10 @@ class DragDownHelper(private val falsingManager: FalsingManager,
}
return true
}
- MotionEvent.ACTION_UP -> if (!falsingManager.isUnlockingDisabled && !isFalseTouch
- && dragDownCallback.canDragDown()) {
+ MotionEvent.ACTION_UP -> if (!falsingManager.isUnlockingDisabled && !isFalseTouch &&
+ dragDownCallback.canDragDown()) {
dragDownCallback.onDraggedDown(startingChild, (y - initialTouchY).toInt())
- if (startingChild == null) {
- cancelExpansion()
- } else {
+ if (startingChild != null) {
expandCallback.setUserLockedChild(startingChild, false)
startingChild = null
}
@@ -132,15 +600,6 @@ class DragDownHelper(private val falsingManager: FalsingManager,
return false
}
- private val isFalseTouch: Boolean
- get() {
- return if (!dragDownCallback.isFalsingCheckNeeded) {
- false
- } else {
- falsingManager.isFalseTouch(Classifier.NOTIFICATION_DRAG_DOWN) || !draggedFarEnough
- }
- }
-
private fun captureStartingChild(x: Float, y: Float) {
if (startingChild == null) {
startingChild = findView(x, y)
@@ -174,7 +633,7 @@ class DragDownHelper(private val falsingManager: FalsingManager,
child.actualHeight = (child.collapsedHeight + rubberband).toInt()
}
- private fun cancelExpansion(child: ExpandableView) {
+ private fun cancelChildExpansion(child: ExpandableView) {
if (child.actualHeight == child.collapsedHeight) {
expandCallback.setUserLockedChild(child, false)
return
@@ -182,7 +641,7 @@ class DragDownHelper(private val falsingManager: FalsingManager,
val anim = ObjectAnimator.ofInt(child, "actualHeight",
child.actualHeight, child.collapsedHeight)
anim.interpolator = Interpolators.FAST_OUT_SLOW_IN
- anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
+ anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
expandCallback.setUserLockedChild(child, false)
@@ -191,73 +650,18 @@ class DragDownHelper(private val falsingManager: FalsingManager,
anim.start()
}
- private fun cancelExpansion() {
- val anim = ValueAnimator.ofFloat(lastHeight, 0f)
- anim.interpolator = Interpolators.FAST_OUT_SLOW_IN
- anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
- anim.addUpdateListener { animation: ValueAnimator ->
- dragDownCallback.setEmptyDragAmount(animation.animatedValue as Float)
- }
- anim.start()
- }
-
private fun stopDragging() {
falsingCollector.onNotificationStopDraggingDown()
if (startingChild != null) {
- cancelExpansion(startingChild!!)
+ cancelChildExpansion(startingChild!!)
startingChild = null
- } else {
- cancelExpansion()
}
isDraggingDown = false
dragDownCallback.onDragDownReset()
}
- private fun findView(x: Float, y: Float): ExpandableView {
+ private fun findView(x: Float, y: Float): ExpandableView? {
host.getLocationOnScreen(temp2)
- return expandCallback.getChildAtRawPosition(x + temp2[0] , y + temp2[1])
- }
-
- val isDragDownEnabled: Boolean
- get() = dragDownCallback.isDragDownEnabledForView(null)
-
- interface DragDownCallback {
-
- /**
- * @return true if the interaction is accepted, false if it should be cancelled
- */
- fun canDragDown(): Boolean
-
- /**
- * Call when a view has been dragged.
- **/
- fun onDraggedDown(startingChild: View?, dragLengthY: Int)
- fun onDragDownReset()
-
- /**
- * The user has dragged either above or below the threshold
- * @param above whether they dragged above it
- */
- fun onCrossedThreshold(above: Boolean)
- fun onTouchSlopExceeded()
- fun setEmptyDragAmount(amount: Float)
- val isFalsingCheckNeeded: Boolean
-
- /**
- * Is dragging down enabled on a given view
- * @param view The view to check or `null` to check if it's enabled at all
- */
- fun isDragDownEnabledForView(view: ExpandableView?): Boolean
-
- /**
- * @return if drag down is enabled anywhere, not just on selected views.
- */
- val isDragDownAnywhereEnabled: Boolean
- }
-
- companion object {
- private const val RUBBERBAND_FACTOR_EXPANDABLE = 0.5f
- private const val RUBBERBAND_FACTOR_STATIC = 0.15f
- private const val SPRING_BACK_ANIMATION_LENGTH_MS = 375
+ return expandCallback.getChildAtRawPosition(x + temp2[0], y + temp2[1])
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 84465a88eed0..9765ace7179f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -42,7 +42,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationRoundnessMa
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.ShadeController
import javax.inject.Inject
import kotlin.math.max
@@ -59,6 +58,7 @@ constructor(
private val roundnessManager: NotificationRoundnessManager,
private val statusBarStateController: StatusBarStateController,
private val falsingManager: FalsingManager,
+ private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
private val falsingCollector: FalsingCollector
) : Gefingerpoken {
companion object {
@@ -66,7 +66,6 @@ constructor(
private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
}
private val mPowerManager: PowerManager?
- private lateinit var shadeController: ShadeController
private val mMinDragDistance: Int
private var mInitialTouchX: Float = 0.0f
@@ -95,7 +94,7 @@ constructor(
var leavingLockscreen: Boolean = false
private set
private val mTouchSlop: Float
- private lateinit var expansionCallback: ExpansionCallback
+ private lateinit var overStretchHandler: OverStretchHandler
private lateinit var stackScrollerController: NotificationStackScrollLayoutController
private val mTemp2 = IntArray(2)
private var mDraggedFarEnough: Boolean = false
@@ -103,7 +102,7 @@ constructor(
private var mPulsing: Boolean = false
var isWakingToShadeLocked: Boolean = false
private set
- private var mEmptyDragAmount: Float = 0.0f
+ private var overStretchAmount: Float = 0.0f
private var mWakeUpHeight: Float = 0.0f
private var mReachedWakeUpHeight: Boolean = false
private var velocityTracker: VelocityTracker? = null
@@ -215,6 +214,7 @@ constructor(
private fun finishExpansion() {
resetClock()
+ val startingChild = mStartingChild
if (mStartingChild != null) {
setUserLocked(mStartingChild!!, false)
mStartingChild = null
@@ -225,7 +225,8 @@ constructor(
mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE,
"com.android.systemui:PULSEDRAG")
}
- shadeController.goToLockedShade(mStartingChild)
+ lockscreenShadeTransitionController.goToLockedShade(startingChild,
+ needsQSAnimation = false)
leavingLockscreen = true
isExpanding = false
if (mStartingChild is ExpandableNotificationRow) {
@@ -252,8 +253,8 @@ constructor(
true /* increaseSpeed */)
expansionHeight = max(mWakeUpHeight, expansionHeight)
}
- val emptyDragAmount = wakeUpCoordinator.setPulseHeight(expansionHeight)
- setEmptyDragAmount(emptyDragAmount * RUBBERBAND_FACTOR_STATIC)
+ val dragDownAmount = wakeUpCoordinator.setPulseHeight(expansionHeight)
+ setOverStretchAmount(dragDownAmount)
}
private fun captureStartingChild(x: Float, y: Float) {
@@ -265,9 +266,9 @@ constructor(
}
}
- private fun setEmptyDragAmount(amount: Float) {
- mEmptyDragAmount = amount
- expansionCallback.setEmptyDragAmount(amount)
+ private fun setOverStretchAmount(amount: Float) {
+ overStretchAmount = amount
+ overStretchHandler.setOverStretchAmount(amount)
}
private fun reset(child: ExpandableView) {
@@ -294,10 +295,12 @@ constructor(
}
private fun resetClock() {
- val anim = ValueAnimator.ofFloat(mEmptyDragAmount, 0f)
+ val anim = ValueAnimator.ofFloat(overStretchAmount, 0f)
anim.interpolator = Interpolators.FAST_OUT_SLOW_IN
anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
- anim.addUpdateListener { animation -> setEmptyDragAmount(animation.animatedValue as Float) }
+ anim.addUpdateListener {
+ animation -> setOverStretchAmount(animation.animatedValue as Float)
+ }
anim.start()
}
@@ -329,11 +332,9 @@ constructor(
fun setUp(
stackScrollerController: NotificationStackScrollLayoutController,
- expansionCallback: ExpansionCallback,
- shadeController: ShadeController
+ overStrechHandler: OverStretchHandler
) {
- this.expansionCallback = expansionCallback
- this.shadeController = shadeController
+ this.overStretchHandler = overStrechHandler
this.stackScrollerController = stackScrollerController
}
@@ -345,7 +346,11 @@ constructor(
isWakingToShadeLocked = false
}
- interface ExpansionCallback {
- fun setEmptyDragAmount(amount: Float)
+ interface OverStretchHandler {
+
+ /**
+ * Set the overstretch amount in pixels This will be rubberbanded later
+ */
+ fun setOverStretchAmount(amount: Float)
}
}
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 caf47207de07..66d23477436f 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
@@ -95,6 +95,7 @@ public class AmbientState {
/** Height of the notifications panel without top padding when expansion completes. */
private float mStackEndHeight;
+ private float mTransitionToFullShadeAmount;
/**
* @return Height of the notifications panel without top padding when expansion completes.
@@ -595,6 +596,21 @@ public class AmbientState {
}
/**
+ * Set the amount of pixels we have currently dragged down if we're transitioning to the full
+ * shade. 0.0f means we're not transitioning yet.
+ */
+ public void setTransitionToFullShadeAmount(float transitionToFullShadeAmount) {
+ mTransitionToFullShadeAmount = transitionToFullShadeAmount;
+ }
+
+ /**
+ * get
+ */
+ public float getTransitionToFullShadeAmount() {
+ return mTransitionToFullShadeAmount;
+ }
+
+ /**
* Returns the currently tracked heads up row, if there is one and it is currently above the
* shelf (still appearing).
*/
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 4dbdb133e1a7..3244ff915d87 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
@@ -46,7 +46,6 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
@@ -71,17 +70,14 @@ import android.widget.ScrollView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardSliceView;
import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -89,7 +85,6 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -108,9 +103,6 @@ import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonV
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -151,7 +143,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
- private final SysuiStatusBarStateController mStatusbarStateController;
private ExpandHelper mExpandHelper;
private NotificationSwipeHelper mSwipeHelper;
@@ -433,13 +424,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private ShadeController mShadeController;
private Runnable mOnStackYChanged;
- private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
- private final LockscreenGestureLogger mLockscreenGestureLogger =
- Dependency.get(LockscreenGestureLogger.class);
protected boolean mClearAllEnabled;
private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
- private NotificationPanelViewController mNotificationPanelController;
private final NotificationSectionsManager mSectionsManager;
private ForegroundServiceDungeonView mFgsSectionView;
@@ -449,6 +436,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private boolean mWillExpand;
private int mGapHeight;
+ /**
+ * The extra inset during the full shade transition
+ */
+ private float mExtraTopInsetForFullShadeTransition;
+
private int mWaterfallTopInset;
private NotificationStackScrollLayoutController mController;
@@ -496,7 +488,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
NotificationSectionsManager notificationSectionsManager,
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
- SysuiStatusBarStateController statusbarStateController,
AmbientState ambientState,
FeatureFlags featureFlags) {
super(context, attrs, 0, 0);
@@ -535,7 +526,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
mGroupMembershipManager = groupMembershipManager;
mGroupExpansionManager = groupExpansionManager;
- mStatusbarStateController = statusbarStateController;
}
void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) {
@@ -1142,8 +1132,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
private void updateStackPosition() {
// Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
+ float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition;
final float fraction = mAmbientState.getExpansionFraction();
- final float stackY = MathUtils.lerp(0, mTopPadding, fraction);
+ final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
mAmbientState.setStackY(stackY);
if (mOnStackYChanged != null) {
mOnStackYChanged.run();
@@ -4278,6 +4269,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
+ mGapHeight;
}
+ /**
+ * @return the padding after the media header on the lockscreen
+ */
+ public int getPaddingAfterMedia() {
+ return mGapHeight + mPaddingBetweenElements;
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public int getEmptyShadeViewHeight() {
return mEmptyShadeView.getHeight();
@@ -4933,12 +4931,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
getChildCount() - offsetFromEnd);
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setNotificationPanelController(
- NotificationPanelViewController notificationPanelViewController) {
- mNotificationPanelController = notificationPanelViewController;
- }
-
/**
* Set how far the wake up is when waking up from pulsing. This is a height and will adjust the
* notification positions accordingly.
@@ -5155,6 +5147,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
/**
+ * Sets the extra top inset for the full shade transition. This is needed to compensate for
+ * media transitioning to quick settings
+ */
+ public void setExtraTopInsetForFullShadeTransition(float inset) {
+ mExtraTopInsetForFullShadeTransition = inset;
+ updateStackPosition();
+ requestChildrenUpdate();
+ }
+
+ /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5526,108 +5528,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
- public void setKeyguardMediaControllorVisible(boolean keyguardMediaControllorVisible) {
- mKeyguardMediaControllorVisible = keyguardMediaControllorVisible;
- }
-
void resetCheckSnoozeLeavebehind() {
setCheckForLeaveBehind(true);
}
- // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
-
- @ShadeViewRefactor(RefactorComponent.INPUT)
- private final DragDownCallback mDragDownCallback = new DragDownCallback() {
-
- @Override
- public boolean canDragDown() {
- return mStatusBarState == StatusBarState.KEYGUARD
- && (mController.hasActiveNotifications() || mKeyguardMediaControllorVisible)
- || mController.isInLockedDownShade();
- }
-
- /* Only ever called as a consequence of a lockscreen expansion gesture. */
- @Override
- public void onDraggedDown(View startingChild, int dragLengthY) {
- boolean canDragDown =
- mController.hasActiveNotifications() || mKeyguardMediaControllorVisible;
- if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) {
- mLockscreenGestureLogger.write(
- MetricsEvent.ACTION_LS_SHADE,
- (int) (dragLengthY / mDisplayMetrics.density),
- 0 /* velocityDp - N/A */);
- mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN);
-
- if (!mAmbientState.isDozing() || startingChild != null) {
- // We have notifications, go to locked shade.
- mShadeController.goToLockedShade(startingChild);
- if (startingChild instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
- row.onExpandedByGesture(true /* drag down is always an open */);
- }
- }
- } else if (mController.isInLockedDownShade()) {
- mStatusbarStateController.setLeaveOpenOnKeyguardHide(true);
- mStatusBar.dismissKeyguardThenExecute(() -> false /* dismissAction */,
- null /* cancelRunnable */, false /* afterKeyguardGone */);
- }
- }
-
- @Override
- public void onDragDownReset() {
- setDimmed(true /* dimmed */, true /* animated */);
- resetScrollPosition();
- resetCheckSnoozeLeavebehind();
- }
-
- @Override
- public void onCrossedThreshold(boolean above) {
- setDimmed(!above /* dimmed */, true /* animate */);
- }
-
- @Override
- public void onTouchSlopExceeded() {
- cancelLongPress();
- mController.checkSnoozeLeavebehind();
- }
-
- @Override
- public void setEmptyDragAmount(float amount) {
- mNotificationPanelController.setEmptyDragAmount(amount);
- }
-
- @Override
- public boolean isFalsingCheckNeeded() {
- return mStatusBarState == StatusBarState.KEYGUARD;
- }
-
- @Override
- public boolean isDragDownEnabledForView(ExpandableView view) {
- if (isDragDownAnywhereEnabled()) {
- return true;
- }
- if (mController.isInLockedDownShade()) {
- if (view == null) {
- // Dragging down is allowed in general
- return true;
- }
- if (view instanceof ExpandableNotificationRow) {
- // Only drag down on sensitive views, otherwise the ExpandHelper will take this
- return ((ExpandableNotificationRow) view).getEntry().isSensitive();
- }
- }
- return false;
- }
-
- @Override
- public boolean isDragDownAnywhereEnabled() {
- return mStatusbarStateController.getState() == StatusBarState.KEYGUARD
- && !mKeyguardBypassEnabledProvider.getBypassEnabled();
- }
- };
-
- public DragDownCallback getDragDownCallback() { return mDragDownCallback; }
-
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f7eb574feac3..0d42428dd11d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -31,6 +31,7 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeDismissed;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
@@ -38,6 +39,7 @@ import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Pair;
import android.view.Display;
import android.view.LayoutInflater;
@@ -59,6 +61,7 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -70,6 +73,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEv
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -107,7 +111,6 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -168,6 +171,7 @@ public class NotificationStackScrollLayoutController {
// TODO: StatusBar should be encapsulated behind a Controller
private final StatusBar mStatusBar;
private final SectionHeaderController mSilentHeaderController;
+ private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private NotificationStackScrollLayout mView;
private boolean mFadeNotificationsOnDismiss;
@@ -181,6 +185,9 @@ public class NotificationStackScrollLayoutController {
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
+ private int mTotalDistanceForFullShadeTransition;
+ private int mTotalExtraMediaInsetFullShadeTransition;
+
@VisibleForTesting
final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
new View.OnAttachStateChangeListener() {
@@ -240,8 +247,20 @@ public class NotificationStackScrollLayoutController {
public void onThemeChanged() {
updateFooter();
}
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ updateResources();
+ }
};
+ private void updateResources() {
+ mTotalExtraMediaInsetFullShadeTransition = mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_transition_extra_media_inset);
+ mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_qs_transition_distance);
+ }
+
private final StatusBarStateController.StateListener mStateListener =
new StatusBarStateController.StateListener() {
@Override
@@ -571,6 +590,7 @@ public class NotificationStackScrollLayoutController {
NotifPipeline notifPipeline,
NotifCollection notifCollection,
NotificationEntryManager notificationEntryManager,
+ LockscreenShadeTransitionController lockscreenShadeTransitionController,
IStatusBarService iStatusBarService,
UiEventLogger uiEventLogger,
ForegroundServiceDismissalFeatureController fgFeatureController,
@@ -592,6 +612,7 @@ public class NotificationStackScrollLayoutController {
mZenModeController = zenModeController;
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
+ mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mResources = resources;
@@ -624,6 +645,7 @@ public class NotificationStackScrollLayoutController {
mRemoteInputManager = remoteInputManager;
mVisualStabilityManager = visualStabilityManager;
mShadeController = shadeController;
+ updateResources();
}
public void attach(NotificationStackScrollLayout view) {
@@ -676,6 +698,8 @@ public class NotificationStackScrollLayoutController {
mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming);
+ mLockscreenShadeTransitionController.setStackScroller(this);
+
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
mFadeNotificationsOnDismiss = // TODO: this should probably be injected directly
@@ -705,7 +729,6 @@ public class NotificationStackScrollLayoutController {
Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
mKeyguardMediaController.setVisibilityChangedListener(visible -> {
- mView.setKeyguardMediaControllorVisible(visible);
if (visible) {
mView.generateAddAnimation(
mKeyguardMediaController.getSinglePaneContainer(),
@@ -1203,11 +1226,6 @@ public class NotificationStackScrollLayoutController {
mView.runAfterAnimationFinished(r);
}
- public void setNotificationPanelController(
- NotificationPanelViewController notificationPanelViewController) {
- mView.setNotificationPanelController(notificationPanelViewController);
- }
-
public void setShelfController(NotificationShelfController notificationShelfController) {
mView.setShelfController(notificationShelfController);
}
@@ -1275,7 +1293,10 @@ public class NotificationStackScrollLayoutController {
NotificationLogger.getNotificationLocation(entry)));
}
- boolean hasActiveNotifications() {
+ /**
+ * @return if the shade has currently any active notifications.
+ */
+ public boolean hasActiveNotifications() {
if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
return !mNotifPipeline.getShadeList().isEmpty();
} else {
@@ -1354,11 +1375,60 @@ public class NotificationStackScrollLayoutController {
}
}
+ /**
+ * @return the expand helper callback.
+ */
+ public ExpandHelper.Callback getExpandHelperCallback() {
+ return mView.getExpandHelperCallback();
+ }
+
+ /**
+ * @return If the shade is in the locked down shade.
+ */
public boolean isInLockedDownShade() {
return mDynamicPrivacyController.isInLockedDownShade();
}
/**
+ * Set the dimmed state for all of the notification views.
+ */
+ public void setDimmed(boolean dimmed, boolean animate) {
+ mView.setDimmed(dimmed, animate);
+ }
+
+ /**
+ * @return the inset during the full shade transition, that needs to be added to the position
+ * of the quick settings edge. This is relevant for media, that is transitioning
+ * from the keyguard host to the quick settings one.
+ */
+ public int getFullShadeTransitionInset() {
+ MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer();
+ if (view == null || view.getHeight() == 0
+ || mStatusBarStateController.getState() != KEYGUARD) {
+ return 0;
+ }
+ return view.getHeight() + mView.getPaddingAfterMedia();
+ }
+
+ /**
+ * Set the amount of pixels we have currently dragged down if we're transitioning to the full
+ * shade. 0.0f means we're not transitioning yet.
+ */
+ public void setTransitionToFullShadeAmount(float amount) {
+ float extraTopInset;
+ MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer();
+ if (view == null || view.getHeight() == 0
+ || mStatusBarStateController.getState() != KEYGUARD) {
+ extraTopInset = 0;
+ } else {
+ extraTopInset = MathUtils.saturate(amount / mTotalDistanceForFullShadeTransition);
+ extraTopInset = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(extraTopInset);
+ extraTopInset = extraTopInset * mTotalExtraMediaInsetFullShadeTransition;
+ }
+ mView.setExtraTopInsetForFullShadeTransition(extraTopInset);
+ }
+
+ /**
* Enum for UiEvent logged from this class
*/
enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index c64b893970c6..ae6a1e52574d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -251,7 +251,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
super.onFinishInflate();
mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext),
new ActivityIntentHelper(mContext));
- mPreviewContainer = findViewById(R.id.preview_container);
mOverlayContainer = findViewById(R.id.overlay_container);
mRightAffordanceView = findViewById(R.id.camera_button);
mLeftAffordanceView = findViewById(R.id.left_button);
@@ -268,7 +267,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mKeyguardStateController.addCallback(this);
setClipChildren(false);
setClipToPadding(false);
- inflateCameraPreview();
mRightAffordanceView.setOnClickListener(this);
mLeftAffordanceView.setOnClickListener(this);
initAccessibility();
@@ -276,13 +274,21 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mFlashlightController = Dependency.get(FlashlightController.class);
mAccessibilityController = Dependency.get(AccessibilityController.class);
mActivityIntentHelper = new ActivityIntentHelper(getContext());
- updateLeftAffordance();
mIndicationPadding = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_area_padding);
updateWalletVisibility();
}
+ /**
+ * Set the container where the previews are rendered.
+ */
+ public void setPreviewContainer(ViewGroup previewContainer) {
+ mPreviewContainer = previewContainer;
+ inflateCameraPreview();
+ updateLeftAffordance();
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -680,6 +686,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
private void inflateCameraPreview() {
+ if (mPreviewContainer == null) {
+ return;
+ }
View previewBefore = mCameraPreview;
boolean visibleBefore = false;
if (previewBefore != null) {
@@ -697,6 +706,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
private void updateLeftPreview() {
+ if (mPreviewContainer == null) {
+ return;
+ }
View previewBefore = mLeftPreview;
if (previewBefore != null) {
mPreviewContainer.removeView(previewBefore);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 069c19770aef..f4710f49524d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -140,7 +140,7 @@ public class KeyguardClockPositionAlgorithm {
*/
private float mQsExpansion;
- private float mEmptyDragAmount;
+ private float mOverStretchAmount;
/**
* Setting if bypass is enabled. If true the clock should always be positioned like it's dark
@@ -181,7 +181,7 @@ public class KeyguardClockPositionAlgorithm {
int notificationStackHeight, float panelExpansion, int parentHeight,
int keyguardStatusHeight, int userSwitchHeight, int clockPreferredY,
int userSwitchPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
- float emptyDragAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
+ float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
float qsExpansion, int cutoutTopInset, boolean isSplitShade) {
mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
userSwitchHeight);
@@ -196,7 +196,7 @@ public class KeyguardClockPositionAlgorithm {
mHasCustomClock = hasCustomClock;
mHasVisibleNotifs = hasVisibleNotifs;
mDarkAmount = dark;
- mEmptyDragAmount = emptyDragAmount;
+ mOverStretchAmount = overStrechAmount;
mBypassEnabled = bypassEnabled;
mUnlockedStackScrollerPadding = unlockedStackScrollerPadding;
mQsExpansion = qsExpansion;
@@ -301,7 +301,7 @@ public class KeyguardClockPositionAlgorithm {
}
clockYDark = clockY + burnInPreventionOffsetY() + shift;
}
- return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount);
+ return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount);
}
private int getUserSwitcherY(float panelExpansion) {
@@ -312,7 +312,7 @@ public class KeyguardClockPositionAlgorithm {
float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(panelExpansion);
float userSwitchY = MathUtils.lerp(userSwitchYBouncer, userSwitchYRegular, shadeExpansion);
- return (int) (userSwitchY + mEmptyDragAmount);
+ return (int) (userSwitchY + mOverStretchAmount);
}
/**
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 12dec14dabd0..089277070658 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -114,6 +114,7 @@ import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -207,7 +208,6 @@ public class NotificationPanelViewController extends PanelViewController {
private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
@VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
new StatusBarStateListener();
- private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
private final BiometricUnlockController mBiometricUnlockController;
private final NotificationPanelView mView;
private final VibratorHelper mVibratorHelper;
@@ -312,10 +312,12 @@ public class NotificationPanelViewController extends PanelViewController {
// Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
// If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
private final int mMaxKeyguardNotifications;
+ private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private boolean mShouldUseSplitNotificationShade;
// Current max allowed keyguard notifications determined by measuring the panel
private int mMaxAllowedKeyguardNotifications;
+ private ViewGroup mPreviewContainer;
private KeyguardAffordanceHelper mAffordanceHelper;
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
@@ -365,7 +367,7 @@ public class NotificationPanelViewController extends PanelViewController {
private int mStatusBarMinHeight;
private int mStatusBarHeaderHeightKeyguard;
private int mNotificationsHeaderCollideDistance;
- private float mEmptyDragAmount;
+ private float mOverStretchAmount;
private float mDownX;
private float mDownY;
private int mDisplayCutoutTopInset = 0; // in pixels
@@ -481,7 +483,6 @@ public class NotificationPanelViewController extends PanelViewController {
private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final UserManager mUserManager;
- private final ShadeController mShadeController;
private final MediaDataManager mMediaDataManager;
private NotificationShadeDepthController mDepthController;
private int mDisplayId;
@@ -505,6 +506,39 @@ public class NotificationPanelViewController extends PanelViewController {
private float mSectionPadding;
/**
+ * The amount of progress we are currently in if we're transitioning to the full shade.
+ * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+ * shade. This value can also go beyond 1.1 when we're overshooting!
+ */
+ private float mTransitioningToFullShadeProgress;
+
+ /**
+ * Position of the qs bottom during the full shade transition. This is needed as the toppadding
+ * can change during state changes, which makes it much harder to do animations
+ */
+ private int mTransitionToFullShadeQSPosition;
+
+ /**
+ * Distance that the full shade transition takes in order for qs to fully transition to the
+ * shade.
+ */
+ private int mDistanceForQSFullShadeTransition;
+
+ /**
+ * The maximum overshoot allowed for the top padding for the full shade transition
+ */
+ private int mMaxOverscrollAmountForDragDown;
+
+ /**
+ * Should we animate the next bounds update
+ */
+ private boolean mAnimateNextNotificationBounds;
+ /**
+ * The delay for the next bounds animation
+ */
+ private long mNotificationBoundsAnimationDelay;
+
+ /**
* Is this a collapse that started on the panel where we should allow the panel to intercept
*/
private boolean mIsPanelCollapseOnQQS;
@@ -521,6 +555,16 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mDelayShowingKeyguardStatusBar;
private boolean mAnimatingQS;
+
+ /**
+ * The end bounds of a clipping animation.
+ */
+ private final Rect mQsClippingAnimationEndBounds = new Rect();
+
+ /**
+ * The animator for the qs clipping bounds.
+ */
+ private ValueAnimator mQsClippingAnimation = null;
private final Rect mKeyguardStatusAreaClipBounds = new Rect();
private int mOldLayoutDirection;
private NotificationShelfController mNotificationShelfController;
@@ -569,7 +613,7 @@ public class NotificationPanelViewController extends PanelViewController {
NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
DynamicPrivacyController dynamicPrivacyController,
KeyguardBypassController bypassController, FalsingManager falsingManager,
- FalsingCollector falsingCollector, ShadeController shadeController,
+ FalsingCollector falsingCollector,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationEntryManager notificationEntryManager,
KeyguardStateController keyguardStateController,
@@ -591,6 +635,7 @@ public class NotificationPanelViewController extends PanelViewController {
KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
+ LockscreenShadeTransitionController lockscreenShadeTransitionController,
QSDetailDisplayer qsDetailDisplayer,
NotificationGroupManagerLegacy groupManager,
NotificationIconAreaController notificationIconAreaController,
@@ -677,6 +722,8 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
};
+ mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
+ lockscreenShadeTransitionController.setNotificationPanelController(this);
mKeyguardStateController.addCallback(keyguardMonitorCallback);
DynamicPrivacyControlListener
dynamicPrivacyControlListener =
@@ -690,7 +737,6 @@ public class NotificationPanelViewController extends PanelViewController {
});
mBottomAreaShadeAlphaAnimator.setDuration(160);
mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
- mShadeController = shadeController;
mLockscreenUserManager = notificationLockscreenUserManager;
mEntryManager = notificationEntryManager;
mConversationNotificationManager = conversationNotificationManager;
@@ -749,14 +795,22 @@ public class NotificationPanelViewController extends PanelViewController {
mOnEmptySpaceClickListener);
addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
+ mPreviewContainer = mView.findViewById(R.id.preview_container);
+ mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
mLastOrientation = mResources.getConfiguration().orientation;
initBottomArea();
mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController);
mQsFrame = mView.findViewById(R.id.qs_frame);
- mPulseExpansionHandler.setUp(
- mNotificationStackScrollLayoutController, mExpansionCallback, mShadeController);
+ mPulseExpansionHandler.setUp(mNotificationStackScrollLayoutController,
+ amount -> {
+ float progress = amount / mView.getHeight();
+ float overstretch = Interpolators.getOvershootInterpolation(progress,
+ (float) mMaxOverscrollAmountForDragDown / mView.getHeight(),
+ 0.2f);
+ setOverStrechAmount(overstretch);
+ });
mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
@Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
@@ -812,6 +866,10 @@ public class NotificationPanelViewController extends PanelViewController {
com.android.internal.R.dimen.status_bar_height);
mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
+ mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_qs_transition_distance);
+ mMaxOverscrollAmountForDragDown = mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_max_top_overshoot);
mScrimCornerRadius = mResources.getDimensionPixelSize(
R.dimen.notification_scrim_corner_radius);
mScreenCornerRadius = mResources.getDimensionPixelSize(
@@ -989,6 +1047,7 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate(
R.layout.keyguard_bottom_area, mView, false);
mKeyguardBottomArea.initFrom(oldBottomArea);
+ mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
mView.addView(mKeyguardBottomArea, index);
initBottomArea();
mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
@@ -1108,58 +1167,28 @@ public class NotificationPanelViewController extends PanelViewController {
* showing.
*/
private void positionClockAndNotifications() {
+ positionClockAndNotifications(false /* forceUpdate */);
+ }
+
+ /**
+ * Positions the clock and notifications dynamically depending on how many notifications are
+ * showing.
+ *
+ * @param forceClockUpdate Should the clock be updated even when not on keyguard
+ */
+ private void positionClockAndNotifications(boolean forceClockUpdate) {
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
- boolean animateClock = animate || mAnimateNextPositionUpdate;
int stackScrollerPadding;
- if (mBarState != KEYGUARD) {
+ boolean onKeyguard = isOnKeyguard();
+ if (onKeyguard || forceClockUpdate) {
+ updateClockAppearance();
+ }
+ if (!onKeyguard) {
stackScrollerPadding = getUnlockedStackScrollerPadding();
} else {
- int totalHeight = mView.getHeight();
- int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
- int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight);
- int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
- boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
- final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
- .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
- mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
- int userIconHeight = mKeyguardQsUserSwitchController != null
- ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
- mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
- totalHeight - bottomPadding,
- mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
- getExpandedFraction(),
- totalHeight,
- mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1
- ? mKeyguardStatusViewController.getHeight()
- : (int) (mKeyguardStatusViewController.getHeight()
- - mShelfHeight / 2.0f - mDarkIconSize / 2.0f),
- userIconHeight,
- clockPreferredY, userSwitcherPreferredY, hasCustomClock(),
- hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
- bypassEnabled, getUnlockedStackScrollerPadding(),
- getQsExpansionFraction(),
- mDisplayCutoutTopInset,
- shouldUseSplitNotificationShade(mFeatureFlags, mResources));
- mClockPositionAlgorithm.run(mClockPositionResult);
- mKeyguardStatusViewController.updatePosition(
- mClockPositionResult.clockX, mClockPositionResult.clockY,
- mClockPositionResult.clockScale, animateClock);
- if (mKeyguardQsUserSwitchController != null) {
- mKeyguardQsUserSwitchController.updatePosition(
- mClockPositionResult.clockX,
- mClockPositionResult.userSwitchY,
- animateClock);
- }
- if (mKeyguardUserSwitcherController != null) {
- mKeyguardUserSwitcherController.updatePosition(
- mClockPositionResult.clockX,
- mClockPositionResult.userSwitchY,
- animateClock);
- }
- updateNotificationTranslucency();
- updateClock();
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
}
+
mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
@@ -1169,6 +1198,60 @@ public class NotificationPanelViewController extends PanelViewController {
mAnimateNextPositionUpdate = false;
}
+ private void updateClockAppearance() {
+ int totalHeight = mView.getHeight();
+ int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
+ int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight);
+ int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
+ boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
+ final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+ .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
+ mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
+ int userIconHeight = mKeyguardQsUserSwitchController != null
+ ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
+ float expandedFraction =
+ mKeyguardStatusViewController.isAnimatingScreenOffFromUnlocked() ? 1.0f
+ : getExpandedFraction();
+ float darkamount = mKeyguardStatusViewController.isAnimatingScreenOffFromUnlocked() ? 1.0f
+ : mInterpolatedDarkAmount;
+ mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
+ totalHeight - bottomPadding,
+ mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
+ expandedFraction,
+ totalHeight,
+ mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1
+ ? mKeyguardStatusViewController.getHeight()
+ : (int) (mKeyguardStatusViewController.getHeight()
+ - mShelfHeight / 2.0f - mDarkIconSize / 2.0f),
+ userIconHeight,
+ clockPreferredY, userSwitcherPreferredY, hasCustomClock(),
+ hasVisibleNotifications, darkamount, mOverStretchAmount,
+ bypassEnabled, getUnlockedStackScrollerPadding(),
+ computeQsExpansionFraction(),
+ mDisplayCutoutTopInset,
+ shouldUseSplitNotificationShade(mFeatureFlags, mResources));
+ mClockPositionAlgorithm.run(mClockPositionResult);
+ boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
+ boolean animateClock = animate || mAnimateNextPositionUpdate;
+ mKeyguardStatusViewController.updatePosition(
+ mClockPositionResult.clockX, mClockPositionResult.clockY,
+ mClockPositionResult.clockScale, animateClock);
+ if (mKeyguardQsUserSwitchController != null) {
+ mKeyguardQsUserSwitchController.updatePosition(
+ mClockPositionResult.clockX,
+ mClockPositionResult.userSwitchY,
+ animateClock);
+ }
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.updatePosition(
+ mClockPositionResult.clockX,
+ mClockPositionResult.userSwitchY,
+ animateClock);
+ }
+ updateNotificationTranslucency();
+ updateClock();
+ }
+
/**
* @return the padding of the stackscroller when unlocked
*/
@@ -1600,7 +1683,7 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean flingExpandsQs(float vel) {
if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
- return getQsExpansionFraction() > 0.5f;
+ return computeQsExpansionFraction() > 0.5f;
} else {
return vel > 0;
}
@@ -1613,7 +1696,7 @@ public class NotificationPanelViewController extends PanelViewController {
return !mQsTouchAboveFalsingThreshold;
}
- private float getQsExpansionFraction() {
+ private float computeQsExpansionFraction() {
return Math.min(
1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight
- mQsMinExpansionHeight));
@@ -1838,7 +1921,7 @@ public class NotificationPanelViewController extends PanelViewController {
mQsTracking = false;
mTrackingPointer = -1;
trackMovement(event);
- float fraction = getQsExpansionFraction();
+ float fraction = computeQsExpansionFraction();
if (fraction != 0f || y >= mInitialTouchY) {
flingQsWithCurrentVelocity(y,
event.getActionMasked() == MotionEvent.ACTION_CANCEL);
@@ -2044,18 +2127,19 @@ public class NotificationPanelViewController extends PanelViewController {
protected void updateQsExpansion() {
if (mQs == null) return;
- float qsExpansionFraction = getQsExpansionFraction();
+ float qsExpansionFraction = computeQsExpansionFraction();
mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
+ setQSClippingBounds();
mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
mDepthController.setQsPanelExpansion(qsExpansionFraction);
}
private Runnable mOnStackYChanged = () -> {
if (mQs != null) {
- setNotificationBounds();
+ setQSClippingBounds();
}
};
@@ -2063,57 +2147,121 @@ public class NotificationPanelViewController extends PanelViewController {
* Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade
* and QS state.
*/
- private void setNotificationBounds() {
+ private void setQSClippingBounds() {
int top = 0;
int bottom = 0;
int left = 0;
int right = 0;
- final int qsPanelBottomY = calculateQsBottomPosition(getQsExpansionFraction());
- final boolean visible = (getQsExpansionFraction() > 0 || qsPanelBottomY > 0)
+ final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
+ final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0)
&& !mShouldUseSplitNotificationShade;
- final float notificationTop = mAmbientState.getStackY() - mAmbientState.getScrollY();
setQsExpansionEnabled(mAmbientState.getScrollY() == 0);
- int radius = mScrimCornerRadius;
if (!mShouldUseSplitNotificationShade) {
- top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop)
- : notificationTop);
+ if (mTransitioningToFullShadeProgress > 0.0f) {
+ // If we're transitioning, let's use the actual value. The else case
+ // can be wrong during transitions when waiting for the keyguard to unlock
+ top = mTransitionToFullShadeQSPosition;
+ } else {
+ float notificationTop = getQSEdgePosition();
+ top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop)
+ : notificationTop);
+ }
bottom = getView().getBottom();
left = getView().getLeft();
right = getView().getRight();
- radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
- Math.min(top / (float) mScrimCornerRadius, 1f));
} else if (qsPanelBottomY > 0) { // so bounds are empty on lockscreen
top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
bottom = mNotificationStackScrollLayoutController.getHeight();
left = mNotificationStackScrollLayoutController.getLeft();
right = mNotificationStackScrollLayoutController.getRight();
}
+ applyQSClippingBounds(left, top, right, bottom, visible);
+ }
- // Fancy clipping for quick settings
- if (mQs != null) {
- mQs.setFancyClipping(top, bottom, radius, visible);
+ private void applyQSClippingBounds(int left, int top, int right, int bottom,
+ boolean visible) {
+ if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
+ if (mQsClippingAnimation != null) {
+ // update the end position of the animator
+ mQsClippingAnimationEndBounds.set(left, top, right, bottom);
+ } else {
+ applyQSClippingImmediately(left, top, right, bottom, visible);
+ }
+ } else {
+ mQsClippingAnimationEndBounds.set(left, top, right, bottom);
+ final int startLeft = mKeyguardStatusAreaClipBounds.left;
+ final int startTop = mKeyguardStatusAreaClipBounds.top;
+ final int startRight = mKeyguardStatusAreaClipBounds.right;
+ final int startBottom = mKeyguardStatusAreaClipBounds.bottom;
+ mQsClippingAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mQsClippingAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mQsClippingAnimation.setDuration(
+ StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
+ mQsClippingAnimation.setStartDelay(mNotificationBoundsAnimationDelay);
+ mQsClippingAnimation.addUpdateListener(animation -> {
+ float fraction = animation.getAnimatedFraction();
+ int animLeft = (int) MathUtils.lerp(startLeft,
+ mQsClippingAnimationEndBounds.left, fraction);
+ int animTop = (int) MathUtils.lerp(startTop,
+ mQsClippingAnimationEndBounds.top, fraction);
+ int animRight = (int) MathUtils.lerp(startRight,
+ mQsClippingAnimationEndBounds.right, fraction);
+ int animBottom = (int) MathUtils.lerp(startBottom,
+ mQsClippingAnimationEndBounds.bottom, fraction);
+ applyQSClippingImmediately(animLeft, animTop, animRight, animBottom,
+ visible /* visible */);
+ });
+ mQsClippingAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mQsClippingAnimation = null;
+ }
+ });
+ mQsClippingAnimation.start();
}
+ mAnimateNextNotificationBounds = false;
+ mNotificationBoundsAnimationDelay = 0;
+ }
+
+ private void applyQSClippingImmediately(int left, int top, int right, int bottom,
+ boolean visible) {
+ // Fancy clipping for quick settings
+ int radius = mScrimCornerRadius;
if (!mShouldUseSplitNotificationShade) {
// The padding on this area is large enough that we can use a cheaper clipping strategy
mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
mKeyguardStatusViewController.setClipBounds(visible
? mKeyguardStatusAreaClipBounds : null);
+ radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
+ Math.min(top / (float) mScrimCornerRadius, 1f));
+ }
+ if (mQs != null) {
+ mQs.setFancyClipping(top, bottom, radius, visible);
}
mScrimController.setNotificationsBounds(left, top, right, bottom);
mScrimController.setScrimCornerRadius(radius);
}
+ private float getQSEdgePosition() {
+ // TODO: replace StackY with unified calculation
+ return mAmbientState.getStackY() - mAmbientState.getScrollY();
+ }
+
private int calculateQsBottomPosition(float qsExpansionFraction) {
- int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
- if (qsExpansionFraction != 0.0) {
- qsBottomY = (int) MathUtils.lerp(
- qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction);
+ if (mTransitioningToFullShadeProgress > 0.0f) {
+ return mTransitionToFullShadeQSPosition;
+ } else {
+ int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
+ if (qsExpansionFraction != 0.0) {
+ qsBottomY = (int) MathUtils.lerp(
+ qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction);
+ }
+ // to account for shade overshooting animation, see setSectionPadding method
+ if (mSectionPadding > 0) qsBottomY += mSectionPadding;
+ return qsBottomY;
}
- // to account for shade overshooting animation, see setSectionPadding method
- if (mSectionPadding > 0) qsBottomY += mSectionPadding;
- return qsBottomY;
}
private String determineAccessibilityPaneTitle() {
@@ -2157,7 +2305,7 @@ public class NotificationPanelViewController extends PanelViewController {
// from a scrolled quick settings.
return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
(float) (mQsMaxExpansionHeight + mQsNotificationTopPadding),
- getQsExpansionFraction());
+ computeQsExpansionFraction());
} else {
return mQsExpansionHeight + mQsNotificationTopPadding;
}
@@ -2194,15 +2342,65 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
-
private void updateQSPulseExpansion() {
if (mQs != null) {
- mQs.setShowCollapsedOnKeyguard(
+ mQs.setPulseExpanding(
mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()
&& mNotificationStackScrollLayoutController.isPulseExpanding());
}
}
+ /**
+ * Set the amount of pixels we have currently dragged down if we're transitioning to the full
+ * shade. 0.0f means we're not transitioning yet.
+ */
+ public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) {
+ mAnimateNextNotificationBounds = animate && !mShouldUseSplitNotificationShade;
+ mNotificationBoundsAnimationDelay = delay;
+ float progress = MathUtils.saturate(pxAmount / mView.getHeight());
+
+ float endPosition = 0;
+ if (pxAmount > 0.0f) {
+ if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0
+ && !mMediaDataManager.hasActiveMedia()) {
+ // No notifications are visible, let's animate to the height of qs instead
+ if (mQs != null) {
+ // Let's interpolate to the header height
+ endPosition = mQs.getHeader().getHeight();
+ }
+ } else {
+ // Interpolating to the new bottom edge position!
+ endPosition = getQSEdgePosition() - mOverStretchAmount;
+
+ // If we have media, we need to put the boundary below it, as the media header
+ // still uses the space during the transition.
+ endPosition +=
+ mNotificationStackScrollLayoutController.getFullShadeTransitionInset();
+ }
+ }
+
+ // Calculate the overshoot amount such that we're reaching the target after our desired
+ // distance, but only reach it fully once we drag a full shade length.
+ float transitionProgress = 0;
+ if (endPosition != 0 && progress != 0) {
+ transitionProgress = Interpolators.getOvershootInterpolation(progress,
+ mMaxOverscrollAmountForDragDown / endPosition,
+ (float) mDistanceForQSFullShadeTransition / (float) mView.getHeight());
+ }
+ mTransitioningToFullShadeProgress = transitionProgress;
+
+ int position = (int) MathUtils.lerp((float) 0, endPosition,
+ mTransitioningToFullShadeProgress);
+ if (mTransitioningToFullShadeProgress > 0.0f) {
+ // we want at least 1 pixel otherwise the panel won't be clipped
+ position = Math.max(1, position);
+ }
+ float overStretchAmount = Math.max(position - endPosition, 0.0f);
+ setOverStrechAmount(overStretchAmount);
+ mTransitionToFullShadeQSPosition = position;
+ updateQsExpansion();
+ }
+
private void trackMovement(MotionEvent event) {
if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
}
@@ -2626,7 +2824,7 @@ public class NotificationPanelViewController extends PanelViewController {
if (!mKeyguardShowing) {
return;
}
- float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
+ float alphaQsExpansion = 1 - Math.min(1, computeQsExpansionFraction() * 2);
float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
* mKeyguardStatusBarAnimateAlpha;
newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
@@ -2649,7 +2847,7 @@ public class NotificationPanelViewController extends PanelViewController {
float expansionAlpha = MathUtils.map(
isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
getExpandedFraction());
- float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
+ float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
alpha *= mBottomAreaShadeAlpha;
mKeyguardBottomArea.setAffordanceAlpha(alpha);
mKeyguardBottomArea.setImportantForAccessibility(
@@ -2671,7 +2869,7 @@ public class NotificationPanelViewController extends PanelViewController {
float expansionAlpha = MathUtils.map(
isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
getExpandedFraction());
- float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
+ float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
mBigClockContainer.setAlpha(alpha);
}
@@ -3223,6 +3421,7 @@ public class NotificationPanelViewController extends PanelViewController {
mHeightListener.onQsHeightChanged();
}
});
+ mLockscreenShadeTransitionController.setQS(mQs);
mNotificationStackScrollLayoutController.setQsContainer((ViewGroup) mQs.getView());
updateQsExpansion();
}
@@ -3460,9 +3659,9 @@ public class NotificationPanelViewController extends PanelViewController {
StatusBar statusBar,
NotificationShelfController notificationShelfController) {
setStatusBar(statusBar);
- mNotificationStackScrollLayoutController.setNotificationPanelController(this);
mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
mNotificationShelfController = notificationShelfController;
+ mLockscreenShadeTransitionController.bindController(notificationShelfController);
updateMaxDisplayedNotifications(true);
}
@@ -3513,10 +3712,6 @@ public class NotificationPanelViewController extends PanelViewController {
return new OnLayoutChangeListener();
}
- public void setEmptyDragAmount(float amount) {
- mExpansionCallback.setEmptyDragAmount(amount);
- }
-
@Override
protected TouchHandler createTouchHandler() {
return new TouchHandler() {
@@ -3985,7 +4180,7 @@ public class NotificationPanelViewController extends PanelViewController {
mClockPositionResult.clockX,
mClockPositionResult.clockYFullyDozing,
mClockPositionResult.clockScale,
- false);
+ false /* animate */);
}
mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
@@ -4001,11 +4196,7 @@ public class NotificationPanelViewController extends PanelViewController {
if (oldState == KEYGUARD && (goingToFullShade
|| statusBarState == StatusBarState.SHADE_LOCKED)) {
animateKeyguardStatusBarOut();
- long
- delay =
- mBarState == StatusBarState.SHADE_LOCKED ? 0
- : mKeyguardStateController.calculateGoingToFullShadeDelay();
- mQs.animateHeaderSlidingIn(delay);
+ updateQSMinHeight();
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == KEYGUARD) {
animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
@@ -4052,11 +4243,12 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
- private class ExpansionCallback implements PulseExpansionHandler.ExpansionCallback {
- public void setEmptyDragAmount(float amount) {
- mEmptyDragAmount = amount * 0.2f;
- positionClockAndNotifications();
- }
+ /**
+ * Sets the overstretch amount in raw pixels when dragging down.
+ */
+ public void setOverStrechAmount(float amount) {
+ mOverStretchAmount = amount;
+ positionClockAndNotifications(true /* forceUpdate */);
}
private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener {
@@ -4103,11 +4295,7 @@ public class NotificationPanelViewController extends PanelViewController {
// Calculate quick setting heights.
int oldMaxHeight = mQsMaxExpansionHeight;
if (mQs != null) {
- float previousMin = mQsMinExpansionHeight;
- mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
- if (mQsExpansionHeight == previousMin) {
- mQsExpansionHeight = mQsMinExpansionHeight;
- }
+ updateQSMinHeight();
mQsMaxExpansionHeight = mQs.getDesiredHeight();
mNotificationStackScrollLayoutController.setMaxTopPadding(
mQsMaxExpansionHeight + mQsNotificationTopPadding);
@@ -4149,6 +4337,14 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
+ private void updateQSMinHeight() {
+ float previousMin = mQsMinExpansionHeight;
+ mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
+ if (mQsExpansionHeight == previousMin) {
+ mQsExpansionHeight = mQsMinExpansionHeight;
+ }
+ }
+
private class DebugDrawable extends Drawable {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index f19cf1bed034..7f4dabd3f59f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -34,15 +34,14 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeLog;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -73,7 +72,6 @@ public class NotificationShadeWindowViewController {
private final DynamicPrivacyController mDynamicPrivacyController;
private final KeyguardBypassController mBypassController;
private final PluginManager mPluginManager;
- private final FalsingManager mFalsingManager;
private final FalsingCollector mFalsingCollector;
private final TunerService mTunerService;
private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@@ -87,6 +85,7 @@ public class NotificationShadeWindowViewController {
private final ShadeController mShadeController;
private final NotificationShadeDepthController mDepthController;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+ private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private GestureDetector mGestureDetector;
@@ -119,7 +118,7 @@ public class NotificationShadeWindowViewController {
PulseExpansionHandler pulseExpansionHandler,
DynamicPrivacyController dynamicPrivacyController,
KeyguardBypassController bypassController,
- FalsingManager falsingManager,
+ LockscreenShadeTransitionController transitionController,
FalsingCollector falsingCollector,
PluginManager pluginManager,
TunerService tunerService,
@@ -143,7 +142,7 @@ public class NotificationShadeWindowViewController {
mPulseExpansionHandler = pulseExpansionHandler;
mDynamicPrivacyController = dynamicPrivacyController;
mBypassController = bypassController;
- mFalsingManager = falsingManager;
+ mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mPluginManager = pluginManager;
mTunerService = tunerService;
@@ -406,11 +405,7 @@ public class NotificationShadeWindowViewController {
}
});
- ExpandHelper.Callback expandHelperCallback = mStackScrollLayout.getExpandHelperCallback();
- DragDownHelper.DragDownCallback dragDownCallback = mStackScrollLayout.getDragDownCallback();
- setDragDownHelper(
- new DragDownHelper(mFalsingManager, expandHelperCallback, dragDownCallback,
- mFalsingCollector, mView, mView.getContext()));
+ setDragDownHelper(mLockscreenShadeTransitionController.getTouchHelper());
mDepthController.setRoot(mView);
mNotificationPanelViewController.addExpansionListener(mDepthController);
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 c34fa2f049e1..bbde3c3e3144 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -28,6 +28,7 @@ import android.os.Handler;
import android.os.Trace;
import android.util.Log;
import android.util.MathUtils;
+import android.util.Pair;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
@@ -42,10 +43,10 @@ import com.android.internal.util.function.TriConsumer;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
@@ -98,6 +99,18 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
public static final int OPAQUE = 2;
private boolean mClipsQsScrim;
+ /**
+ * The amount of progress we are currently in if we're transitioning to the full shade.
+ * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+ * shade.
+ */
+ private float mTransitionToFullShadeProgress;
+
+ /**
+ * If we're currently transitioning to the full shade.
+ */
+ private boolean mTransitioningToFullShade;
+
@IntDef(prefix = {"VISIBILITY_"}, value = {
TRANSPARENT,
SEMI_TRANSPARENT,
@@ -357,7 +370,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
+ mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
+ mNotificationsAlpha);
}
- applyExpansionToAlpha();
+ applyStateToAlpha();
// Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
// We need to disable focus otherwise AOD would end up with a gray overlay.
@@ -499,10 +512,37 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
if (!(relevantState && mExpansionAffectsAlpha)) {
return;
}
- applyAndDispatchExpansion();
+ applyAndDispatchState();
+ }
+ }
+
+ /**
+ * Set the amount of progress we are currently in if we're transitioning to the full shade.
+ * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
+ * shade.
+ */
+ public void setTransitionToFullShadeProgress(float progress) {
+ if (progress != mTransitionToFullShadeProgress) {
+ mTransitionToFullShadeProgress = progress;
+ setTransitionToFullShade(progress > 0.0f);
+ applyAndDispatchState();
+ }
+ }
+
+ /**
+ * Set if we're currently transitioning to the full shade
+ */
+ private void setTransitionToFullShade(boolean transitioning) {
+ if (transitioning != mTransitioningToFullShade) {
+ mTransitioningToFullShade = transitioning;
+ if (transitioning) {
+ // Let's make sure the shade locked is ready
+ ScrimState.SHADE_LOCKED.prepare(mState);
+ }
}
}
+
/**
* Set bounds for notifications background, all coordinates are absolute
*/
@@ -534,7 +574,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
if (!(relevantState && mExpansionAffectsAlpha)) {
return;
}
- applyAndDispatchExpansion();
+ applyAndDispatchState();
}
}
@@ -553,6 +593,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
if (mScrimBehind != null) {
mScrimBehind.enableBottomEdgeConcave(mClipsQsScrim);
}
+ if (mState != ScrimState.UNINITIALIZED) {
+ // the clipScrimState has changed, let's reprepare ourselves
+ mState.prepare(mState);
+ applyAndDispatchState();
+ }
}
@VisibleForTesting
@@ -583,7 +628,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
}
- private void applyExpansionToAlpha() {
+ private void applyStateToAlpha() {
if (!mExpansionAffectsAlpha) {
return;
}
@@ -608,47 +653,40 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mInFrontAlpha = 0;
} else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED
|| mState == ScrimState.PULSING) {
- // Either darken of make the scrim transparent when you
- // pull down the shade
- float interpolatedFract = getInterpolatedFraction();
- float stateBehind = mClipsQsScrim ? mState.getNotifAlpha() : mState.getBehindAlpha();
- float backAlpha;
- if (mDarkenWhileDragging) {
- backAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind,
- interpolatedFract);
- } else {
- backAlpha = MathUtils.lerp(0 /* start */, stateBehind,
- interpolatedFract);
+ Pair<Integer, Float> result = calculateBackStateForState(mState);
+ int behindTint = result.first;
+ float behindAlpha = result.second;
+ if (mTransitionToFullShadeProgress > 0.0f) {
+ Pair<Integer, Float> shadeResult = calculateBackStateForState(
+ ScrimState.SHADE_LOCKED);
+ behindAlpha = MathUtils.lerp(behindAlpha, shadeResult.second,
+ mTransitionToFullShadeProgress);
+ behindTint = ColorUtils.blendARGB(behindTint, shadeResult.first,
+ mTransitionToFullShadeProgress);
}
mInFrontAlpha = mState.getFrontAlpha();
- int backTint;
if (mClipsQsScrim) {
- backTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(),
- mState.getNotifTint(), interpolatedFract);
- } else {
- backTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
- mState.getBehindTint(), interpolatedFract);
- }
- if (mQsExpansion > 0) {
- backAlpha = MathUtils.lerp(backAlpha, mDefaultScrimAlpha, mQsExpansion);
- int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint()
- : ScrimState.SHADE_LOCKED.getBehindTint();
- backTint = ColorUtils.blendARGB(backTint, stateTint, mQsExpansion);
- }
- if (mClipsQsScrim) {
- mNotificationsAlpha = backAlpha;
- mNotificationsTint = backTint;
+ mNotificationsAlpha = behindAlpha;
+ mNotificationsTint = behindTint;
mBehindAlpha = 1;
mBehindTint = Color.BLACK;
} else {
- mBehindAlpha = backAlpha;
+ mBehindAlpha = behindAlpha;
if (mState == ScrimState.SHADE_LOCKED) {
// going from KEYGUARD to SHADE_LOCKED state
mNotificationsAlpha = getInterpolatedFraction();
} else {
mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion);
}
- mBehindTint = backTint;
+ if (mState == ScrimState.KEYGUARD && mTransitionToFullShadeProgress > 0.0f) {
+ // Interpolate the notification alpha when transitioning!
+ mNotificationsAlpha = MathUtils.lerp(
+ mNotificationsAlpha,
+ getInterpolatedFraction(),
+ mTransitionToFullShadeProgress);
+ }
+ mNotificationsTint = mState.getNotifTint();
+ mBehindTint = behindTint;
}
}
if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
@@ -658,8 +696,39 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
}
- private void applyAndDispatchExpansion() {
- applyExpansionToAlpha();
+ private Pair<Integer, Float> calculateBackStateForState(ScrimState state) {
+ // Either darken of make the scrim transparent when you
+ // pull down the shade
+ float interpolatedFract = getInterpolatedFraction();
+ float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha();
+ float behindAlpha;
+ int behindTint;
+ if (mDarkenWhileDragging) {
+ behindAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind,
+ interpolatedFract);
+ } else {
+ behindAlpha = MathUtils.lerp(0 /* start */, stateBehind,
+ interpolatedFract);
+ }
+ if (mClipsQsScrim) {
+ behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(),
+ state.getNotifTint(), interpolatedFract);
+ } else {
+ behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
+ state.getBehindTint(), interpolatedFract);
+ }
+ if (mQsExpansion > 0) {
+ behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion);
+ int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint()
+ : ScrimState.SHADE_LOCKED.getBehindTint();
+ behindTint = ColorUtils.blendARGB(behindTint, stateTint, mQsExpansion);
+ }
+ return new Pair<>(behindTint, behindAlpha);
+ }
+
+
+ private void applyAndDispatchState() {
+ applyStateToAlpha();
if (mUpdatePending) {
return;
}
@@ -1195,7 +1264,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
public void setExpansionAffectsAlpha(boolean expansionAffectsAlpha) {
mExpansionAffectsAlpha = expansionAffectsAlpha;
if (expansionAffectsAlpha) {
- applyAndDispatchExpansion();
+ applyAndDispatchState();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index 2fa6795e9df0..2d41e5e9be2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -14,8 +14,6 @@
package com.android.systemui.statusbar.phone;
-import android.view.View;
-
import com.android.systemui.statusbar.StatusBarState;
/**
@@ -78,15 +76,6 @@ public interface ShadeController {
void runPostCollapseRunnables();
/**
- * If secure with redaction: Show bouncer, go to unlocked shade.
- *
- * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
- *
- * @param startingChild The view to expand after going to the shade.
- */
- void goToLockedShade(View startingChild);
-
- /**
* Close the shade if it was open
*
* @return true if the shade was open, else false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index a930a897c2dc..d4458e29a306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import android.util.Log;
-import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
@@ -182,12 +181,6 @@ public class ShadeControllerImpl implements ShadeController {
}
@Override
- public void goToLockedShade(View startingChild) {
- // TODO: Move this code out of StatusBar into ShadeController.
- getStatusBar().goToLockedShade(startingChild);
- }
-
- @Override
public boolean collapsePanel() {
if (!getNotificationPanelViewController().isFullyCollapsed()) {
// close the shade if it was open
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index ded3be43501a..ec012bfde9b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -194,6 +194,7 @@ import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LiftReveal;
import com.android.systemui.statusbar.LightRevealScrim;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -347,6 +348,8 @@ public class StatusBar extends SystemUI implements DemoMode,
ONLY_CORE_APPS = onlyCoreApps;
}
+ private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+
public interface ExpansionChangedListener {
void onExpansionChanged(float expansion, boolean expanded);
}
@@ -438,9 +441,6 @@ public class StatusBar extends SystemUI implements DemoMode,
KeyguardIndicationController mKeyguardIndicationController;
- // RemoteInputView to be activated after unlock
- private View mPendingRemoteInputView;
-
private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
private View mReportRejectedTouch;
@@ -650,12 +650,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private final ScreenLifecycle mScreenLifecycle;
private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final View.OnClickListener mGoToLockedShadeListener = v -> {
- if (mState == StatusBarState.KEYGUARD) {
- wakeUpIfDozing(SystemClock.uptimeMillis(), v, "SHADE_CLICK");
- goToLockedShade(null);
- }
- };
private boolean mNoAnimationOnNextBarModeChange;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -798,6 +792,7 @@ public class StatusBar extends SystemUI implements DemoMode,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
+ LockscreenShadeTransitionController lockscreenShadeTransitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
super(context);
@@ -882,6 +877,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mAnimationScheduler = animationScheduler;
mStatusBarLocationPublisher = locationPublisher;
mFeatureFlags = featureFlags;
+ mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
+ lockscreenShadeTransitionController.setStatusbar(this);
mExpansionChangedListeners = new ArrayList<>();
@@ -1422,7 +1419,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mDozeScrimController, mScrimController, mNotificationShadeWindowController,
mDynamicPrivacyController, mKeyguardStateController,
mKeyguardIndicationController,
- this /* statusBar */, mShadeController, mCommandQueue, mInitController,
+ this /* statusBar */, mShadeController,
+ mLockscreenShadeTransitionController, mCommandQueue, mInitController,
mNotificationInterruptStateProvider);
mNotificationShelfController.setOnActivatedListener(mPresenter);
@@ -1502,7 +1500,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private void inflateShelf() {
mNotificationShelfController = mSuperStatusBarViewFactory
.getNotificationShelfController(mStackScroller);
- mNotificationShelfController.setOnClickListener(mGoToLockedShadeListener);
}
@Override
@@ -1675,7 +1672,7 @@ public class StatusBar extends SystemUI implements DemoMode,
final boolean expandEnabled = mDeviceProvisionedController.isDeviceProvisioned()
&& (mUserSetup || mUserSwitcherController == null
|| !mUserSwitcherController.isSimpleUserSwitcher())
- && ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0)
+ && !isShadeDisabled()
&& ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
&& !mDozing
&& !ONLY_CORE_APPS;
@@ -1683,6 +1680,10 @@ public class StatusBar extends SystemUI implements DemoMode,
Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
}
+ public boolean isShadeDisabled() {
+ return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
+ }
+
public void addQsTile(ComponentName tile) {
if (mQSPanelController != null && mQSPanelController.getHost() != null) {
mQSPanelController.getHost().addTile(tile);
@@ -3333,7 +3334,6 @@ public class StatusBar extends SystemUI implements DemoMode,
public void showKeyguard() {
mStatusBarStateController.setKeyguardRequested(true);
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
- mPendingRemoteInputView = null;
updateIsKeyguard();
mAssistManagerLazy.get().onLockscreenShown();
}
@@ -3392,11 +3392,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
updatePanelExpansionForKeyguard();
- if (mDraggedDownEntry != null) {
- mDraggedDownEntry.setUserLocked(false);
- mDraggedDownEntry.notifyHeightChanged(false /* needsAnimation */);
- mDraggedDownEntry = null;
- }
}
private void updatePanelExpansionForKeyguard() {
@@ -3524,11 +3519,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
}
long delay = mKeyguardStateController.calculateGoingToFullShadeDelay();
- mNotificationPanelViewController.animateToFullShade(delay);
- if (mDraggedDownEntry != null) {
- mDraggedDownEntry.setUserLocked(false);
- mDraggedDownEntry = null;
- }
+ mLockscreenShadeTransitionController.onHideKeyguard(delay);
// Disable layout transitions in navbar for this transition because the load is just
// too heavy for the CPU and GPU on any device.
@@ -3719,6 +3710,22 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
+ /**
+ * Show the bouncer if we're currently on the keyguard or shade locked and aren't hiding.
+ * @param performAction the action to perform when the bouncer is dismissed.
+ * @param cancelAction the action to perform when unlock is aborted.
+ */
+ public void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction,
+ Runnable cancelAction) {
+ if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
+ && !mKeyguardViewMediator.isHiding()) {
+ mStatusBarKeyguardViewManager.dismissWithAction(performAction, cancelAction,
+ false /* afterKeyguardGone */);
+ } else if (cancelAction != null) {
+ cancelAction.run();
+ }
+ }
+
void instantCollapseNotificationPanel() {
mNotificationPanelViewController.instantCollapse();
mShadeController.runPostCollapseRunnables();
@@ -3896,47 +3903,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
/**
- * If secure with redaction: Show bouncer, go to unlocked shade.
- *
- * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
- *
- * @param expandView The view to expand after going to the shade.
- */
- void goToLockedShade(View expandView) {
- if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
- return;
- }
-
- int userId = mLockscreenUserManager.getCurrentUserId();
- ExpandableNotificationRow row = null;
- NotificationEntry entry = null;
- if (expandView instanceof ExpandableNotificationRow) {
- entry = ((ExpandableNotificationRow) expandView).getEntry();
- entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
- // Indicate that the group expansion is changing at this time -- this way the group
- // and children backgrounds / divider animations will look correct.
- entry.setGroupExpansionChanging(true);
- userId = entry.getSbn().getUserId();
- }
- boolean fullShadeNeedsBouncer = !mLockscreenUserManager.
- userAllowsPrivateNotificationsInPublic(mLockscreenUserManager.getCurrentUserId())
- || !mLockscreenUserManager.shouldShowLockscreenNotifications()
- || mFalsingCollector.shouldEnforceBouncer();
- if (mKeyguardBypassController.getBypassEnabled()) {
- fullShadeNeedsBouncer = false;
- }
- if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
- mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
- showBouncerIfKeyguard();
- mDraggedDownEntry = entry;
- mPendingRemoteInputView = null;
- } else {
- mNotificationPanelViewController.animateToFullShade(0 /* delay */);
- mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED);
- }
- }
-
- /**
* Propagation of the bouncer state, indicating that it's fully visible.
*/
public void setBouncerShowing(boolean bouncerShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 75c544d3aa4b..aa58527cb32e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -48,6 +48,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -112,6 +113,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
private final KeyguardIndicationController mKeyguardIndicationController;
private final StatusBar mStatusBar;
private final ShadeController mShadeController;
+ private final LockscreenShadeTransitionController mShadeTransitionController;
private final CommandQueue mCommandQueue;
private final AccessibilityManager mAccessibilityManager;
@@ -138,6 +140,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
KeyguardIndicationController keyguardIndicationController,
StatusBar statusBar,
ShadeController shadeController,
+ LockscreenShadeTransitionController shadeTransitionController,
CommandQueue commandQueue,
InitController initController,
NotificationInterruptStateProvider notificationInterruptStateProvider) {
@@ -149,6 +152,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mStatusBar = statusBar;
mShadeController = shadeController;
+ mShadeTransitionController = shadeTransitionController;
mCommandQueue = commandQueue;
mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -394,7 +398,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
if (nowExpanded) {
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
- mShadeController.goToLockedShade(clickedEntry.getRow());
+ mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
} else if (clickedEntry.isSensitive()
&& mDynamicPrivacyController.isInLockedDownShade()) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index d0d2cb288aa5..9722d6841c62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -51,6 +51,7 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -213,6 +214,7 @@ public interface StatusBarPhoneModule {
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
+ LockscreenShadeTransitionController transitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
return new StatusBar(
@@ -299,6 +301,7 @@ public interface StatusBarPhoneModule {
ongoingCallController,
animationScheduler,
locationPublisher,
+ transitionController,
featureFlags,
keyguardUnlockAnimationController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index a974421a9b7c..c6aef4a18373 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import org.junit.Assert.assertNotNull
@@ -79,6 +80,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock
+ private lateinit var configurationController: ConfigurationController
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
@@ -98,6 +101,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
bypassController,
mediaCarouselController,
notificationLockscreenUserManager,
+ configurationController,
wakefulnessLifecycle,
statusBarKeyguardViewManager)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
new file mode 100644
index 000000000000..18b6c3074d08
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -0,0 +1,225 @@
+package com.android.systemui.statusbar
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.util.DisplayMetrics
+import androidx.test.filters.SmallTest
+import com.android.systemui.ExpandHelper
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.media.MediaHierarchyManager
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.stack.AmbientState
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.policy.ConfigurationController
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
+
+private fun <T> anyObject(): T {
+ return Mockito.anyObject<T>()
+}
+
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner::class)
+class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
+
+ lateinit var transitionController: LockscreenShadeTransitionController
+ lateinit var row: ExpandableNotificationRow
+ @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
+ @Mock lateinit var lockscreenGestureLogger: LockscreenGestureLogger
+ @Mock lateinit var keyguardBypassController: KeyguardBypassController
+ @Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
+ @Mock lateinit var falsingCollector: FalsingCollector
+ @Mock lateinit var ambientState: AmbientState
+ @Mock lateinit var displayMetrics: DisplayMetrics
+ @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
+ @Mock lateinit var scrimController: ScrimController
+ @Mock lateinit var configurationController: ConfigurationController
+ @Mock lateinit var falsingManager: FalsingManager
+ @Mock lateinit var notificationPanelController: NotificationPanelViewController
+ @Mock lateinit var nsslController: NotificationStackScrollLayoutController
+ @Mock lateinit var featureFlags: FeatureFlags
+ @Mock lateinit var stackscroller: NotificationStackScrollLayout
+ @Mock lateinit var expandHelperCallback: ExpandHelper.Callback
+ @Mock lateinit var statusbar: StatusBar
+ @Mock lateinit var qS: QS
+ @JvmField @Rule val mockito = MockitoJUnit.rule()
+
+ @Before
+ fun setup() {
+ val helper = NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this))
+ row = helper.createRow()
+ transitionController = LockscreenShadeTransitionController(
+ statusBarStateController = statusbarStateController,
+ lockscreenGestureLogger = lockscreenGestureLogger,
+ keyguardBypassController = keyguardBypassController,
+ lockScreenUserManager = lockScreenUserManager,
+ falsingCollector = falsingCollector,
+ ambientState = ambientState,
+ displayMetrics = displayMetrics,
+ mediaHierarchyManager = mediaHierarchyManager,
+ scrimController = scrimController,
+ featureFlags = featureFlags,
+ context = context,
+ configurationController = configurationController,
+ falsingManager = falsingManager
+ )
+ whenever(nsslController.view).thenReturn(stackscroller)
+ whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
+ transitionController.notificationPanelController = notificationPanelController
+ transitionController.statusbar = statusbar
+ transitionController.qS = qS
+ transitionController.setStackScroller(nsslController)
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(nsslController.isInLockedDownShade).thenReturn(false)
+ whenever(qS.isFullyCollapsed).thenReturn(true)
+ whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
+ true)
+ whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
+ whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
+ whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false)
+ whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
+ clearInvocations(statusbar)
+ }
+
+ @After
+ fun tearDown() {
+ transitionController.dragDownAnimator?.cancel()
+ }
+
+ @Test
+ fun testCantDragDownWhenQSExpanded() {
+ assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
+ whenever(qS.isFullyCollapsed).thenReturn(false)
+ assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
+ }
+
+ @Test
+ fun testCanDragDownInLockedDownShade() {
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ assertFalse("Can drag down in shade locked", transitionController.canDragDown())
+ whenever(nsslController.isInLockedDownShade).thenReturn(true)
+ assertTrue("Can't drag down in locked down shade", transitionController.canDragDown())
+ }
+
+ @Test
+ fun testGoingToLockedShade() {
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ }
+
+ @Test
+ fun testGoToLockedShadeOnlyOnKeyguard() {
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ transitionController.goToLockedShade(null)
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE)
+ transitionController.goToLockedShade(null)
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.FULLSCREEN_USER_SWITCHER)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ }
+
+ @Test
+ fun testDontGoWhenShadeDisabled() {
+ whenever(statusbar.isShadeDisabled).thenReturn(true)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ }
+
+ @Test
+ fun testUserExpandsViewOnGoingToFullShade() {
+ assertFalse("Row shouldn't be user expanded yet", row.isUserExpanded)
+ transitionController.goToLockedShade(row)
+ assertTrue("Row wasn't user expanded on drag down", row.isUserExpanded)
+ }
+
+ @Test
+ fun testTriggeringBouncerWhenPrivateNotificationsArentAllowed() {
+ whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
+ false)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
+ verify(statusbar).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ }
+
+ @Test
+ fun testTriggeringBouncerNoNotificationsOnLockscreen() {
+ whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
+ verify(statusbar).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ }
+
+ @Test
+ fun testGoToLockedShadeCreatesQSAnimation() {
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ verify(notificationPanelController).animateToFullShade(anyLong())
+ assertNotNull(transitionController.dragDownAnimator)
+ }
+
+ @Test
+ fun testGoToLockedShadeDoesntCreateQSAnimation() {
+ transitionController.goToLockedShade(null, needsQSAnimation = false)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ verify(notificationPanelController).animateToFullShade(anyLong())
+ assertNull(transitionController.dragDownAnimator)
+ }
+
+ @Test
+ fun testDragDownAmountDoesntCallOutInLockedDownShade() {
+ whenever(nsslController.isInLockedDownShade).thenReturn(true)
+ transitionController.dragDownAmount = 10f
+ verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
+ verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
+ verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat())
+ verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(),
+ anyBoolean(), anyLong())
+ verify(qS, never()).setTransitionToFullShadeAmount(anyFloat(), anyBoolean())
+ }
+
+ @Test
+ fun testDragDownAmountCallsOut() {
+ transitionController.dragDownAmount = 10f
+ verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
+ verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
+ verify(scrimController).setTransitionToFullShadeProgress(anyFloat())
+ verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(),
+ anyBoolean(), anyLong())
+ verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyBoolean())
+ }
+}
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 84fb3689b0b0..8758e1609fa8 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
@@ -142,7 +142,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mNotificationSectionsManager,
mGroupMembershipManger,
mGroupExpansionManager,
- mStatusBarStateController,
mAmbientState,
mFeatureFlags);
mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index 895339fb7aaa..f376e88b2cb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -50,6 +50,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -121,6 +122,7 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
@Mock private NotificationEntryManager mEntryManager;
@Mock private IStatusBarService mIStatusBarService;
@Mock private UiEventLogger mUiEventLogger;
+ @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Mock private ForegroundServiceDismissalFeatureController mFgFeatureController;
@Mock private ForegroundServiceSectionController mFgServicesSectionController;
@Mock private ForegroundServiceDungeonView mForegroundServiceDungeonView;
@@ -173,6 +175,7 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
mNotifPipeline,
mNotifCollection,
mEntryManager,
+ mLockscreenShadeTransitionController,
mIStatusBarService,
mUiEventLogger,
mFgFeatureController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 45761df2cfdb..0783003e4973 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -82,6 +82,7 @@ import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -225,6 +226,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private NotificationShadeDepthController mNotificationShadeDepthController;
@Mock
+ private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Mock
private AuthController mAuthController;
@Mock
private ScrimController mScrimController;
@@ -311,7 +314,9 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mKeyguardBypassController, mHeadsUpManager,
mock(NotificationRoundnessManager.class),
mStatusBarStateController,
- new FalsingManagerFake(), new FalsingCollectorFake());
+ new FalsingManagerFake(),
+ mLockscreenShadeTransitionController,
+ new FalsingCollectorFake());
when(mKeyguardStatusViewComponentFactory.build(any()))
.thenReturn(mKeyguardStatusViewComponent);
when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
@@ -327,7 +332,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mResources,
mLayoutInflater,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
- new FalsingManagerFake(), new FalsingCollectorFake(), mShadeController,
+ new FalsingManagerFake(), new FalsingCollectorFake(),
mNotificationLockscreenUserManager, mNotificationEntryManager,
mKeyguardStateController, mStatusBarStateController, mDozeLog,
mDozeParameters, mCommandQueue, mVibratorHelper,
@@ -341,6 +346,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mKeyguardQsUserSwitchComponentFactory,
mKeyguardUserSwitcherComponentFactory,
mKeyguardStatusBarViewComponentFactory,
+ mLockscreenShadeTransitionController,
mQSDetailDisplayer,
mGroupManager,
mNotificationAreaController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 0a3eec4d41ff..6c1a3c90d83d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -31,12 +31,12 @@ import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -89,6 +89,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Before
public void setUp() {
@@ -112,7 +113,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
mPulseExpansionHandler,
mDynamicPrivacyController,
mBypassController,
- new FalsingManagerFake(),
+ mLockscreenShadeTransitionController,
new FalsingCollectorFake(),
mPluginManager,
mTunerService,
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 f98f00cd9187..a431a781dbb4 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
@@ -41,6 +41,7 @@ import android.graphics.Color;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.MathUtils;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -1121,6 +1122,32 @@ public class ScrimControllerTest extends SysuiTestCase {
assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.8f, /* expansion */ 0.2f);
}
+ @Test
+ public void testNotificationTransparency_followsTransitionToFullShade() {
+ mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ mScrimController.setPanelExpansion(1.0f);
+ finishAnimationsImmediately();
+ float shadeLockedAlpha = mNotificationsScrim.getViewAlpha();
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ mScrimController.setPanelExpansion(1.0f);
+ finishAnimationsImmediately();
+ float keyguardAlpha = mNotificationsScrim.getViewAlpha();
+
+ mScrimController.setClipsQsScrim(true);
+ float progress = 0.5f;
+ mScrimController.setTransitionToFullShadeProgress(progress);
+ assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
+ mNotificationsScrim.getViewAlpha(), 0.2);
+ progress = 0.0f;
+ mScrimController.setTransitionToFullShadeProgress(progress);
+ assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
+ mNotificationsScrim.getViewAlpha(), 0.2);
+ progress = 1.0f;
+ mScrimController.setTransitionToFullShadeProgress(progress);
+ assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress),
+ mNotificationsScrim.getViewAlpha(), 0.2);
+ }
+
private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
mScrimController.setPanelExpansion(expansion);
finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 8601de5611d5..ce45f2618f2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -40,6 +40,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -127,7 +128,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class),
mock(KeyguardStateController.class),
mock(KeyguardIndicationController.class), mStatusBar,
- mock(ShadeControllerImpl.class), mCommandQueue, mInitController,
+ mock(ShadeControllerImpl.class), mock(LockscreenShadeTransitionController.class),
+ mCommandQueue, mInitController,
mNotificationInterruptStateProvider);
mInitController.executePostInitTasks();
ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index b3d52b80c815..5a3683e8e3f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -101,6 +101,7 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -267,6 +268,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private OngoingCallController mOngoingCallController;
@Mock private SystemStatusAnimationScheduler mAnimationScheduler;
@Mock private StatusBarLocationPublisher mLocationPublisher;
+ @Mock private LockscreenShadeTransitionController mLockscreenTransitionController;
@Mock private FeatureFlags mFeatureFlags;
@Mock private IWallpaperManager mWallpaperManager;
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@@ -437,6 +439,7 @@ public class StatusBarTest extends SysuiTestCase {
mOngoingCallController,
mAnimationScheduler,
mLocationPublisher,
+ mLockscreenTransitionController,
mFeatureFlags,
mKeyguardUnlockAnimationController);
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),