Launching the camera now from systemUI including animations

The GestureLaunchService now informs systemUI that a launch
has been requested and the systemUI, depending on its state
will launch the Camera in the correct mode, including
animations.

Bug: 22957192
Bug: 22958025
Change-Id: I815437c8bd33638245ac61a750f64af74fe3e1e3
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c1645c3..feed3903 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -69,5 +69,10 @@
 
     void showAssistDisclosure();
     void startAssist(in Bundle args);
+
+    /**
+     * Notifies the status bar that a camera launch gesture has been detected.
+     */
+    void onCameraLaunchGestureDetected();
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index a1b07b5..025451d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -64,6 +64,7 @@
     private static final int MSG_APP_TRANSITION_STARTING    = 21 << MSG_SHIFT;
     private static final int MSG_ASSIST_DISCLOSURE          = 22 << MSG_SHIFT;
     private static final int MSG_START_ASSIST               = 23 << MSG_SHIFT;
+    private static final int MSG_CAMERA_LAUNCH_GESTURE      = 24 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -109,6 +110,7 @@
         public void appTransitionStarting(long startTime, long duration);
         public void showAssistDisclosure();
         public void startAssist(Bundle args);
+        public void onCameraLaunchGestureDetected();
     }
 
     public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -293,6 +295,14 @@
         }
     }
 
+    @Override
+    public void onCameraLaunchGestureDetected() {
+        synchronized (mList) {
+            mHandler.removeMessages(MSG_CAMERA_LAUNCH_GESTURE);
+            mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE).sendToTarget();
+        }
+    }
+
     private final class H extends Handler {
         public void handleMessage(Message msg) {
             final int what = msg.what & MSG_MASK;
@@ -391,6 +401,9 @@
                 case MSG_START_ASSIST:
                     mCallbacks.startAssist((Bundle) msg.obj);
                     break;
+                case MSG_CAMERA_LAUNCH_GESTURE:
+                    mCallbacks.onCameraLaunchGestureDetected();
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 164c496..8058933 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -36,6 +36,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.ImageView;
+
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -79,6 +80,7 @@
     private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
     private boolean mSupportHardware;
     private boolean mFinishing;
+    private boolean mLaunchingAffordance;
 
     private CanvasProperty<Float> mHwCircleRadius;
     private CanvasProperty<Float> mHwCenterX;
@@ -152,7 +154,7 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        mSupportHardware = canvas.isHardwareAccelerated();
+        mSupportHardware = false;//canvas.isHardwareAccelerated();
         drawBackgroundCircle(canvas);
         canvas.save();
         canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
@@ -161,9 +163,11 @@
     }
 
     public void setPreviewView(View v) {
+        View oldPreviewView = mPreviewView;
         mPreviewView = v;
         if (mPreviewView != null) {
-            mPreviewView.setVisibility(INVISIBLE);
+            mPreviewView.setVisibility(mLaunchingAffordance
+                    ? oldPreviewView.getVisibility() : INVISIBLE);
         }
     }
 
@@ -176,7 +180,7 @@
     }
 
     private void drawBackgroundCircle(Canvas canvas) {
-        if (mCircleRadius > 0) {
+        if (mCircleRadius > 0 || mFinishing) {
             if (mFinishing && mSupportHardware) {
                 DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
                 displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
@@ -207,11 +211,12 @@
         cancelAnimator(mPreviewClipper);
         mFinishing = true;
         mCircleStartRadius = mCircleRadius;
-        float maxCircleSize = getMaxCircleSize();
+        final float maxCircleSize = getMaxCircleSize();
         Animator animatorToRadius;
         if (mSupportHardware) {
             initHwProperties();
             animatorToRadius = getRtAnimatorToRadius(maxCircleSize);
+            startRtAlphaFadeIn();
         } else {
             animatorToRadius = getAnimatorToRadius(maxCircleSize);
         }
@@ -222,6 +227,8 @@
             public void onAnimationEnd(Animator animation) {
                 mAnimationEndRunnable.run();
                 mFinishing = false;
+                mCircleRadius = maxCircleSize;
+                invalidate();
             }
         });
         animatorToRadius.start();
@@ -241,6 +248,36 @@
         }
     }
 
+    /**
+     * Fades in the Circle on the RenderThread. It's used when finishing the circle when it had
+     * alpha 0 in the beginning.
+     */
+    private void startRtAlphaFadeIn() {
+        if (mCircleRadius == 0 && mPreviewView == null) {
+            Paint modifiedPaint = new Paint(mCirclePaint);
+            modifiedPaint.setColor(mCircleColor);
+            modifiedPaint.setAlpha(0);
+            mHwCirclePaint = CanvasProperty.createPaint(modifiedPaint);
+            RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
+                    RenderNodeAnimator.PAINT_ALPHA, 255);
+            animator.setTarget(this);
+            animator.setInterpolator(PhoneStatusBar.ALPHA_IN);
+            animator.setDuration(250);
+            animator.start();
+        }
+    }
+
+    public void instantFinishAnimation() {
+        cancelAnimator(mPreviewClipper);
+        if (mPreviewView != null) {
+            mPreviewView.setClipBounds(null);
+            mPreviewView.setVisibility(View.VISIBLE);
+        }
+        mCircleRadius = getMaxCircleSize();
+        setImageAlpha(0, false);
+        invalidate();
+    }
+
     private void startRtCircleFadeOut(long duration) {
         RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
                 RenderNodeAnimator.PAINT_ALPHA, 0);
@@ -443,6 +480,7 @@
     public void setImageAlpha(float alpha, boolean animate, long duration,
             Interpolator interpolator, Runnable runnable) {
         cancelAnimator(mAlphaAnimator);
+        alpha = mLaunchingAffordance ? 0 : alpha;
         int endAlpha = (int) (alpha * 255);
         final Drawable background = getBackground();
         if (!animate) {
@@ -509,4 +547,8 @@
             return false;
         }
     }
+
+    public void setLaunchingAffordance(boolean launchingAffordance) {
+        mLaunchingAffordance = launchingAffordance;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 10019f9..60ebfdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -86,9 +86,9 @@
         mContext = context;
         mCallback = callback;
         initIcons();
-        updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true);
-        updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false, true);
-        updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true);
+        updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true, false);
+        updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false, true, false);
+        updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true, false);
         initDimens();
     }
 
@@ -144,9 +144,7 @@
                 } else {
                     mTouchSlopExeeded = false;
                 }
-                mCallback.onSwipingStarted(targetView == mRightIcon);
-                mSwipingInProgress = true;
-                mTargetedView = targetView;
+                startSwiping(targetView);
                 mInitialTouchX = x;
                 mInitialTouchY = y;
                 mTranslationOnDown = mTranslation;
@@ -192,6 +190,12 @@
         return true;
     }
 
+    private void startSwiping(View targetView) {
+        mCallback.onSwipingStarted(targetView == mRightIcon);
+        mSwipingInProgress = true;
+        mTargetedView = targetView;
+    }
+
     private View getIconAtPosition(float x, float y) {
         if (leftSwipePossible() && isOnIcon(mLeftIcon, x, y)) {
             return mLeftIcon;
@@ -324,7 +328,7 @@
         boolean velIsInWrongDirection = vel * mTranslation < 0;
         snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
         vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
-        fling(vel, snapBack || forceSnapBack);
+        fling(vel, snapBack || forceSnapBack, mTranslation < 0);
     }
 
     private boolean isBelowFalsingThreshold() {
@@ -336,9 +340,8 @@
         return (int) (mMinTranslationAmount * factor);
     }
 
-    private void fling(float vel, final boolean snapBack) {
-        float target = mTranslation < 0
-                ? -mCallback.getMaxTranslationDistance()
+    private void fling(float vel, final boolean snapBack, boolean right) {
+        float target = right ? -mCallback.getMaxTranslationDistance()
                 : mCallback.getMaxTranslationDistance();
         target = snapBack ? 0 : target;
 
@@ -352,8 +355,8 @@
         });
         animator.addListener(mFlingEndListener);
         if (!snapBack) {
-            startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable);
-            mCallback.onAnimationToSideStarted(mTranslation < 0, mTranslation, vel);
+            startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable, right);
+            mCallback.onAnimationToSideStarted(right, mTranslation, vel);
         } else {
             reset(true);
         }
@@ -364,8 +367,9 @@
         }
     }
 
-    private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) {
-        KeyguardAffordanceView targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
+    private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable,
+            boolean right) {
+        KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
         targetView.finishAnimation(velocity, mAnimationEndRunnable);
     }
 
@@ -383,19 +387,20 @@
             fadeOutAlpha = Math.max(fadeOutAlpha, 0.0f);
 
             boolean animateIcons = isReset && animateReset;
+            boolean forceNoCircleAnimation = isReset && !animateReset;
             float radius = getRadiusFromTranslation(absTranslation);
             boolean slowAnimation = isReset && isBelowFalsingThreshold();
             if (!isReset) {
                 updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
-                        false, false, false);
+                        false, false, false, false);
             } else {
                 updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
-                        animateIcons, slowAnimation, false);
+                        animateIcons, slowAnimation, false, forceNoCircleAnimation);
             }
             updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
-                    animateIcons, slowAnimation, false);
+                    animateIcons, slowAnimation, false, forceNoCircleAnimation);
             updateIcon(mCenterIcon, 0.0f, fadeOutAlpha * mCenterIcon.getRestingAlpha(),
-                    animateIcons, slowAnimation, false);
+                    animateIcons, slowAnimation, false, forceNoCircleAnimation);
 
             mTranslation = translation;
         }
@@ -431,16 +436,21 @@
 
     public void animateHideLeftRightIcon() {
         cancelAnimation();
-        updateIcon(mRightIcon, 0f, 0f, true, false, false);
-        updateIcon(mLeftIcon, 0f, 0f, true, false, false);
+        updateIcon(mRightIcon, 0f, 0f, true, false, false, false);
+        updateIcon(mLeftIcon, 0f, 0f, true, false, false, false);
     }
 
     private void updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha,
-            boolean animate, boolean slowRadiusAnimation, boolean force) {
+                            boolean animate, boolean slowRadiusAnimation, boolean force,
+                            boolean forceNoCircleAnimation) {
         if (view.getVisibility() != View.VISIBLE && !force) {
             return;
         }
-        view.setCircleRadius(circleRadius, slowRadiusAnimation);
+        if (forceNoCircleAnimation) {
+            view.setCircleRadiusWithoutAnimation(circleRadius);
+        } else {
+            view.setCircleRadius(circleRadius, slowRadiusAnimation);
+        }
         updateIconAlpha(view, alpha, animate);
     }
 
@@ -503,8 +513,36 @@
         mMotionCancelled = true;
         if (mSwipingInProgress) {
             mCallback.onSwipingAborted();
+            mSwipingInProgress = false;
         }
-        mSwipingInProgress = false;
+    }
+
+    public boolean isSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
+    public void launchAffordance(boolean animate, boolean left) {
+        if (mSwipingInProgress) {
+            // We don't want to mess with the state if the user is actually swiping already.
+            return;
+        }
+        KeyguardAffordanceView targetView = left ? mLeftIcon : mRightIcon;
+        KeyguardAffordanceView otherView = left ? mRightIcon : mLeftIcon;
+        startSwiping(targetView);
+        if (animate) {
+            fling(0, false, !left);
+            updateIcon(otherView, 0.0f, 0, true, false, true, false);
+            updateIcon(mCenterIcon, 0.0f, 0, true, false, true, false);
+        } else {
+            mCallback.onAnimationToSideStarted(!left, mTranslation, 0);
+            mTranslation = left ? mCallback.getMaxTranslationDistance()
+                    : mCallback.getMaxTranslationDistance();
+            updateIcon(mCenterIcon, 0.0f, 0.0f, false, false, true, false);
+            updateIcon(otherView, 0.0f, 0.0f, false, false, true, false);
+            targetView.instantFinishAnimation();
+            mFlingEndListener.onAnimationEnd(null);
+            mAnimationEndRunnable.run();
+        }
     }
 
     public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 579889d..d30411a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -80,7 +80,7 @@
     private static final Intent SECURE_CAMERA_INTENT =
             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
                     .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-    private static final Intent INSECURE_CAMERA_INTENT =
+    public static final Intent INSECURE_CAMERA_INTENT =
             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
     private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
     private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
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 5be768d..5557f9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -203,6 +203,7 @@
     private int mLastOrientation = -1;
     private boolean mClosingWithAlphaFadeOut;
     private boolean mHeadsUpAnimatingAway;
+    private boolean mLaunchingAffordance;
 
     private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
         @Override
@@ -488,7 +489,9 @@
         mIsLaunchTransitionFinished = false;
         mBlockTouches = false;
         mUnlockIconActive = false;
-        mAfforanceHelper.reset(true);
+        if (!mLaunchingAffordance) {
+            mAfforanceHelper.reset(false);
+        }
         closeQs();
         mStatusBar.dismissPopups();
         mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
@@ -2393,4 +2396,36 @@
     public boolean hasOverlappingRendering() {
         return !mDozing;
     }
+
+    public void launchCamera(boolean animate) {
+        // If we are launching it when we are occluded already we don't want it to animate,
+        // nor setting these flags, since the occluded state doesn't change anymore, hence it's
+        // never reset.
+        if (!isFullyCollapsed()) {
+            mLaunchingAffordance = true;
+            setLaunchingAffordance(true);
+        } else {
+            animate = false;
+        }
+        mAfforanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+    }
+
+    public void onAffordanceLaunchEnded() {
+        mLaunchingAffordance = false;
+        setLaunchingAffordance(false);
+    }
+
+    /**
+     * Set whether we are currently launching an affordance. This is currently only set when
+     * launched via a camera gesture.
+     */
+    private void setLaunchingAffordance(boolean launchingAffordance) {
+        getLeftIcon().setLaunchingAffordance(launchingAffordance);
+        getRightIcon().setLaunchingAffordance(launchingAffordance);
+        getCenterIcon().setLaunchingAffordance(launchingAffordance);
+    }
+
+    public boolean canCameraGestureBeLaunched() {
+        return !mAfforanceHelper.isSwipingInProgress();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 649dad3..ce00fbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -482,6 +482,8 @@
     private Runnable mLaunchTransitionEndRunnable;
     private boolean mLaunchTransitionFadingAway;
     private ExpandableNotificationRow mDraggedDownRow;
+    private boolean mLaunchCameraOnScreenTurningOn;
+    private PowerManager.WakeLock mGestureWakeLock;
 
     // Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
     private int mLastLoggedStateFingerprint;
@@ -903,7 +905,8 @@
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mBroadcastReceiver.onReceive(mContext,
                 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
-
+        mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+                "GestureWakeLock");
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -3370,6 +3373,8 @@
 
     private void onLaunchTransitionFadingEnded() {
         mNotificationPanel.setAlpha(1.0f);
+        mNotificationPanel.onAffordanceLaunchEnded();
+        releaseGestureWakeLock();
         runLaunchTransitionEndRunnable();
         mLaunchTransitionFadingAway = false;
         mScrimController.forceHideScrims(false /* hide */);
@@ -3457,6 +3462,8 @@
 
     private void onLaunchTransitionTimeout() {
         Log.w(TAG, "Launch transition: Timeout!");
+        mNotificationPanel.onAffordanceLaunchEnded();
+        releaseGestureWakeLock();
         mNotificationPanel.resetViews();
     }
 
@@ -3508,10 +3515,18 @@
             mQSPanel.refreshAllTiles();
         }
         mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+        releaseGestureWakeLock();
+        mNotificationPanel.onAffordanceLaunchEnded();
         mNotificationPanel.setAlpha(1f);
         return staying;
     }
 
+    private void releaseGestureWakeLock() {
+        if (mGestureWakeLock.isHeld()) {
+            mGestureWakeLock.release();
+        }
+    }
+
     public long calculateGoingToFullShadeDelay() {
         return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
     }
@@ -3636,6 +3651,11 @@
         return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed();
     }
 
+    public void endAffordanceLaunch() {
+        releaseGestureWakeLock();
+        mNotificationPanel.onAffordanceLaunchEnded();
+    }
+
     public boolean onBackPressed() {
         if (mStatusBarKeyguardViewManager.onBackPressed()) {
             return true;
@@ -3867,6 +3887,9 @@
     }
 
     public void onFinishedGoingToSleep() {
+        mNotificationPanel.onAffordanceLaunchEnded();
+        releaseGestureWakeLock();
+        mLaunchCameraOnScreenTurningOn = false;
         mDeviceInteractive = false;
         mWakeUpComingFromTouch = false;
         mWakeUpTouchLocation = null;
@@ -3883,6 +3906,10 @@
 
     public void onScreenTurningOn() {
         mNotificationPanel.onScreenTurningOn();
+        if (mLaunchCameraOnScreenTurningOn) {
+            mNotificationPanel.launchCamera(false);
+            mLaunchCameraOnScreenTurningOn = false;
+        }
     }
 
     public void onScreenTurnedOn() {
@@ -4038,6 +4065,38 @@
         }
     }
 
+    @Override
+    public void onCameraLaunchGestureDetected() {
+        if (!mNotificationPanel.canCameraGestureBeLaunched()) {
+            return;
+        }
+        if (!mDeviceInteractive) {
+            PowerManager pm = mContext.getSystemService(PowerManager.class);
+            pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE");
+            mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
+        }
+        if (!mStatusBarKeyguardViewManager.isShowing()) {
+            startActivity(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT,
+                    true /* dismissShade */);
+        } else {
+            if (!mDeviceInteractive) {
+                // Avoid flickering of the scrim when we instant launch the camera and the bouncer
+                // comes on.
+                mScrimController.dontAnimateBouncerChangesUntilNextFrame();
+                mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+            }
+            if (mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
+                mNotificationPanel.launchCamera(mDeviceInteractive /* animate */);
+            } else {
+                // We need to defer the camera launch until the screen comes on, since otherwise
+                // we will dismiss us too early since we are waiting on an activity to be drawn and
+                // incorrectly get notified because of the screen on event (which resumes and pauses
+                // some activities)
+                mLaunchCameraOnScreenTurningOn = true;
+            }
+        }
+    }
+
     public void notifyFpAuthModeChanged() {
         updateDozing();
     }
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 cc6f396..b9e9292 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -87,6 +87,7 @@
     private View mDraggedHeadsUpView;
     private boolean mForceHideScrims;
     private boolean mSkipFirstFrame;
+    private boolean mDontAnimateBouncerChanges;
 
     public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
             boolean scrimSrcEnabled) {
@@ -125,7 +126,7 @@
 
     public void setBouncerShowing(boolean showing) {
         mBouncerShowing = showing;
-        mAnimateChange = !mExpanding;
+        mAnimateChange = !mExpanding && !mDontAnimateBouncerChanges;
         scheduleUpdate();
     }
 
@@ -360,6 +361,9 @@
     public boolean onPreDraw() {
         mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
         mUpdatePending = false;
+        if (mDontAnimateBouncerChanges) {
+            mDontAnimateBouncerChanges = false;
+        }
         updateScrims();
         mDurationOverride = -1;
         mAnimationDelay = 0;
@@ -496,4 +500,8 @@
         mAnimateChange = false;
         scheduleUpdate();
     }
+
+    public void dontAnimateBouncerChangesUntilNextFrame() {
+        mDontAnimateBouncerChanges = true;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 59ee5c5..d0604c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -179,6 +179,10 @@
         mPhoneStatusBar.onScreenTurningOn();
     }
 
+    public boolean isScreenTurnedOn() {
+        return mScreenTurnedOn;
+    }
+
     public void onScreenTurnedOn() {
         mScreenTurnedOn = true;
         if (mDeferScrimFadeOut) {
@@ -385,6 +389,7 @@
      */
     public boolean onBackPressed() {
         if (mBouncer.isShowing()) {
+            mPhoneStatusBar.endAffordanceLaunch();
             reset();
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 920b682..2587b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -171,6 +171,10 @@
     }
 
     @Override
+    public void onCameraLaunchGestureDetected() {
+    }
+
+    @Override
     protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldInterrupt,
             boolean alertAgain) {
     }
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 55af9f0..0fb0470 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -19,12 +19,9 @@
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.Sensor;
@@ -35,12 +32,12 @@
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.os.Vibrator;
-import android.provider.MediaStore;
 import android.provider.Settings;
 import android.util.Slog;
 
+import com.android.server.statusbar.StatusBarManagerInternal;
+
 /**
  * The service that listens for gestures detected in sensor firmware and starts the intent
  * accordingly.
@@ -57,7 +54,6 @@
 
     private Sensor mCameraLaunchSensor;
     private Vibrator mVibrator;
-    private KeyguardManager mKeyGuard;
     private Context mContext;
 
     /** The wake lock held when a gesture is detected. */
@@ -83,11 +79,9 @@
             }
 
             mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
-            mKeyGuard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
             PowerManager powerManager = (PowerManager) mContext.getSystemService(
                     Context.POWER_SERVICE);
-            mWakeLock = powerManager.newWakeLock(
-                    PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
+            mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                     "GestureLauncherService");
             updateCameraRegistered();
 
@@ -227,29 +221,17 @@
             if (DBG) Slog.d(TAG, String.format(
                     "userSetupComplete = %s, performing camera launch gesture.",
                     userSetupComplete));
-            boolean locked = mKeyGuard != null && mKeyGuard.inKeyguardRestrictedInputMode();
-            String action = locked
-                    ? MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE
-                    : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
-            Intent intent = new Intent(action);
-            PackageManager pm = mContext.getPackageManager();
-            ResolveInfo componentInfo = pm.resolveActivity(intent,
-                PackageManager.MATCH_DEFAULT_ONLY);
-            if (componentInfo == null) {
-                if (DBG) Slog.d(TAG, "Couldn't find an app to process the camera intent.");
-                return;
-            }
 
             if (mVibrator != null && mVibrator.hasVibrator()) {
                 mVibrator.vibrate(1000L);
             }
-            // Turn on the screen before the camera launches.
+            // Make sure we don't sleep too early
             mWakeLock.acquire(500L);
-            intent.setComponent(new ComponentName(componentInfo.activityInfo.packageName,
-                    componentInfo.activityInfo.name));
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+            StatusBarManagerInternal service = LocalServices.getService(
+                    StatusBarManagerInternal.class);
+            service.onCameraLaunchGestureDetected();
             mWakeLock.release();
+
         }
 
         @Override
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5b0c06b..0423aa3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4410,7 +4410,7 @@
                     if (mAppsToBeHidden.isEmpty()) {
                         if (dismissKeyguard && !mKeyguardSecure) {
                             mAppsThatDismissKeyguard.add(appToken);
-                        } else {
+                        } else if (win.isDrawnLw()) {
                             mWinShowWhenLocked = win;
                             mHideLockScreen = true;
                             mForceStatusBarFromKeyguard = false;
@@ -4444,7 +4444,7 @@
                         mWinDismissingKeyguard = win;
                         mSecureDismissingKeyguard = mKeyguardSecure;
                         mForceStatusBarFromKeyguard = mShowingLockscreen && mKeyguardSecure;
-                    } else if (mAppsToBeHidden.isEmpty() && showWhenLocked) {
+                    } else if (mAppsToBeHidden.isEmpty() && showWhenLocked && win.isDrawnLw()) {
                         if (DEBUG_LAYOUT) Slog.v(TAG,
                                 "Setting mHideLockScreen to true by win " + win);
                         mHideLockScreen = true;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 130815e..5d01931 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -28,4 +28,5 @@
     void showScreenPinningRequest();
     void showAssistDisclosure();
     void startAssist(Bundle args);
+    void onCameraLaunchGestureDetected();
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 2a817ea..0fb1169 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -176,6 +176,16 @@
                 }
             }
         }
+
+        @Override
+        public void onCameraLaunchGestureDetected() {
+            if (mBar != null) {
+                try {
+                    mBar.onCameraLaunchGestureDetected();
+                } catch (RemoteException e) {
+                }
+            }
+        }
     };
 
     // ================================================================================