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