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