Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.view.autofill; |
| 18 | |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 19 | import static android.view.View.AUTOFILL_TYPE_DATE; |
| 20 | import static android.view.View.AUTOFILL_TYPE_LIST; |
| 21 | import static android.view.View.AUTOFILL_TYPE_TEXT; |
| 22 | import static android.view.View.AUTOFILL_TYPE_TOGGLE; |
Felipe Leme | 9f9ee25 | 2017-04-27 13:56:22 -0700 | [diff] [blame] | 23 | import static android.view.autofill.Helper.sDebug; |
Felipe Leme | a11aadd | 2018-05-31 13:08:32 -0700 | [diff] [blame] | 24 | import static android.view.autofill.Helper.sVerbose; |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 25 | |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 26 | import android.annotation.NonNull; |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 27 | import android.annotation.Nullable; |
Felipe Leme | a11aadd | 2018-05-31 13:08:32 -0700 | [diff] [blame] | 28 | import android.os.Looper; |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 29 | import android.os.Parcel; |
| 30 | import android.os.Parcelable; |
Felipe Leme | a8fce3b | 2017-04-04 14:22:12 -0700 | [diff] [blame] | 31 | import android.text.TextUtils; |
Felipe Leme | a11aadd | 2018-05-31 13:08:32 -0700 | [diff] [blame] | 32 | import android.util.Log; |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 33 | import android.view.View; |
| 34 | |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 35 | import com.android.internal.util.Preconditions; |
| 36 | |
| 37 | import java.util.Objects; |
| 38 | |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 39 | /** |
| 40 | * Abstracts how a {@link View} can be autofilled by an |
| 41 | * {@link android.service.autofill.AutofillService}. |
| 42 | * |
| 43 | * <p>Each {@link AutofillValue} is associated with a {@code type}, as defined by |
| 44 | * {@link View#getAutofillType()}. |
| 45 | */ |
| 46 | public final class AutofillValue implements Parcelable { |
Felipe Leme | a11aadd | 2018-05-31 13:08:32 -0700 | [diff] [blame] | 47 | |
| 48 | private static final String TAG = "AutofillValue"; |
| 49 | |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 50 | private final @View.AutofillType int mType; |
| 51 | private final @NonNull Object mValue; |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 52 | |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 53 | private AutofillValue(@View.AutofillType int type, @NonNull Object value) { |
| 54 | mType = type; |
| 55 | mValue = value; |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | /** |
| 59 | * Gets the value to autofill a text field. |
| 60 | * |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 61 | * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.</p> |
| 62 | * |
| 63 | * @throws IllegalStateException if the value is not a text value |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 64 | */ |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 65 | @NonNull public CharSequence getTextValue() { |
| 66 | Preconditions.checkState(isText(), "value must be a text value, not type=" + mType); |
| 67 | return (CharSequence) mValue; |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * Checks is this is a text value. |
| 72 | * |
| 73 | * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.</p> |
| 74 | */ |
| 75 | public boolean isText() { |
| 76 | return mType == AUTOFILL_TYPE_TEXT; |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Gets the value to autofill a toggable field. |
| 81 | * |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 82 | * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.</p> |
| 83 | * |
| 84 | * @throws IllegalStateException if the value is not a toggle value |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 85 | */ |
| 86 | public boolean getToggleValue() { |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 87 | Preconditions.checkState(isToggle(), "value must be a toggle value, not type=" + mType); |
| 88 | return (Boolean) mValue; |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Checks is this is a toggle value. |
| 93 | * |
| 94 | * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.</p> |
| 95 | */ |
| 96 | public boolean isToggle() { |
| 97 | return mType == AUTOFILL_TYPE_TOGGLE; |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 98 | } |
| 99 | |
| 100 | /** |
| 101 | * Gets the value to autofill a selection list field. |
| 102 | * |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 103 | * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.</p> |
| 104 | * |
| 105 | * @throws IllegalStateException if the value is not a list value |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 106 | */ |
| 107 | public int getListValue() { |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 108 | Preconditions.checkState(isList(), "value must be a list value, not type=" + mType); |
| 109 | return (Integer) mValue; |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Checks is this is a list value. |
| 114 | * |
| 115 | * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.</p> |
| 116 | */ |
| 117 | public boolean isList() { |
| 118 | return mType == AUTOFILL_TYPE_LIST; |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | /** |
| 122 | * Gets the value to autofill a date field. |
| 123 | * |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 124 | * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.</p> |
| 125 | * |
| 126 | * @throws IllegalStateException if the value is not a date value |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 127 | */ |
| 128 | public long getDateValue() { |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 129 | Preconditions.checkState(isDate(), "value must be a date value, not type=" + mType); |
| 130 | return (Long) mValue; |
| 131 | } |
| 132 | |
| 133 | /** |
| 134 | * Checks is this is a date value. |
| 135 | * |
| 136 | * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.</p> |
| 137 | */ |
| 138 | public boolean isDate() { |
| 139 | return mType == AUTOFILL_TYPE_DATE; |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 140 | } |
| 141 | |
Felipe Leme | 82e3793 | 2017-03-10 10:05:56 -0800 | [diff] [blame] | 142 | /** |
| 143 | * Used to define whether a field is empty so it's not sent to service on save. |
| 144 | * |
| 145 | * <p>Only applies to some types, like text. |
| 146 | * |
| 147 | * @hide |
| 148 | */ |
| 149 | public boolean isEmpty() { |
Felipe Leme | 313cf3f | 2017-06-16 22:57:58 +0000 | [diff] [blame] | 150 | return isText() && ((CharSequence) mValue).length() == 0; |
Felipe Leme | 82e3793 | 2017-03-10 10:05:56 -0800 | [diff] [blame] | 151 | } |
| 152 | |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 153 | ///////////////////////////////////// |
| 154 | // Object "contract" methods. // |
| 155 | ///////////////////////////////////// |
| 156 | |
| 157 | @Override |
| 158 | public int hashCode() { |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 159 | return mType + mValue.hashCode(); |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 160 | } |
| 161 | |
| 162 | @Override |
| 163 | public boolean equals(Object obj) { |
| 164 | if (this == obj) return true; |
| 165 | if (obj == null) return false; |
| 166 | if (getClass() != obj.getClass()) return false; |
| 167 | final AutofillValue other = (AutofillValue) obj; |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 168 | |
| 169 | if (mType != other.mType) return false; |
| 170 | |
| 171 | if (isText()) { |
| 172 | return mValue.toString().equals(other.mValue.toString()); |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 173 | } else { |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 174 | return Objects.equals(mValue, other.mValue); |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 175 | } |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 176 | } |
| 177 | |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 178 | @Override |
| 179 | public String toString() { |
Felipe Leme | 9f9ee25 | 2017-04-27 13:56:22 -0700 | [diff] [blame] | 180 | if (!sDebug) return super.toString(); |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 181 | |
Felipe Leme | 9f9ee25 | 2017-04-27 13:56:22 -0700 | [diff] [blame] | 182 | final StringBuilder string = new StringBuilder() |
| 183 | .append("[type=").append(mType) |
| 184 | .append(", value="); |
| 185 | if (isText()) { |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 186 | Helper.appendRedacted(string, (CharSequence) mValue); |
Felipe Leme | 9f9ee25 | 2017-04-27 13:56:22 -0700 | [diff] [blame] | 187 | } else { |
| 188 | string.append(mValue); |
| 189 | } |
| 190 | return string.append(']').toString(); |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 191 | } |
| 192 | |
| 193 | ///////////////////////////////////// |
| 194 | // Parcelable "contract" methods. // |
| 195 | ///////////////////////////////////// |
| 196 | |
| 197 | @Override |
| 198 | public int describeContents() { |
| 199 | return 0; |
| 200 | } |
| 201 | |
| 202 | @Override |
| 203 | public void writeToParcel(Parcel parcel, int flags) { |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 204 | parcel.writeInt(mType); |
| 205 | |
| 206 | switch (mType) { |
| 207 | case AUTOFILL_TYPE_TEXT: |
| 208 | parcel.writeCharSequence((CharSequence) mValue); |
| 209 | break; |
| 210 | case AUTOFILL_TYPE_TOGGLE: |
| 211 | parcel.writeInt((Boolean) mValue ? 1 : 0); |
| 212 | break; |
| 213 | case AUTOFILL_TYPE_LIST: |
| 214 | parcel.writeInt((Integer) mValue); |
| 215 | break; |
| 216 | case AUTOFILL_TYPE_DATE: |
| 217 | parcel.writeLong((Long) mValue); |
| 218 | break; |
| 219 | } |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 220 | } |
| 221 | |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 222 | private AutofillValue(@NonNull Parcel parcel) { |
| 223 | mType = parcel.readInt(); |
| 224 | |
| 225 | switch (mType) { |
| 226 | case AUTOFILL_TYPE_TEXT: |
| 227 | mValue = parcel.readCharSequence(); |
| 228 | break; |
| 229 | case AUTOFILL_TYPE_TOGGLE: |
| 230 | int rawValue = parcel.readInt(); |
| 231 | mValue = rawValue != 0; |
| 232 | break; |
| 233 | case AUTOFILL_TYPE_LIST: |
| 234 | mValue = parcel.readInt(); |
| 235 | break; |
| 236 | case AUTOFILL_TYPE_DATE: |
| 237 | mValue = parcel.readLong(); |
| 238 | break; |
| 239 | default: |
| 240 | throw new IllegalArgumentException("type=" + mType + " not valid"); |
| 241 | } |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 242 | } |
| 243 | |
| 244 | public static final Parcelable.Creator<AutofillValue> CREATOR = |
| 245 | new Parcelable.Creator<AutofillValue>() { |
| 246 | @Override |
| 247 | public AutofillValue createFromParcel(Parcel source) { |
| 248 | return new AutofillValue(source); |
| 249 | } |
| 250 | |
| 251 | @Override |
| 252 | public AutofillValue[] newArray(int size) { |
| 253 | return new AutofillValue[size]; |
| 254 | } |
| 255 | }; |
| 256 | |
| 257 | //////////////////// |
| 258 | // Factory methods // |
| 259 | //////////////////// |
| 260 | |
| 261 | /** |
| 262 | * Creates a new {@link AutofillValue} to autofill a {@link View} representing a text field. |
| 263 | * |
| 264 | * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info. |
Felipe Leme | a11aadd | 2018-05-31 13:08:32 -0700 | [diff] [blame] | 265 | * |
| 266 | * <p><b>Note:</b> This method is not thread safe and can throw an exception if the |
| 267 | * {@code value} is modified by a different thread before it returns. |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 268 | */ |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 269 | public static AutofillValue forText(@Nullable CharSequence value) { |
Felipe Leme | a11aadd | 2018-05-31 13:08:32 -0700 | [diff] [blame] | 270 | if (sVerbose && !Looper.getMainLooper().isCurrentThread()) { |
| 271 | Log.v(TAG, "forText() not called on main thread: " + Thread.currentThread()); |
| 272 | } |
| 273 | |
Felipe Leme | a8fce3b | 2017-04-04 14:22:12 -0700 | [diff] [blame] | 274 | return value == null ? null : new AutofillValue(AUTOFILL_TYPE_TEXT, |
| 275 | TextUtils.trimNoCopySpans(value)); |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 276 | } |
| 277 | |
| 278 | /** |
| 279 | * Creates a new {@link AutofillValue} to autofill a {@link View} representing a toggable |
| 280 | * field. |
| 281 | * |
| 282 | * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info. |
| 283 | */ |
| 284 | public static AutofillValue forToggle(boolean value) { |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 285 | return new AutofillValue(AUTOFILL_TYPE_TOGGLE, value); |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 286 | } |
| 287 | |
| 288 | /** |
| 289 | * Creates a new {@link AutofillValue} to autofill a {@link View} representing a selection |
| 290 | * list. |
| 291 | * |
| 292 | * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info. |
| 293 | */ |
| 294 | public static AutofillValue forList(int value) { |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 295 | return new AutofillValue(AUTOFILL_TYPE_LIST, value); |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 296 | } |
| 297 | |
| 298 | /** |
| 299 | * Creates a new {@link AutofillValue} to autofill a {@link View} representing a date. |
| 300 | * |
| 301 | * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info. |
| 302 | */ |
| 303 | public static AutofillValue forDate(long value) { |
Philip P. Moltmann | 9668903 | 2017-03-09 13:19:55 -0800 | [diff] [blame] | 304 | return new AutofillValue(AUTOFILL_TYPE_DATE, value); |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 305 | } |
| 306 | } |