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 17 package android.service.autofill; 18 19 import static android.view.autofill.Helper.sDebug; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.app.assist.AssistStructure; 24 import android.app.assist.AssistStructure.ViewNode; 25 import android.os.Bundle; 26 import android.os.CancellationSignal; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 import android.util.ArrayMap; 30 import android.util.SparseIntArray; 31 import android.view.autofill.AutofillId; 32 33 import com.android.internal.util.DataClass; 34 35 import java.util.ArrayDeque; 36 37 /** 38 * This class represents a context for each fill request made via {@link 39 * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)}. 40 * It contains a snapshot of the UI state, the view ids that were returned by 41 * the {@link AutofillService autofill service} as both required to trigger a save 42 * and optional that can be saved, and the id of the corresponding {@link 43 * FillRequest}. 44 * <p> 45 * This context allows you to inspect the values for the interesting views 46 * in the context they appeared. Also a reference to the corresponding fill 47 * request is useful to store meta-data in the client state bundle passed 48 * to {@link FillResponse.Builder#setClientState(Bundle)} to avoid interpreting 49 * the UI state again while saving. 50 */ 51 @DataClass( 52 genHiddenConstructor = true, 53 genAidl = false) 54 public final class FillContext implements Parcelable { 55 56 /** 57 * The id of the {@link FillRequest fill request} this context 58 * corresponds to. This is useful to associate your custom client 59 * state with every request to avoid reinterpreting the UI when saving 60 * user data. 61 */ 62 private final int mRequestId; 63 64 /** 65 * The screen content. 66 */ 67 private final @NonNull AssistStructure mStructure; 68 69 /** 70 * The AutofillId of the view that triggered autofill. 71 */ 72 private final @NonNull AutofillId mFocusedId; 73 74 /** 75 * Lookup table AutofillId->ViewNode to speed up {@link #findViewNodesByAutofillIds} 76 * This is purely a cache and can be deleted at any time 77 */ 78 private transient @Nullable ArrayMap<AutofillId, AssistStructure.ViewNode> mViewNodeLookupTable; 79 80 81 @Override toString()82 public String toString() { 83 if (!sDebug) return super.toString(); 84 85 return "FillContext [reqId=" + mRequestId + ", focusedId=" + mFocusedId + "]"; 86 } 87 88 /** 89 * Finds {@link ViewNode ViewNodes} that have the requested ids. 90 * 91 * @param ids The ids of the node to find. 92 * 93 * @return The nodes indexed in the same way as the ids. 94 * 95 * @hide 96 */ findViewNodesByAutofillIds(@onNull AutofillId[] ids)97 @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId[] ids) { 98 final ArrayDeque<ViewNode> nodesToProcess = new ArrayDeque<>(); 99 final ViewNode[] foundNodes = new AssistStructure.ViewNode[ids.length]; 100 101 // Indexes of foundNodes that are not found yet 102 final SparseIntArray missingNodeIndexes = new SparseIntArray(ids.length); 103 104 for (int i = 0; i < ids.length; i++) { 105 if (mViewNodeLookupTable != null) { 106 int lookupTableIndex = mViewNodeLookupTable.indexOfKey(ids[i]); 107 108 if (lookupTableIndex >= 0) { 109 foundNodes[i] = mViewNodeLookupTable.valueAt(lookupTableIndex); 110 } else { 111 missingNodeIndexes.put(i, /* ignored */ 0); 112 } 113 } else { 114 missingNodeIndexes.put(i, /* ignored */ 0); 115 } 116 } 117 118 final int numWindowNodes = mStructure.getWindowNodeCount(); 119 for (int i = 0; i < numWindowNodes; i++) { 120 nodesToProcess.add(mStructure.getWindowNodeAt(i).getRootViewNode()); 121 } 122 123 while (missingNodeIndexes.size() > 0 && !nodesToProcess.isEmpty()) { 124 final ViewNode node = nodesToProcess.removeFirst(); 125 126 for (int i = 0; i < missingNodeIndexes.size(); i++) { 127 final int index = missingNodeIndexes.keyAt(i); 128 final AutofillId id = ids[index]; 129 130 if (id != null && id.equals(node.getAutofillId())) { 131 foundNodes[index] = node; 132 133 if (mViewNodeLookupTable == null) { 134 mViewNodeLookupTable = new ArrayMap<>(ids.length); 135 } 136 137 mViewNodeLookupTable.put(id, node); 138 139 missingNodeIndexes.removeAt(i); 140 break; 141 } 142 } 143 144 for (int i = 0; i < node.getChildCount(); i++) { 145 nodesToProcess.addLast(node.getChildAt(i)); 146 } 147 } 148 149 // Remember which ids could not be resolved to not search for them again the next time 150 for (int i = 0; i < missingNodeIndexes.size(); i++) { 151 if (mViewNodeLookupTable == null) { 152 mViewNodeLookupTable = new ArrayMap<>(missingNodeIndexes.size()); 153 } 154 155 mViewNodeLookupTable.put(ids[missingNodeIndexes.keyAt(i)], null); 156 } 157 158 return foundNodes; 159 } 160 161 162 163 // Code below generated by codegen v1.0.0. 164 // 165 // DO NOT MODIFY! 166 // 167 // To regenerate run: 168 // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/FillContext.java 169 // 170 // CHECKSTYLE:OFF Generated code 171 172 /** 173 * Creates a new FillContext. 174 * 175 * @param requestId 176 * The id of the {@link FillRequest fill request} this context 177 * corresponds to. This is useful to associate your custom client 178 * state with every request to avoid reinterpreting the UI when saving 179 * user data. 180 * @param structure 181 * The screen content. 182 * @param focusedId 183 * The AutofillId of the view that triggered autofill. 184 * @hide 185 */ 186 @DataClass.Generated.Member FillContext( int requestId, @NonNull AssistStructure structure, @NonNull AutofillId focusedId)187 public FillContext( 188 int requestId, 189 @NonNull AssistStructure structure, 190 @NonNull AutofillId focusedId) { 191 this.mRequestId = requestId; 192 this.mStructure = structure; 193 com.android.internal.util.AnnotationValidations.validate( 194 NonNull.class, null, mStructure); 195 this.mFocusedId = focusedId; 196 com.android.internal.util.AnnotationValidations.validate( 197 NonNull.class, null, mFocusedId); 198 199 // onConstructed(); // You can define this method to get a callback 200 } 201 202 /** 203 * The id of the {@link FillRequest fill request} this context 204 * corresponds to. This is useful to associate your custom client 205 * state with every request to avoid reinterpreting the UI when saving 206 * user data. 207 */ 208 @DataClass.Generated.Member getRequestId()209 public int getRequestId() { 210 return mRequestId; 211 } 212 213 /** 214 * The screen content. 215 */ 216 @DataClass.Generated.Member getStructure()217 public @NonNull AssistStructure getStructure() { 218 return mStructure; 219 } 220 221 /** 222 * The AutofillId of the view that triggered autofill. 223 */ 224 @DataClass.Generated.Member getFocusedId()225 public @NonNull AutofillId getFocusedId() { 226 return mFocusedId; 227 } 228 229 @Override 230 @DataClass.Generated.Member writeToParcel(Parcel dest, int flags)231 public void writeToParcel(Parcel dest, int flags) { 232 // You can override field parcelling by defining methods like: 233 // void parcelFieldName(Parcel dest, int flags) { ... } 234 235 dest.writeInt(mRequestId); 236 dest.writeTypedObject(mStructure, flags); 237 dest.writeTypedObject(mFocusedId, flags); 238 } 239 240 @Override 241 @DataClass.Generated.Member describeContents()242 public int describeContents() { return 0; } 243 244 @DataClass.Generated.Member 245 public static final @NonNull Parcelable.Creator<FillContext> CREATOR 246 = new Parcelable.Creator<FillContext>() { 247 @Override 248 public FillContext[] newArray(int size) { 249 return new FillContext[size]; 250 } 251 252 @Override 253 @SuppressWarnings({"unchecked", "RedundantCast"}) 254 public FillContext createFromParcel(Parcel in) { 255 // You can override field unparcelling by defining methods like: 256 // static FieldType unparcelFieldName(Parcel in) { ... } 257 258 int requestId = in.readInt(); 259 AssistStructure structure = (AssistStructure) in.readTypedObject(AssistStructure.CREATOR); 260 AutofillId focusedId = (AutofillId) in.readTypedObject(AutofillId.CREATOR); 261 return new FillContext( 262 requestId, 263 structure, 264 focusedId); 265 } 266 }; 267 268 @DataClass.Generated( 269 time = 1565152135263L, 270 codegenVersion = "1.0.0", 271 sourceFile = "frameworks/base/core/java/android/service/autofill/FillContext.java", 272 inputSignatures = "private final int mRequestId\nprivate final @android.annotation.NonNull android.app.assist.AssistStructure mStructure\nprivate final @android.annotation.NonNull android.view.autofill.AutofillId mFocusedId\nprivate transient @android.annotation.Nullable android.util.ArrayMap<android.view.autofill.AutofillId,android.app.assist.AssistStructure.ViewNode> mViewNodeLookupTable\npublic @java.lang.Override java.lang.String toString()\npublic @android.annotation.NonNull android.app.assist.AssistStructure.ViewNode[] findViewNodesByAutofillIds(android.view.autofill.AutofillId[])\nclass FillContext extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false)") 273 @Deprecated __metadata()274 private void __metadata() {} 275 276 } 277