Make it clear which type of AutoFillValue is set
throw and handle errors if the wrong value is set for a view
Test: android.autofillservice.cts.AutofillValueTest
Change-Id: Ida80da7913a210bede6c47d6b7a6f215a012a84c
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 1f2ed00..1132159 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1012,9 +1012,8 @@
mAutofillValue = value;
// TODO(b/33197203, b/33802548): decide whether to set text as well (so it would work
// with "legacy" views) or just the autofill value
- final CharSequence text = value.getTextValue();
- if (text != null) {
- mText.mText = text;
+ if (value.isText()) {
+ mText.mText = value.getTextValue();
}
}
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index 0c7620e..9babb59 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -16,13 +16,22 @@
package android.view.autofill;
+import static android.view.View.AUTOFILL_TYPE_DATE;
+import static android.view.View.AUTOFILL_TYPE_LIST;
+import static android.view.View.AUTOFILL_TYPE_TEXT;
+import static android.view.View.AUTOFILL_TYPE_TOGGLE;
import static android.view.autofill.Helper.DEBUG;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
/**
* Abstracts how a {@link View} can be autofilled by an
* {@link android.service.autofill.AutofillService}.
@@ -31,52 +40,96 @@
* {@link View#getAutofillType()}.
*/
public final class AutofillValue implements Parcelable {
- private final String mText;
- private final int mListIndex;
- private final boolean mToggle;
- private final long mDate;
+ private final @View.AutofillType int mType;
+ private final @NonNull Object mValue;
- private AutofillValue(CharSequence text, int listIndex, boolean toggle, long date) {
- mText = (text == null) ? null : text.toString();
- mListIndex = listIndex;
- mToggle = toggle;
- mDate = date;
+ private AutofillValue(@View.AutofillType int type, @NonNull Object value) {
+ mType = type;
+ mValue = value;
}
/**
* Gets the value to autofill a text field.
*
- * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.</p>
+ *
+ * @throws IllegalStateException if the value is not a text value
*/
- public CharSequence getTextValue() {
- return mText;
+ @NonNull public CharSequence getTextValue() {
+ Preconditions.checkState(isText(), "value must be a text value, not type=" + mType);
+ return (CharSequence) mValue;
+ }
+
+ /**
+ * Checks is this is a text value.
+ *
+ * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.</p>
+ */
+ public boolean isText() {
+ return mType == AUTOFILL_TYPE_TEXT;
}
/**
* Gets the value to autofill a toggable field.
*
- * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.</p>
+ *
+ * @throws IllegalStateException if the value is not a toggle value
*/
public boolean getToggleValue() {
- return mToggle;
+ Preconditions.checkState(isToggle(), "value must be a toggle value, not type=" + mType);
+ return (Boolean) mValue;
+ }
+
+ /**
+ * Checks is this is a toggle value.
+ *
+ * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.</p>
+ */
+ public boolean isToggle() {
+ return mType == AUTOFILL_TYPE_TOGGLE;
}
/**
* Gets the value to autofill a selection list field.
*
- * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.</p>
+ *
+ * @throws IllegalStateException if the value is not a list value
*/
public int getListValue() {
- return mListIndex;
+ Preconditions.checkState(isList(), "value must be a list value, not type=" + mType);
+ return (Integer) mValue;
+ }
+
+ /**
+ * Checks is this is a list value.
+ *
+ * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.</p>
+ */
+ public boolean isList() {
+ return mType == AUTOFILL_TYPE_LIST;
}
/**
* Gets the value to autofill a date field.
*
- * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.</p>
+ *
+ * @throws IllegalStateException if the value is not a date value
*/
public long getDateValue() {
- return mDate;
+ Preconditions.checkState(isDate(), "value must be a date value, not type=" + mType);
+ return (Long) mValue;
+ }
+
+ /**
+ * Checks is this is a date value.
+ *
+ * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.</p>
+ */
+ public boolean isDate() {
+ return mType == AUTOFILL_TYPE_DATE;
}
/////////////////////////////////////
@@ -85,13 +138,7 @@
@Override
public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((mText == null) ? 0 : mText.hashCode());
- result = prime * result + mListIndex;
- result = prime * result + (mToggle ? 1231 : 1237);
- result = prime * result + (int) (mDate ^ (mDate >>> 32));
- return result;
+ return mType + mValue.hashCode();
}
@Override
@@ -100,32 +147,31 @@
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final AutofillValue other = (AutofillValue) obj;
- if (mText == null) {
- if (other.mText != null) return false;
+
+ if (mType != other.mType) return false;
+
+ if (isText()) {
+ return mValue.toString().equals(other.mValue.toString());
} else {
- if (!mText.equals(other.mText)) return false;
+ return Objects.equals(mValue, other.mValue);
}
- if (mListIndex != other.mListIndex) return false;
- if (mToggle != other.mToggle) return false;
- if (mDate != other.mDate) return false;
- return true;
}
/** @hide */
public String coerceToString() {
// TODO(b/33197203): How can we filter on toggles or list values?
- return mText;
+ return mValue.toString();
}
@Override
public String toString() {
if (!DEBUG) return super.toString();
- if (mText != null) {
- return mText.length() + "_chars";
+ if (isText()) {
+ return ((CharSequence) mValue).length() + "_chars";
}
- return "[l=" + mListIndex + ", t=" + mToggle + ", d=" + mDate + "]";
+ return "[type=" + mType + ", value=" + mValue + "]";
}
/////////////////////////////////////
@@ -139,17 +185,44 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeString(mText);
- parcel.writeInt(mListIndex);
- parcel.writeInt(mToggle ? 1 : 0);
- parcel.writeLong(mDate);
+ parcel.writeInt(mType);
+
+ switch (mType) {
+ case AUTOFILL_TYPE_TEXT:
+ parcel.writeCharSequence((CharSequence) mValue);
+ break;
+ case AUTOFILL_TYPE_TOGGLE:
+ parcel.writeInt((Boolean) mValue ? 1 : 0);
+ break;
+ case AUTOFILL_TYPE_LIST:
+ parcel.writeInt((Integer) mValue);
+ break;
+ case AUTOFILL_TYPE_DATE:
+ parcel.writeLong((Long) mValue);
+ break;
+ }
}
- private AutofillValue(Parcel parcel) {
- mText = parcel.readString();
- mListIndex = parcel.readInt();
- mToggle = parcel.readInt() == 1;
- mDate = parcel.readLong();
+ private AutofillValue(@NonNull Parcel parcel) {
+ mType = parcel.readInt();
+
+ switch (mType) {
+ case AUTOFILL_TYPE_TEXT:
+ mValue = parcel.readCharSequence();
+ break;
+ case AUTOFILL_TYPE_TOGGLE:
+ int rawValue = parcel.readInt();
+ mValue = rawValue != 0;
+ break;
+ case AUTOFILL_TYPE_LIST:
+ mValue = parcel.readInt();
+ break;
+ case AUTOFILL_TYPE_DATE:
+ mValue = parcel.readLong();
+ break;
+ default:
+ throw new IllegalArgumentException("type=" + mType + " not valid");
+ }
}
public static final Parcelable.Creator<AutofillValue> CREATOR =
@@ -175,9 +248,8 @@
* <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
*/
// TODO(b/33197203): use cache
- @Nullable
public static AutofillValue forText(@Nullable CharSequence value) {
- return value == null ? null : new AutofillValue(value, 0, false, 0);
+ return value == null ? null : new AutofillValue(AUTOFILL_TYPE_TEXT, value);
}
/**
@@ -187,7 +259,7 @@
* <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
*/
public static AutofillValue forToggle(boolean value) {
- return new AutofillValue(null, 0, value, 0);
+ return new AutofillValue(AUTOFILL_TYPE_TOGGLE, value);
}
/**
@@ -197,7 +269,7 @@
* <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
*/
public static AutofillValue forList(int value) {
- return new AutofillValue(null, value, false, 0);
+ return new AutofillValue(AUTOFILL_TYPE_LIST, value);
}
/**
@@ -206,6 +278,6 @@
* <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
*/
public static AutofillValue forDate(long value) {
- return new AutofillValue(null, 0, false, value);
+ return new AutofillValue(AUTOFILL_TYPE_DATE, value);
}
}
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index 053574f..020e80a 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -23,6 +23,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
@@ -38,6 +39,8 @@
* @attr ref android.R.styleable#AbsSpinner_entries
*/
public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
+ private static final String LOG_TAG = AbsSpinner.class.getSimpleName();
+
SpinnerAdapter mAdapter;
int mHeightMeasureSpec;
@@ -514,8 +517,11 @@
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
- final int position = value.getListValue();
- setSelection(position);
+ if (value.isList()) {
+ setSelection(value.getListValue());
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+ }
}
@Override
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index d246405..ae58e2a 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -28,6 +28,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Gravity;
import android.view.SoundEffectConstants;
import android.view.ViewDebug;
@@ -55,6 +56,7 @@
* </p>
*/
public abstract class CompoundButton extends Button implements Checkable {
+ private static final String LOG_TAG = CompoundButton.class.getSimpleName();
private boolean mChecked;
private boolean mBroadcasting;
@@ -585,7 +587,11 @@
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
- setChecked(value.getToggleValue());
+ if (value.isToggle()) {
+ setChecked(value.getToggleValue());
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+ }
}
@Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 31a88d4..f63573f 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -29,6 +29,7 @@
import android.os.Parcelable;
import android.text.format.DateUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewStructure;
@@ -83,6 +84,8 @@
*/
@Widget
public class DatePicker extends FrameLayout {
+ private static final String LOG_TAG = DatePicker.class.getSimpleName();
+
/**
* Presentation mode for the Holo-style date picker that uses a set of
* {@link android.widget.NumberPicker}s.
@@ -775,7 +778,11 @@
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
- mDelegate.updateDate(value.getDateValue());
+ if (value.isDate()) {
+ mDelegate.updateDate(value.getDateValue());
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+ }
}
@Override
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index dc9976d..3dba3f9 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -55,6 +55,7 @@
*
*/
public class RadioGroup extends LinearLayout {
+ private static final String LOG_TAG = RadioGroup.class.getSimpleName();
// holds the checked id; the selection is empty by default
private int mCheckedId = -1;
@@ -428,7 +429,14 @@
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
- final int index = value.getListValue();
+ int index;
+ if (value.isList()) {
+ index = value.getListValue();
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+ return;
+ }
+
final View child = getChildAt(index);
if (child == null) {
Log.w(VIEW_LOG_TAG, "RadioGroup.autoFill(): no child with index " + index);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d591316f..65c7bd1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10014,10 +10014,12 @@
@Override
public void autofill(AutofillValue value) {
- final CharSequence text = value.getTextValue();
-
- if (text != null && isTextEditable()) {
- setText(text, mBufferType, true, 0);
+ if (value.isText()) {
+ if (isTextEditable()) {
+ setText(value.getTextValue(), mBufferType, true, 0);
+ }
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
}
}
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 9825f1e..cfa78b5 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -27,6 +27,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.MathUtils;
import android.view.View;
import android.view.ViewStructure;
@@ -53,6 +54,8 @@
*/
@Widget
public class TimePicker extends FrameLayout {
+ private static final String LOG_TAG = TimePicker.class.getSimpleName();
+
/**
* Presentation mode for the Holo-style time picker that uses a set of
* {@link android.widget.NumberPicker}s.
@@ -530,7 +533,11 @@
public void autofill(AutofillValue value) {
if (!isEnabled()) return;
- mDelegate.setDate(value.getDateValue());
+ if (value.isDate()) {
+ mDelegate.setDate(value.getDateValue());
+ } else {
+ Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+ }
}
@Override