diff options
| author | 2016-03-31 14:39:39 -0700 | |
|---|---|---|
| committer | 2016-04-27 06:57:48 -0700 | |
| commit | 4c1fcc892e802840e3daad8be9eedb2eb1887ef8 (patch) | |
| tree | 2822ceeeac1876a36e77ea2629fbf7e90cbd10ad | |
| parent | 9fa8b54589b68dc6da3a7201cad1fc43e01e59e3 (diff) | |
Fix remote input view clobbering
- If a notification is updated, make sure that the focus
is kept even when the change cannot be applied inline.
- Make sure to update the pending intent to match one
of the new actions in case the old one is no longer
valid, and to prevent sending text to the wrong action
unintentionally.
- Keep focus when switching between heads-up and expanded.
- Prevent the action container from becoming transiently
gone during reapplying.
Fixes: 27357771
Change-Id: Ie9f49935827f6efb15b73f8d686f0c110448456a
3 files changed, 118 insertions, 4 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d7705b9ca830..d2dc7b7a9fc1 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3351,7 +3351,8 @@ public class Notification implements Parcelable } 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 @@ public class Notification implements Parcelable } 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 7be50c4ecc46..81303fe6be9c 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 @@ public class NotificationContentView extends FrameLayout { 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 @@ public class NotificationContentView extends FrameLayout { 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 @@ public class NotificationContentView extends FrameLayout { } 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 @@ public class NotificationContentView extends FrameLayout { }); } + 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 @@ public class NotificationContentView extends FrameLayout { updateShowingLegacyBackground(); selectLayout(false /* animate */, true /* force */); setDark(mDark, false /* animate */, 0 /* delay */); + mPreviousExpandedRemoteInputIntent = null; + mPreviousHeadsUpRemoteInputIntent = null; } private void updateSingleLineView() { @@ -771,19 +801,23 @@ public class NotificationContentView extends FrameLayout { 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 @@ public class NotificationContentView extends FrameLayout { 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 ecd1772890ef..0fdd99fe7f58 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 class RemoteInputView extends LinearLayout implements View.OnClickListene } public void focus() { + setVisibility(VISIBLE); mController.addRemoteInput(mEntry); mEditText.setInnerFocusable(true); mEditText.mShowImeOnInputConnection = true; @@ -275,6 +277,63 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } + 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. |