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