Loading core/java/android/app/Notification.java +4 −1 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +55 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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() { Loading Loading @@ -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) { Loading Loading @@ -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; } Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +59 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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. Loading Loading
core/java/android/app/Notification.java +4 −1 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading
packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +55 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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 Loading Loading @@ -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() { Loading Loading @@ -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) { Loading Loading @@ -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; } Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +59 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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. Loading