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

Commit fe2f96e0 authored by Milo Sredkov's avatar Milo Sredkov Committed by Android (Google) Code Review
Browse files

Merge "Add setEditChoicesBeforeSending to RemoteInput"

parents fee6e9c1 b2af7f97
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -5983,6 +5983,7 @@ package android.app {
    method public java.util.Set<java.lang.String> getAllowedDataTypes();
    method public java.lang.CharSequence[] getChoices();
    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
    method public int getEditChoicesBeforeSending();
    method public android.os.Bundle getExtras();
    method public java.lang.CharSequence getLabel();
    method public java.lang.String getResultKey();
@@ -5992,6 +5993,9 @@ package android.app {
    method public static void setResultsSource(android.content.Intent, int);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
    field public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0; // 0x0
    field public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1; // 0x1
    field public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2; // 0x2
    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
    field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
    field public static final int SOURCE_CHOICE = 1; // 0x1
@@ -6006,6 +6010,7 @@ package android.app {
    method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
    method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
    method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
    method public android.app.RemoteInput.Builder setEditChoicesBeforeSending(int);
    method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
  }
+54 −4
Original line number Diff line number Diff line
@@ -93,6 +93,22 @@ public final class RemoteInput implements Parcelable {
    /** The user selected one of the choices from {@link #getChoices}. */
    public static final int SOURCE_CHOICE = 1;

    /** @hide */
    @IntDef(prefix = {"EDIT_CHOICES_BEFORE_SENDING_"},
            value = {EDIT_CHOICES_BEFORE_SENDING_AUTO, EDIT_CHOICES_BEFORE_SENDING_DISABLED,
                    EDIT_CHOICES_BEFORE_SENDING_ENABLED})
    @Retention(RetentionPolicy.SOURCE)
    public @interface EditChoicesBeforeSending {}

    /** The platform will determine whether choices will be edited before being sent to the app. */
    public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0;

    /** Tapping on a choice should send the input immediately, without letting the user edit it. */
    public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1;

    /** Tapping on a choice should let the user edit the input before it is sent to the app. */
    public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2;

    // Flags bitwise-ored to mFlags
    private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1;

@@ -103,17 +119,25 @@ public final class RemoteInput implements Parcelable {
    private final CharSequence mLabel;
    private final CharSequence[] mChoices;
    private final int mFlags;
    @EditChoicesBeforeSending private final int mEditChoicesBeforeSending;
    private final Bundle mExtras;
    private final ArraySet<String> mAllowedDataTypes;

    private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
            int flags, Bundle extras, ArraySet<String> allowedDataTypes) {
            int flags, int editChoicesBeforeSending, Bundle extras,
            ArraySet<String> allowedDataTypes) {
        this.mResultKey = resultKey;
        this.mLabel = label;
        this.mChoices = choices;
        this.mFlags = flags;
        this.mEditChoicesBeforeSending = editChoicesBeforeSending;
        this.mExtras = extras;
        this.mAllowedDataTypes = allowedDataTypes;
        if (getEditChoicesBeforeSending() == EDIT_CHOICES_BEFORE_SENDING_ENABLED
                && !getAllowFreeFormInput()) {
            throw new IllegalArgumentException(
                    "setEditChoicesBeforeSending requires setAllowFreeFormInput");
        }
    }

    /**
@@ -168,6 +192,15 @@ public final class RemoteInput implements Parcelable {
        return (mFlags & FLAG_ALLOW_FREE_FORM_INPUT) != 0;
    }

    /**
     * Gets whether tapping on a choice should let the user edit the input before it is sent to the
     * app.
     */
    @EditChoicesBeforeSending
    public int getEditChoicesBeforeSending() {
        return mEditChoicesBeforeSending;
    }

    /**
     * Get additional metadata carried around with this remote input.
     */
@@ -185,6 +218,8 @@ public final class RemoteInput implements Parcelable {
        private CharSequence mLabel;
        private CharSequence[] mChoices;
        private int mFlags = DEFAULT_FLAGS;
        @EditChoicesBeforeSending
        private int mEditChoicesBeforeSending = EDIT_CHOICES_BEFORE_SENDING_AUTO;

        /**
         * Create a builder object for {@link RemoteInput} objects.
@@ -269,7 +304,20 @@ public final class RemoteInput implements Parcelable {
         */
        @NonNull
        public Builder setAllowFreeFormInput(boolean allowFreeFormTextInput) {
            setFlag(mFlags, allowFreeFormTextInput);
            setFlag(FLAG_ALLOW_FREE_FORM_INPUT, allowFreeFormTextInput);
            return this;
        }

        /**
         * Specifies whether tapping on a choice should let the user edit the input before it is
         * sent to the app. The default is {@link #EDIT_CHOICES_BEFORE_SENDING_AUTO}.
         *
         * It cannot be used if {@link #setAllowFreeFormInput} has been set to false.
         */
        @NonNull
        public Builder setEditChoicesBeforeSending(
                @EditChoicesBeforeSending int editChoicesBeforeSending) {
            mEditChoicesBeforeSending = editChoicesBeforeSending;
            return this;
        }

@@ -312,8 +360,8 @@ public final class RemoteInput implements Parcelable {
         */
        @NonNull
        public RemoteInput build() {
            return new RemoteInput(
                    mResultKey, mLabel, mChoices, mFlags, mExtras, mAllowedDataTypes);
            return new RemoteInput(mResultKey, mLabel, mChoices, mFlags, mEditChoicesBeforeSending,
                    mExtras, mAllowedDataTypes);
        }
    }

@@ -322,6 +370,7 @@ public final class RemoteInput implements Parcelable {
        mLabel = in.readCharSequence();
        mChoices = in.readCharSequenceArray();
        mFlags = in.readInt();
        mEditChoicesBeforeSending = in.readInt();
        mExtras = in.readBundle();
        mAllowedDataTypes = (ArraySet<String>) in.readArraySet(null);
    }
@@ -507,6 +556,7 @@ public final class RemoteInput implements Parcelable {
        out.writeCharSequence(mLabel);
        out.writeCharSequenceArray(mChoices);
        out.writeInt(mFlags);
        out.writeInt(mEditChoicesBeforeSending);
        out.writeBundle(mExtras);
        out.writeArraySet(mAllowedDataTypes);
    }
+97 −82
Original line number Diff line number Diff line
@@ -218,6 +218,73 @@ public class NotificationRemoteInputManager implements Dumpable {
                return false;
            }

            return activateRemoteInput(view, inputs, input, pendingIntent);
        }
    };

    private ShadeController getShadeController() {
        if (mShadeController == null) {
            mShadeController = Dependency.get(ShadeController.class);
        }
        return mShadeController;
    }

    public NotificationRemoteInputManager(Context context) {
        mContext = context;
        mBarService = IStatusBarService.Stub.asInterface(
                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
        addLifetimeExtenders();
        mKeyguardManager = context.getSystemService(KeyguardManager.class);
    }

    /** Initializes this component with the provided dependencies. */
    public void setUpWithCallback(Callback callback, RemoteInputController.Delegate delegate) {
        mCallback = callback;
        mRemoteInputController = new RemoteInputController(delegate);
        mRemoteInputController.addCallback(new RemoteInputController.Callback() {
            @Override
            public void onRemoteInputSent(NotificationData.Entry entry) {
                if (FORCE_REMOTE_INPUT_HISTORY
                        && isNotificationKeptForRemoteInputHistory(entry.key)) {
                    mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
                } else if (mEntriesKeptForRemoteInputActive.contains(entry)) {
                    // We're currently holding onto this notification, but from the apps point of
                    // view it is already canceled, so we'll need to cancel it on the apps behalf
                    // after sending - unless the app posts an update in the mean time, so wait a
                    // bit.
                    Dependency.get(Dependency.MAIN_HANDLER).postDelayed(() -> {
                        if (mEntriesKeptForRemoteInputActive.remove(entry)) {
                            mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
                        }
                    }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
                }
                try {
                    mBarService.onNotificationDirectReplied(entry.notification.getKey());
                } catch (RemoteException e) {
                    // Nothing to do, system going down
                }
            }
        });
        mSmartReplyController.setCallback((entry, reply) -> {
            StatusBarNotification newSbn =
                    rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */);
            mEntryManager.updateNotification(newSbn, null /* ranking */);
        });
    }

    /**
     * Activates a given {@link RemoteInput}
     *
     * @param view The view of the action button or suggestion chip that was tapped.
     * @param inputs The remote inputs that need to be sent to the app.
     * @param input The remote input that needs to be activated.
     * @param pendingIntent The pending intent to be sent to the app.
     * @return Whether the {@link RemoteInput} was activated.
     */
    public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
            PendingIntent pendingIntent) {

        ViewParent p = view.getParent();
        RemoteInputView riv = null;
        while (p != null) {
@@ -301,58 +368,6 @@ public class NotificationRemoteInputManager implements Dumpable {
        }
        return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
    }
    };

    private ShadeController getShadeController() {
        if (mShadeController == null) {
            mShadeController = Dependency.get(ShadeController.class);
        }
        return mShadeController;
    }

    public NotificationRemoteInputManager(Context context) {
        mContext = context;
        mBarService = IStatusBarService.Stub.asInterface(
                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
        addLifetimeExtenders();
        mKeyguardManager = context.getSystemService(KeyguardManager.class);
    }

    /** Initializes this component with the provided dependencies. */
    public void setUpWithCallback(Callback callback, RemoteInputController.Delegate delegate) {
        mCallback = callback;
        mRemoteInputController = new RemoteInputController(delegate);
        mRemoteInputController.addCallback(new RemoteInputController.Callback() {
            @Override
            public void onRemoteInputSent(NotificationData.Entry entry) {
                if (FORCE_REMOTE_INPUT_HISTORY
                        && isNotificationKeptForRemoteInputHistory(entry.key)) {
                    mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
                } else if (mEntriesKeptForRemoteInputActive.contains(entry)) {
                    // We're currently holding onto this notification, but from the apps point of
                    // view it is already canceled, so we'll need to cancel it on the apps behalf
                    // after sending - unless the app posts an update in the mean time, so wait a
                    // bit.
                    Dependency.get(Dependency.MAIN_HANDLER).postDelayed(() -> {
                        if (mEntriesKeptForRemoteInputActive.remove(entry)) {
                            mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
                        }
                    }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
                }
                try {
                    mBarService.onNotificationDirectReplied(entry.notification.getKey());
                } catch (RemoteException e) {
                    // Nothing to do, system going down
                }
            }
        });
        mSmartReplyController.setCallback((entry, reply) -> {
            StatusBarNotification newSbn =
                    rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */);
            mEntryManager.updateNotification(newSbn, null /* ranking */);
        });
    }

    /**
     * Adds all the notification lifetime extenders. Each extender represents a reason for the
+14 −1
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -63,6 +64,7 @@ public class SmartReplyView extends ViewGroup {

    private final SmartReplyConstants mConstants;
    private final KeyguardDismissUtil mKeyguardDismissUtil;
    private final NotificationRemoteInputManager mRemoteInputManager;
    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());

    /**
@@ -112,6 +114,7 @@ public class SmartReplyView extends ViewGroup {
        super(context, attrs);
        mConstants = Dependency.get(SmartReplyConstants.class);
        mKeyguardDismissUtil = Dependency.get(KeyguardDismissUtil.class);
        mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);

        mHeightUpperLimit = NotificationUtils.getFontScaledHeight(mContext,
            R.dimen.smart_reply_button_max_height);
@@ -242,6 +245,16 @@ public class SmartReplyView extends ViewGroup {
        b.setText(choice);

        OnDismissAction action = () -> {
            // TODO(b/111437455): Also for EDIT_CHOICES_BEFORE_SENDING_AUTO, depending on flags.
            if (smartReplies.remoteInput.getEditChoicesBeforeSending()
                    == RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED) {
                entry.remoteInputText = choice;
                mRemoteInputManager.activateRemoteInput(b,
                        new RemoteInput[] { smartReplies.remoteInput }, smartReplies.remoteInput,
                        smartReplies.pendingIntent);
                return false;
            }

            smartReplyController.smartReplySent(
                    entry, replyIndex, b.getText(), smartReplies.fromAssistant);
            Bundle results = new Bundle();