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

Commit 979013d0 authored by Felipe Leme's avatar Felipe Leme
Browse files

Initial implementation of the new Save APIs.

Test: manual verification with sample app (CTS tests coming later)

Bug: 62534917

Change-Id: I085a9c933bb5e8316d673976e059e13abd7098e5
parent 5526fa1c
Loading
Loading
Loading
Loading
+63 −0
Original line number Original line Diff line number Diff line
@@ -36907,6 +36907,30 @@ package android.service.autofill {
    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
  }
  }
  public final class CharSequenceTransformation implements android.os.Parcelable {
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.CharSequenceTransformation> CREATOR;
  }
  public static class CharSequenceTransformation.Builder {
    ctor public CharSequenceTransformation.Builder();
    method public android.service.autofill.CharSequenceTransformation.Builder addField(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
    method public android.service.autofill.CharSequenceTransformation build();
  }
  public final class CustomDescription implements android.os.Parcelable {
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.CustomDescription> CREATOR;
  }
  public static class CustomDescription.Builder {
    ctor public CustomDescription.Builder(android.widget.RemoteViews);
    method public android.service.autofill.CustomDescription.Builder addChild(int, android.service.autofill.Transformation);
    method public android.service.autofill.CustomDescription build();
  }
  public final class Dataset implements android.os.Parcelable {
  public final class Dataset implements android.os.Parcelable {
    method public int describeContents();
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    method public void writeToParcel(android.os.Parcel, int);
@@ -36980,6 +37004,25 @@ package android.service.autofill {
    method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
    method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
  }
  }
  public final class ImageTransformation implements android.os.Parcelable {
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.ImageTransformation> CREATOR;
  }
  public static class ImageTransformation.Builder {
    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId);
    method public android.service.autofill.ImageTransformation.Builder addOption(java.lang.String, int);
    method public android.service.autofill.ImageTransformation build();
  }
  public final class LuhnChecksumValidator implements android.os.Parcelable {
    ctor public LuhnChecksumValidator(android.view.autofill.AutofillId...);
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR;
  }
  public final class SaveCallback {
  public final class SaveCallback {
    method public void onFailure(java.lang.CharSequence);
    method public void onFailure(java.lang.CharSequence);
    method public void onSuccess();
    method public void onSuccess();
@@ -37003,10 +37046,12 @@ package android.service.autofill {
  public static final class SaveInfo.Builder {
  public static final class SaveInfo.Builder {
    ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
    ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
    method public android.service.autofill.SaveInfo build();
    method public android.service.autofill.SaveInfo build();
    method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
    method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
    method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
    method public android.service.autofill.SaveInfo.Builder setFlags(int);
    method public android.service.autofill.SaveInfo.Builder setFlags(int);
    method public android.service.autofill.SaveInfo.Builder setNegativeAction(int, android.content.IntentSender);
    method public android.service.autofill.SaveInfo.Builder setNegativeAction(int, android.content.IntentSender);
    method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
    method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
    method public android.service.autofill.SaveInfo.Builder setValidator(android.service.autofill.Validator);
  }
  }
  public final class SaveRequest implements android.os.Parcelable {
  public final class SaveRequest implements android.os.Parcelable {
@@ -37017,6 +37062,24 @@ package android.service.autofill {
    field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
    field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
  }
  }
  public final class SimpleRegexValidator implements android.os.Parcelable {
    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.lang.String);
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
  }
  public abstract interface Transformation {
  }
  public abstract interface Validator {
  }
  public final class Validators {
    method public static android.service.autofill.Validator and(android.service.autofill.Validator...);
    method public static android.service.autofill.Validator or(android.service.autofill.Validator...);
  }
}
}
package android.service.carrier {
package android.service.carrier {
+63 −0
Original line number Original line Diff line number Diff line
@@ -39983,6 +39983,30 @@ package android.service.autofill {
    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
  }
  }
  public final class CharSequenceTransformation implements android.os.Parcelable {
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.CharSequenceTransformation> CREATOR;
  }
  public static class CharSequenceTransformation.Builder {
    ctor public CharSequenceTransformation.Builder();
    method public android.service.autofill.CharSequenceTransformation.Builder addField(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
    method public android.service.autofill.CharSequenceTransformation build();
  }
  public final class CustomDescription implements android.os.Parcelable {
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.CustomDescription> CREATOR;
  }
  public static class CustomDescription.Builder {
    ctor public CustomDescription.Builder(android.widget.RemoteViews);
    method public android.service.autofill.CustomDescription.Builder addChild(int, android.service.autofill.Transformation);
    method public android.service.autofill.CustomDescription build();
  }
  public final class Dataset implements android.os.Parcelable {
  public final class Dataset implements android.os.Parcelable {
    method public int describeContents();
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    method public void writeToParcel(android.os.Parcel, int);
@@ -40056,6 +40080,25 @@ package android.service.autofill {
    method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
    method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
  }
  }
  public final class ImageTransformation implements android.os.Parcelable {
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.ImageTransformation> CREATOR;
  }
  public static class ImageTransformation.Builder {
    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId);
    method public android.service.autofill.ImageTransformation.Builder addOption(java.lang.String, int);
    method public android.service.autofill.ImageTransformation build();
  }
  public final class LuhnChecksumValidator implements android.os.Parcelable {
    ctor public LuhnChecksumValidator(android.view.autofill.AutofillId...);
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR;
  }
  public final class SaveCallback {
  public final class SaveCallback {
    method public void onFailure(java.lang.CharSequence);
    method public void onFailure(java.lang.CharSequence);
    method public void onSuccess();
    method public void onSuccess();
@@ -40079,10 +40122,12 @@ package android.service.autofill {
  public static final class SaveInfo.Builder {
  public static final class SaveInfo.Builder {
    ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
    ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
    method public android.service.autofill.SaveInfo build();
    method public android.service.autofill.SaveInfo build();
    method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
    method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
    method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
    method public android.service.autofill.SaveInfo.Builder setFlags(int);
    method public android.service.autofill.SaveInfo.Builder setFlags(int);
    method public android.service.autofill.SaveInfo.Builder setNegativeAction(int, android.content.IntentSender);
    method public android.service.autofill.SaveInfo.Builder setNegativeAction(int, android.content.IntentSender);
    method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
    method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
    method public android.service.autofill.SaveInfo.Builder setValidator(android.service.autofill.Validator);
  }
  }
  public final class SaveRequest implements android.os.Parcelable {
  public final class SaveRequest implements android.os.Parcelable {
@@ -40093,6 +40138,24 @@ package android.service.autofill {
    field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
    field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
  }
  }
  public final class SimpleRegexValidator implements android.os.Parcelable {
    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.lang.String);
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
  }
  public abstract interface Transformation {
  }
  public abstract interface Validator {
  }
  public final class Validators {
    method public static android.service.autofill.Validator and(android.service.autofill.Validator...);
    method public static android.service.autofill.Validator or(android.service.autofill.Validator...);
  }
}
}
package android.service.carrier {
package android.service.carrier {
+63 −0
Original line number Original line Diff line number Diff line
@@ -37074,6 +37074,30 @@ package android.service.autofill {
    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
  }
  }
  public final class CharSequenceTransformation implements android.os.Parcelable {
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.CharSequenceTransformation> CREATOR;
  }
  public static class CharSequenceTransformation.Builder {
    ctor public CharSequenceTransformation.Builder();
    method public android.service.autofill.CharSequenceTransformation.Builder addField(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
    method public android.service.autofill.CharSequenceTransformation build();
  }
  public final class CustomDescription implements android.os.Parcelable {
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.CustomDescription> CREATOR;
  }
  public static class CustomDescription.Builder {
    ctor public CustomDescription.Builder(android.widget.RemoteViews);
    method public android.service.autofill.CustomDescription.Builder addChild(int, android.service.autofill.Transformation);
    method public android.service.autofill.CustomDescription build();
  }
  public final class Dataset implements android.os.Parcelable {
  public final class Dataset implements android.os.Parcelable {
    method public int describeContents();
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    method public void writeToParcel(android.os.Parcel, int);
@@ -37147,6 +37171,25 @@ package android.service.autofill {
    method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
    method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
  }
  }
  public final class ImageTransformation implements android.os.Parcelable {
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.ImageTransformation> CREATOR;
  }
  public static class ImageTransformation.Builder {
    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId);
    method public android.service.autofill.ImageTransformation.Builder addOption(java.lang.String, int);
    method public android.service.autofill.ImageTransformation build();
  }
  public final class LuhnChecksumValidator implements android.os.Parcelable {
    ctor public LuhnChecksumValidator(android.view.autofill.AutofillId...);
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR;
  }
  public final class SaveCallback {
  public final class SaveCallback {
    method public void onFailure(java.lang.CharSequence);
    method public void onFailure(java.lang.CharSequence);
    method public void onSuccess();
    method public void onSuccess();
@@ -37170,10 +37213,12 @@ package android.service.autofill {
  public static final class SaveInfo.Builder {
  public static final class SaveInfo.Builder {
    ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
    ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
    method public android.service.autofill.SaveInfo build();
    method public android.service.autofill.SaveInfo build();
    method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
    method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
    method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
    method public android.service.autofill.SaveInfo.Builder setFlags(int);
    method public android.service.autofill.SaveInfo.Builder setFlags(int);
    method public android.service.autofill.SaveInfo.Builder setNegativeAction(int, android.content.IntentSender);
    method public android.service.autofill.SaveInfo.Builder setNegativeAction(int, android.content.IntentSender);
    method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
    method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
    method public android.service.autofill.SaveInfo.Builder setValidator(android.service.autofill.Validator);
  }
  }
  public final class SaveRequest implements android.os.Parcelable {
  public final class SaveRequest implements android.os.Parcelable {
@@ -37184,6 +37229,24 @@ package android.service.autofill {
    field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
    field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
  }
  }
  public final class SimpleRegexValidator implements android.os.Parcelable {
    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.lang.String);
    method public int describeContents();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
  }
  public abstract interface Transformation {
  }
  public abstract interface Validator {
  }
  public final class Validators {
    method public static android.service.autofill.Validator and(android.service.autofill.Validator...);
    method public static android.service.autofill.Validator or(android.service.autofill.Validator...);
  }
}
}
package android.service.carrier {
package android.service.carrier {
+202 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.service.autofill;

import static android.view.autofill.Helper.sDebug;

import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.view.autofill.AutofillId;
import android.widget.RemoteViews;
import android.widget.TextView;

import com.android.internal.util.Preconditions;

/**
 * Replaces a {@link TextView} child of a {@link CustomDescription} with the contents of one or
 * more regular expressions (regexs).
 *
 * <p>When it contains more than one field, the fields that match their regex are added to the
 * overall transformation result.
 *
 * <p>For example, a transformation to mask a credit card number contained in just one field would
 * be:
 *
 * <pre class="prettyprint">
 * new CharSequenceTransformation.Builder()
 *   .addField(ccNumberId, "^.*(\\d\\d\\d\\d)$", "...$1")
 *   .build();
 * </pre>
 *
 * <p>But a tranformation that generates a {@code Exp: MM / YYYY} credit expiration date from two
 * fields (month and year) would be:
 *
 * <pre class="prettyprint">
 * new CharSequenceTransformation.Builder()
 *   .addField(ccExpMonthId, "^(\\d\\d)$", "Exp: $1")
 *   .addField(ccExpYearId, "^(\\d\\d\\d\\d)$", " / $1");
 * </pre>
 */
//TODO(b/62534917): add unit tests
public final class CharSequenceTransformation extends InternalTransformation implements Parcelable {
    private static final String TAG = "CharSequenceTransformation";
    private final ArrayMap<AutofillId, Pair<String, String>> mFields;

    private CharSequenceTransformation(Builder builder) {
        mFields = builder.mFields;
    }

    /** @hide */
    @Override
    public void apply(@NonNull ValueFinder finder, @NonNull RemoteViews parentTemplate,
            int childViewId) {
        final StringBuilder converted = new StringBuilder();
        final int size = mFields.size();
        if (sDebug) Log.d(TAG, size + " multiple fields on id " + childViewId);
        for (int i = 0; i < size; i++) {
            final AutofillId id = mFields.keyAt(i);
            final Pair<String, String> regex = mFields.valueAt(i);
            final String value = finder.findByAutofillId(id);
            if (value == null) {
                Log.w(TAG, "No value for id " + id);
                return;
            }
            final String convertedValue = value.replaceAll(regex.first, regex.second);
            converted.append(convertedValue);
        }
        parentTemplate.setCharSequence(childViewId, "setText", converted);
    }

    /**
     * Builder for {@link CharSequenceTransformation} objects.
     */
    public static class Builder {
        private ArrayMap<AutofillId, Pair<String, String>> mFields;
        private boolean mDestroyed;

        //TODO(b/62534917): add constructor that takes a field so we force it to have at least one
        // (and then remove the check for empty from build())

        /**
         * Adds the transformed contents of a field to the overall result of this transformation.
         *
         * @param id id of the screen field.
         * @param regex regular expression with groups (delimited by {@code (} and {@code (}) that
         * are used to substitute parts of the value.
         * @param subst the string that substitutes the matched regex, using {@code $} for
         * group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
         *
         * @return this builder.
         */
        public Builder addField(@NonNull AutofillId id, @NonNull String regex,
                @NonNull String subst) {
            //TODO(b/62534917): throw exception if regex /subts are invalid
            throwIfDestroyed();
            Preconditions.checkNotNull(id);
            Preconditions.checkNotNull(regex);
            Preconditions.checkNotNull(subst);
            if (mFields == null) {
                mFields = new ArrayMap<>();
            }
            mFields.put(id, new Pair<>(regex, subst));
            return this;
        }

        /**
         * Creates a new {@link CharSequenceTransformation} instance.
         *
         * @throws IllegalStateException if no call to {@link #addField(AutofillId, String, String)}
         * was made.
         */
        public CharSequenceTransformation build() {
            throwIfDestroyed();
            Preconditions.checkState(mFields != null && !mFields.isEmpty(),
                    "Must add at least one field");
            mDestroyed = true;
            return new CharSequenceTransformation(this);
        }

        private void throwIfDestroyed() {
            Preconditions.checkState(!mDestroyed, "Already called build()");
        }
    }

    /////////////////////////////////////
    // Object "contract" methods. //
    /////////////////////////////////////
    @Override
    public String toString() {
        if (!sDebug) return super.toString();

        return "MultipleViewsCharSequenceTransformation: [fields=" + mFields + "]";
    }

    /////////////////////////////////////
    // Parcelable "contract" methods. //
    /////////////////////////////////////
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        final int size = mFields.size();
        final AutofillId[] ids = new AutofillId[size];
        final String[] regexs = new String[size];
        final String[] substs = new String[size];
        Pair<String, String> pair;
        for (int i = 0; i < size; i++) {
            ids[i] = mFields.keyAt(i);
            pair = mFields.valueAt(i);
            regexs[i] = pair.first;
            substs[i] = pair.second;
        }
        parcel.writeParcelableArray(ids, flags);
        parcel.writeStringArray(regexs);
        parcel.writeStringArray(substs);
    }

    public static final Parcelable.Creator<CharSequenceTransformation> CREATOR =
            new Parcelable.Creator<CharSequenceTransformation>() {
        @Override
        public CharSequenceTransformation createFromParcel(Parcel parcel) {
            // Always go through the builder to ensure the data ingested by
            // the system obeys the contract of the builder to avoid attacks
            // using specially crafted parcels.
            final CharSequenceTransformation.Builder builder =
                    new CharSequenceTransformation.Builder();
            final AutofillId[] ids = parcel.readParcelableArray(null, AutofillId.class);
            final String[] regexs = parcel.createStringArray();
            final String[] substs = parcel.createStringArray();
            final int size = ids.length;
            for (int i = 0; i < size; i++) {
                builder.addField(ids[i], regexs[i], substs[i]);
            }
            return builder.build();
        }

        @Override
        public CharSequenceTransformation[] newArray(int size) {
            return new CharSequenceTransformation[size];
        }
    };
}
+218 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading