DO NOT MERGE Only send notification activity intents after dismissing Keyguard

Defers delivery of contentIntent and activity actions until
the lockscreen has been dismissed, so that activities don't
launch beneath it.

Bug: 14491010
Change-Id: Ic8c61c18a75d4f0da2d82a0a8a038a5d98ebb71f
(cherry picked from commit 79f640dd0d02c1cc199937b160d8f7abd3c1eaeb)
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 2685447..d2bf30c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -112,7 +112,9 @@
     }
 
     public interface OnDismissAction {
-        /* returns true if the dismiss should be deferred */
+        /**
+         * @return true if the dismiss should be deferred
+         */
         boolean onDismiss();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 9435e85..f4db625 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -84,6 +84,8 @@
 import java.util.Arrays;
 import java.util.Locale;
 
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
+
 public abstract class BaseStatusBar extends SystemUI implements
         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener {
     public static final String TAG = "StatusBar";
@@ -213,33 +215,47 @@
 
     private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
         @Override
-        public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
+        public boolean onClickHandler(
+                final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
             if (DEBUG) {
                 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
             }
             final boolean isActivity = pendingIntent.isActivity();
             if (isActivity) {
-                try {
-                    // The intent we are sending is for the application, which
-                    // won't have permission to immediately start an activity after
-                    // the user switches to home.  We know it is safe to do at this
-                    // point, so make sure new activity switches are now allowed.
-                    ActivityManagerNative.getDefault().resumeAppSwitches();
-                    // Also, notifications can be launched from the lock screen,
-                    // so dismiss the lock screen when the activity starts.
-                    ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
-                } catch (RemoteException e) {
-                }
-            }
+                startNotificationActivity(new OnDismissAction() {
+                    @Override
+                    public boolean onDismiss() {
+                        try {
+                            // The intent we are sending is for the application, which
+                            // won't have permission to immediately start an activity after
+                            // the user switches to home.  We know it is safe to do at this
+                            // point, so make sure new activity switches are now allowed.
+                            ActivityManagerNative.getDefault().resumeAppSwitches();
+                            // Also, notifications can be launched from the lock screen,
+                            // so dismiss the lock screen when the activity starts.
+                            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+                        } catch (RemoteException e) {
+                        }
 
-            boolean handled = super.onClickHandler(view, pendingIntent, fillInIntent);
+                        boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
 
-            if (isActivity && handled) {
-                // close the shade if it was open
-                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-                visibilityChanged(false);
+                        // close the shade if it was open
+                        if (handled) {
+                            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                            visibilityChanged(false);
+                        }
+                        return handled; // Wait for activity start.
+                    }
+                });
+                return true;
+            } else {
+                return super.onClickHandler(view, pendingIntent, fillInIntent);
             }
-            return handled;
+        }
+
+        private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
+                Intent fillInIntent) {
+            return super.onClickHandler(view, pendingIntent, fillInIntent);
         }
     };
 
@@ -434,6 +450,14 @@
         }
     }
 
+    /**
+     * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
+     * @param action A dismiss action that is called if it's safe to start the activity.
+     */
+    protected void startNotificationActivity(OnDismissAction action) {
+        action.onDismiss();
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         final Locale locale = mContext.getResources().getConfiguration().locale;
@@ -999,47 +1023,55 @@
             mIsHeadsUp = forHun;
         }
 
-        public void onClick(View v) {
-            try {
-                // The intent we are sending is for the application, which
-                // won't have permission to immediately start an activity after
-                // the user switches to home.  We know it is safe to do at this
-                // point, so make sure new activity switches are now allowed.
-                ActivityManagerNative.getDefault().resumeAppSwitches();
-                // Also, notifications can be launched from the lock screen,
-                // so dismiss the lock screen when the activity starts.
-                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
-            } catch (RemoteException e) {
-            }
+        public void onClick(final View v) {
+            startNotificationActivity(new OnDismissAction() {
+                public boolean onDismiss() {
+                    try {
+                        // The intent we are sending is for the application, which
+                        // won't have permission to immediately start an activity after
+                        // the user switches to home.  We know it is safe to do at this
+                        // point, so make sure new activity switches are now allowed.
+                        ActivityManagerNative.getDefault().resumeAppSwitches();
+                        // Also, notifications can be launched from the lock screen,
+                        // so dismiss the lock screen when the activity starts.
+                        ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+                    } catch (RemoteException e) {
+                    }
 
-            if (mIntent != null) {
-                int[] pos = new int[2];
-                v.getLocationOnScreen(pos);
-                Intent overlay = new Intent();
-                overlay.setSourceBounds(
-                        new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
-                try {
-                    mIntent.send(mContext, 0, overlay);
-                } catch (PendingIntent.CanceledException e) {
-                    // the stack trace isn't very helpful here.  Just log the exception message.
-                    Log.w(TAG, "Sending contentIntent failed: " + e);
+                    boolean sent = false;
+                    if (mIntent != null) {
+                        int[] pos = new int[2];
+                        v.getLocationOnScreen(pos);
+                        Intent overlay = new Intent();
+                        overlay.setSourceBounds(new Rect(pos[0], pos[1],
+                                pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+                        try {
+                            mIntent.send(mContext, 0, overlay);
+                            sent = true;
+                        } catch (PendingIntent.CanceledException e) {
+                            // the stack trace isn't very helpful here.
+                            // Just log the exception message.
+                            Log.w(TAG, "Sending contentIntent failed: " + e);
+                        }
+                    }
+
+                    try {
+                        if (mIsHeadsUp) {
+                            mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
+                        }
+                        mBarService.onNotificationClick(mNotificationKey);
+                    } catch (RemoteException ex) {
+                        // system process is dead if we're here.
+                    }
+
+                    // close the shade if it was open
+                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                    visibilityChanged(false);
+
+                    boolean waitForActivityLaunch = sent && mIntent.isActivity();
+                    return waitForActivityLaunch;
                 }
-
-                KeyguardTouchDelegate.getInstance(mContext).dismiss();
-            }
-
-            try {
-                if (mIsHeadsUp) {
-                    mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
-                }
-                mBarService.onNotificationClick(mNotificationKey);
-            } catch (RemoteException ex) {
-                // system process is dead if we're here.
-            }
-
-            // close the shade if it was open
-            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-            visibilityChanged(false);
+            });
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index d8e1766..9138867 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -28,6 +28,7 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 import static com.android.keyguard.KeyguardSecurityModel.*;
 
 /**
@@ -69,6 +70,12 @@
         }
     }
 
+    public void showWithDismissAction(OnDismissAction r) {
+        ensureView();
+        mKeyguardView.setOnDismissAction(r);
+        show();
+    }
+
     public void hide() {
         if (mKeyguardView != null) {
             mKeyguardView.cleanUp();
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 152ca3f..0922abd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -22,6 +22,7 @@
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
@@ -2346,6 +2347,15 @@
         }
     };
 
+    @Override
+    protected void startNotificationActivity(OnDismissAction action) {
+        if (mStatusBarKeyguardViewManager.isShowing()) {
+            mStatusBarKeyguardViewManager.dismissWithAction(action);
+        } else {
+            action.onDismiss();
+        }
+    }
+
     // SystemUIService notifies SystemBars of configuration changes, which then calls down here
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
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 1040c15..3849d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -29,6 +29,8 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
 
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
+
 /**
  * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
  * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
@@ -108,6 +110,13 @@
         updateStates();
     }
 
+    public void dismissWithAction(OnDismissAction r) {
+        if (!mOccluded) {
+            mBouncer.showWithDismissAction(r);
+        }
+        updateStates();
+    }
+
     /**
      * Reset the state of the view.
      */