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

Commit b92e4c32 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
parent 1032a069
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();
+2 −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;
@@ -128,6 +129,7 @@ public final class NotificationEntry extends ListEntry {
    public CharSequence remoteInputText;
    public String remoteInputMimeType;
    public Uri remoteInputUri;
    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,12 +70,12 @@ import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -110,8 +108,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;
@@ -122,9 +120,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;

@@ -133,7 +128,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;
@@ -142,7 +136,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;

@@ -175,10 +168,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,
@@ -267,8 +257,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(
@@ -295,15 +285,19 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene

    @VisibleForTesting
    protected 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);
@@ -329,10 +323,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() {
@@ -343,7 +336,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;

@@ -369,7 +362,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)
@@ -380,14 +374,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);
        }

@@ -439,6 +430,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
                    mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
                    mEntry.getSbn().getInstanceId());
        }

        setAttachment(null);
    }

@@ -479,7 +471,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;
        }
    }

@@ -606,7 +597,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);

@@ -629,7 +620,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);
@@ -638,7 +628,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;
    }
@@ -655,7 +645,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() {
@@ -855,7 +845,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.
@@ -926,7 +916,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);