summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adrian Roos <roosa@google.com> 2016-03-31 14:39:39 -0700
committer Adrian Roos <roosa@google.com> 2016-04-27 06:57:48 -0700
commit4c1fcc892e802840e3daad8be9eedb2eb1887ef8 (patch)
tree2822ceeeac1876a36e77ea2629fbf7e90cbd10ad
parent9fa8b54589b68dc6da3a7201cad1fc43e01e59e3 (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
-rw-r--r--core/java/android/app/Notification.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java59
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.