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
This commit is contained in:
Selim Cinek
2021-05-11 21:39:20 +02:00
parent 91579bde59
commit 8304b95fad
36 changed files with 1654 additions and 549 deletions

View File

@@ -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;
@@ -71,6 +72,27 @@ public class Interpolators {
public static final Interpolator TOUCH_RESPONSE_REVERSE =
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

View File

@@ -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);
@@ -78,11 +83,24 @@ 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;

View File

@@ -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"

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -197,6 +197,13 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
animate);
}
/**
* @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.
*/

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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
) {
@@ -182,6 +185,58 @@ class MediaHierarchyManager @Inject constructor(
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
}
/**
* 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
}

View File

@@ -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
@@ -541,18 +576,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
&& !isKeyguardShowing();
}
@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");

View File

@@ -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 {
class DragDownHelper(
private val falsingManager: FalsingManager,
private val falsingCollector: FalsingCollector,
private val dragDownCallback: LockscreenShadeTransitionController,
context: Context
) : Gefingerpoken {
private val minDragDistance: Int
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])
}
}

View File

@@ -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)
}
}

View File

@@ -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.
@@ -594,6 +595,21 @@ public class AmbientState {
mTrackedHeadsUpRow = row;
}
/**
* 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).

View File

@@ -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.
@@ -5154,6 +5146,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mShadeController = shadeController;
}
/**
* 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
*/
@@ -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

View File

@@ -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,10 +1375,59 @@ 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
*/

View File

@@ -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);

View File

@@ -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);
}
/**

View File

@@ -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;
@@ -504,6 +505,39 @@ public class NotificationPanelViewController extends PanelViewController {
private Runnable mExpandAfterLayoutRunnable;
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
*/
@@ -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

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -14,8 +14,6 @@
package com.android.systemui.statusbar.phone;
import android.view.View;
import com.android.systemui.statusbar.StatusBarState;
/**
@@ -77,15 +75,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
*

View File

@@ -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;
@@ -181,12 +180,6 @@ public class ShadeControllerImpl implements ShadeController {
mStatusBarKeyguardViewManager.readyForKeyguardDone();
}
@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()) {

View File

@@ -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();
@@ -3895,47 +3902,6 @@ public class StatusBar extends SystemUI implements DemoMode,
return mNotificationPanelViewController.getKeyguardBottomAreaView();
}
/**
* 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.
*/

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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())

View File

@@ -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())
}
}

View File

@@ -142,7 +142,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mNotificationSectionsManager,
mGroupMembershipManger,
mGroupExpansionManager,
mStatusBarStateController,
mAmbientState,
mFeatureFlags);
mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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();

View File

@@ -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 =

View File

@@ -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),