Merge "Fix remote input view clobbering" into nyc-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d7705b9..d2dc7b7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3351,7 +3351,8 @@
         }
 
         private void resetStandardTemplateWithActions(RemoteViews big) {
-            big.setViewVisibility(R.id.actions_container, View.GONE);
+            // actions_container is only reset when there are no actions to avoid focus issues with
+            // remote inputs.
             big.setViewVisibility(R.id.actions, View.GONE);
             big.removeAllViews(R.id.actions);
 
@@ -3396,6 +3397,8 @@
                     }
                     big.addView(R.id.actions, button);
                 }
+            } else {
+                big.setViewVisibility(R.id.actions_container, View.GONE);
             }
 
             CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 7be50c4..81303fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar;
 
 import android.app.Notification;
+import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.content.Context;
 import android.graphics.Rect;
@@ -118,6 +119,8 @@
     private int mTransformationStartVisibleType;
     private boolean mUserExpanding;
     private int mSingleLineWidthIndention;
+    private PendingIntent mPreviousExpandedRemoteInputIntent;
+    private PendingIntent mPreviousHeadsUpRemoteInputIntent;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -280,13 +283,19 @@
             mContractedChild.animate().cancel();
             removeView(mContractedChild);
         }
+        mPreviousExpandedRemoteInputIntent =
+                mExpandedRemoteInput != null ? mExpandedRemoteInput.getPendingIntent() : null;
         if (mExpandedChild != null) {
             mExpandedChild.animate().cancel();
             removeView(mExpandedChild);
+            mExpandedRemoteInput = null;
         }
+        mPreviousHeadsUpRemoteInputIntent =
+                mHeadsUpRemoteInput != null ? mHeadsUpRemoteInput.getPendingIntent() : null;
         if (mHeadsUpChild != null) {
             mHeadsUpChild.animate().cancel();
             removeView(mHeadsUpChild);
+            mHeadsUpRemoteInput = null;
         }
         mContractedChild = null;
         mExpandedChild = null;
@@ -496,6 +505,12 @@
         }
         int visibleType = calculateVisibleType();
         if (visibleType != mVisibleType || force) {
+            View visibleView = getViewForVisibleType(visibleType);
+            if (visibleView != null) {
+                visibleView.setVisibility(VISIBLE);
+                transferRemoteInputFocus(visibleType);
+            }
+
             if (animate && ((visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
                     || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
                     || (visibleType == VISIBLE_TYPE_SINGLELINE && mSingleLineView != null)
@@ -559,6 +574,19 @@
         });
     }
 
+    private void transferRemoteInputFocus(int visibleType) {
+        if (visibleType == VISIBLE_TYPE_HEADSUP
+                && mHeadsUpRemoteInput != null
+                && (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive())) {
+            mHeadsUpRemoteInput.stealFocusFrom(mExpandedRemoteInput);
+        }
+        if (visibleType == VISIBLE_TYPE_EXPANDED
+                && mExpandedRemoteInput != null
+                && (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive())) {
+            mExpandedRemoteInput.stealFocusFrom(mHeadsUpRemoteInput);
+        }
+    }
+
     /**
      * @param visibleType one of the static enum types in this view
      * @return the corresponding transformable view according to the given visible type
@@ -736,6 +764,8 @@
         updateShowingLegacyBackground();
         selectLayout(false /* animate */, true /* force */);
         setDark(mDark, false /* animate */, 0 /* delay */);
+        mPreviousExpandedRemoteInputIntent = null;
+        mPreviousHeadsUpRemoteInputIntent = null;
     }
 
     private void updateSingleLineView() {
@@ -771,19 +801,23 @@
 
         View bigContentView = mExpandedChild;
         if (bigContentView != null) {
-            mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput);
+            mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasRemoteInput,
+                    mPreviousExpandedRemoteInputIntent);
         } else {
             mExpandedRemoteInput = null;
         }
+
         View headsUpContentView = mHeadsUpChild;
         if (headsUpContentView != null) {
-            mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput);
+            mHeadsUpRemoteInput = applyRemoteInput(headsUpContentView, entry, hasRemoteInput,
+                    mPreviousHeadsUpRemoteInputIntent);
         } else {
             mHeadsUpRemoteInput = null;
         }
     }
 
-    private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry, boolean hasRemoteInput) {
+    private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
+            boolean hasRemoteInput, PendingIntent existingPendingIntent) {
         View actionContainerCandidate = view.findViewById(
                 com.android.internal.R.id.actions_container);
         if (actionContainerCandidate instanceof FrameLayout) {
@@ -814,6 +848,24 @@
                 existing.setBackgroundColor(NotificationColorUtil.ensureTextBackgroundColor(color,
                         mContext.getColor(R.color.remote_input_text),
                         mContext.getColor(R.color.remote_input_hint)));
+
+                if (existingPendingIntent != null || existing.isActive()) {
+                    // The current action could be gone, or the pending intent no longer valid.
+                    // If we find a matching action in the new notification, focus, otherwise close.
+                    Notification.Action[] actions = entry.notification.getNotification().actions;
+                    if (existingPendingIntent != null) {
+                        existing.setPendingIntent(existingPendingIntent);
+                    }
+                    if (existing.updatePendingIntentFromActions(actions)) {
+                        if (!existing.isActive()) {
+                            existing.focus();
+                        }
+                    } else {
+                        if (existing.isActive()) {
+                            existing.close();
+                        }
+                    }
+                }
             }
             return existing;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index ecd1772..0fdd99f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.content.Context;
@@ -197,6 +198,7 @@
     }
 
     public void focus() {
+        setVisibility(VISIBLE);
         mController.addRemoteInput(mEntry);
         mEditText.setInnerFocusable(true);
         mEditText.mShowImeOnInputConnection = true;
@@ -275,6 +277,63 @@
         }
     }
 
+    public boolean isActive() {
+        return mEditText.isFocused();
+    }
+
+    public void stealFocusFrom(RemoteInputView other) {
+        other.close();
+        setPendingIntent(other.mPendingIntent);
+        setRemoteInput(other.mRemoteInputs, other.mRemoteInput);
+        focus();
+    }
+
+    /**
+     * Tries to find an action in {@param actions} that matches the current pending intent
+     * of this view and updates its state to that of the found action
+     *
+     * @return true if a matching action was found, false otherwise
+     */
+    public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
+        boolean found = false;
+        if (mPendingIntent == null || actions == null) {
+            return false;
+        }
+        Intent current = mPendingIntent.getIntent();
+        if (current == null) {
+            return false;
+        }
+
+        for (Notification.Action a : actions) {
+            RemoteInput[] inputs = a.getRemoteInputs();
+            if (a.actionIntent == null || inputs == null) {
+                continue;
+            }
+            Intent candidate = a.actionIntent.getIntent();
+            if (!current.filterEquals(candidate)) {
+                continue;
+            }
+
+            RemoteInput input = null;
+            for (RemoteInput i : inputs) {
+                if (i.getAllowFreeFormInput()) {
+                    input = i;
+                }
+            }
+            if (input == null) {
+                continue;
+            }
+            setPendingIntent(a.actionIntent);
+            setRemoteInput(inputs, input);
+            return true;
+        }
+        return false;
+    }
+
+    public PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
     /**
      * An EditText that changes appearance based on whether it's focusable and becomes
      * un-focusable whenever the user navigates away from it or it becomes invisible.