diff options
| -rw-r--r-- | api/current.txt | 2 | ||||
| -rw-r--r-- | api/system-current.txt | 2 | ||||
| -rw-r--r-- | api/test-current.txt | 3 | ||||
| -rw-r--r-- | core/java/android/service/autofill/ImageTransformation.java | 80 |
4 files changed, 45 insertions, 42 deletions
diff --git a/api/current.txt b/api/current.txt index ce2a13ecbb89..5a2498d48e9e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -37054,7 +37054,7 @@ package android.service.autofill { } public static class ImageTransformation.Builder { - ctor public ImageTransformation.Builder(android.view.autofill.AutofillId); + ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, int); method public android.service.autofill.ImageTransformation.Builder addOption(java.lang.String, int); method public android.service.autofill.ImageTransformation build(); } diff --git a/api/system-current.txt b/api/system-current.txt index 8ba88636d770..970877f0ee4c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -40135,7 +40135,7 @@ package android.service.autofill { } public static class ImageTransformation.Builder { - ctor public ImageTransformation.Builder(android.view.autofill.AutofillId); + ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, int); method public android.service.autofill.ImageTransformation.Builder addOption(java.lang.String, int); method public android.service.autofill.ImageTransformation build(); } diff --git a/api/test-current.txt b/api/test-current.txt index 77e640aa4a93..e704b8cd91c9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -37222,13 +37222,14 @@ package android.service.autofill { } public final class ImageTransformation implements android.os.Parcelable { + method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int); 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); + ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, int); method public android.service.autofill.ImageTransformation.Builder addOption(java.lang.String, int); method public android.service.autofill.ImageTransformation build(); } diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java index 9f6eedc8b3a2..67efe13139dd 100644 --- a/core/java/android/service/autofill/ImageTransformation.java +++ b/core/java/android/service/autofill/ImageTransformation.java @@ -18,7 +18,9 @@ package android.service.autofill; import static android.view.autofill.Helper.sDebug; +import android.annotation.DrawableRes; import android.annotation.NonNull; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; @@ -29,6 +31,8 @@ import android.widget.RemoteViews; import com.android.internal.util.Preconditions; +import java.util.regex.Pattern; + /** * Replaces the content of a child {@link ImageView} of a * {@link RemoteViews presentation template} with the first image that matches a regular expression @@ -37,26 +41,20 @@ import com.android.internal.util.Preconditions; * <p>Typically used to display credit card logos. Example: * * <pre class="prettyprint"> - * new ImageTransformation.Builder(ccNumberId) - * .addOption("^4815.*$", R.drawable.ic_credit_card_logo1) + * new ImageTransformation.Builder(ccNumberId, "^4815.*$", R.drawable.ic_credit_card_logo1) * .addOption("^1623.*$", R.drawable.ic_credit_card_logo2) * .addOption("^42.*$", R.drawable.ic_credit_card_logo3) * .build(); * </pre> * * <p>There is no imposed limit in the number of options, but keep in mind that regexs are - * expensive to evaluate, so try to: - * <ul> - * <li>Use the minimum number of regex per image. - * <li>Add the most common images first. - * </ul> + * expensive to evaluate, so use the minimum number of regexs. */ -//TODO(b/62534917): add unit tests public final class ImageTransformation extends InternalTransformation implements Parcelable { private static final String TAG = "ImageTransformation"; private final AutofillId mId; - private final ArrayMap<String, Integer> mOptions; + private final ArrayMap<Pattern, Integer> mOptions; private ImageTransformation(Builder builder) { mId = builder.mId; @@ -64,6 +62,7 @@ public final class ImageTransformation extends InternalTransformation implements } /** @hide */ + @TestApi @Override public void apply(@NonNull ValueFinder finder, @NonNull RemoteViews parentTemplate, int childViewId) { @@ -79,8 +78,8 @@ public final class ImageTransformation extends InternalTransformation implements } for (int i = 0; i < size; i++) { - final String regex = mOptions.keyAt(i); - if (value.matches(regex)) { + final Pattern regex = mOptions.keyAt(i); + if (regex.matcher(value).matches()) { Log.d(TAG, "Found match at " + i + ": " + regex); parentTemplate.setImageViewResource(childViewId, mOptions.valueAt(i)); return; @@ -94,19 +93,22 @@ public final class ImageTransformation extends InternalTransformation implements */ public static class Builder { private final AutofillId mId; - private ArrayMap<String, Integer> mOptions; + private final ArrayMap<Pattern, Integer> mOptions = new ArrayMap<>(); private boolean mDestroyed; /** - * Default constructor. + * Create a new builder for a autofill id and add a first option. * * @param id id of the screen field that will be used to evaluate whether the image should * be used. + * @param regex regular expression defining what should be matched to use this image. + * @param resId resource id of the image (in the autofill service's package). The + * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id. */ - //TODO(b/62534917): add a regex/resid so we force it to have at least one - // (and then remove the check for empty from build()) - public Builder(@NonNull AutofillId id) { + public Builder(@NonNull AutofillId id, @NonNull String regex, @DrawableRes int resId) { mId = Preconditions.checkNotNull(id); + + addOption(regex, resId); } /** @@ -118,13 +120,15 @@ public final class ImageTransformation extends InternalTransformation implements * * @return this build */ - public Builder addOption(String regex, int resId) { - //TODO(b/62534917): throw exception if regex / resId are invalid + public Builder addOption(@NonNull String regex, @DrawableRes int resId) { throwIfDestroyed(); - if (mOptions == null) { - mOptions = new ArrayMap<>(); - } - mOptions.put(regex, resId); + + Preconditions.checkArgument(resId != 0); + + // Check regex + Pattern pattern = Pattern.compile(regex); + + mOptions.put(pattern, resId); return this; } @@ -163,19 +167,15 @@ public final class ImageTransformation extends InternalTransformation implements public int describeContents() { return 0; } - @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeParcelable(mId, flags); - if (mOptions == null) { - parcel.writeStringArray(null); - return; - } + final int size = mOptions.size(); final String[] regexs = new String[size]; final int[] resIds = new int[size]; for (int i = 0; i < size; i++) { - regexs[i] = mOptions.keyAt(i); + regexs[i] = mOptions.keyAt(i).pattern(); resIds[i] = mOptions.valueAt(i); } parcel.writeStringArray(regexs); @@ -186,19 +186,21 @@ public final class ImageTransformation extends InternalTransformation implements new Parcelable.Creator<ImageTransformation>() { @Override public ImageTransformation 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 ImageTransformation.Builder builder = - new ImageTransformation.Builder(parcel.readParcelable(null)); + final AutofillId id = parcel.readParcelable(null); + final String[] regexs = parcel.createStringArray(); - if (regexs != null) { - final int[] resIds = parcel.createIntArray(); - final int size = regexs.length; - for (int i = 0; i < size; i++) { - builder.addOption(regexs[i], resIds[i]); - } + final int[] resIds = parcel.createIntArray(); + + // 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 ImageTransformation.Builder builder = new ImageTransformation.Builder(id, + regexs[0], resIds[0]); + + final int size = regexs.length; + for (int i = 1; i < size; i++) { + builder.addOption(regexs[i], resIds[i]); } + return builder.build(); } |