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