Fading away notification panel individually
Previously the notification panel was fading away all together, which doesn't work when the notifications need to stay around. We're now fading the whole panel away. Bug: 134952761 Test: pick up phone and unlock, observe normal behavior Change-Id: I48d12776dc8b4bd51c1a35c2b822caeef38eb850
This commit is contained in:
@@ -23,9 +23,11 @@
|
||||
android:fromAlpha="0.0" android:toAlpha="1.0"
|
||||
android:fillEnabled="true" android:fillBefore="true"
|
||||
android:interpolator="@interpolator/linear"
|
||||
android:startOffset="80"
|
||||
android:duration="233"/>
|
||||
<translate android:fromYDelta="5%p" android:toYDelta="0"
|
||||
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
|
||||
android:interpolator="@interpolator/fast_out_slow_in"
|
||||
android:startOffset="80"
|
||||
android:duration="233" />
|
||||
</set>
|
||||
@@ -98,6 +98,12 @@
|
||||
<item type="id" name="keyguard_hun_animator_start_tag"/>
|
||||
<item type="id" name="keyguard_hun_animator_end_tag"/>
|
||||
|
||||
<item type="id" name="view_group_fade_helper_modified_views"/>
|
||||
<item type="id" name="view_group_fade_helper_animator"/>
|
||||
<item type="id" name="view_group_fade_helper_previous_value_tag"/>
|
||||
<item type="id" name="view_group_fade_helper_restore_tag"/>
|
||||
<item type="id" name="view_group_fade_helper_hardware_layer"/>
|
||||
|
||||
<!-- Accessibility actions for the notification menu -->
|
||||
<item type="id" name="action_snooze_undo"/>
|
||||
<item type="id" name="action_snooze_shorter"/>
|
||||
|
||||
@@ -61,6 +61,7 @@ import android.util.EventLog;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManagerPolicyConstants;
|
||||
import android.view.animation.Animation;
|
||||
@@ -85,6 +86,7 @@ import com.android.systemui.SystemUIFactory;
|
||||
import com.android.systemui.UiOffloadThread;
|
||||
import com.android.systemui.classifier.FalsingManagerFactory;
|
||||
import com.android.systemui.statusbar.phone.BiometricUnlockController;
|
||||
import com.android.systemui.statusbar.phone.KeyguardBypassController;
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelView;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
@@ -2060,9 +2062,11 @@ public class KeyguardViewMediator extends SystemUI {
|
||||
|
||||
public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
|
||||
ViewGroup container, NotificationPanelView panelView,
|
||||
BiometricUnlockController biometricUnlockController, ViewGroup lockIconContainer) {
|
||||
BiometricUnlockController biometricUnlockController, ViewGroup lockIconContainer,
|
||||
View notificationContainer, KeyguardBypassController bypassController) {
|
||||
mStatusBarKeyguardViewManager.registerStatusBar(statusBar, container, panelView,
|
||||
biometricUnlockController, mDismissCallbackRegistry, lockIconContainer);
|
||||
biometricUnlockController, mDismissCallbackRegistry, lockIconContainer,
|
||||
notificationContainer, bypassController);
|
||||
return mStatusBarKeyguardViewManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.notification
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ValueAnimator
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.android.systemui.Interpolators
|
||||
import com.android.systemui.R
|
||||
|
||||
/**
|
||||
* Class to help with fading of view groups without fading one subview
|
||||
*/
|
||||
class ViewGroupFadeHelper {
|
||||
companion object {
|
||||
private val visibilityIncluder = {
|
||||
view: View -> view.visibility == View.VISIBLE
|
||||
}
|
||||
|
||||
/**
|
||||
* Fade out all views of a root except a single child. This will iterate over all children
|
||||
* of the view and make sure that the animation works smoothly.
|
||||
* @param root the view root to fade the children away
|
||||
* @param excludedView which view should remain
|
||||
* @param duration the duration of the animation
|
||||
*/
|
||||
@JvmStatic
|
||||
fun fadeOutAllChildrenExcept(root: ViewGroup, excludedView: View, duration: Long,
|
||||
endRunnable: Runnable?) {
|
||||
// starting from the view going up, we are adding the siblings of the child to the set
|
||||
// of views that need to be faded.
|
||||
val viewsToFadeOut = gatherViews(root, excludedView, visibilityIncluder)
|
||||
|
||||
// Applying the right layertypes for the animation
|
||||
for (viewToFade in viewsToFadeOut) {
|
||||
if (viewToFade.hasOverlappingRendering
|
||||
&& viewToFade.layerType == View.LAYER_TYPE_NONE) {
|
||||
viewToFade.setLayerType(View.LAYER_TYPE_HARDWARE, null)
|
||||
viewToFade.setTag(R.id.view_group_fade_helper_hardware_layer, true)
|
||||
}
|
||||
}
|
||||
|
||||
val animator = ValueAnimator.ofFloat(1.0f, 0.0f).apply {
|
||||
this.duration = duration
|
||||
interpolator = Interpolators.ALPHA_OUT
|
||||
addUpdateListener { animation ->
|
||||
val previousSetAlpha = root.getTag(
|
||||
R.id.view_group_fade_helper_previous_value_tag) as Float?
|
||||
val newAlpha = animation.animatedValue as Float
|
||||
for (viewToFade in viewsToFadeOut) {
|
||||
if (viewToFade.alpha != previousSetAlpha) {
|
||||
// A value was set that wasn't set from our view, let's store it and restore
|
||||
// it at the end
|
||||
viewToFade.setTag(R.id.view_group_fade_helper_restore_tag, viewToFade.alpha)
|
||||
}
|
||||
viewToFade.alpha = newAlpha
|
||||
}
|
||||
root.setTag(R.id.view_group_fade_helper_previous_value_tag, newAlpha)
|
||||
}
|
||||
addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
endRunnable?.run()
|
||||
}
|
||||
})
|
||||
start()
|
||||
}
|
||||
root.setTag(R.id.view_group_fade_helper_modified_views, viewsToFadeOut)
|
||||
root.setTag(R.id.view_group_fade_helper_animator, animator)
|
||||
}
|
||||
|
||||
private fun gatherViews(root: ViewGroup, excludedView: View,
|
||||
shouldInclude: (View) -> Boolean): MutableSet<View> {
|
||||
val viewsToFadeOut = mutableSetOf<View>()
|
||||
var parent = excludedView.parent as ViewGroup?
|
||||
var viewContainingExcludedView = excludedView;
|
||||
while (parent != null) {
|
||||
for (i in 0 until parent.childCount) {
|
||||
val child = parent.getChildAt(i)
|
||||
if (shouldInclude.invoke(child) && viewContainingExcludedView != child) {
|
||||
viewsToFadeOut.add(child)
|
||||
}
|
||||
}
|
||||
if (parent == root) {
|
||||
break;
|
||||
}
|
||||
viewContainingExcludedView = parent
|
||||
parent = parent.parent as ViewGroup?
|
||||
}
|
||||
return viewsToFadeOut
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all view alphas for views previously transformed away.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun reset(root: ViewGroup) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val modifiedViews = root.getTag(R.id.view_group_fade_helper_modified_views)
|
||||
as MutableSet<View>?
|
||||
val animator = root.getTag(R.id.view_group_fade_helper_animator) as Animator?
|
||||
if (modifiedViews == null || animator == null) {
|
||||
// nothing to restore
|
||||
return
|
||||
}
|
||||
animator.cancel()
|
||||
val lastSetValue = root.getTag(
|
||||
R.id.view_group_fade_helper_previous_value_tag) as Float?
|
||||
for (viewToFade in modifiedViews) {
|
||||
val restoreAlpha = viewToFade.getTag(
|
||||
R.id.view_group_fade_helper_restore_tag) as Float?
|
||||
if (restoreAlpha == null) {
|
||||
continue
|
||||
}
|
||||
if (lastSetValue == viewToFade.alpha) {
|
||||
// it was modified after the transition!
|
||||
viewToFade.alpha = restoreAlpha
|
||||
}
|
||||
val needsLayerReset = viewToFade.getTag(
|
||||
R.id.view_group_fade_helper_hardware_layer) as Boolean?
|
||||
if (needsLayerReset == true) {
|
||||
viewToFade.setLayerType(View.LAYER_TYPE_NONE, null)
|
||||
viewToFade.setTag(R.id.view_group_fade_helper_hardware_layer, null)
|
||||
}
|
||||
viewToFade.setTag(R.id.view_group_fade_helper_restore_tag, null)
|
||||
}
|
||||
root.setTag(R.id.view_group_fade_helper_modified_views, null)
|
||||
root.setTag(R.id.view_group_fade_helper_previous_value_tag, null)
|
||||
root.setTag(R.id.view_group_fade_helper_animator, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,6 +202,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
|
||||
import com.android.systemui.statusbar.notification.NotificationListController;
|
||||
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
|
||||
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
|
||||
import com.android.systemui.statusbar.notification.VisualStabilityManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
|
||||
@@ -1232,7 +1233,8 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
new Handler(), mKeyguardUpdateMonitor, mKeyguardBypassController);
|
||||
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
|
||||
getBouncerContainer(), mNotificationPanel, mBiometricUnlockController,
|
||||
mStatusBarWindow.findViewById(R.id.lock_icon_container));
|
||||
mStatusBarWindow.findViewById(R.id.lock_icon_container), mStackScroller,
|
||||
mKeyguardBypassController);
|
||||
mKeyguardIndicationController
|
||||
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
|
||||
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
|
||||
@@ -3169,6 +3171,7 @@ public class StatusBar extends SystemUI implements DemoMode,
|
||||
mNotificationPanel.onAffordanceLaunchEnded();
|
||||
mNotificationPanel.animate().cancel();
|
||||
mNotificationPanel.setAlpha(1f);
|
||||
ViewGroupFadeHelper.reset(mNotificationPanel);
|
||||
updateScrimController();
|
||||
Trace.endSection();
|
||||
return staying;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.statusbar.phone;
|
||||
|
||||
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
|
||||
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_FADING;
|
||||
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
|
||||
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
|
||||
|
||||
@@ -51,6 +52,7 @@ import com.android.systemui.statusbar.NotificationMediaManager;
|
||||
import com.android.systemui.statusbar.RemoteInputController;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
||||
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
|
||||
import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
import com.android.systemui.statusbar.policy.KeyguardMonitor;
|
||||
@@ -82,6 +84,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
// make everything a bit slower to bridge a gap until the user is unlocked and home screen has
|
||||
// dranw its first frame.
|
||||
private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000;
|
||||
private static final long BYPASS_PANEL_FADE_DURATION = 67;
|
||||
|
||||
private static String TAG = "StatusBarKeyguardViewManager";
|
||||
|
||||
@@ -132,6 +135,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
|
||||
private ViewGroup mContainer;
|
||||
private ViewGroup mLockIconContainer;
|
||||
private View mNotificationContainer;
|
||||
|
||||
protected KeyguardBouncer mBouncer;
|
||||
protected boolean mShowing;
|
||||
@@ -165,9 +169,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
(KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
|
||||
private final NotificationMediaManager mMediaManager =
|
||||
Dependency.get(NotificationMediaManager.class);
|
||||
private final StatusBarStateController mStatusBarStateController =
|
||||
Dependency.get(StatusBarStateController.class);
|
||||
private final SysuiStatusBarStateController mStatusBarStateController =
|
||||
(SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
|
||||
private final DockManager mDockManager;
|
||||
private KeyguardBypassController mBypassController;
|
||||
|
||||
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
|
||||
new KeyguardUpdateMonitorCallback() {
|
||||
@@ -205,7 +210,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
NotificationPanelView notificationPanelView,
|
||||
BiometricUnlockController biometricUnlockController,
|
||||
DismissCallbackRegistry dismissCallbackRegistry,
|
||||
ViewGroup lockIconContainer) {
|
||||
ViewGroup lockIconContainer, View notificationContainer,
|
||||
KeyguardBypassController bypassController) {
|
||||
mStatusBar = statusBar;
|
||||
mContainer = container;
|
||||
mLockIconContainer = lockIconContainer;
|
||||
@@ -218,6 +224,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
mExpansionCallback);
|
||||
mNotificationPanelView = notificationPanelView;
|
||||
notificationPanelView.setExpansionListener(this);
|
||||
mBypassController = bypassController;
|
||||
mNotificationContainer = notificationContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -555,12 +563,32 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
mBiometricUnlockController.startKeyguardFadingAway();
|
||||
hideBouncer(true /* destroyView */);
|
||||
if (wakeUnlockPulsing) {
|
||||
mStatusBar.fadeKeyguardWhilePulsing();
|
||||
if (needsBypassFading()) {
|
||||
ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView,
|
||||
mNotificationContainer,
|
||||
BYPASS_PANEL_FADE_DURATION,
|
||||
() -> {
|
||||
mStatusBar.hideKeyguard();
|
||||
onKeyguardFadedAway();
|
||||
});
|
||||
} else {
|
||||
mStatusBar.fadeKeyguardWhilePulsing();
|
||||
}
|
||||
wakeAndUnlockDejank();
|
||||
} else {
|
||||
boolean staying = mStatusBar.hideKeyguard();
|
||||
boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
|
||||
if (!staying) {
|
||||
mStatusBarWindowController.setKeyguardFadingAway(true);
|
||||
if (needsBypassFading()) {
|
||||
ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView,
|
||||
mNotificationContainer,
|
||||
BYPASS_PANEL_FADE_DURATION,
|
||||
() -> {
|
||||
mStatusBar.hideKeyguard();
|
||||
});
|
||||
} else {
|
||||
mStatusBar.hideKeyguard();
|
||||
}
|
||||
// hide() will happen asynchronously and might arrive after the scrims
|
||||
// were already hidden, this means that the transition callback won't
|
||||
// be triggered anymore and StatusBarWindowController will be forever in
|
||||
@@ -568,6 +596,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
mStatusBar.updateScrimController();
|
||||
wakeAndUnlockDejank();
|
||||
} else {
|
||||
mStatusBar.hideKeyguard();
|
||||
mStatusBar.finishKeyguardFadingAway();
|
||||
mBiometricUnlockController.finishKeyguardFadingAway();
|
||||
}
|
||||
@@ -580,6 +609,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
StatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN);
|
||||
}
|
||||
|
||||
private boolean needsBypassFading() {
|
||||
return (mBiometricUnlockController.getMode() == MODE_UNLOCK_FADING
|
||||
|| mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING
|
||||
|| mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK)
|
||||
&& mBypassController.getBypassEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDensityOrFontScaleChanged() {
|
||||
hideBouncer(true /* destroyView */);
|
||||
@@ -602,6 +638,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
public void onKeyguardFadedAway() {
|
||||
mContainer.postDelayed(() -> mStatusBarWindowController.setKeyguardFadingAway(false),
|
||||
100);
|
||||
ViewGroupFadeHelper.reset(mNotificationPanelView);
|
||||
mStatusBar.finishKeyguardFadingAway();
|
||||
mBiometricUnlockController.finishKeyguardFadingAway();
|
||||
WindowManagerGlobal.getInstance().trimMemory(
|
||||
@@ -821,8 +858,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
* @return Whether subtle animation should be used for unlocking the device.
|
||||
*/
|
||||
public boolean shouldSubtleWindowAnimationsForUnlock() {
|
||||
return mStatusBar.mKeyguardBypassController.getBypassEnabled()
|
||||
&& mStatusBar.mState == StatusBarState.KEYGUARD && !mBouncer.isAnimatingAway();
|
||||
return needsBypassFading();
|
||||
}
|
||||
|
||||
public boolean isGoingToNotificationShade() {
|
||||
|
||||
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
|
||||
import android.content.Context;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -39,6 +40,7 @@ import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
||||
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -70,7 +72,11 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private ViewGroup mLockIconContainer;
|
||||
@Mock
|
||||
private StatusBarStateController mStatusBarStateController;
|
||||
private SysuiStatusBarStateController mStatusBarStateController;
|
||||
@Mock
|
||||
private View mNotificationContainer;
|
||||
@Mock
|
||||
private KeyguardBypassController mBypassController;
|
||||
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
||||
|
||||
@Before
|
||||
@@ -83,7 +89,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
mViewMediatorCallback, mLockPatternUtils);
|
||||
mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
|
||||
mNotificationPanelView, mBiometrucUnlockController, mDismissCallbackRegistry,
|
||||
mLockIconContainer);
|
||||
mLockIconContainer, mNotificationContainer, mBypassController);
|
||||
mStatusBarKeyguardViewManager.show(null);
|
||||
}
|
||||
|
||||
@@ -221,9 +227,11 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
|
||||
NotificationPanelView notificationPanelView,
|
||||
BiometricUnlockController fingerprintUnlockController,
|
||||
DismissCallbackRegistry dismissCallbackRegistry,
|
||||
ViewGroup lockIconContainer) {
|
||||
ViewGroup lockIconContainer, View notificationContainer,
|
||||
KeyguardBypassController bypassController) {
|
||||
super.registerStatusBar(statusBar, container, notificationPanelView,
|
||||
fingerprintUnlockController, dismissCallbackRegistry, lockIconContainer);
|
||||
fingerprintUnlockController, dismissCallbackRegistry, lockIconContainer,
|
||||
notificationContainer, bypassController);
|
||||
mBouncer = StatusBarKeyguardViewManagerTest.this.mBouncer;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user