blob: 70f434c128639e5e8b9c5eca6ffc5e6c53724481 [file] [log] [blame]
Svet Ganov013efe12017-04-13 21:56:16 -07001/*
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
17package android.service.autofill;
18
Felipe Leme9f9ee252017-04-27 13:56:22 -070019import static android.view.autofill.Helper.sDebug;
Philip P. Moltmannc7619632017-04-25 11:57:37 -070020
Svet Ganov013efe12017-04-13 21:56:16 -070021import android.annotation.NonNull;
Philip P. Moltmann22567d32017-05-09 12:56:50 -070022import android.annotation.Nullable;
Svet Ganov013efe12017-04-13 21:56:16 -070023import android.app.assist.AssistStructure;
Philip P. Moltmann22567d32017-05-09 12:56:50 -070024import android.app.assist.AssistStructure.ViewNode;
Svet Ganov013efe12017-04-13 21:56:16 -070025import android.os.Bundle;
26import android.os.CancellationSignal;
27import android.os.Parcel;
28import android.os.Parcelable;
Philip P. Moltmann22567d32017-05-09 12:56:50 -070029import android.util.ArrayMap;
30import android.util.SparseIntArray;
31import android.view.autofill.AutofillId;
32
Philip P. Moltmann22567d32017-05-09 12:56:50 -070033import java.util.LinkedList;
Svet Ganov013efe12017-04-13 21:56:16 -070034
35/**
36 * This class represents a context for each fill request made via {@link
37 * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)}.
38 * It contains a snapshot of the UI state, the view ids that were returned by
39 * the {@link AutofillService autofill service} as both required to trigger a save
40 * and optional that can be saved, and the id of the corresponding {@link
41 * FillRequest}.
42 * <p>
43 * This context allows you to inspect the values for the interesting views
44 * in the context they appeared. Also a reference to the corresponding fill
45 * request is useful to store meta-data in the client state bundle passed
46 * to {@link FillResponse.Builder#setClientState(Bundle)} to avoid interpreting
47 * the UI state again while saving.
48 */
49public final class FillContext implements Parcelable {
50 private final int mRequestId;
51 private final @NonNull AssistStructure mStructure;
Adam He2dfc8692019-02-26 12:35:43 -080052 private final @NonNull AutofillId mFocusedId;
Svet Ganov013efe12017-04-13 21:56:16 -070053
Philip P. Moltmann22567d32017-05-09 12:56:50 -070054 /**
55 * Lookup table AutofillId->ViewNode to speed up {@link #findViewNodesByAutofillIds}
56 * This is purely a cache and can be deleted at any time
57 */
58 @Nullable private ArrayMap<AutofillId, AssistStructure.ViewNode> mViewNodeLookupTable;
59
60
Svet Ganov013efe12017-04-13 21:56:16 -070061 /** @hide */
Adam He2dfc8692019-02-26 12:35:43 -080062 public FillContext(int requestId, @NonNull AssistStructure structure,
63 @NonNull AutofillId autofillId) {
Svet Ganov013efe12017-04-13 21:56:16 -070064 mRequestId = requestId;
65 mStructure = structure;
Adam He2dfc8692019-02-26 12:35:43 -080066 mFocusedId = autofillId;
Svet Ganov013efe12017-04-13 21:56:16 -070067 }
68
69 private FillContext(Parcel parcel) {
Adam He2dfc8692019-02-26 12:35:43 -080070 this(parcel.readInt(), parcel.readParcelable(null), parcel.readParcelable(null));
Svet Ganov013efe12017-04-13 21:56:16 -070071 }
72
73 /**
74 * Gets the id of the {@link FillRequest fill request} this context
75 * corresponds to. This is useful to associate your custom client
76 * state with every request to avoid reinterpreting the UI when saving
77 * user data.
78 *
79 * @return The request id.
80 */
81 public int getRequestId() {
82 return mRequestId;
83 }
84
85 /**
86 * @return The screen content.
87 */
Adam He2dfc8692019-02-26 12:35:43 -080088 @NonNull
Svet Ganov013efe12017-04-13 21:56:16 -070089 public AssistStructure getStructure() {
90 return mStructure;
91 }
92
Adam He2dfc8692019-02-26 12:35:43 -080093 /**
94 * @return the AutofillId of the view that triggered autofill.
95 */
96 @NonNull
97 public AutofillId getFocusedId() {
98 return mFocusedId;
99 }
100
Svet Ganov013efe12017-04-13 21:56:16 -0700101 @Override
Philip P. Moltmannc7619632017-04-25 11:57:37 -0700102 public String toString() {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700103 if (!sDebug) return super.toString();
Felipe Leme0aa4c502017-04-26 12:36:01 -0700104
Adam He2dfc8692019-02-26 12:35:43 -0800105 return "FillContext [reqId=" + mRequestId + ", focusedId=" + mFocusedId + "]";
Philip P. Moltmannc7619632017-04-25 11:57:37 -0700106 }
107
108 @Override
Svet Ganov013efe12017-04-13 21:56:16 -0700109 public int describeContents() {
110 return 0;
111 }
112
113 @Override
114 public void writeToParcel(Parcel parcel, int flags) {
115 parcel.writeInt(mRequestId);
116 parcel.writeParcelable(mStructure, flags);
Adam He2dfc8692019-02-26 12:35:43 -0800117 parcel.writeParcelable(mFocusedId, flags);
Svet Ganov013efe12017-04-13 21:56:16 -0700118 }
119
Philip P. Moltmann22567d32017-05-09 12:56:50 -0700120 /**
Felipe Leme94b56202017-06-16 11:49:48 -0700121 * Finds {@link ViewNode ViewNodes} that have the requested ids.
Philip P. Moltmann22567d32017-05-09 12:56:50 -0700122 *
Felipe Leme94b56202017-06-16 11:49:48 -0700123 * @param ids The ids of the node to find.
Philip P. Moltmann22567d32017-05-09 12:56:50 -0700124 *
Felipe Leme94b56202017-06-16 11:49:48 -0700125 * @return The nodes indexed in the same way as the ids.
Philip P. Moltmann22567d32017-05-09 12:56:50 -0700126 *
127 * @hide
128 */
Felipe Leme94b56202017-06-16 11:49:48 -0700129 @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId[] ids) {
Philip P. Moltmann22567d32017-05-09 12:56:50 -0700130 final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
131 final ViewNode[] foundNodes = new AssistStructure.ViewNode[ids.length];
132
133 // Indexes of foundNodes that are not found yet
134 final SparseIntArray missingNodeIndexes = new SparseIntArray(ids.length);
135
136 for (int i = 0; i < ids.length; i++) {
137 if (mViewNodeLookupTable != null) {
138 int lookupTableIndex = mViewNodeLookupTable.indexOfKey(ids[i]);
139
140 if (lookupTableIndex >= 0) {
141 foundNodes[i] = mViewNodeLookupTable.valueAt(lookupTableIndex);
142 } else {
143 missingNodeIndexes.put(i, /* ignored */ 0);
144 }
145 } else {
146 missingNodeIndexes.put(i, /* ignored */ 0);
147 }
148 }
149
150 final int numWindowNodes = mStructure.getWindowNodeCount();
151 for (int i = 0; i < numWindowNodes; i++) {
152 nodesToProcess.add(mStructure.getWindowNodeAt(i).getRootViewNode());
153 }
154
155 while (missingNodeIndexes.size() > 0 && !nodesToProcess.isEmpty()) {
156 final ViewNode node = nodesToProcess.removeFirst();
157
158 for (int i = 0; i < missingNodeIndexes.size(); i++) {
159 final int index = missingNodeIndexes.keyAt(i);
160 final AutofillId id = ids[index];
161
162 if (id.equals(node.getAutofillId())) {
163 foundNodes[index] = node;
164
165 if (mViewNodeLookupTable == null) {
166 mViewNodeLookupTable = new ArrayMap<>(ids.length);
167 }
168
169 mViewNodeLookupTable.put(id, node);
170
171 missingNodeIndexes.removeAt(i);
172 break;
173 }
174 }
175
176 for (int i = 0; i < node.getChildCount(); i++) {
177 nodesToProcess.addLast(node.getChildAt(i));
178 }
179 }
180
181 // Remember which ids could not be resolved to not search for them again the next time
182 for (int i = 0; i < missingNodeIndexes.size(); i++) {
183 if (mViewNodeLookupTable == null) {
184 mViewNodeLookupTable = new ArrayMap<>(missingNodeIndexes.size());
185 }
186
187 mViewNodeLookupTable.put(ids[missingNodeIndexes.keyAt(i)], null);
188 }
189
190 return foundNodes;
191 }
192
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700193 public static final @android.annotation.NonNull Parcelable.Creator<FillContext> CREATOR =
Svet Ganov013efe12017-04-13 21:56:16 -0700194 new Parcelable.Creator<FillContext>() {
195 @Override
Adam He2dfc8692019-02-26 12:35:43 -0800196 @NonNull
Svet Ganov013efe12017-04-13 21:56:16 -0700197 public FillContext createFromParcel(Parcel parcel) {
198 return new FillContext(parcel);
199 }
200
201 @Override
Adam He2dfc8692019-02-26 12:35:43 -0800202 @NonNull
Svet Ganov013efe12017-04-13 21:56:16 -0700203 public FillContext[] newArray(int size) {
204 return new FillContext[size];
205 }
206 };
207}