From 4f02a9c1a339e9b9b10bb0c8721b5b353d022064 Mon Sep 17 00:00:00 2001 From: Steve Elliott Date: Mon, 28 Jun 2021 12:47:03 -0400 Subject: [PATCH] Persist remote input image attachment across layouts Fixes: 188646667 Test: manual, atest Change-Id: I85f7726c4b8c1bfcf64a1ff3f4be092e970747ae Merged-In: I85f7726c4b8c1bfcf64a1ff3f4be092e970747ae --- .../NotificationRemoteInputManager.java | 8 +-- .../collection/NotificationEntry.java | 5 ++ .../statusbar/policy/RemoteInputView.java | 59 ++++++++----------- .../NotificationRemoteInputManagerTest.java | 14 ++--- 4 files changed, 40 insertions(+), 46 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 0b67e7eeb1327..4552138761c0a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -369,7 +369,7 @@ public class NotificationRemoteInputManager implements Dumpable { }); mSmartReplyController.setCallback((entry, reply) -> { StatusBarNotification newSbn = - rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */, + rebuildNotificationWithRemoteInputInserted(entry, reply, true /* showSpinner */, null /* mimeType */, null /* uri */); mEntryManager.updateNotification(newSbn, null /* ranking */); }); @@ -638,12 +638,12 @@ public class NotificationRemoteInputManager implements Dumpable { @VisibleForTesting StatusBarNotification rebuildNotificationForCanceledSmartReplies( NotificationEntry entry) { - return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */, + return rebuildNotificationWithRemoteInputInserted(entry, null /* remoteInputTest */, false /* showSpinner */, null /* mimeType */, null /* uri */); } @VisibleForTesting - StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry, + StatusBarNotification rebuildNotificationWithRemoteInputInserted(NotificationEntry entry, CharSequence remoteInputText, boolean showSpinner, String mimeType, Uri uri) { StatusBarNotification sbn = entry.getSbn(); @@ -746,7 +746,7 @@ public class NotificationRemoteInputManager implements Dumpable { } String remoteInputMimeType = entry.remoteInputMimeType; Uri remoteInputUri = entry.remoteInputUri; - StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry, + StatusBarNotification newSbn = rebuildNotificationWithRemoteInputInserted(entry, remoteInputText, false /* showSpinner */, remoteInputMimeType, remoteInputUri); entry.onRemoteInputInserted(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 9f82152eb5ed9..96b0e7819c7a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -51,6 +51,7 @@ import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.util.ArraySet; +import android.view.ContentInfo; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -126,8 +127,12 @@ public final class NotificationEntry extends ListEntry { public int targetSdk; private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET; public CharSequence remoteInputText; + // Mimetype and Uri used to display the image in the notification *after* it has been sent. public String remoteInputMimeType; public Uri remoteInputUri; + // ContentInfo used to keep the attachment permission alive until RemoteInput is sent or + // cancelled. + public ContentInfo remoteInputAttachment; private Notification.BubbleMetadata mBubbleMetadata; private ShortcutInfo mShortcutInfo; 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 e6c4e82cec7be..b18dfd2866c43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -20,7 +20,6 @@ import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; @@ -38,7 +37,6 @@ import android.graphics.Rect; import android.graphics.drawable.GradientDrawable; import android.net.Uri; import android.os.Bundle; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.text.Editable; @@ -72,13 +70,13 @@ import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.internal.graphics.ColorUtils; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto; -import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -111,8 +109,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private final SendButtonTextWatcher mTextWatcher; private final TextView.OnEditorActionListener mEditorActionHandler; - private final NotificationRemoteInputManager mRemoteInputManager; private final UiEventLogger mUiEventLogger; + private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; private final List mEditTextFocusChangeListeners = new ArrayList<>(); private final List mOnSendListeners = new ArrayList<>(); private RemoteEditText mEditText; @@ -123,9 +121,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInput[] mRemoteInputs; private RemoteInput mRemoteInput; private RemoteInputController mController; - private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; - - private IStatusBarService mStatusBarManagerService; private NotificationEntry mEntry; @@ -134,7 +129,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private int mRevealCx; private int mRevealCy; private int mRevealR; - private ContentInfo mAttachment; private boolean mColorized; private int mTint; @@ -143,7 +137,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private NotificationViewWrapper mWrapper; private Consumer mOnVisibilityChangedListener; private NotificationRemoteInputManager.BouncerChecker mBouncerChecker; - private LinearLayout mContentView; private ImageView mDelete; private ImageView mDeleteBg; @@ -174,10 +167,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mTextWatcher = new SendButtonTextWatcher(); mEditorActionHandler = new EditorActionHandler(); mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class); - mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); mUiEventLogger = Dependency.get(UiEventLogger.class); - mStatusBarManagerService = IStatusBarService.Stub.asInterface( - ServiceManager.getService(Context.STATUS_BAR_SERVICE)); TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{ com.android.internal.R.attr.colorAccent, com.android.internal.R.attr.colorSurface, @@ -266,8 +256,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mDeleteBg.setImageTintBlendMode(BlendMode.SRC_IN); mDelete.setImageTintBlendMode(BlendMode.SRC_IN); mDelete.setOnClickListener(v -> setAttachment(null)); - mContentView = findViewById(R.id.remote_input_content); - mContentView.setBackground(mContentBackground); + LinearLayout contentView = findViewById(R.id.remote_input_content); + contentView.setBackground(mContentBackground); mEditText = findViewById(R.id.remote_input_text); mEditText.setInnerFocusable(false); mEditText.setWindowInsetsAnimationCallback( @@ -293,15 +283,19 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } private void setAttachment(ContentInfo item) { - if (mAttachment != null) { + if (mEntry.remoteInputAttachment != null && mEntry.remoteInputAttachment != item) { // We need to release permissions when sending the attachment to the target // app or if it is deleted by the user. When sending to the target app, we // can safely release permissions as soon as the call to // `mController.grantInlineReplyUriPermission` is made (ie, after the grant // to the target app has been created). - mAttachment.releasePermissions(); + mEntry.remoteInputAttachment.releasePermissions(); + } + mEntry.remoteInputAttachment = item; + if (item != null) { + mEntry.remoteInputUri = item.getClip().getItemAt(0).getUri(); + mEntry.remoteInputMimeType = item.getClip().getDescription().getMimeType(0); } - mAttachment = item; View attachment = findViewById(R.id.remote_input_content_container); ImageView iconView = findViewById(R.id.remote_input_attachment_image); iconView.setImageDrawable(null); @@ -323,10 +317,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene * @return returns intent with granted URI permissions that should be used immediately */ private Intent prepareRemoteInput() { - if (mAttachment == null) return prepareRemoteInputFromText(); - return prepareRemoteInputFromData( - mAttachment.getClip().getDescription().getMimeType(0), - mAttachment.getClip().getItemAt(0).getUri()); + return mEntry.remoteInputAttachment == null + ? prepareRemoteInputFromText() + : prepareRemoteInputFromData(mEntry.remoteInputMimeType, mEntry.remoteInputUri); } private Intent prepareRemoteInputFromText() { @@ -337,7 +330,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene results); mEntry.remoteInputText = mEditText.getText().toString(); - // TODO(b/188646667): store attachment to entry + setAttachment(null); mEntry.remoteInputUri = null; mEntry.remoteInputMimeType = null; @@ -363,7 +356,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent, bundle); - CharSequence attachmentText = mAttachment.getClip().getDescription().getLabel(); + CharSequence attachmentText = + mEntry.remoteInputAttachment.getClip().getDescription().getLabel(); CharSequence attachmentLabel = TextUtils.isEmpty(attachmentText) ? mContext.getString(R.string.remote_input_image_insertion_text) @@ -374,14 +368,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene : "\"" + attachmentLabel + "\" " + mEditText.getText(); mEntry.remoteInputText = fullText; - // TODO(b/188646667): store attachment to entry - mEntry.remoteInputMimeType = contentType; - mEntry.remoteInputUri = data; // mirror prepareRemoteInputFromText for text input if (mEntry.editedSuggestionInfo == null) { RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT); - } else if (mAttachment == null) { + } else if (mEntry.remoteInputAttachment == null) { RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE); } @@ -437,6 +428,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(), mEntry.getSbn().getInstanceId()); } + setAttachment(null); } @@ -477,7 +469,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private void onDefocus(boolean animate, boolean logClose) { mController.removeRemoteInput(mEntry, mToken); mEntry.remoteInputText = mEditText.getText(); - // TODO(b/188646667): store attachment to entry // During removal, we get reattached and lose focus. Not hiding in that // case to prevent flicker. @@ -565,7 +556,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEntry.editedSuggestionInfo = editedSuggestionInfo; if (editedSuggestionInfo != null) { mEntry.remoteInputText = editedSuggestionInfo.originalText; - // TODO(b/188646667): store attachment to entry + mEntry.remoteInputAttachment = null; } } @@ -608,7 +599,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText.setSelection(mEditText.length()); mEditText.requestFocus(); mController.addRemoteInput(mEntry, mToken); - // TODO(b/188646667): restore attachment from entry + setAttachment(mEntry.remoteInputAttachment); mRemoteInputQuickSettingsDisabler.setRemoteInputActive(true); @@ -631,7 +622,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private void reset() { mResetting = true; mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText()); - // TODO(b/188646667): store attachment at time of reset to entry mEditText.getText().clear(); mEditText.setEnabled(true); @@ -640,7 +630,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mController.removeSpinning(mEntry.getKey(), mToken); updateSendButton(); onDefocus(false /* animate */, false /* logClose */); - // TODO(b/188646667): clear attachment + setAttachment(null); mResetting = false; } @@ -657,7 +647,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } private void updateSendButton() { - mSendButton.setEnabled(mEditText.length() != 0 || mAttachment != null); + mSendButton.setEnabled(mEditText.length() != 0 || mEntry.remoteInputAttachment != null); } public void close() { @@ -857,7 +847,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene && event.getAction() == KeyEvent.ACTION_DOWN; if (isSoftImeEvent || isKeyboardEnterKey) { - if (mEditText.length() > 0 || mAttachment != null) { + if (mEditText.length() > 0 || mEntry.remoteInputAttachment != null) { sendRemoteInput(prepareRemoteInput()); } // Consume action to prevent IME from closing. @@ -928,7 +918,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene // our focus, so we'll need to save our text here. if (mRemoteInputView != null) { mRemoteInputView.mEntry.remoteInputText = getText(); - // TODO(b/188646667): store attachment to entry } } return; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java index 35c92b6e49414..8e949e7d1e37f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java @@ -161,7 +161,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { String mimeType = "image/jpeg"; String text = "image inserted"; StatusBarNotification newSbn = - mRemoteInputManager.rebuildNotificationWithRemoteInput( + mRemoteInputManager.rebuildNotificationWithRemoteInputInserted( mEntry, text, false, mimeType, uri); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); @@ -174,7 +174,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { @Test public void testRebuildWithRemoteInput_noExistingInputNoSpinner() { StatusBarNotification newSbn = - mRemoteInputManager.rebuildNotificationWithRemoteInput( + mRemoteInputManager.rebuildNotificationWithRemoteInputInserted( mEntry, "A Reply", false, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); @@ -189,7 +189,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { @Test public void testRebuildWithRemoteInput_noExistingInputWithSpinner() { StatusBarNotification newSbn = - mRemoteInputManager.rebuildNotificationWithRemoteInput( + mRemoteInputManager.rebuildNotificationWithRemoteInputInserted( mEntry, "A Reply", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); @@ -205,14 +205,14 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { public void testRebuildWithRemoteInput_withExistingInput() { // Setup a notification entry with 1 remote input. StatusBarNotification newSbn = - mRemoteInputManager.rebuildNotificationWithRemoteInput( + mRemoteInputManager.rebuildNotificationWithRemoteInputInserted( mEntry, "A Reply", false, null, null); NotificationEntry entry = new NotificationEntryBuilder() .setSbn(newSbn) .build(); // Try rebuilding to add another reply. - newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput( + newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInputInserted( entry, "Reply 2", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); @@ -228,14 +228,14 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { String mimeType = "image/jpeg"; String text = "image inserted"; StatusBarNotification newSbn = - mRemoteInputManager.rebuildNotificationWithRemoteInput( + mRemoteInputManager.rebuildNotificationWithRemoteInputInserted( mEntry, text, false, mimeType, uri); NotificationEntry entry = new NotificationEntryBuilder() .setSbn(newSbn) .build(); // Try rebuilding to add another reply. - newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput( + newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInputInserted( entry, "Reply 2", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);