Support for passive interrupts

Bug: 111414690
Test: manual, lift, swipe down, go to shade locked
Test: adb shell setprop persist.sysui.expand_shade_on_wake_up 0
Test: adb shell setprop persist.sysui.go_to_shade_on_wake_up 0
Change-Id: I59018a72b85cfcf75344d83bbf9e3a122a66c018
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index bb05980..1e61a77 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -47,6 +47,12 @@
 
     void onIgnoreTouchWhilePulsing(boolean ignore);
 
+    /**
+     * If the device was waken up by a passive interrupt that will show the lock screen without
+     * expanding the notification panel/shade.
+     */
+    void setPassiveInterrupt(boolean lightInterrupt);
+
     interface Callback {
         /**
          * Called when a high priority notification is added.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index cb91d78..d69b1bf 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -142,6 +142,7 @@
                     mDozeHost.onDoubleTap(screenX, screenY);
                     mMachine.wakeUp();
                 } else if (isPickup || isWakeLockScreen) {
+                    mDozeHost.setPassiveInterrupt(true);
                     mMachine.wakeUp();
                 } else {
                     mDozeHost.extendPulse();
@@ -210,6 +211,7 @@
             case INITIALIZED:
                 mBroadcastReceiver.register(mContext);
                 mDozeHost.addCallback(mHostCallback);
+                mDozeHost.setPassiveInterrupt(false);
                 checkTriggersAtInit();
                 break;
             case DOZE:
@@ -219,6 +221,7 @@
                     mDozeSensors.reregisterAllSensors();
                 }
                 mDozeSensors.setListening(true);
+                mDozeHost.setPassiveInterrupt(false);
                 break;
             case DOZE_AOD_PAUSED:
             case DOZE_AOD_PAUSING:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 30d9ef7..8526afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -131,7 +131,7 @@
                 if (!isFalseTouch() && mDragDownCallback.onDraggedDown(mStartingChild,
                         (int) (y - mInitialTouchY))) {
                     if (mStartingChild == null) {
-                        mDragDownCallback.setEmptyDragAmount(0f);
+                        cancelExpansion();
                     } else {
                         mCallback.setUserLockedChild(mStartingChild, false);
                         mStartingChild = null;
@@ -206,11 +206,8 @@
         ValueAnimator anim = ValueAnimator.ofFloat(mLastHeight, 0);
         anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
-        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                mDragDownCallback.setEmptyDragAmount((Float) animation.getAnimatedValue());
-            }
+        anim.addUpdateListener(animation -> {
+            mDragDownCallback.setEmptyDragAmount((Float) animation.getAnimatedValue());
         });
         anim.start();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 7fa0426..1495abf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -793,7 +793,7 @@
     private void setOpenedAmount(float openedAmount) {
         mNoAnimationsInThisFrame = openedAmount == 1.0f && mOpenedAmount == 0.0f;
         mOpenedAmount = openedAmount;
-        if (!mAmbientState.isPanelFullWidth()) {
+        if (!mAmbientState.isPanelFullWidth() || mAmbientState.isDark()) {
             // We don't do a transformation at all, lets just assume we are fully opened
             openedAmount = 1.0f;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 0bc54a3..659f6c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -5654,19 +5654,21 @@
         @Override
         public boolean onDraggedDown(View startingChild, int dragLengthY) {
             if (mStatusBarState == StatusBarState.KEYGUARD
-                    && hasActiveNotifications() && (!mStatusBar.isDozing()
-                    || mStatusBar.isPulsing())) {
+                    && hasActiveNotifications()) {
                 mLockscreenGestureLogger.write(
                         MetricsEvent.ACTION_LS_SHADE,
                         (int) (dragLengthY / mDisplayMetrics.density),
                         0 /* velocityDp - N/A */);
 
-                // We have notifications, go to locked shade.
-                mStatusBar.goToLockedShade(startingChild);
-                if (startingChild instanceof ExpandableNotificationRow) {
-                    ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
-                    row.onExpandedByGesture(true /* drag down is always an open */);
+                if (mNotificationPanel.onDraggedDown() || startingChild != null) {
+                    // We have notifications, go to locked shade.
+                    mStatusBar.goToLockedShade(startingChild);
+                    if (startingChild instanceof ExpandableNotificationRow) {
+                        ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
+                        row.onExpandedByGesture(true /* drag down is always an open */);
+                    }
                 }
+
                 return true;
             } else {
                 // abort gesture.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 33bc164..836a55f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -108,11 +108,7 @@
      * Dozing and receiving a notification (AOD notification.)
      */
     private boolean mPulsing;
-
-    /**
-     * Distance in pixels between the top of the screen and the first view of the bouncer.
-     */
-    private int mBouncerTop;
+    private float mEmptyDragAmount;
 
     /**
      * Refreshes the dimension values.
@@ -131,9 +127,8 @@
     }
 
     public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
-            float panelExpansion, int parentHeight,
-            int keyguardStatusHeight, float dark, boolean secure, boolean pulsing,
-            int bouncerTop) {
+            float panelExpansion, int parentHeight, int keyguardStatusHeight, float dark,
+            boolean secure, boolean pulsing, float emptyDragAmount) {
         mMinTopMargin = minTopMargin + mContainerTopPadding;
         mMaxShadeBottom = maxShadeBottom;
         mNotificationStackHeight = notificationStackHeight;
@@ -143,7 +138,7 @@
         mDarkAmount = dark;
         mCurrentlySecure = secure;
         mPulsing = pulsing;
-        mBouncerTop = bouncerTop;
+        mEmptyDragAmount = emptyDragAmount;
     }
 
     public void run(Result result) {
@@ -194,15 +189,14 @@
         }
 
         float clockYRegular = getExpandedClockPosition();
-        boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop;
-        float clockYTarget = mCurrentlySecure && hasEnoughSpace ?
-                mMinTopMargin : -mKeyguardStatusHeight;
+        float clockYBouncer = -mKeyguardStatusHeight;
 
         // Move clock up while collapsing the shade
         float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(mPanelExpansion);
-        final float clockY = MathUtils.lerp(clockYTarget, clockYRegular, shadeExpansion);
+        float clockY = MathUtils.lerp(clockYBouncer, clockYRegular, shadeExpansion);
+        clockYDark = MathUtils.lerp(clockYBouncer, clockYDark, shadeExpansion);
 
-        return (int) MathUtils.lerp(clockY, clockYDark, mDarkAmount);
+        return (int) (MathUtils.lerp(clockY, clockYDark, mDarkAmount) + mEmptyDragAmount);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 7507702..e31bad65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -38,6 +38,7 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.os.PowerManager;
+import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
@@ -104,6 +105,11 @@
 
     private static final boolean DEBUG = false;
 
+    private static final boolean EXPAND_ON_WAKE_UP = SystemProperties.getBoolean(
+            "persist.sysui.expand_shade_on_wake_up", true);
+    private static final boolean WAKE_UP_TO_SHADE = SystemProperties.getBoolean(
+            "persist.sysui.go_to_shade_on_wake_up", true);
+
     /**
      * Fling expanding QS.
      */
@@ -280,6 +286,12 @@
      */
     private float mLinearDarkAmount;
 
+    /**
+     * State where the device isn't dozing anymore, but the lock screen isn't fully awake.
+     * The screen will be dimmed down with the shade collapsed.
+     */
+    private boolean mSemiAwake;
+
     private float mDarkAmountTarget;
     private boolean mPulsing;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
@@ -573,7 +585,7 @@
                     mInterpolatedDarkAmount,
                     mStatusBar.isKeyguardCurrentlySecure(),
                     mPulsing,
-                    mBouncerTop);
+                    mEmptyDragAmount);
             mClockPositionAlgorithm.run(mClockPositionResult);
             PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
                     mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
@@ -1235,6 +1247,12 @@
         if (keyguardShowing) {
             updateDozingVisibilities(false /* animate */);
         }
+
+        // Expand notification shade if the device was is semi-awake state
+        if (mBarState == StatusBarState.SHADE && isSemiAwake()) {
+            mNotificationStackScroller.setDark(false /* dark */, false /* animated */,
+                    null /* touchLocation */);
+        }
         resetVerticalPanelPosition();
         updateQsState();
     }
@@ -2335,13 +2353,7 @@
     }
 
     public void setEmptyDragAmount(float amount) {
-        float factor = 0.8f;
-        if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
-            factor = 0.4f;
-        } else if (!mStatusBar.hasActiveNotifications()) {
-            factor = 0.4f;
-        }
-        mEmptyDragAmount = amount * factor;
+        mEmptyDragAmount = amount * 0.2f;
         positionClockAndNotifications();
     }
 
@@ -2769,11 +2781,14 @@
         mNotificationStackScroller.setAnimationsEnabled(!disabled);
     }
 
-    public void setDozing(boolean dozing, boolean animate,
-            PointF wakeUpTouchLocation) {
-        mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
+    public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation,
+            boolean passiveInterrupted) {
         if (dozing == mDozing) return;
         mDozing = dozing;
+        mSemiAwake = !EXPAND_ON_WAKE_UP && !mDozing && passiveInterrupted;
+        if (!mSemiAwake) {
+            mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
+        }
 
         if (mBarState == StatusBarState.KEYGUARD
                 || mBarState == StatusBarState.SHADE_LOCKED) {
@@ -2787,24 +2802,38 @@
             } else {
                 mDarkAnimator.cancel();
             }
+            if (mSemiAwake) {
+                setDarkAmount(0, 0);
+            }
         }
         mDarkAmountTarget = darkAmount;
-        if (animate) {
-            if (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f) {
-                mDarkInterpolator = dozing
-                        ? Interpolators.FAST_OUT_SLOW_IN
-                        : Interpolators.TOUCH_RESPONSE_REVERSE;
+        if (!mSemiAwake) {
+            if (animate) {
+                startDarkAnimation();
+            } else {
+                setDarkAmount(darkAmount, darkAmount);
             }
-            mNotificationStackScroller.notifyDarkAnimationStart(dozing);
-            mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, darkAmount);
-            mDarkAnimator.setInterpolator(Interpolators.LINEAR);
-            mDarkAnimator.setDuration(mNotificationStackScroller.getDarkAnimationDuration(dozing));
-            mDarkAnimator.start();
-        } else {
-            setDarkAmount(darkAmount, darkAmount);
         }
     }
 
+    public boolean isSemiAwake() {
+        return mSemiAwake;
+    }
+
+    private void startDarkAnimation() {
+        if (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f) {
+            mDarkInterpolator = mDozing
+                    ? Interpolators.FAST_OUT_SLOW_IN
+                    : Interpolators.TOUCH_RESPONSE_REVERSE;
+        }
+        mNotificationStackScroller.notifyDarkAnimationStart(mDozing);
+        mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozing ? 1 : 0);
+        mDarkAnimator.setInterpolator(Interpolators.LINEAR);
+        mDarkAnimator.setDuration(
+                mNotificationStackScroller.getDarkAnimationDuration(mDozing));
+        mDarkAnimator.start();
+    }
+
     private void setDarkAmount(float linearAmount, float amount) {
         mInterpolatedDarkAmount = amount;
         mLinearDarkAmount = linearAmount;
@@ -2989,4 +3018,22 @@
         mNotificationStackScroller.setScrimController(scrimController);
         updateShowEmptyShadeView();
     }
+
+    /**
+     * Whenever a user drags down on the empty area (pulling down the shade and clock) and lets go.
+     *
+     * @return {@code true} if dragging down should take the user to SHADE_LOCKED.
+     */
+    public boolean onDraggedDown() {
+        if (isSemiAwake()) {
+            mSemiAwake = false;
+            mNotificationStackScroller.setDark(false /* dark */, true /* animate */,
+                    null /* touchLocation */);
+            startDarkAnimation();
+            mStatusBar.updateScrimController();
+
+            return WAKE_UP_TO_SHADE;
+        }
+        return true;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e3a7b75..1bed26d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -63,7 +63,7 @@
 public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener,
         Dumpable {
 
-    private static final String TAG = "ScrimController";
+    static final String TAG = "ScrimController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     /**
@@ -96,6 +96,11 @@
      */
     public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.70f;
     /**
+     * A scrim varies its opacity based on a busyness factor, for example
+     * how many notifications are currently visible.
+     */
+    public static final float GRADIENT_SCRIM_DARK_KEYGUARD = 0.80f;
+    /**
      * The most common scrim, the one under the keyguard.
      */
     protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA;
@@ -361,7 +366,7 @@
             mExpansionFraction = fraction;
 
             final boolean keyguardOrUnlocked = mState == ScrimState.UNLOCKED
-                    || mState == ScrimState.KEYGUARD;
+                    || mState == ScrimState.KEYGUARD || mState == ScrimState.DARK_KEYGUARD;
             if (!keyguardOrUnlocked || !mExpansionAffectsAlpha) {
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 085f7b6..ade063d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -49,6 +49,8 @@
                     // fade it out afterwards.
                     mBlankScreen = true;
                 }
+            } else if (previousState == ScrimState.KEYGUARD) {
+                mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
             } else {
                 mAnimationDuration = ScrimController.ANIMATION_DURATION;
             }
@@ -59,8 +61,24 @@
         @Override
         public float getBehindAlpha(float busynessFactor) {
             return MathUtils.map(0 /* start */, 1 /* stop */,
-                   mScrimBehindAlphaKeyguard, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
-                   busynessFactor);
+                    mScrimBehindAlphaKeyguard, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
+                    busynessFactor);
+        }
+    },
+
+    /**
+     * On semi-awake lock screen.
+     */
+    DARK_KEYGUARD(7) {
+
+        @Override
+        public void prepare(ScrimState previousState) {
+            mBlankScreen = mDisplayRequiresBlanking && previousState != ScrimState.AOD;
+            mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+            mCurrentBehindAlpha = ScrimController.GRADIENT_SCRIM_DARK_KEYGUARD;
+            mCurrentInFrontAlpha = 0;
+            mCurrentInFrontTint = Color.BLACK;
+            mCurrentBehindTint = Color.BLACK;
         }
     },
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index cc9adb8..56e5a1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3617,7 +3617,8 @@
 
         mDozeScrimController.setDozing(mDozing);
         mKeyguardIndicationController.setDozing(mDozing);
-        mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
+        mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation,
+                mDozeServiceHost.wasPassivelyInterrupted());
         mNotificationLogger.setDozing(mDozing);
         mGroupManager.setDozing(mDozing);
         updateQsExpansionEnabled();
@@ -4203,7 +4204,6 @@
             mAmbientPulseManager.releaseAllImmediately();
             mVisualStabilityManager.setScreenOn(true);
             mNotificationPanel.setTouchAndAnimationDisabled(false);
-            mDozeServiceHost.stopDozing();
             updateVisibleToUser();
             updateIsKeyguard();
             updateScrimController();
@@ -4401,6 +4401,9 @@
             // FLAG_DISMISS_KEYGUARD_ACTIVITY.
             ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
                     ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
+            if (mNotificationPanel.isSemiAwake()) {
+                state = ScrimState.DARK_KEYGUARD;
+            }
             mScrimController.transitionTo(state);
         } else if (isInLaunchTransition() || mLaunchCameraOnScreenTurningOn
                 || launchingAffordanceWithPreview) {
@@ -4412,7 +4415,8 @@
         } else if (mDozing) {
             mScrimController.transitionTo(ScrimState.AOD);
         } else if (mIsKeyguard && !wakeAndUnlocking) {
-            mScrimController.transitionTo(ScrimState.KEYGUARD);
+            mScrimController.transitionTo(mNotificationPanel.isSemiAwake()
+                    ? ScrimState.DARK_KEYGUARD : ScrimState.KEYGUARD);
         } else {
             mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         }
@@ -4432,6 +4436,7 @@
         private boolean mAnimateWakeup;
         private boolean mAnimateScreenOff;
         private boolean mIgnoreTouchWhilePulsing;
+        private boolean mPassivelyInterrupted;
 
         @Override
         public String toString() {
@@ -4515,6 +4520,11 @@
         }
 
         @Override
+        public void setPassiveInterrupt(boolean passiveInterrupt) {
+            mPassivelyInterrupted = passiveInterrupt;
+        }
+
+        @Override
         public void onIgnoreTouchWhilePulsing(boolean ignore) {
             if (ignore != mIgnoreTouchWhilePulsing) {
                 DozeLog.tracePulseTouchDisabledByProx(mContext, ignore);
@@ -4633,6 +4643,10 @@
         public boolean shouldAnimateScreenOff() {
             return mAnimateScreenOff;
         }
+
+        public boolean wasPassivelyInterrupted() {
+            return mPassivelyInterrupted;
+        }
     }
 
     public boolean shouldIgnoreTouch() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index 2398fd3..6abd407 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -115,6 +115,11 @@
     }
 
     @Override
+    public void setPassiveInterrupt(boolean lightInterrupt) {
+
+    }
+
+    @Override
     public void setDozeScreenBrightness(int value) {
     }