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 package android.view.autofill; 17 18 import static android.service.autofill.Flags.FLAG_AUTOFILL_W_METRICS; 19 20 import android.annotation.FlaggedApi; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.TestApi; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.view.View; 27 28 import java.util.Objects; 29 30 /** 31 * A unique identifier for an autofill node inside an {@link android.app.Activity}. 32 */ 33 public final class AutofillId implements Parcelable { 34 35 /** @hide */ 36 public static final int NO_SESSION = 0; 37 38 private static final int FLAG_IS_VIRTUAL_INT = 0x1; 39 private static final int FLAG_IS_VIRTUAL_LONG = 0x2; 40 private static final int FLAG_HAS_SESSION = 0x4; 41 42 private final int mViewId; 43 private int mFlags; 44 private final int mVirtualIntId; 45 private final long mVirtualLongId; 46 private int mSessionId; 47 48 /** @hide */ 49 @TestApi AutofillId(int id)50 public AutofillId(int id) { 51 this(/* flags= */ 0, id, View.NO_ID, NO_SESSION); 52 } 53 54 /** @hide */ 55 @TestApi AutofillId(@onNull AutofillId hostId, int virtualChildId)56 public AutofillId(@NonNull AutofillId hostId, int virtualChildId) { 57 this(FLAG_IS_VIRTUAL_INT, hostId.mViewId, virtualChildId, NO_SESSION); 58 } 59 60 /** @hide */ 61 @TestApi AutofillId(int hostId, int virtualChildId)62 public AutofillId(int hostId, int virtualChildId) { 63 this(FLAG_IS_VIRTUAL_INT, hostId, virtualChildId, NO_SESSION); 64 } 65 66 /** @hide */ AutofillId(@onNull AutofillId hostId, int virtualChildId, int sessionId)67 public AutofillId(@NonNull AutofillId hostId, int virtualChildId, int sessionId) { 68 this(FLAG_IS_VIRTUAL_INT | FLAG_HAS_SESSION, hostId.mViewId, virtualChildId, sessionId); 69 } 70 71 /** @hide */ 72 @TestApi AutofillId(@onNull AutofillId hostId, long virtualChildId, int sessionId)73 public AutofillId(@NonNull AutofillId hostId, long virtualChildId, int sessionId) { 74 this(FLAG_IS_VIRTUAL_LONG | FLAG_HAS_SESSION, hostId.mViewId, virtualChildId, sessionId); 75 } 76 AutofillId(int flags, int parentId, long virtualChildId, int sessionId)77 private AutofillId(int flags, int parentId, long virtualChildId, int sessionId) { 78 mFlags = flags; 79 mViewId = parentId; 80 mVirtualIntId = ((flags & FLAG_IS_VIRTUAL_INT) != 0) ? (int) virtualChildId : View.NO_ID; 81 mVirtualLongId = ((flags & FLAG_IS_VIRTUAL_LONG) != 0) ? virtualChildId : View.NO_ID; 82 mSessionId = sessionId; 83 } 84 85 /** @hide */ 86 @NonNull 87 public static final AutofillId NO_AUTOFILL_ID = new AutofillId(0); 88 89 /** 90 * Creates an {@link AutofillId} with the virtual id. 91 * 92 * This method is used by a {@link View} that contains the virtual view hierarchy. Use this 93 * method to create the {@link AutofillId} for each virtual view. 94 * 95 * @param host the view hosting the virtual view hierarchy which is used to show autofill 96 * suggestions. 97 * @param virtualId id identifying the virtual view inside the host view. 98 * @return an {@link AutofillId} for the virtual view 99 */ 100 @NonNull create(@onNull View host, int virtualId)101 public static AutofillId create(@NonNull View host, int virtualId) { 102 Objects.requireNonNull(host); 103 return new AutofillId(host.getAutofillId(), virtualId); 104 } 105 106 /** @hide */ 107 @NonNull 108 @TestApi withoutSession(@onNull AutofillId id)109 public static AutofillId withoutSession(@NonNull AutofillId id) { 110 final int flags = id.mFlags & ~FLAG_HAS_SESSION; 111 final long virtualChildId = 112 ((id.mFlags & FLAG_IS_VIRTUAL_LONG) != 0) ? id.mVirtualLongId 113 : id.mVirtualIntId; 114 return new AutofillId(flags, id.mViewId, virtualChildId, NO_SESSION); 115 } 116 117 /** 118 * Returns the assigned unique identifier of this AutofillID. 119 * 120 * See @link{android.view.View#getAutofillId()} for more information on 121 * how this is generated for native Views. 122 */ 123 @FlaggedApi(FLAG_AUTOFILL_W_METRICS) getViewId()124 public int getViewId() { 125 return mViewId; 126 } 127 128 /** 129 * Gets the virtual id. This is set if the view is a virtual view, most commonly set if the View 130 * is of {@link android.webkit.WebView}. 131 */ 132 @FlaggedApi(FLAG_AUTOFILL_W_METRICS) getAutofillVirtualId()133 public int getAutofillVirtualId() { 134 return mVirtualIntId; 135 } 136 137 /** Checks whether this AutofillId represents a virtual view. */ 138 @FlaggedApi(FLAG_AUTOFILL_W_METRICS) isVirtual()139 public boolean isVirtual() { 140 return !isNonVirtual(); 141 } 142 143 /** 144 * Checks if this node is generate as part of a {@link android.app.assist.AssistStructure}. This 145 * will usually return true if it should be used by an autofill service provider, and false 146 * otherwise. 147 */ 148 @FlaggedApi(FLAG_AUTOFILL_W_METRICS) isInAutofillSession()149 public boolean isInAutofillSession() { 150 return hasSession(); 151 } 152 153 /** 154 * Gets the virtual child id. 155 * 156 * <p>Should only be used on subsystems where such id is represented by an {@code int} 157 * (Assist and Autofill). 158 * 159 * @hide 160 */ getVirtualChildIntId()161 public int getVirtualChildIntId() { 162 return mVirtualIntId; 163 } 164 165 /** 166 * Gets the virtual child id. 167 * 168 * <p>Should only be used on subsystems where such id is represented by a {@code long} 169 * (ContentCapture). 170 * 171 * @hide 172 */ getVirtualChildLongId()173 public long getVirtualChildLongId() { 174 return mVirtualLongId; 175 } 176 177 /** 178 * Checks whether this node represents a virtual child, whose id is represented by an 179 * {@code int}. 180 * 181 * <p>Should only be used on subsystems where such id is represented by an {@code int} 182 * (Assist and Autofill). 183 * 184 * @hide 185 */ isVirtualInt()186 public boolean isVirtualInt() { 187 return (mFlags & FLAG_IS_VIRTUAL_INT) != 0; 188 } 189 190 /** 191 * Checks whether this node represents a virtual child, whose id is represented by an 192 * {@code long}. 193 * 194 * <p>Should only be used on subsystems where such id is represented by a {@code long} 195 * (ContentCapture). 196 * 197 * @hide 198 */ isVirtualLong()199 public boolean isVirtualLong() { 200 return (mFlags & FLAG_IS_VIRTUAL_LONG) != 0; 201 } 202 203 /** 204 * Checks whether this node represents a non-virtual child. 205 * 206 * @hide 207 */ 208 @TestApi isNonVirtual()209 public boolean isNonVirtual() { 210 return !isVirtualInt() && !isVirtualLong(); 211 } 212 213 /** @hide */ hasSession()214 public boolean hasSession() { 215 return (mFlags & FLAG_HAS_SESSION) != 0; 216 } 217 218 /** 219 * Used to get the Session identifier associated with this AutofillId. 220 * 221 * @return a non-zero integer if {@link #isInAutofillSession()} returns true 222 */ 223 @FlaggedApi(FLAG_AUTOFILL_W_METRICS) getSessionId()224 public int getSessionId() { 225 return mSessionId; 226 } 227 228 /** @hide */ setSessionId(int sessionId)229 public void setSessionId(int sessionId) { 230 mFlags |= FLAG_HAS_SESSION; 231 mSessionId = sessionId; 232 } 233 234 /** @hide */ resetSessionId()235 public void resetSessionId() { 236 mFlags &= ~FLAG_HAS_SESSION; 237 mSessionId = NO_SESSION; 238 } 239 240 ///////////////////////////////// 241 // Object "contract" methods. // 242 ///////////////////////////////// 243 244 @Override hashCode()245 public int hashCode() { 246 final int prime = 31; 247 int result = 1; 248 result = prime * result + mViewId; 249 result = prime * result + mVirtualIntId; 250 result = prime * result + (int) (mVirtualLongId ^ (mVirtualLongId >>> 32)); 251 result = prime * result + mSessionId; 252 return result; 253 } 254 255 @Override equals(@ullable Object obj)256 public boolean equals(@Nullable Object obj) { 257 if (this == obj) return true; 258 if (obj == null) return false; 259 if (getClass() != obj.getClass()) return false; 260 final AutofillId other = (AutofillId) obj; 261 if (mViewId != other.mViewId) return false; 262 if (mVirtualIntId != other.mVirtualIntId) return false; 263 if (mVirtualLongId != other.mVirtualLongId) return false; 264 if (mSessionId != other.mSessionId) return false; 265 return true; 266 } 267 268 /** @hide */ 269 @TestApi equalsIgnoreSession(@ullable AutofillId other)270 public boolean equalsIgnoreSession(@Nullable AutofillId other) { 271 if (this == other) return true; 272 if (other == null) return false; 273 if (mViewId != other.mViewId) return false; 274 if (mVirtualIntId != other.mVirtualIntId) return false; 275 if (mVirtualLongId != other.mVirtualLongId) return false; 276 return true; 277 } 278 279 @Override toString()280 public String toString() { 281 final StringBuilder builder = new StringBuilder().append(mViewId); 282 if (isVirtualInt()) { 283 builder.append(":i").append(mVirtualIntId); 284 } else if (isVirtualLong()) { 285 builder.append(":l").append(mVirtualLongId); 286 } 287 288 if (hasSession()) { 289 builder.append('@').append(mSessionId); 290 } 291 return builder.toString(); 292 } 293 294 @Override describeContents()295 public int describeContents() { 296 return 0; 297 } 298 299 @Override writeToParcel(Parcel parcel, int flags)300 public void writeToParcel(Parcel parcel, int flags) { 301 parcel.writeInt(mViewId); 302 parcel.writeInt(mFlags); 303 if (hasSession()) { 304 parcel.writeInt(mSessionId); 305 } 306 if (isVirtualInt()) { 307 parcel.writeInt(mVirtualIntId); 308 } else if (isVirtualLong()) { 309 parcel.writeLong(mVirtualLongId); 310 } 311 } 312 313 public static final @android.annotation.NonNull Parcelable.Creator<AutofillId> CREATOR = 314 new Parcelable.Creator<AutofillId>() { 315 @Override 316 public AutofillId createFromParcel(Parcel source) { 317 final int viewId = source.readInt(); 318 final int flags = source.readInt(); 319 final int sessionId = (flags & FLAG_HAS_SESSION) != 0 ? source.readInt() : NO_SESSION; 320 if ((flags & FLAG_IS_VIRTUAL_INT) != 0) { 321 return new AutofillId(flags, viewId, source.readInt(), sessionId); 322 } 323 if ((flags & FLAG_IS_VIRTUAL_LONG) != 0) { 324 return new AutofillId(flags, viewId, source.readLong(), sessionId); 325 } 326 return new AutofillId(flags, viewId, View.NO_ID, sessionId); 327 } 328 329 @Override 330 public AutofillId[] newArray(int size) { 331 return new AutofillId[size]; 332 } 333 }; 334 } 335