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