summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt2
-rw-r--r--api/system-current.txt2
-rw-r--r--api/test-current.txt8
-rw-r--r--core/java/android/service/autofill/CharSequenceTransformation.java82
-rw-r--r--core/java/android/service/autofill/ValueFinder.java2
-rw-r--r--core/java/android/view/autofill/AutofillId.java2
6 files changed, 63 insertions, 35 deletions
diff --git a/api/current.txt b/api/current.txt
index 0d422c5b49bf..ce2a13ecbb89 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36957,7 +36957,7 @@ package android.service.autofill {
}
public static class CharSequenceTransformation.Builder {
- ctor public CharSequenceTransformation.Builder();
+ ctor public CharSequenceTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
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();
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 4cae69ceb7fd..8ba88636d770 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -40038,7 +40038,7 @@ package android.service.autofill {
}
public static class CharSequenceTransformation.Builder {
- ctor public CharSequenceTransformation.Builder();
+ ctor public CharSequenceTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
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();
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 5d2c5574aac5..77e640aa4a93 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -37124,13 +37124,14 @@ package android.service.autofill {
}
public final class CharSequenceTransformation 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.CharSequenceTransformation> CREATOR;
}
public static class CharSequenceTransformation.Builder {
- ctor public CharSequenceTransformation.Builder();
+ ctor public CharSequenceTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
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();
}
@@ -37296,6 +37297,10 @@ package android.service.autofill {
method public static android.service.autofill.Validator or(android.service.autofill.Validator...);
}
+ public abstract interface ValueFinder {
+ method public abstract java.lang.String findByAutofillId(android.view.autofill.AutofillId);
+ }
+
}
package android.service.carrier {
@@ -48263,6 +48268,7 @@ package android.view.animation {
package android.view.autofill {
public final class AutofillId implements android.os.Parcelable {
+ ctor public AutofillId(int);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.autofill.AutofillId> CREATOR;
diff --git a/core/java/android/service/autofill/CharSequenceTransformation.java b/core/java/android/service/autofill/CharSequenceTransformation.java
index 7472aba99c21..9d8345ce7b3f 100644
--- a/core/java/android/service/autofill/CharSequenceTransformation.java
+++ b/core/java/android/service/autofill/CharSequenceTransformation.java
@@ -19,6 +19,7 @@ package android.service.autofill;
import static android.view.autofill.Helper.sDebug;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
@@ -30,6 +31,8 @@ import android.widget.TextView;
import com.android.internal.util.Preconditions;
+import java.util.regex.Pattern;
+
/**
* Replaces a {@link TextView} child of a {@link CustomDescription} with the contents of one or
* more regular expressions (regexs).
@@ -41,24 +44,20 @@ import com.android.internal.util.Preconditions;
* be:
*
* <pre class="prettyprint">
- * new CharSequenceTransformation.Builder()
- * .addField(ccNumberId, "^.*(\\d\\d\\d\\d)$", "...$1")
- * .build();
+ * new CharSequenceTransformation.Builder(ccNumberId, "^.*(\\d\\d\\d\\d)$", "...$1").build();
* </pre>
*
- * <p>But a tranformation that generates a {@code Exp: MM / YYYY} credit expiration date from two
+ * <p>But a transformation 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")
+ * new CharSequenceTransformation.Builder(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;
+ @NonNull private final ArrayMap<AutofillId, Pair<Pattern, String>> mFields;
private CharSequenceTransformation(Builder builder) {
mFields = builder.mFields;
@@ -66,6 +65,7 @@ public final class CharSequenceTransformation extends InternalTransformation imp
/** @hide */
@Override
+ @TestApi
public void apply(@NonNull ValueFinder finder, @NonNull RemoteViews parentTemplate,
int childViewId) {
final StringBuilder converted = new StringBuilder();
@@ -73,14 +73,21 @@ public final class CharSequenceTransformation extends InternalTransformation imp
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 Pair<Pattern, 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);
+ try {
+ // replaceAll throws an exception if the subst is invalid
+ final String convertedValue = regex.first.matcher(value).replaceAll(regex.second);
+ converted.append(convertedValue);
+ } catch (Exception e) {
+ // Do not log full exception to avoid leaks
+ Log.w(TAG, "Cannot apply " + regex.first.pattern() + "->" + regex.second + " to "
+ + "field with autofill id" + id + ": " + e.getClass());
+ }
}
parentTemplate.setCharSequence(childViewId, "setText", converted);
}
@@ -89,18 +96,32 @@ public final class CharSequenceTransformation extends InternalTransformation imp
* Builder for {@link CharSequenceTransformation} objects.
*/
public static class Builder {
- private ArrayMap<AutofillId, Pair<String, String>> mFields;
+ @NonNull private final ArrayMap<AutofillId, Pair<Pattern, String>> mFields =
+ new ArrayMap<>();
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())
+ /**
+ * Creates a new builder and adds the first 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. The pattern will be {@link Pattern#compile
+ * compiled} without setting any flags.
+ * @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).
+ */
+ public Builder(@NonNull AutofillId id, @NonNull String regex, @NonNull String subst) {
+ addField(id, regex, subst);
+ }
/**
* 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.
+ * are used to substitute parts of the value. The pattern will be {@link Pattern#compile
+ * compiled} without setting any flags.
* @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).
*
@@ -108,28 +129,23 @@ public final class CharSequenceTransformation extends InternalTransformation imp
*/
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));
+
+ // Check if the regex is valid
+ Pattern pattern = Pattern.compile(regex);
+
+ mFields.put(id, new Pair<>(pattern, 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);
}
@@ -163,11 +179,11 @@ public final class CharSequenceTransformation extends InternalTransformation imp
final AutofillId[] ids = new AutofillId[size];
final String[] regexs = new String[size];
final String[] substs = new String[size];
- Pair<String, String> pair;
+ Pair<Pattern, String> pair;
for (int i = 0; i < size; i++) {
ids[i] = mFields.keyAt(i);
pair = mFields.valueAt(i);
- regexs[i] = pair.first;
+ regexs[i] = pair.first.pattern();
substs[i] = pair.second;
}
parcel.writeParcelableArray(ids, flags);
@@ -179,16 +195,18 @@ public final class CharSequenceTransformation extends InternalTransformation imp
new Parcelable.Creator<CharSequenceTransformation>() {
@Override
public CharSequenceTransformation createFromParcel(Parcel parcel) {
+ final AutofillId[] ids = parcel.readParcelableArray(null, AutofillId.class);
+ final String[] regexs = parcel.createStringArray();
+ final String[] substs = parcel.createStringArray();
+
// 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();
+ new CharSequenceTransformation.Builder(ids[0], regexs[0], substs[0]);
+
final int size = ids.length;
- for (int i = 0; i < size; i++) {
+ for (int i = 1; i < size; i++) {
builder.addField(ids[i], regexs[i], substs[i]);
}
return builder.build();
diff --git a/core/java/android/service/autofill/ValueFinder.java b/core/java/android/service/autofill/ValueFinder.java
index d02a35890545..1705b7d922aa 100644
--- a/core/java/android/service/autofill/ValueFinder.java
+++ b/core/java/android/service/autofill/ValueFinder.java
@@ -17,6 +17,7 @@ package android.service.autofill;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.view.autofill.AutofillId;
/**
@@ -24,6 +25,7 @@ import android.view.autofill.AutofillId;
*
* @hide
*/
+@TestApi
public interface ValueFinder {
/**
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 1cee529b9d62..5ce2421aac87 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -15,6 +15,7 @@
*/
package android.view.autofill;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;
@@ -29,6 +30,7 @@ public final class AutofillId implements Parcelable {
private final int mVirtualId;
/** @hide */
+ @TestApi
public AutofillId(int id) {
mVirtual = false;
mViewId = id;