Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4f02a9c1 authored by Steve Elliott's avatar Steve Elliott
Browse files

Persist remote input image attachment across layouts

Fixes: 188646667
Test: manual, atest
Change-Id: I85f7726c4b8c1bfcf64a1ff3f4be092e970747ae
Merged-In: I85f7726c4b8c1bfcf64a1ff3f4be092e970747ae
parent 4e229f44
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -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();
+5 −0
Original line number Diff line number Diff line
@@ -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;

+24 −35
Original line number Diff line number Diff line
@@ -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<OnFocusChangeListener> mEditTextFocusChangeListeners = new ArrayList<>();
    private final List<OnSendRemoteInputListener> 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<Boolean> 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;
+7 −7
Original line number Diff line number Diff line
@@ -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);