Loading core/java/android/app/Notification.java +67 −16 Original line number Diff line number Diff line Loading @@ -1004,6 +1004,31 @@ public class Notification implements Parcelable */ public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory"; /** * {@link #extras} key: this is a remote input history which can include media messages * in addition to text, as supplied to * {@link Builder#setRemoteInputHistory(RemoteInputHistoryItem[])} or * {@link Builder#setRemoteInputHistory(CharSequence[])}. * * SystemUI can populate this through * {@link Builder#setRemoteInputHistory(RemoteInputHistoryItem[])} with the most recent inputs * that have been sent through a {@link RemoteInput} of this Notification. These items can * represent either media content (specified by a URI and a MIME type) or a text message * (described by a CharSequence). * * To maintain compatibility, this can also be set by apps with * {@link Builder#setRemoteInputHistory(CharSequence[])}, which will create a * {@link RemoteInputHistoryItem} for each of the provided text-only messages. * * The extra with this key is of type {@link RemoteInputHistoryItem[]} and contains the most * recent entry at the 0 index, the second most recent at the 1 index, etc. * * @see Builder#setRemoteInputHistory(RemoteInputHistoryItem[]) * @hide */ public static final String EXTRA_REMOTE_INPUT_HISTORY_ITEMS = "android.remoteInputHistoryItems"; /** * {@link #extras} key: boolean as supplied to * {@link Builder#setShowRemoteInputSpinner(boolean)}. Loading Loading @@ -3833,12 +3858,37 @@ public class Notification implements Parcelable if (text == null) { mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null); } else { final int N = Math.min(MAX_REPLY_HISTORY, text.length); CharSequence[] safe = new CharSequence[N]; for (int i = 0; i < N; i++) { final int itemCount = Math.min(MAX_REPLY_HISTORY, text.length); CharSequence[] safe = new CharSequence[itemCount]; RemoteInputHistoryItem[] items = new RemoteInputHistoryItem[itemCount]; for (int i = 0; i < itemCount; i++) { safe[i] = safeCharSequence(text[i]); items[i] = new RemoteInputHistoryItem(text[i]); } mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe); // Also add these messages as structured history items. mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, items); } return this; } /** * Set the remote input history, with support for embedding URIs and mime types for * images and other media. * @hide */ @NonNull public Builder setRemoteInputHistory(RemoteInputHistoryItem[] items) { if (items == null) { mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, null); } else { final int itemCount = Math.min(MAX_REPLY_HISTORY, items.length); RemoteInputHistoryItem[] history = new RemoteInputHistoryItem[itemCount]; for (int i = 0; i < itemCount; i++) { history[i] = items[i]; } mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, history); } return this; } Loading Loading @@ -5246,16 +5296,17 @@ public class Notification implements Parcelable big.setViewVisibility(R.id.actions_container, View.GONE); } CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY); if (validRemoteInput && replyText != null && replyText.length > 0 && !TextUtils.isEmpty(replyText[0]) RemoteInputHistoryItem[] replyText = (RemoteInputHistoryItem[]) mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); if (validRemoteInput && replyText != null && replyText.length > 0 && !TextUtils.isEmpty(replyText[0].getText()) && p.maxRemoteInputHistory > 0) { boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER); big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE); big.setViewVisibility(R.id.notification_material_reply_text_1_container, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_1, processTextSpans(replyText[0])); processTextSpans(replyText[0].getText())); setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p); big.setViewVisibility(R.id.notification_material_reply_progress, showSpinner ? View.VISIBLE : View.GONE); Loading @@ -5264,19 +5315,19 @@ public class Notification implements Parcelable ColorStateList.valueOf( isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p))); if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1]) if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1].getText()) && p.maxRemoteInputHistory > 1) { big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_2, processTextSpans(replyText[1])); processTextSpans(replyText[1].getText())); setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p); if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2]) if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2].getText()) && p.maxRemoteInputHistory > 2) { big.setViewVisibility( R.id.notification_material_reply_text_3, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_3, processTextSpans(replyText[2])); processTextSpans(replyText[2].getText())); setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p); } } Loading Loading @@ -7517,7 +7568,7 @@ public class Notification implements Parcelable @Nullable private final Person mSender; /** True if this message was generated from the extra * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY} * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS} */ private final boolean mRemoteInputHistory; Loading Loading @@ -7569,7 +7620,7 @@ public class Notification implements Parcelable * Should be <code>null</code> for messages by the current user, in which case * the platform will insert the user set in {@code MessagingStyle(Person)}. * @param remoteInputHistory True if the messages was generated from the extra * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}. * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}. * <p> * The person provided should contain an Icon, set with * {@link Person.Builder#setIcon(Icon)} and also have a name provided Loading Loading @@ -7676,7 +7727,7 @@ public class Notification implements Parcelable /** * @return True if the message was generated from * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}. * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}. * @hide */ public boolean isRemoteInputHistory() { Loading Loading @@ -7906,8 +7957,8 @@ public class Notification implements Parcelable if (mBuilder.mActions.size() > 0) { maxRows--; } CharSequence[] remoteInputHistory = mBuilder.mN.extras.getCharSequenceArray( EXTRA_REMOTE_INPUT_HISTORY); RemoteInputHistoryItem[] remoteInputHistory = (RemoteInputHistoryItem[]) mBuilder.mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); if (remoteInputHistory != null && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) { // Let's remove some messages to make room for the remote input history. Loading core/java/android/app/RemoteInputHistoryItem.java 0 → 100644 +90 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; /** * Stores historical input from a RemoteInput attached to a Notification. * * History items represent either a text message (specified by providing a CharSequence, * or a media message (specified by providing a URI and a MIME type). Media messages must also * include text to insert when the image cannot be loaded, ex. when URI read permission has not been * granted correctly. * * @hide */ public class RemoteInputHistoryItem implements Parcelable { private CharSequence mText; private String mMimeType; private Uri mUri; public RemoteInputHistoryItem(String mimeType, Uri uri, CharSequence backupText) { this.mMimeType = mimeType; this.mUri = uri; this.mText = Notification.safeCharSequence(backupText); } public RemoteInputHistoryItem(CharSequence text) { this.mText = Notification.safeCharSequence(text); } protected RemoteInputHistoryItem(Parcel in) { mText = in.readCharSequence(); mMimeType = in.readStringNoHelper(); mUri = in.readParcelable(Uri.class.getClassLoader()); } public static final Creator<RemoteInputHistoryItem> CREATOR = new Creator<RemoteInputHistoryItem>() { @Override public RemoteInputHistoryItem createFromParcel(Parcel in) { return new RemoteInputHistoryItem(in); } @Override public RemoteInputHistoryItem[] newArray(int size) { return new RemoteInputHistoryItem[size]; } }; public CharSequence getText() { return mText; } public String getMimeType() { return mMimeType; } public Uri getUri() { return mUri; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeCharSequence(mText); dest.writeStringNoHelper(mMimeType); dest.writeParcelable(mUri, flags); } } core/java/com/android/internal/widget/MessagingLayout.java +12 −6 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.StyleRes; import android.app.Notification; import android.app.Person; import android.app.RemoteInputHistoryItem; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; Loading Loading @@ -161,8 +162,9 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer if (headerText != null) { mConversationTitle = headerText.getText(); } addRemoteInputHistoryToMessages(newMessages, extras.getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY)); RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[]) extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); addRemoteInputHistoryToMessages(newMessages, history); boolean showSpinner = extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); bind(newMessages, newHistoricMessages, showSpinner); Loading @@ -175,14 +177,18 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer private void addRemoteInputHistoryToMessages( List<Notification.MessagingStyle.Message> newMessages, CharSequence[] remoteInputHistory) { RemoteInputHistoryItem[] remoteInputHistory) { if (remoteInputHistory == null || remoteInputHistory.length == 0) { return; } for (int i = remoteInputHistory.length - 1; i >= 0; i--) { CharSequence message = remoteInputHistory[i]; newMessages.add(new Notification.MessagingStyle.Message( message, 0, (Person) null, true /* remoteHistory */)); RemoteInputHistoryItem historyMessage = remoteInputHistory[i]; Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message( historyMessage.getText(), 0, (Person) null, true /* remoteHistory */); if (historyMessage.getUri() != null) { message.setData(historyMessage.getMimeType(), historyMessage.getUri()); } newMessages.add(message); } } Loading packages/SystemUI/res/values/strings.xml +4 −0 Original line number Diff line number Diff line Loading @@ -194,6 +194,10 @@ <!-- Power menu item for taking a screenshot [CHAR LIMIT=20]--> <string name="global_action_screenshot">Screenshot</string> <!-- text to show in place of RemoteInput images when they cannot be shown. [CHAR LIMIT=50] --> <string name="remote_input_image_insertion_text">Image inserted</string> <!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] --> <string name="screenshot_saving_ticker">Saving screenshot\u2026</string> <!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] --> Loading packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +28 −14 Original line number Diff line number Diff line Loading @@ -25,8 +25,10 @@ import android.app.KeyguardManager; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; import android.app.RemoteInputHistoryItem; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; Loading Loading @@ -345,7 +347,8 @@ public class NotificationRemoteInputManager implements Dumpable { }); mSmartReplyController.setCallback((entry, reply) -> { StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */); rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */, null /* mimeType */, null /* uri */); mEntryManager.updateNotification(newSbn, null /* ranking */); }); } Loading Loading @@ -527,28 +530,36 @@ public class NotificationRemoteInputManager implements Dumpable { StatusBarNotification rebuildNotificationForCanceledSmartReplies( NotificationEntry entry) { return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */, false /* showSpinner */); false /* showSpinner */, null /* mimeType */, null /* uri */); } @VisibleForTesting StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry, CharSequence remoteInputText, boolean showSpinner) { CharSequence remoteInputText, boolean showSpinner, String mimeType, Uri uri) { StatusBarNotification sbn = entry.getSbn(); Notification.Builder b = Notification.Builder .recoverBuilder(mContext, sbn.getNotification().clone()); if (remoteInputText != null) { CharSequence[] oldHistory = sbn.getNotification().extras .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY); CharSequence[] newHistory; if (oldHistory == null) { newHistory = new CharSequence[1]; if (remoteInputText != null || uri != null) { RemoteInputHistoryItem[] oldHistoryItems = (RemoteInputHistoryItem[]) sbn.getNotification().extras.getParcelableArray( Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); RemoteInputHistoryItem[] newHistoryItems; if (oldHistoryItems == null) { newHistoryItems = new RemoteInputHistoryItem[1]; } else { newHistory = new CharSequence[oldHistory.length + 1]; System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length); newHistoryItems = new RemoteInputHistoryItem[oldHistoryItems.length + 1]; System.arraycopy(oldHistoryItems, 0, newHistoryItems, 1, oldHistoryItems.length); } newHistory[0] = String.valueOf(remoteInputText); b.setRemoteInputHistory(newHistory); RemoteInputHistoryItem newItem; if (uri != null) { newItem = new RemoteInputHistoryItem(mimeType, uri, remoteInputText); } else { newItem = new RemoteInputHistoryItem(remoteInputText); } newHistoryItems[0] = newItem; b.setRemoteInputHistory(newHistoryItems); } b.setShowRemoteInputSpinner(showSpinner); b.setHideSmartReplies(true); Loading Loading @@ -631,8 +642,11 @@ public class NotificationRemoteInputManager implements Dumpable { if (TextUtils.isEmpty(remoteInputText)) { remoteInputText = entry.remoteInputTextWhenReset; } String remoteInputMimeType = entry.remoteInputMimeType; Uri remoteInputUri = entry.remoteInputUri; StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry, remoteInputText, false /* showSpinner */); remoteInputText, false /* showSpinner */, remoteInputMimeType, remoteInputUri); entry.onRemoteInputInserted(); if (newSbn == null) { Loading Loading
core/java/android/app/Notification.java +67 −16 Original line number Diff line number Diff line Loading @@ -1004,6 +1004,31 @@ public class Notification implements Parcelable */ public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory"; /** * {@link #extras} key: this is a remote input history which can include media messages * in addition to text, as supplied to * {@link Builder#setRemoteInputHistory(RemoteInputHistoryItem[])} or * {@link Builder#setRemoteInputHistory(CharSequence[])}. * * SystemUI can populate this through * {@link Builder#setRemoteInputHistory(RemoteInputHistoryItem[])} with the most recent inputs * that have been sent through a {@link RemoteInput} of this Notification. These items can * represent either media content (specified by a URI and a MIME type) or a text message * (described by a CharSequence). * * To maintain compatibility, this can also be set by apps with * {@link Builder#setRemoteInputHistory(CharSequence[])}, which will create a * {@link RemoteInputHistoryItem} for each of the provided text-only messages. * * The extra with this key is of type {@link RemoteInputHistoryItem[]} and contains the most * recent entry at the 0 index, the second most recent at the 1 index, etc. * * @see Builder#setRemoteInputHistory(RemoteInputHistoryItem[]) * @hide */ public static final String EXTRA_REMOTE_INPUT_HISTORY_ITEMS = "android.remoteInputHistoryItems"; /** * {@link #extras} key: boolean as supplied to * {@link Builder#setShowRemoteInputSpinner(boolean)}. Loading Loading @@ -3833,12 +3858,37 @@ public class Notification implements Parcelable if (text == null) { mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null); } else { final int N = Math.min(MAX_REPLY_HISTORY, text.length); CharSequence[] safe = new CharSequence[N]; for (int i = 0; i < N; i++) { final int itemCount = Math.min(MAX_REPLY_HISTORY, text.length); CharSequence[] safe = new CharSequence[itemCount]; RemoteInputHistoryItem[] items = new RemoteInputHistoryItem[itemCount]; for (int i = 0; i < itemCount; i++) { safe[i] = safeCharSequence(text[i]); items[i] = new RemoteInputHistoryItem(text[i]); } mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe); // Also add these messages as structured history items. mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, items); } return this; } /** * Set the remote input history, with support for embedding URIs and mime types for * images and other media. * @hide */ @NonNull public Builder setRemoteInputHistory(RemoteInputHistoryItem[] items) { if (items == null) { mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, null); } else { final int itemCount = Math.min(MAX_REPLY_HISTORY, items.length); RemoteInputHistoryItem[] history = new RemoteInputHistoryItem[itemCount]; for (int i = 0; i < itemCount; i++) { history[i] = items[i]; } mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, history); } return this; } Loading Loading @@ -5246,16 +5296,17 @@ public class Notification implements Parcelable big.setViewVisibility(R.id.actions_container, View.GONE); } CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY); if (validRemoteInput && replyText != null && replyText.length > 0 && !TextUtils.isEmpty(replyText[0]) RemoteInputHistoryItem[] replyText = (RemoteInputHistoryItem[]) mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); if (validRemoteInput && replyText != null && replyText.length > 0 && !TextUtils.isEmpty(replyText[0].getText()) && p.maxRemoteInputHistory > 0) { boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER); big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE); big.setViewVisibility(R.id.notification_material_reply_text_1_container, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_1, processTextSpans(replyText[0])); processTextSpans(replyText[0].getText())); setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p); big.setViewVisibility(R.id.notification_material_reply_progress, showSpinner ? View.VISIBLE : View.GONE); Loading @@ -5264,19 +5315,19 @@ public class Notification implements Parcelable ColorStateList.valueOf( isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p))); if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1]) if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1].getText()) && p.maxRemoteInputHistory > 1) { big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_2, processTextSpans(replyText[1])); processTextSpans(replyText[1].getText())); setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p); if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2]) if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2].getText()) && p.maxRemoteInputHistory > 2) { big.setViewVisibility( R.id.notification_material_reply_text_3, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_3, processTextSpans(replyText[2])); processTextSpans(replyText[2].getText())); setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p); } } Loading Loading @@ -7517,7 +7568,7 @@ public class Notification implements Parcelable @Nullable private final Person mSender; /** True if this message was generated from the extra * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY} * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS} */ private final boolean mRemoteInputHistory; Loading Loading @@ -7569,7 +7620,7 @@ public class Notification implements Parcelable * Should be <code>null</code> for messages by the current user, in which case * the platform will insert the user set in {@code MessagingStyle(Person)}. * @param remoteInputHistory True if the messages was generated from the extra * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}. * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}. * <p> * The person provided should contain an Icon, set with * {@link Person.Builder#setIcon(Icon)} and also have a name provided Loading Loading @@ -7676,7 +7727,7 @@ public class Notification implements Parcelable /** * @return True if the message was generated from * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}. * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}. * @hide */ public boolean isRemoteInputHistory() { Loading Loading @@ -7906,8 +7957,8 @@ public class Notification implements Parcelable if (mBuilder.mActions.size() > 0) { maxRows--; } CharSequence[] remoteInputHistory = mBuilder.mN.extras.getCharSequenceArray( EXTRA_REMOTE_INPUT_HISTORY); RemoteInputHistoryItem[] remoteInputHistory = (RemoteInputHistoryItem[]) mBuilder.mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); if (remoteInputHistory != null && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) { // Let's remove some messages to make room for the remote input history. Loading
core/java/android/app/RemoteInputHistoryItem.java 0 → 100644 +90 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; /** * Stores historical input from a RemoteInput attached to a Notification. * * History items represent either a text message (specified by providing a CharSequence, * or a media message (specified by providing a URI and a MIME type). Media messages must also * include text to insert when the image cannot be loaded, ex. when URI read permission has not been * granted correctly. * * @hide */ public class RemoteInputHistoryItem implements Parcelable { private CharSequence mText; private String mMimeType; private Uri mUri; public RemoteInputHistoryItem(String mimeType, Uri uri, CharSequence backupText) { this.mMimeType = mimeType; this.mUri = uri; this.mText = Notification.safeCharSequence(backupText); } public RemoteInputHistoryItem(CharSequence text) { this.mText = Notification.safeCharSequence(text); } protected RemoteInputHistoryItem(Parcel in) { mText = in.readCharSequence(); mMimeType = in.readStringNoHelper(); mUri = in.readParcelable(Uri.class.getClassLoader()); } public static final Creator<RemoteInputHistoryItem> CREATOR = new Creator<RemoteInputHistoryItem>() { @Override public RemoteInputHistoryItem createFromParcel(Parcel in) { return new RemoteInputHistoryItem(in); } @Override public RemoteInputHistoryItem[] newArray(int size) { return new RemoteInputHistoryItem[size]; } }; public CharSequence getText() { return mText; } public String getMimeType() { return mMimeType; } public Uri getUri() { return mUri; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeCharSequence(mText); dest.writeStringNoHelper(mMimeType); dest.writeParcelable(mUri, flags); } }
core/java/com/android/internal/widget/MessagingLayout.java +12 −6 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.StyleRes; import android.app.Notification; import android.app.Person; import android.app.RemoteInputHistoryItem; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; Loading Loading @@ -161,8 +162,9 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer if (headerText != null) { mConversationTitle = headerText.getText(); } addRemoteInputHistoryToMessages(newMessages, extras.getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY)); RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[]) extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); addRemoteInputHistoryToMessages(newMessages, history); boolean showSpinner = extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); bind(newMessages, newHistoricMessages, showSpinner); Loading @@ -175,14 +177,18 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer private void addRemoteInputHistoryToMessages( List<Notification.MessagingStyle.Message> newMessages, CharSequence[] remoteInputHistory) { RemoteInputHistoryItem[] remoteInputHistory) { if (remoteInputHistory == null || remoteInputHistory.length == 0) { return; } for (int i = remoteInputHistory.length - 1; i >= 0; i--) { CharSequence message = remoteInputHistory[i]; newMessages.add(new Notification.MessagingStyle.Message( message, 0, (Person) null, true /* remoteHistory */)); RemoteInputHistoryItem historyMessage = remoteInputHistory[i]; Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message( historyMessage.getText(), 0, (Person) null, true /* remoteHistory */); if (historyMessage.getUri() != null) { message.setData(historyMessage.getMimeType(), historyMessage.getUri()); } newMessages.add(message); } } Loading
packages/SystemUI/res/values/strings.xml +4 −0 Original line number Diff line number Diff line Loading @@ -194,6 +194,10 @@ <!-- Power menu item for taking a screenshot [CHAR LIMIT=20]--> <string name="global_action_screenshot">Screenshot</string> <!-- text to show in place of RemoteInput images when they cannot be shown. [CHAR LIMIT=50] --> <string name="remote_input_image_insertion_text">Image inserted</string> <!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] --> <string name="screenshot_saving_ticker">Saving screenshot\u2026</string> <!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] --> Loading
packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +28 −14 Original line number Diff line number Diff line Loading @@ -25,8 +25,10 @@ import android.app.KeyguardManager; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; import android.app.RemoteInputHistoryItem; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; Loading Loading @@ -345,7 +347,8 @@ public class NotificationRemoteInputManager implements Dumpable { }); mSmartReplyController.setCallback((entry, reply) -> { StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */); rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */, null /* mimeType */, null /* uri */); mEntryManager.updateNotification(newSbn, null /* ranking */); }); } Loading Loading @@ -527,28 +530,36 @@ public class NotificationRemoteInputManager implements Dumpable { StatusBarNotification rebuildNotificationForCanceledSmartReplies( NotificationEntry entry) { return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */, false /* showSpinner */); false /* showSpinner */, null /* mimeType */, null /* uri */); } @VisibleForTesting StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry, CharSequence remoteInputText, boolean showSpinner) { CharSequence remoteInputText, boolean showSpinner, String mimeType, Uri uri) { StatusBarNotification sbn = entry.getSbn(); Notification.Builder b = Notification.Builder .recoverBuilder(mContext, sbn.getNotification().clone()); if (remoteInputText != null) { CharSequence[] oldHistory = sbn.getNotification().extras .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY); CharSequence[] newHistory; if (oldHistory == null) { newHistory = new CharSequence[1]; if (remoteInputText != null || uri != null) { RemoteInputHistoryItem[] oldHistoryItems = (RemoteInputHistoryItem[]) sbn.getNotification().extras.getParcelableArray( Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); RemoteInputHistoryItem[] newHistoryItems; if (oldHistoryItems == null) { newHistoryItems = new RemoteInputHistoryItem[1]; } else { newHistory = new CharSequence[oldHistory.length + 1]; System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length); newHistoryItems = new RemoteInputHistoryItem[oldHistoryItems.length + 1]; System.arraycopy(oldHistoryItems, 0, newHistoryItems, 1, oldHistoryItems.length); } newHistory[0] = String.valueOf(remoteInputText); b.setRemoteInputHistory(newHistory); RemoteInputHistoryItem newItem; if (uri != null) { newItem = new RemoteInputHistoryItem(mimeType, uri, remoteInputText); } else { newItem = new RemoteInputHistoryItem(remoteInputText); } newHistoryItems[0] = newItem; b.setRemoteInputHistory(newHistoryItems); } b.setShowRemoteInputSpinner(showSpinner); b.setHideSmartReplies(true); Loading Loading @@ -631,8 +642,11 @@ public class NotificationRemoteInputManager implements Dumpable { if (TextUtils.isEmpty(remoteInputText)) { remoteInputText = entry.remoteInputTextWhenReset; } String remoteInputMimeType = entry.remoteInputMimeType; Uri remoteInputUri = entry.remoteInputUri; StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry, remoteInputText, false /* showSpinner */); remoteInputText, false /* showSpinner */, remoteInputMimeType, remoteInputUri); entry.onRemoteInputInserted(); if (newSbn == null) { Loading