Merge changes Ia438f4a2,I356c2d57 into pi-dev

* changes:
  Bouncer notification and QS scrim
  Fix bouncer not showing on top of QS or notif
This commit is contained in:
Lucas Dupin
2018-03-19 03:11:51 +00:00
committed by Android (Google) Code Review
10 changed files with 318 additions and 46 deletions

View File

@@ -16,6 +16,7 @@
package com.android.systemui;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.util.Assert;
import android.os.Handler;
@@ -30,9 +31,13 @@ public class DejankUtils {
private static final Choreographer sChoreographer = Choreographer.getInstance();
private static final Handler sHandler = new Handler();
private static final ArrayList<Runnable> sPendingRunnables = new ArrayList<>();
/**
* Only for testing.
*/
private static boolean sImmediate;
private static final Runnable sAnimationCallbackRunnable = new Runnable() {
@Override
public void run() {
@@ -51,6 +56,10 @@ public class DejankUtils {
* <p>Needs to be called from the main thread.
*/
public static void postAfterTraversal(Runnable r) {
if (sImmediate) {
r.run();
return;
}
Assert.isMainThread();
sPendingRunnables.add(r);
postAnimationCallback();
@@ -71,4 +80,9 @@ public class DejankUtils {
sChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, sAnimationCallbackRunnable,
null);
}
@VisibleForTesting
public static void setImmediate(boolean immediate) {
sImmediate = immediate;
}
}

View File

@@ -27,6 +27,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency.DependencyProvider;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -95,7 +96,7 @@ public class SystemUIFactory {
LockPatternUtils lockPatternUtils,
ViewGroup container, DismissCallbackRegistry dismissCallbackRegistry) {
return new KeyguardBouncer(context, callback, lockPatternUtils, container,
dismissCallbackRegistry);
dismissCallbackRegistry, FalsingManager.getInstance(context));
}
public ScrimController createScrimController(LightBarController lightBarController,

View File

@@ -16,10 +16,14 @@
package com.android.systemui.statusbar.phone;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import android.content.Context;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
import android.util.StatsLog;
@@ -29,7 +33,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardHostView;
@@ -42,9 +45,6 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
/**
* A class which manages the bouncer on the lockscreen.
*/
@@ -76,13 +76,13 @@ public class KeyguardBouncer {
public KeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, ViewGroup container,
DismissCallbackRegistry dismissCallbackRegistry) {
DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager) {
mContext = context;
mCallback = callback;
mLockPatternUtils = lockPatternUtils;
mContainer = container;
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
mFalsingManager = FalsingManager.getInstance(mContext);
mFalsingManager = falsingManager;
mDismissCallbackRegistry = dismissCallbackRegistry;
mHandler = new Handler();
}
@@ -91,7 +91,14 @@ public class KeyguardBouncer {
show(resetSecuritySelection, true /* notifyFalsing */);
}
public void show(boolean resetSecuritySelection, boolean notifyFalsing) {
/**
* Shows the bouncer.
*
* @param resetSecuritySelection Cleans keyguard view
* @param animated true when the bouncer show show animated, false when the user will be
* dragging it and animation should be deferred.
*/
public void show(boolean resetSecuritySelection, boolean animated) {
final int keyguardUserId = KeyguardUpdateMonitor.getCurrentUser();
if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) {
// In split system user mode, we never unlock system user.
@@ -104,9 +111,11 @@ public class KeyguardBouncer {
// are valid.
// Later, at the end of the animation, when the bouncer is at the top of the screen,
// onFullyShown() will be called and FalsingManager will stop recording touches.
if (notifyFalsing) {
if (animated) {
mFalsingManager.onBouncerShown();
setExpansion(0);
}
if (resetSecuritySelection) {
// showPrimarySecurityScreen() updates the current security method. This is needed in
// case we are already showing and the current security method changed.
@@ -157,7 +166,9 @@ public class KeyguardBouncer {
public void onFullyHidden() {
if (!mShowingSoon) {
cancelShowRunnable();
mRoot.setVisibility(View.INVISIBLE);
if (mRoot != null) {
mRoot.setVisibility(View.INVISIBLE);
}
mFalsingManager.onBouncerHidden();
}
}
@@ -202,11 +213,19 @@ public class KeyguardBouncer {
* and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}
*/
public void showPromptReason(int reason) {
mKeyguardView.showPromptReason(reason);
if (mKeyguardView != null) {
mKeyguardView.showPromptReason(reason);
} else {
Log.w(TAG, "Trying to show prompt reason on empty bouncer");
}
}
public void showMessage(String message, int color) {
mKeyguardView.showMessage(message, color);
if (mKeyguardView != null) {
mKeyguardView.showMessage(message, color);
} else {
Log.w(TAG, "Trying to show message on empty bouncer");
}
}
private void cancelShowRunnable() {
@@ -290,7 +309,8 @@ public class KeyguardBouncer {
*/
public void setExpansion(float fraction) {
if (mKeyguardView != null) {
mKeyguardView.setAlpha(MathUtils.map(ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction));
float alpha = MathUtils.map(ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction);
mKeyguardView.setAlpha(MathUtils.constrain(alpha, 0f, 1f));
mKeyguardView.setTranslationY(fraction * mKeyguardView.getHeight());
}
}

View File

@@ -947,6 +947,17 @@ public abstract class PanelView extends FrameLayout {
return mClosing || mLaunchingNotification;
}
/**
* Bouncer might need a scrim when you double tap on notifications or edit QS.
* On other cases, when you drag up the bouncer with the finger or just fling,
* the scrim should be hidden to avoid occluding the clock.
*
* @return true when we need a scrim to show content on top of the notification panel.
*/
public boolean needsScrimming() {
return !isTracking() && !isCollapsing() && !isFullyCollapsed();
}
public boolean isTracking() {
return mTracking;
}

View File

@@ -449,7 +449,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
if (mNeedsDrawableColorUpdate) {
mNeedsDrawableColorUpdate = false;
final GradientColors currentScrimColors;
if (mState == ScrimState.KEYGUARD || mState == ScrimState.BOUNCER_OCCLUDED
if (mState == ScrimState.KEYGUARD || mState == ScrimState.BOUNCER_SCRIMMED
|| mState == ScrimState.BOUNCER) {
// Always animate color changes if we're seeing the keyguard
mScrimInFront.setColors(mLockColors, true /* animated */);

View File

@@ -79,7 +79,7 @@ public enum ScrimState {
/**
* Showing password challenge on top of a FLAG_SHOW_WHEN_LOCKED activity.
*/
BOUNCER_OCCLUDED(2) {
BOUNCER_SCRIMMED(2) {
@Override
public void prepare(ScrimState previousState) {
mCurrentBehindAlpha = 0;

View File

@@ -3907,12 +3907,12 @@ public class StatusBar extends SystemUI implements DemoMode,
private void showBouncerIfKeyguard() {
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
showBouncer();
showBouncer(true /* animated */);
}
}
protected void showBouncer() {
mStatusBarKeyguardViewManager.dismiss();
protected void showBouncer(boolean animated) {
mStatusBarKeyguardViewManager.showBouncer(animated);
}
private void instantExpandNotificationsPanel() {
@@ -4026,7 +4026,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void onTrackingStopped(boolean expand) {
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
if (!expand && !mUnlockMethodCache.canSkipBouncer()) {
showBouncerIfKeyguard();
showBouncer(false /* animated */);
}
}
}
@@ -4165,7 +4165,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
mLeaveOpenOnKeyguardHide = true;
showBouncer();
showBouncer(true /* animated */);
mPendingRemoteInputView = clicked;
}
@@ -4631,9 +4631,10 @@ public class StatusBar extends SystemUI implements DemoMode,
!= FingerprintUnlockController.MODE_UNLOCK);
if (mBouncerShowing) {
final boolean qsExpanded = mQSPanel != null && mQSPanel.isExpanded();
mScrimController.transitionTo(mIsOccluded || qsExpanded ?
ScrimState.BOUNCER_OCCLUDED : ScrimState.BOUNCER);
// Bouncer needs the front scrim when it's on top of an activity,
// tapping on a notification or editing QS.
mScrimController.transitionTo(mIsOccluded || mNotificationPanel.needsScrimming() ?
ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER);
} else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
} else if (mBrightnessMirrorVisible) {

View File

@@ -198,9 +198,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
cancelPendingWakeupAction();
}
private void showBouncer() {
public void showBouncer(boolean animated) {
if (mShowing) {
mBouncer.show(false /* resetSecuritySelection */);
mBouncer.show(false /* resetSecuritySelection */, animated);
}
updateStates();
}
@@ -485,10 +485,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mStatusBar.executeRunnableDismissingKeyguard(null, null, true, false, true);
}
public void dismiss() {
showBouncer();
}
/**
* WARNING: This method might cause Binder calls.
*/

View File

@@ -16,39 +16,268 @@
package com.android.systemui.statusbar.phone;
import static org.mockito.Mockito.mock;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.calls;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.graphics.Color;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.UiThreadTest;
import android.view.ContextThemeWrapper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardHostView;
import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.R;
import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class KeyguardBouncerTest extends SysuiTestCase {
@UiThreadTest
@Test
public void inflateDetached() {
final ViewGroup container = new FrameLayout(getContext());
final KeyguardBouncer bouncer = new KeyguardBouncer(getContext(),
mock(ViewMediatorCallback.class), mock(LockPatternUtils.class), container, mock(
DismissCallbackRegistry.class));
@Mock
private FalsingManager mFalsingManager;
@Mock
private ViewMediatorCallback mViewMediatorCallback;
@Mock
private LockPatternUtils mLockPatternUtils;
@Mock
private DismissCallbackRegistry mDismissCallbackRegistry;
@Mock
private KeyguardHostView mKeyguardHostView;
@Mock
private ViewTreeObserver mViewTreeObserver;
// Detached bouncer should still be able to be inflated
bouncer.inflateView();
private KeyguardBouncer mBouncer;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
DejankUtils.setImmediate(true);
final ViewGroup container = new FrameLayout(getContext());
when(mKeyguardHostView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
when(mKeyguardHostView.getHeight()).thenReturn(500);
mBouncer = new KeyguardBouncer(getContext(), mViewMediatorCallback,
mLockPatternUtils, container, mDismissCallbackRegistry, mFalsingManager) {
@Override
protected void inflateView() {
super.inflateView();
mKeyguardView = mKeyguardHostView;
}
};
}
@Test
public void testInflateView_doesntCrash() {
mBouncer.inflateView();
}
@Test
public void testShow_notifiesFalsingManager() {
mBouncer.show(true);
verify(mFalsingManager).onBouncerShown();
mBouncer.show(true, false);
verifyNoMoreInteractions(mFalsingManager);
}
/**
* Regression test: Invisible bouncer when occluded.
*/
@Test
public void testShow_bouncerIsVisible() {
// Expand notification panel as if we were in the keyguard.
mBouncer.ensureView();
mBouncer.setExpansion(1);
reset(mKeyguardHostView);
when(mKeyguardHostView.getHeight()).thenReturn(500);
mBouncer.show(true);
verify(mKeyguardHostView).setAlpha(eq(1f));
verify(mKeyguardHostView).setTranslationY(eq(0f));
}
@Test
public void testShow_notifiesVisibility() {
mBouncer.show(true);
verify(mViewMediatorCallback).onBouncerVisiblityChanged(eq(true));
// Not called again when visible
reset(mViewMediatorCallback);
mBouncer.show(true);
verifyNoMoreInteractions(mViewMediatorCallback);
}
@Test
public void testShow_triesToDismissKeyguard() {
mBouncer.show(true);
verify(mKeyguardHostView).dismiss(anyInt());
}
@Test
public void testShow_resetsSecuritySelection() {
mBouncer.show(false);
verify(mKeyguardHostView, never()).showPrimarySecurityScreen();
mBouncer.hide(false);
mBouncer.show(true);
verify(mKeyguardHostView).showPrimarySecurityScreen();
}
@Test
public void testShow_animatesKeyguardView() {
mBouncer.show(true);
verify(mKeyguardHostView).startAppearAnimation();
}
@Test
public void testShow_showsErrorMessage() {
final String errorMessage = "an error message";
when(mViewMediatorCallback.consumeCustomMessage()).thenReturn(errorMessage);
mBouncer.show(true);
verify(mKeyguardHostView).showErrorMessage(eq(errorMessage));
}
@Test
public void testOnFullyShown_notifiesFalsingManager() {
mBouncer.onFullyShown();
verify(mFalsingManager).onBouncerShown();
}
@Test
public void testOnFullyHidden_notifiesFalsingManager() {
mBouncer.onFullyHidden();
verify(mFalsingManager).onBouncerHidden();
}
@Test
public void testHide_notifiesFalsingManager() {
mBouncer.hide(false);
verify(mFalsingManager).onBouncerHidden();
}
@Test
public void testHide_notifiesVisibility() {
mBouncer.hide(false);
verify(mViewMediatorCallback).onBouncerVisiblityChanged(eq(false));
}
@Test
public void testHide_notifiesDismissCallbackIfVisible() {
mBouncer.hide(false);
verifyZeroInteractions(mDismissCallbackRegistry);
mBouncer.show(false);
mBouncer.hide(false);
verify(mDismissCallbackRegistry).notifyDismissCancelled();
}
@Test
public void testShowPromptReason_propagates() {
mBouncer.ensureView();
mBouncer.showPromptReason(1);
verify(mKeyguardHostView).showPromptReason(eq(1));
}
@Test
public void testShowMessage_propagates() {
final String message = "a message";
mBouncer.ensureView();
mBouncer.showMessage(message, Color.GREEN);
verify(mKeyguardHostView).showMessage(eq(message), eq(Color.GREEN));
}
@Test
public void testShowOnDismissAction_showsBouncer() {
final KeyguardHostView.OnDismissAction dismissAction = () -> false;
final Runnable cancelAction = () -> {};
mBouncer.showWithDismissAction(dismissAction, cancelAction);
verify(mKeyguardHostView).setOnDismissAction(dismissAction, cancelAction);
Assert.assertTrue("Should be showing", mBouncer.isShowing());
}
@Test
public void testStartPreHideAnimation_notifiesView() {
final boolean[] ran = {false};
final Runnable r = () -> ran[0] = true;
mBouncer.startPreHideAnimation(r);
Assert.assertTrue("Callback should have been invoked", ran[0]);
ran[0] = false;
mBouncer.ensureView();
mBouncer.startPreHideAnimation(r);
verify(mKeyguardHostView).startDisappearAnimation(r);
Assert.assertFalse("Callback should have been deferred", ran[0]);
}
@Test
public void testIsShowing() {
Assert.assertFalse("Show wasn't invoked yet", mBouncer.isShowing());
mBouncer.show(true);
Assert.assertTrue("Should be showing", mBouncer.isShowing());
}
@Test
public void testSetExpansion() {
mBouncer.ensureView();
mBouncer.setExpansion(0.5f);
verify(mKeyguardHostView).setAlpha(anyFloat());
verify(mKeyguardHostView).setTranslationY(anyFloat());
}
@Test
public void testNeedsFullscreenBouncer_asksKeyguardView() {
mBouncer.ensureView();
mBouncer.needsFullscreenBouncer();
verify(mKeyguardHostView).getSecurityMode();
verify(mKeyguardHostView, never()).getCurrentSecurityMode();
}
@Test
public void testIsFullscreenBouncer_asksKeyguardView() {
mBouncer.ensureView();
mBouncer.isFullscreenBouncer();
verify(mKeyguardHostView).getCurrentSecurityMode();
verify(mKeyguardHostView, never()).getSecurityMode();
}
@Test
public void testIsSecure() {
Assert.assertTrue("Bouncer is secure before inflating views", mBouncer.isSecure());
mBouncer.ensureView();
for (KeyguardSecurityModel.SecurityMode mode : KeyguardSecurityModel.SecurityMode.values()){
reset(mKeyguardHostView);
when(mKeyguardHostView.getSecurityMode()).thenReturn(mode);
Assert.assertEquals("Security doesn't match for mode: " + mode,
mBouncer.isSecure(), mode != KeyguardSecurityModel.SecurityMode.None);
}
}
}

View File

@@ -181,7 +181,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void transitionToBouncer() {
mScrimController.transitionTo(ScrimState.BOUNCER_OCCLUDED);
mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible without tint