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

Commit 715aec89 authored by Tim Yu's avatar Tim Yu
Browse files

Allow better customization of Autofill dialogs.

1. Allow Autofill Service to use a custom icon/header for SaveUI
2. Allow Save/Fill Dialog icon to be hidden

Fix: 242703084
Test: manual:
 * atest android.autofillservice.cts.saveui
 * atest android.autofillservice.cts.dialog.LoginActivityTest
 * manually check that custom icon/header is shown/hidden

Change-Id: Ic4bb6964a8e8355b7b2a191d810143745dd95059
parent 6ddb6bbb
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -39234,9 +39234,13 @@ package android.service.autofill {
    method @NonNull public android.service.autofill.FillResponse.Builder setFlags(int);
    method @NonNull public android.service.autofill.FillResponse.Builder setFooter(@NonNull android.widget.RemoteViews);
    method @NonNull public android.service.autofill.FillResponse.Builder setHeader(@NonNull android.widget.RemoteViews);
    method @NonNull public android.service.autofill.FillResponse.Builder setIconResourceId(@DrawableRes int);
    method @NonNull public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
    method @NonNull public android.service.autofill.FillResponse.Builder setPresentationCancelIds(@Nullable int[]);
    method @NonNull public android.service.autofill.FillResponse.Builder setSaveInfo(@NonNull android.service.autofill.SaveInfo);
    method @NonNull public android.service.autofill.FillResponse.Builder setServiceDisplayNameResourceId(@StringRes int);
    method @NonNull public android.service.autofill.FillResponse.Builder setShowFillDialogIcon(boolean);
    method @NonNull public android.service.autofill.FillResponse.Builder setShowSaveDialogIcon(boolean);
    method @NonNull public android.service.autofill.FillResponse.Builder setUserData(@NonNull android.service.autofill.UserData);
  }
+107 −0
Original line number Diff line number Diff line
@@ -20,9 +20,11 @@ import static android.service.autofill.AutofillServiceHelper.assertValid;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
import static android.view.autofill.Helper.sDebug;

import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.Activity;
@@ -107,6 +109,10 @@ public final class FillResponse implements Parcelable {
    private final @Nullable UserData mUserData;
    private final @Nullable int[] mCancelIds;
    private final boolean mSupportsInlineSuggestions;
    private final @DrawableRes int mIconResourceId;
    private final @StringRes int mServiceDisplayNameResourceId;
    private final boolean mShowFillDialogIcon;
    private final boolean mShowSaveDialogIcon;

    private FillResponse(@NonNull Builder builder) {
        mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -130,6 +136,10 @@ public final class FillResponse implements Parcelable {
        mUserData = builder.mUserData;
        mCancelIds = builder.mCancelIds;
        mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions;
        mIconResourceId = builder.mIconResourceId;
        mServiceDisplayNameResourceId = builder.mServiceDisplayNameResourceId;
        mShowFillDialogIcon = builder.mShowFillDialogIcon;
        mShowSaveDialogIcon = builder.mShowSaveDialogIcon;
    }

    /** @hide */
@@ -217,6 +227,26 @@ public final class FillResponse implements Parcelable {
        return mUserData;
    }

    /** @hide */
    public @DrawableRes int getIconResourceId() {
        return mIconResourceId;
    }

    /** @hide */
    public @StringRes int getServiceDisplayNameResourceId() {
        return mServiceDisplayNameResourceId;
    }

    /** @hide */
    public boolean getShowFillDialogIcon() {
        return mShowFillDialogIcon;
    }

    /** @hide */
    public boolean getShowSaveDialogIcon() {
        return mShowSaveDialogIcon;
    }

    /** @hide */
    @TestApi
    public int getFlags() {
@@ -278,6 +308,10 @@ public final class FillResponse implements Parcelable {
        private UserData mUserData;
        private int[] mCancelIds;
        private boolean mSupportsInlineSuggestions;
        private int mIconResourceId;
        private int mServiceDisplayNameResourceId;
        private boolean mShowFillDialogIcon = true;
        private boolean mShowSaveDialogIcon = true;

        /**
         * Triggers a custom UI before autofilling the screen with any data set in this
@@ -728,6 +762,70 @@ public final class FillResponse implements Parcelable {
            return this;
        }

        /**
         * Overwrites Save/Fill dialog header icon with a specific one specified by resource id.
         * The image is pulled from the package, so id should be defined in the manifest.
         *
         * @param id {@link android.graphics.drawable.Drawable} resource id of the icon to be used.
         * A value of 0 indicates to use the default header icon.
         *
         * @return this builder
         */
        @NonNull
        public Builder setIconResourceId(@DrawableRes int id) {
            throwIfDestroyed();

            mIconResourceId = id;
            return this;
        }

        /**
         * Overrides the service name in the Save Dialog header with a specific string defined
         * in the service provider's manifest.xml
         *
         * @param id Resoure Id of the custom string defined in the provider's manifest. If set
         * to 0, the default name will be used.
         *
         * @return this builder
         */
        @NonNull
        public Builder setServiceDisplayNameResourceId(@StringRes int id) {
            throwIfDestroyed();

            mServiceDisplayNameResourceId = id;
            return this;
        }

        /**
         * Whether or not to show the Autofill provider icon inside of the Fill Dialog
         *
         * @param show True to show, false to hide. Defaults to true.
         *
         * @return this builder
         */
        @NonNull
        public Builder setShowFillDialogIcon(boolean show) {
            throwIfDestroyed();

            mShowFillDialogIcon = show;
            return this;
        }

        /**
         * Whether or not to show the Autofill provider icon inside of the Save Dialog
         *
         * @param show True to show, false to hide. Defaults to true.
         *
         * @return this builder
         */
        @NonNull
        public Builder setShowSaveDialogIcon(boolean show) {
            throwIfDestroyed();

            mShowSaveDialogIcon = show;
            return this;
        }

        /**
         * Sets a header to be shown as the first element in the list of datasets.
         *
@@ -1024,6 +1122,10 @@ public final class FillResponse implements Parcelable {
        parcel.writeParcelableArray(mIgnoredIds, flags);
        parcel.writeLong(mDisableDuration);
        parcel.writeParcelableArray(mFieldClassificationIds, flags);
        parcel.writeInt(mIconResourceId);
        parcel.writeInt(mServiceDisplayNameResourceId);
        parcel.writeBoolean(mShowFillDialogIcon);
        parcel.writeBoolean(mShowSaveDialogIcon);
        parcel.writeInt(mFlags);
        parcel.writeIntArray(mCancelIds);
        parcel.writeInt(mRequestId);
@@ -1089,6 +1191,11 @@ public final class FillResponse implements Parcelable {
            if (fieldClassifactionIds != null) {
                builder.setFieldClassificationIds(fieldClassifactionIds);
            }

            builder.setIconResourceId(parcel.readInt());
            builder.setServiceDisplayNameResourceId(parcel.readInt());
            builder.setShowFillDialogIcon(parcel.readBoolean());
            builder.setShowSaveDialogIcon(parcel.readBoolean());
            builder.setFlags(parcel.readInt());
            final int[] cancelIds = parcel.createIntArray();
            builder.setPresentationCancelIds(cancelIds);
+63 −7
Original line number Diff line number Diff line
@@ -2651,8 +2651,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                final CharSequence serviceLabel;
                final Drawable serviceIcon;
                synchronized (mLock) {
                    serviceLabel = mService.getServiceLabelLocked();
                    serviceIcon = mService.getServiceIconLocked();
                    serviceIcon = getServiceIcon(response);
                    serviceLabel = getServiceLabel(response);
                }
                if (serviceLabel == null || serviceIcon == null) {
                    wtf(null, "showSaveLocked(): no service label or icon");
@@ -2662,7 +2662,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState

                getUiForShowing().showSaveUi(serviceLabel, serviceIcon,
                        mService.getServicePackageName(), saveInfo, this,
                        mComponentName, this, mPendingSaveUi, isUpdate, mCompatMode);
                        mComponentName, this, mPendingSaveUi, isUpdate, mCompatMode,
                        response.getShowSaveDialogIcon());
                if (client != null) {
                    try {
                        client.setSaveUiState(id, true);
@@ -3600,9 +3601,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
                if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed.");
                return false;
            }

        }

        final Drawable serviceIcon = getServiceIcon();
        Drawable serviceIcon = null;
        synchronized (mLock) {
            serviceIcon = getServiceIcon(response);
        }

        getUiForShowing().showFillDialog(filledId, response, filterText,
                mService.getServicePackageName(), mComponentName, serviceIcon, this,
@@ -3610,13 +3615,64 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
        return true;
    }

    /**
     * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able
     * to be fetched, use the default provider icon instead
     *
     * @return Drawable of the provider icon, if it was able to be fetched. Null otherwise
     */
    @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's
                                   // actually the same object as mLock.
                                   // TODO: Expose mService.mLock or redesign instead.
    private Drawable getServiceIcon() {
        synchronized (mLock) {
            return mService.getServiceIconLocked();
    @GuardedBy("mLock")
    private Drawable getServiceIcon(FillResponse response) {
        Drawable serviceIcon = null;
        // Try to get the custom Icon, if one was passed through FillResponse
        int iconResourceId = response.getIconResourceId();
        if (iconResourceId != 0) {
            serviceIcon = mService.getMaster().getContext().getPackageManager()
                .getDrawable(
                    mService.getServicePackageName(),
                    iconResourceId,
                    null);
        }

        // Custom icon wasn't fetched, use the default package icon instead
        if (serviceIcon == null) {
            serviceIcon = mService.getServiceIconLocked();
        }

        return serviceIcon;
    }

    /**
     * Get the custom label that was passed through FillResponse. If the custom label
     * wasn't able to be fetched, use the default provider icon instead
     *
     * @return Drawable of the provider icon, if it was able to be fetched. Null otherwise
     */
    @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's
                                   // actually the same object as mLock.
                                   // TODO: Expose mService.mLock or redesign instead.
    @GuardedBy("mLock")
    private CharSequence getServiceLabel(FillResponse response) {
        CharSequence serviceLabel = null;
        // Try to get the custom Service name, if one was passed through FillResponse
        int customServiceNameId = response.getServiceDisplayNameResourceId();
        if (customServiceNameId != 0) {
            serviceLabel = mService.getMaster().getContext().getPackageManager()
                .getText(
                    mService.getServicePackageName(),
                    customServiceNameId,
                    null);
        }

        // Custom label wasn't fetched, use the default package name instead
        if (serviceLabel == null) {
            serviceLabel = mService.getServiceLabelLocked();
        }

        return serviceLabel;
    }

    /**
+2 −2
Original line number Diff line number Diff line
@@ -315,7 +315,7 @@ public final class AutoFillUI {
            @Nullable String servicePackageName, @NonNull SaveInfo info,
            @NonNull ValueFinder valueFinder, @NonNull ComponentName componentName,
            @NonNull AutoFillUiCallback callback, @NonNull PendingUi pendingSaveUi,
            boolean isUpdate, boolean compatMode) {
            boolean isUpdate, boolean compatMode, boolean showServiceIcon) {
        if (sVerbose) {
            Slog.v(TAG, "showSaveUi(update=" + isUpdate + ") for " + componentName.toShortString()
                    + ": " + info);
@@ -379,7 +379,7 @@ public final class AutoFillUI {
                public void startIntentSender(IntentSender intentSender, Intent intent) {
                    callback.startIntentSender(intentSender, intent);
                }
            }, mUiModeMgr.isNightMode(), isUpdate, compatMode);
            }, mUiModeMgr.isNightMode(), isUpdate, compatMode, showServiceIcon);
        });
    }

+3 −1
Original line number Diff line number Diff line
@@ -117,7 +117,9 @@ final class DialogFillUi {
        final LayoutInflater inflater = LayoutInflater.from(mContext);
        final View decor = inflater.inflate(R.layout.autofill_fill_dialog, null);

        if (response.getShowFillDialogIcon()) {
            setServiceIcon(decor, serviceIcon);
        }
        setHeader(decor, response);

        mVisibleDatasetsMaxCount = getVisibleDatasetsMaxCount();
Loading