blob: 26c9b243ad18d4aa07a832d0080b50de72acc3f1 [file] [log] [blame]
Felipe Leme3461d3c2017-01-19 08:54:55 -08001/*
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.view.autofill;
18
Felipe Leme73fedac2017-05-12 09:52:07 -070019import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
Felipe Leme9f9ee252017-04-27 13:56:22 -070020import static android.view.autofill.Helper.sDebug;
21import static android.view.autofill.Helper.sVerbose;
Felipe Lemed633f072017-02-14 10:17:17 -080022
Felipe Lemee6010f22017-03-03 11:19:51 -080023import android.annotation.IntDef;
Philip P. Moltmann44611812017-02-23 12:52:46 -080024import android.annotation.NonNull;
Felipe Lemee6010f22017-03-03 11:19:51 -080025import android.annotation.Nullable;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060026import android.annotation.SystemService;
Felipe Leme3461d3c2017-01-19 08:54:55 -080027import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000028import android.content.Intent;
29import android.content.IntentSender;
Felipe Leme3461d3c2017-01-19 08:54:55 -080030import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070031import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000032import android.os.Bundle;
Felipe Lemec24a56a2017-08-03 14:27:57 -070033import android.os.IBinder;
Svet Ganov782043c2017-02-11 00:52:02 +000034import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080035import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070036import android.service.autofill.AutofillService;
37import android.service.autofill.FillEventHistory;
Felipe Leme4753bb02017-03-22 20:24:00 -070038import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070039import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080040import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070041import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080042import android.view.View;
43
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070044import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070045import com.android.internal.logging.MetricsLogger;
46import com.android.internal.logging.nano.MetricsProto;
47
Felipe Lemec24a56a2017-08-03 14:27:57 -070048import java.io.PrintWriter;
Felipe Lemee6010f22017-03-03 11:19:51 -080049import java.lang.annotation.Retention;
50import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000051import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070052import java.util.ArrayList;
Svet Ganov782043c2017-02-11 00:52:02 +000053import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070054import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000055
Felipe Leme3461d3c2017-01-19 08:54:55 -080056/**
Felipe Leme744976e2017-07-31 11:34:14 -070057 * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
58 * Autofill Framework lifecycle.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070059 *
Felipe Leme744976e2017-07-31 11:34:14 -070060 * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
61 * activity context; the autofill context is created when one of the following methods is called for
62 * the first time in an activity context, and the current user has an enabled autofill service:
63 *
64 * <ul>
65 * <li>{@link #notifyViewEntered(View)}
66 * <li>{@link #notifyViewEntered(View, int, Rect)}
67 * <li>{@link #requestAutofill(View)}
68 * </ul>
69 *
70 * <p>Tipically, the context is automatically created when the first view of the activity is
71 * focused because {@code View.onFocusChanged()} indirectly calls
72 * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
73 * explicitly create it (for example, a custom view developer could offer a contextual menu action
74 * in a text-field view to let users manually request autofill).
75 *
76 * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
77 * that represents the view hierarchy by calling
78 * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
79 * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
80 * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
81 * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
82 * the hierarchy.
83 *
84 * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
85 * parses it looking for views that can be autofilled. If the service finds such views, it returns
86 * a data structure to the Android System containing the following optional info:
87 *
88 * <ul>
89 * <li>Datasets used to autofill subsets of views in the activity.
90 * <li>Id of views that the service can save their values for future autofilling.
91 * </ul>
92 *
93 * <p>When the service returns datasets, the Android System displays an autofill dataset picker
94 * UI affordance associated with the view, when the view is focused on and is part of a dataset.
95 * The application can be notified when the affordance is shown by registering an
96 * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
97 * selects a dataset from the affordance, all views present in the dataset are autofilled, through
98 * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
99 *
100 * <p>When the service returns ids of savable views, the Android System keeps track of changes
101 * made to these views, so they can be used to determine if the autofill save UI is shown later.
102 *
103 * <p>The context is then finished when one of the following occurs:
104 *
105 * <ul>
106 * <li>{@link #commit()} is called or all savable views are gone.
107 * <li>{@link #cancel()} is called.
108 * </ul>
109 *
110 * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
111 * shows a save UI affordance if the value of savable views have changed. If the user selects the
112 * option to Save, the current value of the views is then sent to the autofill service.
113 *
114 * <p>It is safe to call into its methods from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800115 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600116@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
Felipe Leme640f30a2017-03-06 15:44:06 -0800117public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -0800118
Felipe Leme640f30a2017-03-06 15:44:06 -0800119 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -0800120
Svet Ganov782043c2017-02-11 00:52:02 +0000121 /**
122 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -0700123 *
Svet Ganov782043c2017-02-11 00:52:02 +0000124 * <p>
125 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +0000126 */
127 public static final String EXTRA_ASSIST_STRUCTURE =
128 "android.view.autofill.extra.ASSIST_STRUCTURE";
129
130 /**
131 * Intent extra: The result of an authentication operation. It is
132 * either a fully populated {@link android.service.autofill.FillResponse}
133 * or a fully populated {@link android.service.autofill.Dataset} if
134 * a response or a dataset is being authenticated respectively.
135 *
136 * <p>
137 * Type: {@link android.service.autofill.FillResponse} or a
138 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +0000139 */
140 public static final String EXTRA_AUTHENTICATION_RESULT =
141 "android.view.autofill.extra.AUTHENTICATION_RESULT";
142
Felipe Leme7320ca92017-03-29 15:09:54 -0700143 /**
144 * Intent extra: The optional extras provided by the
145 * {@link android.service.autofill.AutofillService}.
146 *
147 * <p>For example, when the service responds to a {@link
148 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
149 * a {@code FillResponse} that requires authentication, the Intent that launches the
150 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -0700151 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -0700152 *
153 * <p>
154 * Type: {@link android.os.Bundle}
155 */
Felipe Leme37a440fd2017-04-28 10:50:20 -0700156 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -0600157 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -0700158
Felipe Lemec24a56a2017-08-03 14:27:57 -0700159
160 /** @hide */
161 public static final String EXTRA_RESTORE_SESSION_TOKEN =
162 "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
163
164 private static final String SESSION_ID_TAG = "android:sessionId";
165 private static final String STATE_TAG = "android:state";
166 private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
167
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700168
Felipe Leme0aa4c502017-04-26 12:36:01 -0700169 /** @hide */ public static final int ACTION_START_SESSION = 1;
170 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
171 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
172 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800173
Felipe Leme9f9ee252017-04-27 13:56:22 -0700174
175 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
176 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
177 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
178
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700179 /** Which bits in an authentication id are used for the dataset id */
180 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
181 /** How many bits in an authentication id are used for the dataset id */
182 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
183 /** @hide The index for an undefined data set */
184 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
185
186 /**
Felipe Lemec24a56a2017-08-03 14:27:57 -0700187 * Used on {@link #onPendingSaveUi(int, IBinder)} to cancel the pending UI.
188 *
189 * @hide
190 */
191 public static final int PENDING_UI_OPERATION_CANCEL = 1;
192
193 /**
194 * Used on {@link #onPendingSaveUi(int, IBinder)} to restore the pending UI.
195 *
196 * @hide
197 */
198 public static final int PENDING_UI_OPERATION_RESTORE = 2;
199
200 /**
201 * Initial state of the autofill context, set when there is no session (i.e., when
202 * {@link #mSessionId} is {@link #NO_SESSION}).
203 *
Felipe Lemec7b45292017-09-19 09:06:20 -0700204 * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
205 * the server.
206 *
Felipe Lemec24a56a2017-08-03 14:27:57 -0700207 * @hide
208 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700209 public static final int STATE_UNKNOWN = 0;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700210
211 /**
212 * State where the autofill context hasn't been {@link #commit() finished} nor
213 * {@link #cancel() canceled} yet.
214 *
215 * @hide
216 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700217 public static final int STATE_ACTIVE = 1;
218
219 /**
220 * State where the autofill context was finished by the server because the autofill
221 * service could not autofill the page.
222 *
223 * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
224 * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
225 *
226 * @hide
227 */
228 public static final int STATE_FINISHED = 2;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700229
230 /**
231 * State where the autofill context has been {@link #commit() finished} but the server still has
232 * a session because the Save UI hasn't been dismissed yet.
233 *
234 * @hide
235 */
Felipe Lemec7b45292017-09-19 09:06:20 -0700236 public static final int STATE_SHOWING_SAVE_UI = 3;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700237
238 /**
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700239 * Makes an authentication id from a request id and a dataset id.
240 *
241 * @param requestId The request id.
242 * @param datasetId The dataset id.
243 * @return The authentication id.
244 * @hide
245 */
246 public static int makeAuthenticationId(int requestId, int datasetId) {
247 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
248 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
249 }
250
251 /**
252 * Gets the request id from an authentication id.
253 *
254 * @param authRequestId The authentication id.
255 * @return The request id.
256 * @hide
257 */
258 public static int getRequestIdFromAuthenticationId(int authRequestId) {
259 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
260 }
261
262 /**
263 * Gets the dataset id from an authentication id.
264 *
265 * @param authRequestId The authentication id.
266 * @return The dataset id.
267 * @hide
268 */
269 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
270 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
271 }
272
Felipe Leme4753bb02017-03-22 20:24:00 -0700273 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000274
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700275 /**
276 * There is currently no session running.
277 * {@hide}
278 */
279 public static final int NO_SESSION = Integer.MIN_VALUE;
280
Svet Ganov782043c2017-02-11 00:52:02 +0000281 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700282
283 private final Object mLock = new Object();
284
285 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000286 private IAutoFillManagerClient mServiceClient;
287
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700288 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800289 private AutofillCallback mCallback;
290
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700291 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000292
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700293 @GuardedBy("mLock")
294 private int mSessionId = NO_SESSION;
295
296 @GuardedBy("mLock")
Felipe Lemec24a56a2017-08-03 14:27:57 -0700297 private int mState = STATE_UNKNOWN;
298
299 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000300 private boolean mEnabled;
301
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700302 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700303 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700304 @Nullable private ParcelableMap mLastAutofilledData;
305
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700306 /** If view tracking is enabled, contains the tracking state */
307 @GuardedBy("mLock")
308 @Nullable private TrackedViews mTrackedViews;
309
Felipe Leme27e20222017-05-18 15:24:11 -0700310 /** Views that are only tracked because they are fillable and could be anchoring the UI. */
311 @GuardedBy("mLock")
312 @Nullable private ArraySet<AutofillId> mFillableIds;
313
Svet Ganov782043c2017-02-11 00:52:02 +0000314 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800315 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000316 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000317 * Asks the client to start an authentication flow.
318 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700319 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000320 * @param intent The authentication intent.
321 * @param fillInIntent The authentication fill-in intent.
322 */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700323 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
324 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800325
326 /**
327 * Tells the client this manager has state to be reset.
328 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700329 void autofillCallbackResetableStateAvailable();
330
331 /**
332 * Request showing the autofill UI.
333 *
334 * @param anchor The real view the UI needs to anchor to.
335 * @param width The width of the fill UI content.
336 * @param height The height of the fill UI content.
337 * @param virtualBounds The bounds of the virtual decendant of the anchor.
338 * @param presenter The presenter that controls the fill UI window.
339 * @return Whether the UI was shown.
340 */
341 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
342 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
343
344 /**
345 * Request hiding the autofill UI.
346 *
347 * @return Whether the UI was hidden.
348 */
349 boolean autofillCallbackRequestHideFillUi();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700350
351 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700352 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700353 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700354 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700355 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700356 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700357
358 /**
359 * Checks is the client is currently visible as understood by autofill.
360 *
361 * @return {@code true} if the client is currently visible
362 */
363 boolean isVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700364
365 /**
Felipe Leme27e20222017-05-18 15:24:11 -0700366 * Finds views by traversing the hierarchies of the client.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700367 *
Phil Weaver846cda932017-06-15 10:10:06 -0700368 * @param viewIds The autofill ids of the views to find
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700369 *
Felipe Leme27e20222017-05-18 15:24:11 -0700370 * @return And array containing the views (empty if no views found).
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700371 */
Phil Weaver846cda932017-06-15 10:10:06 -0700372 @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
Felipe Leme27e20222017-05-18 15:24:11 -0700373
374 /**
375 * Finds a view by traversing the hierarchies of the client.
376 *
Phil Weaver846cda932017-06-15 10:10:06 -0700377 * @param viewId The autofill id of the views to find
Felipe Leme27e20222017-05-18 15:24:11 -0700378 *
379 * @return The view, or {@code null} if not found
380 */
Phil Weaver846cda932017-06-15 10:10:06 -0700381 @Nullable View findViewByAutofillIdTraversal(int viewId);
Felipe Leme9876a6f2017-05-30 15:47:28 -0700382
383 /**
384 * Runs the specified action on the UI thread.
385 */
386 void runOnUiThread(Runnable action);
Svet Ganov782043c2017-02-11 00:52:02 +0000387 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800388
389 /**
390 * @hide
391 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800392 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Lemebab851c2017-02-03 18:45:08 -0800393 mContext = context;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800394 mService = service;
395 }
396
397 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700398 * Restore state after activity lifecycle
399 *
400 * @param savedInstanceState The state to be restored
401 *
402 * {@hide}
403 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700404 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700405 if (!hasAutofillFeature()) {
406 return;
407 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700408 synchronized (mLock) {
409 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
410
Felipe Lemec24a56a2017-08-03 14:27:57 -0700411 if (isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700412 Log.w(TAG, "New session was started before onCreate()");
413 return;
414 }
415
416 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
Felipe Lemec24a56a2017-08-03 14:27:57 -0700417 mState = savedInstanceState.getInt(STATE_TAG, STATE_UNKNOWN);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700418
419 if (mSessionId != NO_SESSION) {
420 ensureServiceClientAddedIfNeededLocked();
421
422 final AutofillClient client = getClientLocked();
423 if (client != null) {
424 try {
425 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
426 mContext.getActivityToken(), mServiceClient.asBinder());
427
428 if (!sessionWasRestored) {
429 Log.w(TAG, "Session " + mSessionId + " could not be restored");
430 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -0700431 mState = STATE_UNKNOWN;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700432 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700433 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700434 Log.d(TAG, "session " + mSessionId + " was restored");
435 }
436
437 client.autofillCallbackResetableStateAvailable();
438 }
439 } catch (RemoteException e) {
440 Log.e(TAG, "Could not figure out if there was an autofill session", e);
441 }
442 }
443 }
444 }
445 }
446
447 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700448 * Called once the client becomes visible.
449 *
450 * @see AutofillClient#isVisibleForAutofill()
451 *
452 * {@hide}
453 */
454 public void onVisibleForAutofill() {
455 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700456 if (mEnabled && isActiveLocked() && mTrackedViews != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700457 mTrackedViews.onVisibleForAutofillLocked();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700458 }
459 }
460 }
461
462 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700463 * Save state before activity lifecycle
464 *
465 * @param outState Place to store the state
466 *
467 * {@hide}
468 */
469 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700470 if (!hasAutofillFeature()) {
471 return;
472 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700473 synchronized (mLock) {
474 if (mSessionId != NO_SESSION) {
475 outState.putInt(SESSION_ID_TAG, mSessionId);
476 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700477 if (mState != STATE_UNKNOWN) {
478 outState.putInt(STATE_TAG, mState);
479 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700480 if (mLastAutofilledData != null) {
481 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
482 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700483 }
484 }
485
486 /**
487 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700488 *
489 * <p>Typically used to determine whether the option to explicitly request autofill should
490 * be offered - see {@link #requestAutofill(View)}.
491 *
492 * @return whether autofill is enabled for the current user.
493 */
494 public boolean isEnabled() {
Svet Ganov43574b02017-04-12 09:25:20 -0700495 if (!hasAutofillFeature()) {
496 return false;
497 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700498 synchronized (mLock) {
499 ensureServiceClientAddedIfNeededLocked();
500 return mEnabled;
501 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700502 }
503
504 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700505 * Should always be called from {@link AutofillService#getFillEventHistory()}.
506 *
507 * @hide
508 */
509 @Nullable public FillEventHistory getFillEventHistory() {
510 try {
511 return mService.getFillEventHistory();
512 } catch (RemoteException e) {
513 e.rethrowFromSystemServer();
514 return null;
515 }
516 }
517
518 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700519 * Explicitly requests a new autofill context.
520 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700521 * <p>Normally, the autofill context is automatically started if necessary when
522 * {@link #notifyViewEntered(View)} is called, but this method should be used in the
523 * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
524 * option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700525 *
526 * @param view view requesting the new autofill context.
527 */
528 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700529 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700530 }
531
532 /**
533 * Explicitly requests a new autofill context for virtual views.
534 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700535 * <p>Normally, the autofill context is automatically started if necessary when
536 * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
537 * cases where it must be explicitly started. For example, when the virtual view offers an
538 * AUTOFILL option on its contextual overflow menu, and the user selects it.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700539 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700540 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
541 * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
542 * the absolute bounds could be calculated by:
543 *
544 * <pre class="prettyprint">
545 * int offset[] = new int[2];
546 * getLocationOnScreen(offset);
547 * Rect absBounds = new Rect(bounds.left + offset[0],
548 * bounds.top + offset[1],
549 * bounds.right + offset[0], bounds.bottom + offset[1]);
550 * </pre>
551 *
552 * @param view the virtual view parent.
553 * @param virtualId id identifying the virtual child inside the parent view.
554 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700555 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700556 public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
557 notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700558 }
559
Felipe Leme2ac463e2017-03-13 14:06:25 -0700560 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700561 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800562 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700563 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800564 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700565 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700566 notifyViewEntered(view, 0);
567 }
568
569 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700570 if (!hasAutofillFeature()) {
571 return;
572 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700573 AutofillCallback callback = null;
574 synchronized (mLock) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700575 if (isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
576 if (sVerbose) {
577 Log.v(TAG, "notifyViewEntered(flags=" + flags + ", view=" + view
578 + "): ignored on state " + getStateAsStringLocked());
579 }
580 return;
581 }
582
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700583 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000584
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700585 if (!mEnabled) {
586 if (mCallback != null) {
587 callback = mCallback;
588 }
589 } else {
590 final AutofillId id = getAutofillId(view);
591 final AutofillValue value = view.getAutofillValue();
592
Felipe Lemec24a56a2017-08-03 14:27:57 -0700593 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700594 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700595 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700596 } else {
597 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700598 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700599 }
Felipe Leme24aae152017-03-15 12:33:01 -0700600 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800601 }
602
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700603 if (callback != null) {
604 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Svet Ganov782043c2017-02-11 00:52:02 +0000605 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800606 }
607
608 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700609 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800610 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700611 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800612 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700613 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700614 if (!hasAutofillFeature()) {
615 return;
616 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700617 synchronized (mLock) {
618 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800619
Felipe Lemec24a56a2017-08-03 14:27:57 -0700620 if (mEnabled && isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700621 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800622
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700623 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700624 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700625 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800626 }
627 }
628
629 /**
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700630 * Called when a {@link View view's} visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700631 *
632 * @param view {@link View} that was exited.
633 * @param isVisible visible if the view is visible in the view hierarchy.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700634 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700635 public void notifyViewVisibilityChanged(@NonNull View view, boolean isVisible) {
636 notifyViewVisibilityChangedInternal(view, 0, isVisible, false);
637 }
638
639 /**
640 * Called when a virtual view's visibility changed.
641 *
642 * @param view {@link View} that was exited.
643 * @param virtualId id identifying the virtual child inside the parent view.
644 * @param isVisible visible if the view is visible in the view hierarchy.
645 */
646 public void notifyViewVisibilityChanged(@NonNull View view, int virtualId, boolean isVisible) {
647 notifyViewVisibilityChangedInternal(view, virtualId, isVisible, true);
648 }
649
650 /**
651 * Called when a view/virtual view's visibility changed.
652 *
653 * @param view {@link View} that was exited.
654 * @param virtualId id identifying the virtual child inside the parent view.
655 * @param isVisible visible if the view is visible in the view hierarchy.
656 * @param virtual Whether the view is virtual.
657 */
658 private void notifyViewVisibilityChangedInternal(@NonNull View view, int virtualId,
659 boolean isVisible, boolean virtual) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700660 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700661 if (mEnabled && isActiveLocked()) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700662 final AutofillId id = virtual ? getAutofillId(view, virtualId)
663 : view.getAutofillId();
Felipe Leme27e20222017-05-18 15:24:11 -0700664 if (!isVisible && mFillableIds != null) {
Felipe Leme27e20222017-05-18 15:24:11 -0700665 if (mFillableIds.contains(id)) {
666 if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
667 requestHideFillUi(id, view);
668 }
669 }
670 if (mTrackedViews != null) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -0700671 mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
Felipe Leme27e20222017-05-18 15:24:11 -0700672 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700673 }
674 }
675 }
676
677 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700678 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800679 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700680 * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
681 * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
682 * the absolute bounds could be calculated by:
683 *
684 * <pre class="prettyprint">
685 * int offset[] = new int[2];
686 * getLocationOnScreen(offset);
687 * Rect absBounds = new Rect(bounds.left + offset[0],
688 * bounds.top + offset[1],
689 * bounds.right + offset[0], bounds.bottom + offset[1]);
690 * </pre>
691 *
692 * @param view the virtual view parent.
693 * @param virtualId id identifying the virtual child inside the parent view.
694 * @param absBounds absolute boundaries of the virtual view in the screen.
Felipe Lemebab851c2017-02-03 18:45:08 -0800695 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700696 public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
697 notifyViewEntered(view, virtualId, absBounds, 0);
Felipe Lemed1146422017-04-26 10:17:05 -0700698 }
699
Felipe Leme6dcec872017-05-25 11:24:23 -0700700 private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700701 if (!hasAutofillFeature()) {
702 return;
703 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700704 AutofillCallback callback = null;
705 synchronized (mLock) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700706 if (isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
707 if (sVerbose) {
708 Log.v(TAG, "notifyViewEntered(flags=" + flags + ", view=" + view
709 + ", virtualId=" + virtualId
710 + "): ignored on state " + getStateAsStringLocked());
711 }
712 return;
713 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700714 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000715
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700716 if (!mEnabled) {
717 if (mCallback != null) {
718 callback = mCallback;
719 }
720 } else {
Felipe Leme6dcec872017-05-25 11:24:23 -0700721 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700722
Felipe Lemec24a56a2017-08-03 14:27:57 -0700723 if (!isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700724 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700725 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700726 } else {
727 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700728 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700729 }
Felipe Leme24aae152017-03-15 12:33:01 -0700730 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800731 }
732
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700733 if (callback != null) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700734 callback.onAutofillEvent(view, virtualId,
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700735 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800736 }
737 }
738
739 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700740 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800741 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700742 * @param view the virtual view parent.
743 * @param virtualId id identifying the virtual child inside the parent view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800744 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700745 public void notifyViewExited(@NonNull View view, int virtualId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700746 if (!hasAutofillFeature()) {
747 return;
748 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700749 synchronized (mLock) {
750 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800751
Felipe Lemec24a56a2017-08-03 14:27:57 -0700752 if (mEnabled && isActiveLocked()) {
Felipe Leme6dcec872017-05-25 11:24:23 -0700753 final AutofillId id = getAutofillId(view, virtualId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800754
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700755 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700756 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700757 }
Svet Ganov782043c2017-02-11 00:52:02 +0000758 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800759 }
760
761 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800762 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800763 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800764 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800765 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700766 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700767 if (!hasAutofillFeature()) {
768 return;
769 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700770 AutofillId id = null;
771 boolean valueWasRead = false;
772 AutofillValue value = null;
773
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700774 synchronized (mLock) {
775 // If the session is gone some fields might still be highlighted, hence we have to
776 // remove the isAutofilled property even if no sessions are active.
777 if (mLastAutofilledData == null) {
778 view.setAutofilled(false);
779 } else {
780 id = getAutofillId(view);
781 if (mLastAutofilledData.containsKey(id)) {
782 value = view.getAutofillValue();
783 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700784
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700785 if (Objects.equals(mLastAutofilledData.get(id), value)) {
786 view.setAutofilled(true);
787 } else {
788 view.setAutofilled(false);
789 mLastAutofilledData.remove(id);
790 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700791 } else {
792 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700793 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700794 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700795
Felipe Lemec24a56a2017-08-03 14:27:57 -0700796 if (!mEnabled || !isActiveLocked()) {
Felipe Lemec7b45292017-09-19 09:06:20 -0700797 if (sVerbose && mEnabled) {
798 Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
799 + getStateAsStringLocked());
800 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700801 return;
802 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800803
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700804 if (id == null) {
805 id = getAutofillId(view);
806 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700807
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700808 if (!valueWasRead) {
809 value = view.getAutofillValue();
810 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700811
Felipe Leme0aa4c502017-04-26 12:36:01 -0700812 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700813 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800814 }
815
Felipe Lemebab851c2017-02-03 18:45:08 -0800816 /**
Felipe Leme6dcec872017-05-25 11:24:23 -0700817 * Called to indicate the value of an autofillable virtual view has changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800818 *
Felipe Leme6dcec872017-05-25 11:24:23 -0700819 * @param view the virtual view parent.
820 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800821 * @param value new value of the child.
822 */
Felipe Leme6dcec872017-05-25 11:24:23 -0700823 public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700824 if (!hasAutofillFeature()) {
825 return;
826 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700827 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700828 if (!mEnabled || !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700829 return;
830 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800831
Felipe Leme6dcec872017-05-25 11:24:23 -0700832 final AutofillId id = getAutofillId(view, virtualId);
Felipe Leme0aa4c502017-04-26 12:36:01 -0700833 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700834 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800835 }
836
837 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700838 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800839 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700840 * <p>This method is typically called by {@link View Views} that manage virtual views; for
841 * example, when the view is rendering an {@code HTML} page with a form and virtual views
842 * that represent the HTML elements, it should call this method after the form is submitted and
843 * another page is rendered.
844 *
845 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
846 * methods such as {@link android.app.Activity#finish()}.
Felipe Lemebab851c2017-02-03 18:45:08 -0800847 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700848 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -0700849 if (!hasAutofillFeature()) {
850 return;
851 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700852 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700853 if (!mEnabled && !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700854 return;
855 }
Svet Ganov782043c2017-02-11 00:52:02 +0000856
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700857 finishSessionLocked();
858 }
Felipe Leme0200d9e2017-01-24 15:10:26 -0800859 }
860
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700861 /**
862 * Called to indicate the current autofill context should be cancelled.
863 *
Felipe Lemeafdfe762017-07-24 11:25:56 -0700864 * <p>This method is typically called by {@link View Views} that manage virtual views; for
865 * example, when the view is rendering an {@code HTML} page with a form and virtual views
866 * that represent the HTML elements, it should call this method if the user does not post the
867 * form but moves to another form in this page.
868 *
869 * <p><b>Note:</b> This method does not need to be called on regular application lifecycle
870 * methods such as {@link android.app.Activity#finish()}.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700871 */
872 public void cancel() {
Svet Ganov43574b02017-04-12 09:25:20 -0700873 if (!hasAutofillFeature()) {
874 return;
875 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700876 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700877 if (!mEnabled && !isActiveLocked()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700878 return;
879 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700880
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700881 cancelSessionLocked();
882 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700883 }
884
Svet Ganovf965b872017-04-28 16:34:02 -0700885 /** @hide */
886 public void disableOwnedAutofillServices() {
887 disableAutofillServices();
888 }
889
Svet Ganovf20a0372017-04-10 17:08:05 -0700890 /**
891 * If the app calling this API has enabled autofill services they
892 * will be disabled.
893 */
Svet Ganovf965b872017-04-28 16:34:02 -0700894 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -0700895 if (!hasAutofillFeature()) {
896 return;
897 }
Svet Ganovf20a0372017-04-10 17:08:05 -0700898 try {
899 mService.disableOwnedAutofillServices(mContext.getUserId());
900 } catch (RemoteException e) {
901 throw e.rethrowFromSystemServer();
902 }
903 }
904
Felipe Lemedb041182017-04-21 17:33:38 -0700905 /**
906 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
907 * enabled for the current user, or {@code false} otherwise.
908 */
909 public boolean hasEnabledAutofillServices() {
910 if (mService == null) return false;
911
912 try {
913 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
914 } catch (RemoteException e) {
915 throw e.rethrowFromSystemServer();
916 }
917 }
918
919 /**
Ricardo Loo33d226c2017-06-27 14:17:33 -0700920 * Returns {@code true} if autofill is supported by the current device and
921 * is supported for this user.
Felipe Lemedb041182017-04-21 17:33:38 -0700922 *
923 * <p>Autofill is typically supported, but it could be unsupported in cases like:
924 * <ol>
925 * <li>Low-end devices.
926 * <li>Device policy rules that forbid its usage.
927 * </ol>
928 */
929 public boolean isAutofillSupported() {
930 if (mService == null) return false;
931
932 try {
933 return mService.isServiceSupported(mContext.getUserId());
934 } catch (RemoteException e) {
935 throw e.rethrowFromSystemServer();
936 }
937 }
938
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700939 private AutofillClient getClientLocked() {
Felipe Leme640f30a2017-03-06 15:44:06 -0800940 if (mContext instanceof AutofillClient) {
941 return (AutofillClient) mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000942 }
Svet Ganov17db9dc2017-02-21 19:54:31 -0800943 return null;
Svet Ganov782043c2017-02-11 00:52:02 +0000944 }
945
946 /** @hide */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700947 public void onAuthenticationResult(int authenticationId, Intent data) {
Svet Ganov43574b02017-04-12 09:25:20 -0700948 if (!hasAutofillFeature()) {
949 return;
950 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -0700951 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -0800952 // handling the cases where it's not RESULT_OK: it works fine if the service does not
953 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
954 // service set the extra and returned RESULT_CANCELED...
955
Felipe Leme9f9ee252017-04-27 13:56:22 -0700956 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -0800957
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700958 synchronized (mLock) {
Felipe Lemec24a56a2017-08-03 14:27:57 -0700959 if (!isActiveLocked() || data == null) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700960 return;
961 }
962 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
963 final Bundle responseData = new Bundle();
964 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
965 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700966 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
967 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700968 } catch (RemoteException e) {
969 Log.e(TAG, "Error delivering authentication result", e);
970 }
Svet Ganov782043c2017-02-11 00:52:02 +0000971 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800972 }
973
Felipe Leme640f30a2017-03-06 15:44:06 -0800974 private static AutofillId getAutofillId(View view) {
Phil Weaver846cda932017-06-15 10:10:06 -0700975 return new AutofillId(view.getAutofillViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -0800976 }
977
Felipe Leme6dcec872017-05-25 11:24:23 -0700978 private static AutofillId getAutofillId(View parent, int virtualId) {
Phil Weaver846cda932017-06-15 10:10:06 -0700979 return new AutofillId(parent.getAutofillViewId(), virtualId);
Felipe Lemebab851c2017-02-03 18:45:08 -0800980 }
981
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700982 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
983 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700984 if (sVerbose) {
985 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Lemec7b45292017-09-19 09:06:20 -0700986 + ", flags=" + flags + ", state=" + getStateAsStringLocked());
Felipe Lemebab851c2017-02-03 18:45:08 -0800987 }
Felipe Lemec7b45292017-09-19 09:06:20 -0700988 if (mState != STATE_UNKNOWN && (flags & FLAG_MANUAL_REQUEST) == 0) {
989 if (sVerbose) {
990 Log.v(TAG, "not automatically starting session for " + id
991 + " on state " + getStateAsStringLocked());
992 }
Felipe Lemec24a56a2017-08-03 14:27:57 -0700993 return;
994 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800995 try {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700996 mSessionId = mService.startSession(mContext.getActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -0800997 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Philip P. Moltmann7b771162017-03-03 17:22:57 -0800998 mCallback != null, flags, mContext.getOpPackageName());
Felipe Lemec24a56a2017-08-03 14:27:57 -0700999 if (mSessionId != NO_SESSION) {
1000 mState = STATE_ACTIVE;
1001 }
Felipe Leme0aa4c502017-04-26 12:36:01 -07001002 final AutofillClient client = getClientLocked();
Svet Ganov17db9dc2017-02-21 19:54:31 -08001003 if (client != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001004 client.autofillCallbackResetableStateAvailable();
Svet Ganov17db9dc2017-02-21 19:54:31 -08001005 }
Svet Ganov782043c2017-02-11 00:52:02 +00001006 } catch (RemoteException e) {
1007 throw e.rethrowFromSystemServer();
1008 }
1009 }
1010
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001011 private void finishSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001012 if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001013
1014 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001015
Svet Ganov782043c2017-02-11 00:52:02 +00001016 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001017 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -08001018 } catch (RemoteException e) {
1019 throw e.rethrowFromSystemServer();
1020 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001021
Felipe Lemec24a56a2017-08-03 14:27:57 -07001022 resetSessionLocked();
Felipe Lemebab851c2017-02-03 18:45:08 -08001023 }
1024
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001025 private void cancelSessionLocked() {
Felipe Lemec7b45292017-09-19 09:06:20 -07001026 if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001027
1028 if (!isActiveLocked()) return;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001029
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001030 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001031 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001032 } catch (RemoteException e) {
1033 throw e.rethrowFromSystemServer();
1034 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001035
Svet Ganov48f10a22017-04-26 18:49:30 -07001036 resetSessionLocked();
1037 }
1038
1039 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001040 mSessionId = NO_SESSION;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001041 mState = STATE_UNKNOWN;
Svet Ganov48f10a22017-04-26 18:49:30 -07001042 mTrackedViews = null;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001043 mFillableIds = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -07001044 }
1045
Felipe Leme0aa4c502017-04-26 12:36:01 -07001046 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
1047 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001048 if (sVerbose && action != ACTION_VIEW_EXITED) {
1049 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
1050 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -08001051 }
Felipe Leme7f33cd32017-05-11 10:10:49 -07001052 boolean restartIfNecessary = (flags & FLAG_MANUAL_REQUEST) != 0;
1053
Felipe Leme3461d3c2017-01-19 08:54:55 -08001054 try {
Felipe Leme7f33cd32017-05-11 10:10:49 -07001055 if (restartIfNecessary) {
1056 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
1057 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
1058 mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action);
1059 if (newId != mSessionId) {
1060 if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
1061 mSessionId = newId;
Felipe Lemec24a56a2017-08-03 14:27:57 -07001062 mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
Felipe Leme7f33cd32017-05-11 10:10:49 -07001063 final AutofillClient client = getClientLocked();
1064 if (client != null) {
1065 client.autofillCallbackResetableStateAvailable();
1066 }
1067 }
1068 } else {
1069 mService.updateSession(mSessionId, id, bounds, value, action, flags,
1070 mContext.getUserId());
1071 }
1072
Felipe Leme3461d3c2017-01-19 08:54:55 -08001073 } catch (RemoteException e) {
1074 throw e.rethrowFromSystemServer();
1075 }
1076 }
Svet Ganov782043c2017-02-11 00:52:02 +00001077
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001078 private void ensureServiceClientAddedIfNeededLocked() {
1079 if (getClientLocked() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +00001080 return;
1081 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001082
Svet Ganov782043c2017-02-11 00:52:02 +00001083 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001084 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +00001085 try {
Felipe Leme9f9ee252017-04-27 13:56:22 -07001086 final int flags = mService.addClient(mServiceClient, mContext.getUserId());
1087 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
1088 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
1089 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Svet Ganov782043c2017-02-11 00:52:02 +00001090 } catch (RemoteException e) {
1091 throw e.rethrowFromSystemServer();
1092 }
1093 }
1094 }
1095
Felipe Lemee6010f22017-03-03 11:19:51 -08001096 /**
1097 * Registers a {@link AutofillCallback} to receive autofill events.
1098 *
1099 * @param callback callback to receive events.
1100 */
1101 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001102 if (!hasAutofillFeature()) {
1103 return;
1104 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001105 synchronized (mLock) {
1106 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001107
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001108 final boolean hadCallback = mCallback != null;
1109 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -08001110
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001111 if (!hadCallback) {
1112 try {
1113 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
1114 } catch (RemoteException e) {
1115 throw e.rethrowFromSystemServer();
1116 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001117 }
1118 }
1119 }
1120
1121 /**
1122 * Unregisters a {@link AutofillCallback} to receive autofill events.
1123 *
1124 * @param callback callback to stop receiving events.
1125 */
1126 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -07001127 if (!hasAutofillFeature()) {
1128 return;
1129 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001130 synchronized (mLock) {
1131 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -08001132
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001133 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001134
Felipe Lemee6010f22017-03-03 11:19:51 -08001135 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001136 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -08001137 } catch (RemoteException e) {
1138 throw e.rethrowFromSystemServer();
1139 }
1140 }
1141 }
1142
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001143 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1144 Rect anchorBounds, IAutofillWindowPresenter presenter) {
1145 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -07001146 if (anchor == null) {
1147 return;
1148 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001149
1150 AutofillCallback callback = null;
1151 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001152 if (mSessionId == sessionId) {
1153 AutofillClient client = getClientLocked();
1154
1155 if (client != null) {
1156 if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
1157 anchorBounds, presenter) && mCallback != null) {
1158 callback = mCallback;
1159 }
1160 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001161 }
1162 }
1163
1164 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001165 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001166 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001167 AutofillCallback.EVENT_INPUT_SHOWN);
1168 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001169 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001170 }
1171 }
1172 }
1173
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001174 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
1175 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001176 synchronized (mLock) {
1177 if (sessionId == mSessionId) {
1178 AutofillClient client = getClientLocked();
1179 if (client != null) {
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001180 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001181 }
1182 }
1183 }
1184 }
1185
Svet Ganov48f10a22017-04-26 18:49:30 -07001186 private void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001187 synchronized (mLock) {
1188 mEnabled = enabled;
Svet Ganov48f10a22017-04-26 18:49:30 -07001189 if (!mEnabled || resetSession) {
1190 // Reset the session state
1191 resetSessionLocked();
1192 }
1193 if (resetClient) {
1194 // Reset connection to system
1195 mServiceClient = null;
1196 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001197 }
1198 }
1199
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001200 /**
1201 * Sets a view as autofilled if the current value is the {code targetValue}.
1202 *
1203 * @param view The view that is to be autofilled
1204 * @param targetValue The value we want to fill into view
1205 */
1206 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
1207 AutofillValue currentValue = view.getAutofillValue();
1208 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001209 synchronized (mLock) {
1210 if (mLastAutofilledData == null) {
1211 mLastAutofilledData = new ParcelableMap(1);
1212 }
1213 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001214 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001215 view.setAutofilled(true);
1216 }
1217 }
1218
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001219 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001220 synchronized (mLock) {
1221 if (sessionId != mSessionId) {
1222 return;
Felipe Leme4753bb02017-03-22 20:24:00 -07001223 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001224
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001225 final AutofillClient client = getClientLocked();
1226 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001227 return;
1228 }
1229
1230 final int itemCount = ids.size();
1231 int numApplied = 0;
1232 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Phil Weaver846cda932017-06-15 10:10:06 -07001233 final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001234
1235 for (int i = 0; i < itemCount; i++) {
1236 final AutofillId id = ids.get(i);
1237 final AutofillValue value = values.get(i);
1238 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001239 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001240 if (view == null) {
1241 Log.w(TAG, "autofill(): no View with id " + viewId);
1242 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -07001243 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001244 if (id.isVirtual()) {
1245 if (virtualValues == null) {
1246 // Most likely there will be just one view with virtual children.
1247 virtualValues = new ArrayMap<>(1);
1248 }
1249 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
1250 if (valuesByParent == null) {
1251 // We don't know the size yet, but usually it will be just a few fields...
1252 valuesByParent = new SparseArray<>(5);
1253 virtualValues.put(view, valuesByParent);
1254 }
1255 valuesByParent.put(id.getVirtualChildId(), value);
1256 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001257 // Mark the view as to be autofilled with 'value'
1258 if (mLastAutofilledData == null) {
1259 mLastAutofilledData = new ParcelableMap(itemCount - i);
1260 }
1261 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001262
1263 view.autofill(value);
1264
1265 // Set as autofilled if the values match now, e.g. when the value was updated
1266 // synchronously.
1267 // If autofill happens async, the view is set to autofilled in
1268 // notifyValueChanged.
1269 setAutofilledIfValuesIs(view, value);
1270
1271 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001272 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001273 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001274
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001275 if (virtualValues != null) {
1276 for (int i = 0; i < virtualValues.size(); i++) {
1277 final View parent = virtualValues.keyAt(i);
1278 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1279 parent.autofill(childrenValues);
1280 numApplied += childrenValues.size();
1281 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001282 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001283
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001284 final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
1285 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
1286 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED,
1287 numApplied);
1288 mMetricsLogger.write(log);
1289 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001290 }
1291
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001292 /**
1293 * Set the tracked views.
1294 *
1295 * @param trackedIds The views to be tracked
1296 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
Felipe Leme27e20222017-05-18 15:24:11 -07001297 * @param fillableIds Views that might anchor FillUI.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001298 */
Felipe Leme27e20222017-05-18 15:24:11 -07001299 private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
1300 boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001301 synchronized (mLock) {
1302 if (mEnabled && mSessionId == sessionId) {
1303 if (saveOnAllViewsInvisible) {
1304 mTrackedViews = new TrackedViews(trackedIds);
1305 } else {
1306 mTrackedViews = null;
1307 }
Felipe Leme27e20222017-05-18 15:24:11 -07001308 if (fillableIds != null) {
1309 if (mFillableIds == null) {
1310 mFillableIds = new ArraySet<>(fillableIds.length);
1311 }
1312 for (AutofillId id : fillableIds) {
1313 mFillableIds.add(id);
1314 }
1315 if (sVerbose) {
1316 Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
1317 + ", mFillableIds" + mFillableIds);
1318 }
1319 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001320 }
1321 }
1322 }
1323
Felipe Lemec24a56a2017-08-03 14:27:57 -07001324 private void setSaveUiState(int sessionId, boolean shown) {
1325 if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
1326 synchronized (mLock) {
1327 if (mSessionId != NO_SESSION) {
1328 // Race condition: app triggered a new session after the previous session was
1329 // finished but before server called setSaveUiState() - need to cancel the new
1330 // session to avoid further inconsistent behavior.
1331 Log.w(TAG, "setSaveUiState(" + sessionId + ", " + shown
1332 + ") called on existing session " + mSessionId + "; cancelling it");
1333 cancelSessionLocked();
1334 }
1335 if (shown) {
1336 mSessionId = sessionId;
1337 mState = STATE_SHOWING_SAVE_UI;
1338 } else {
1339 mSessionId = NO_SESSION;
1340 mState = STATE_UNKNOWN;
1341 }
1342 }
1343 }
1344
Felipe Lemec7b45292017-09-19 09:06:20 -07001345 private void setSessionFinished() {
1346 if (sVerbose) Log.v(TAG, "setSessionFinished()");
1347 synchronized (mLock) {
1348 resetSessionLocked();
1349 mState = STATE_FINISHED;
1350 }
1351 }
1352
Felipe Leme27e20222017-05-18 15:24:11 -07001353 private void requestHideFillUi(AutofillId id) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001354 final View anchor = findView(id);
Felipe Leme27e20222017-05-18 15:24:11 -07001355 if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001356 if (anchor == null) {
1357 return;
1358 }
Felipe Leme27e20222017-05-18 15:24:11 -07001359 requestHideFillUi(id, anchor);
1360 }
1361
1362 private void requestHideFillUi(AutofillId id, View anchor) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001363
1364 AutofillCallback callback = null;
1365 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001366 // We do not check the session id for two reasons:
1367 // 1. If local and remote session id are off sync the UI would be stuck shown
1368 // 2. There is a race between the user state being destroyed due the fill
1369 // service being uninstalled and the UI being dismissed.
1370 AutofillClient client = getClientLocked();
1371 if (client != null) {
1372 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1373 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001374 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001375 }
1376 }
1377
1378 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001379 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001380 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001381 AutofillCallback.EVENT_INPUT_HIDDEN);
1382 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001383 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001384 }
1385 }
1386 }
1387
Felipe Lemec7b45292017-09-19 09:06:20 -07001388 private void notifyNoFillUi(int sessionId, AutofillId id, boolean sessionFinished) {
1389 if (sVerbose) {
1390 Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
1391 + ", finished=" + sessionFinished);
1392 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001393 final View anchor = findView(id);
1394 if (anchor == null) {
1395 return;
1396 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001397
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001398 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001399 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001400 if (mSessionId == sessionId && getClientLocked() != null) {
1401 callback = mCallback;
1402 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001403 }
1404
1405 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001406 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001407 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001408 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1409 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001410 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001411 }
Felipe Lemec7b45292017-09-19 09:06:20 -07001412 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001413
Felipe Lemec7b45292017-09-19 09:06:20 -07001414 if (sessionFinished) {
1415 // Callback call was "hijacked" to also update the session state.
1416 setSessionFinished();
Felipe Leme4753bb02017-03-22 20:24:00 -07001417 }
1418 }
1419
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001420 /**
1421 * Get an array of viewIds from a List of {@link AutofillId}.
1422 *
1423 * @param autofillIds The autofill ids to convert
1424 *
1425 * @return The array of viewIds.
1426 */
Felipe Leme27e20222017-05-18 15:24:11 -07001427 // TODO: move to Helper as static method
1428 @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
1429 final int numIds = autofillIds.length;
1430 final int[] viewIds = new int[numIds];
1431 for (int i = 0; i < numIds; i++) {
1432 viewIds[i] = autofillIds[i].getViewId();
1433 }
1434
1435 return viewIds;
1436 }
1437
1438 // TODO: move to Helper as static method
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001439 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1440 final int numIds = autofillIds.size();
1441 final int[] viewIds = new int[numIds];
1442 for (int i = 0; i < numIds; i++) {
1443 viewIds[i] = autofillIds.get(i).getViewId();
1444 }
1445
1446 return viewIds;
1447 }
1448
1449 /**
1450 * Find a single view by its id.
1451 *
1452 * @param autofillId The autofill id of the view
1453 *
1454 * @return The view or {@code null} if view was not found
1455 */
1456 private View findView(@NonNull AutofillId autofillId) {
1457 final AutofillClient client = getClientLocked();
1458
1459 if (client == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001460 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001461 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001462
Phil Weaver846cda932017-06-15 10:10:06 -07001463 return client.findViewByAutofillIdTraversal(autofillId.getViewId());
Felipe Lemee6010f22017-03-03 11:19:51 -08001464 }
1465
Felipe Lemebb810922017-04-25 15:54:06 -07001466 /** @hide */
1467 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001468 return mService != null;
1469 }
1470
Felipe Lemec24a56a2017-08-03 14:27:57 -07001471 /** @hide */
1472 public void onPendingSaveUi(int operation, IBinder token) {
1473 if (sVerbose) Log.v(TAG, "onPendingSaveUi(" + operation + "): " + token);
1474
1475 synchronized (mLock) {
1476 try {
1477 mService.onPendingSaveUi(operation, token);
1478 } catch (RemoteException e) {
1479 e.rethrowFromSystemServer();
1480 }
1481 }
1482 }
1483
1484 /** @hide */
1485 public void dump(String outerPrefix, PrintWriter pw) {
1486 pw.print(outerPrefix); pw.println("AutofillManager:");
1487 final String pfx = outerPrefix + " ";
1488 pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
Felipe Lemec7b45292017-09-19 09:06:20 -07001489 pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001490 pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
1491 pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
1492 pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
1493 pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
1494 pw.print(pfx); pw.print("tracked views: ");
1495 if (mTrackedViews == null) {
1496 pw.println("null");
1497 } else {
1498 final String pfx2 = pfx + " ";
1499 pw.println();
1500 pw.print(pfx2); pw.print("visible:"); pw.println(mTrackedViews.mVisibleTrackedIds);
1501 pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
1502 }
1503 pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
1504 }
1505
Felipe Lemec7b45292017-09-19 09:06:20 -07001506 private String getStateAsStringLocked() {
1507 switch (mState) {
1508 case STATE_UNKNOWN:
1509 return "STATE_UNKNOWN";
1510 case STATE_ACTIVE:
1511 return "STATE_ACTIVE";
1512 case STATE_FINISHED:
1513 return "STATE_FINISHED";
1514 case STATE_SHOWING_SAVE_UI:
1515 return "STATE_SHOWING_SAVE_UI";
1516 default:
1517 return "INVALID:" + mState;
1518 }
1519 }
1520
Felipe Lemec24a56a2017-08-03 14:27:57 -07001521 private boolean isActiveLocked() {
1522 return mState == STATE_ACTIVE;
1523 }
1524
Felipe Lemec7b45292017-09-19 09:06:20 -07001525 private boolean isFinishedLocked() {
1526 return mState == STATE_FINISHED;
1527 }
1528
Felipe Leme9876a6f2017-05-30 15:47:28 -07001529 private void post(Runnable runnable) {
1530 final AutofillClient client = getClientLocked();
1531 if (client == null) {
1532 if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
1533 return;
1534 }
1535 client.runOnUiThread(runnable);
1536 }
1537
Felipe Lemee6010f22017-03-03 11:19:51 -08001538 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001539 * View tracking information. Once all tracked views become invisible the session is finished.
1540 */
1541 private class TrackedViews {
1542 /** Visible tracked views */
1543 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1544
1545 /** Invisible tracked views */
1546 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1547
1548 /**
1549 * Check if set is null or value is in set.
1550 *
1551 * @param set The set or null (== empty set)
1552 * @param value The value that might be in the set
1553 *
1554 * @return {@code true} iff set is not empty and value is in set
1555 */
Felipe Leme27e20222017-05-18 15:24:11 -07001556 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001557 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1558 return set != null && set.contains(value);
1559 }
1560
1561 /**
1562 * Add a value to a set. If set is null, create a new set.
1563 *
1564 * @param set The set or null (== empty set)
1565 * @param valueToAdd The value to add
1566 *
1567 * @return The set including the new value. If set was {@code null}, a set containing only
1568 * the new value.
1569 */
Felipe Leme27e20222017-05-18 15:24:11 -07001570 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001571 @NonNull
1572 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1573 if (set == null) {
1574 set = new ArraySet<>(1);
1575 }
1576
1577 set.add(valueToAdd);
1578
1579 return set;
1580 }
1581
1582 /**
1583 * Remove a value from a set.
1584 *
1585 * @param set The set or null (== empty set)
1586 * @param valueToRemove The value to remove
1587 *
1588 * @return The set without the removed value. {@code null} if set was null, or is empty
1589 * after removal.
1590 */
Felipe Leme27e20222017-05-18 15:24:11 -07001591 // TODO: move to Helper as static method
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001592 @Nullable
1593 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1594 if (set == null) {
1595 return null;
1596 }
1597
1598 set.remove(valueToRemove);
1599
1600 if (set.isEmpty()) {
1601 return null;
1602 }
1603
1604 return set;
1605 }
1606
1607 /**
1608 * Set the tracked views.
1609 *
1610 * @param trackedIds The views to be tracked
1611 */
Felipe Leme27e20222017-05-18 15:24:11 -07001612 TrackedViews(@Nullable AutofillId[] trackedIds) {
1613 final AutofillClient client = getClientLocked();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001614 if (trackedIds != null && client != null) {
1615 final boolean[] isVisible;
1616
1617 if (client.isVisibleForAutofill()) {
1618 isVisible = client.getViewVisibility(getViewIds(trackedIds));
1619 } else {
1620 // All false
Felipe Leme27e20222017-05-18 15:24:11 -07001621 isVisible = new boolean[trackedIds.length];
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001622 }
1623
Felipe Leme27e20222017-05-18 15:24:11 -07001624 final int numIds = trackedIds.length;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001625 for (int i = 0; i < numIds; i++) {
Felipe Leme27e20222017-05-18 15:24:11 -07001626 final AutofillId id = trackedIds[i];
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001627
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001628 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07001629 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001630 } else {
1631 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1632 }
1633 }
1634 }
1635
Felipe Leme9f9ee252017-04-27 13:56:22 -07001636 if (sVerbose) {
1637 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001638 + " mVisibleTrackedIds=" + mVisibleTrackedIds
1639 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1640 }
1641
1642 if (mVisibleTrackedIds == null) {
1643 finishSessionLocked();
1644 }
1645 }
1646
1647 /**
1648 * Called when a {@link View view's} visibility changes.
1649 *
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001650 * @param id the id of the view/virtual view whose visibility changed.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001651 * @param isVisible visible if the view is visible in the view hierarchy.
1652 */
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001653 void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001654 AutofillClient client = getClientLocked();
1655
Felipe Leme9f9ee252017-04-27 13:56:22 -07001656 if (sDebug) {
Svetoslav Ganov4db89ba2017-06-28 16:50:37 -07001657 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001658 + isVisible);
1659 }
1660
1661 if (client != null && client.isVisibleForAutofill()) {
1662 if (isVisible) {
1663 if (isInSet(mInvisibleTrackedIds, id)) {
1664 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1665 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1666 }
1667 } else {
1668 if (isInSet(mVisibleTrackedIds, id)) {
1669 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1670 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1671 }
1672 }
1673 }
1674
1675 if (mVisibleTrackedIds == null) {
Felipe Leme27e20222017-05-18 15:24:11 -07001676 if (sVerbose) {
1677 Log.v(TAG, "No more visible ids. Invisibile = " + mInvisibleTrackedIds);
1678 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001679 finishSessionLocked();
1680 }
1681 }
1682
1683 /**
1684 * Called once the client becomes visible.
1685 *
1686 * @see AutofillClient#isVisibleForAutofill()
1687 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001688 void onVisibleForAutofillLocked() {
1689 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001690 // hence update the visibility state for all views.
1691 AutofillClient client = getClientLocked();
1692 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1693 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1694 if (client != null) {
1695 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001696 final ArrayList<AutofillId> orderedInvisibleIds =
1697 new ArrayList<>(mInvisibleTrackedIds);
1698 final boolean[] isVisible = client.getViewVisibility(
1699 getViewIds(orderedInvisibleIds));
1700
1701 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1702 for (int i = 0; i < numInvisibleTrackedIds; i++) {
1703 final AutofillId id = orderedInvisibleIds.get(i);
1704 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001705 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1706
Felipe Leme9f9ee252017-04-27 13:56:22 -07001707 if (sDebug) {
1708 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001709 }
1710 } else {
1711 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1712 }
1713 }
1714 }
1715
1716 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001717 final ArrayList<AutofillId> orderedVisibleIds =
1718 new ArrayList<>(mVisibleTrackedIds);
1719 final boolean[] isVisible = client.getViewVisibility(
1720 getViewIds(orderedVisibleIds));
1721
1722 final int numVisibleTrackedIds = orderedVisibleIds.size();
1723 for (int i = 0; i < numVisibleTrackedIds; i++) {
1724 final AutofillId id = orderedVisibleIds.get(i);
1725
1726 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001727 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1728 } else {
1729 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1730
Felipe Leme9f9ee252017-04-27 13:56:22 -07001731 if (sDebug) {
1732 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001733 }
1734 }
1735 }
1736 }
1737
1738 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
1739 mVisibleTrackedIds = updatedVisibleTrackedIds;
1740 }
1741
1742 if (mVisibleTrackedIds == null) {
1743 finishSessionLocked();
1744 }
1745 }
1746 }
1747
1748 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001749 * Callback for autofill related events.
Felipe Lemee6010f22017-03-03 11:19:51 -08001750 *
1751 * <p>Typically used for applications that display their own "auto-complete" views, so they can
Felipe Leme744976e2017-07-31 11:34:14 -07001752 * enable / disable such views when the autofill UI affordance is shown / hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08001753 */
1754 public abstract static class AutofillCallback {
1755
1756 /** @hide */
1757 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
1758 @Retention(RetentionPolicy.SOURCE)
1759 public @interface AutofillEventType {}
1760
1761 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001762 * The autofill input UI affordance associated with the view was shown.
Felipe Lemee6010f22017-03-03 11:19:51 -08001763 *
1764 * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
1765 * should be hidden upon receiving this event.
1766 */
1767 public static final int EVENT_INPUT_SHOWN = 1;
1768
1769 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001770 * The autofill input UI affordance associated with the view was hidden.
Felipe Lemee6010f22017-03-03 11:19:51 -08001771 *
1772 * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
1773 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
1774 */
1775 public static final int EVENT_INPUT_HIDDEN = 2;
1776
1777 /**
Felipe Leme744976e2017-07-31 11:34:14 -07001778 * The autofill input UI affordance associated with the view isn't shown because
Felipe Leme24aae152017-03-15 12:33:01 -07001779 * autofill is not available.
1780 *
1781 * <p>If the view provides its own auto-complete UI affordance but was not displaying it
1782 * to avoid flickering, it could shown it upon receiving this event.
1783 */
1784 public static final int EVENT_INPUT_UNAVAILABLE = 3;
1785
1786 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08001787 * Called after a change in the autofill state associated with a view.
1788 *
1789 * @param view view associated with the change.
1790 *
1791 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1792 */
Felipe Leme81f01d92017-03-16 17:13:25 -07001793 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
1794 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001795
1796 /**
1797 * Called after a change in the autofill state associated with a virtual view.
1798 *
1799 * @param view parent view associated with the change.
Felipe Leme6dcec872017-05-25 11:24:23 -07001800 * @param virtualId id identifying the virtual child inside the parent view.
Felipe Lemee6010f22017-03-03 11:19:51 -08001801 *
1802 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1803 */
Felipe Leme6dcec872017-05-25 11:24:23 -07001804 public void onAutofillEvent(@NonNull View view, int virtualId,
1805 @AutofillEventType int event) {
Felipe Leme81f01d92017-03-16 17:13:25 -07001806 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001807 }
1808
Felipe Leme640f30a2017-03-06 15:44:06 -08001809 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
1810 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00001811
Felipe Leme640f30a2017-03-06 15:44:06 -08001812 AutofillManagerClient(AutofillManager autofillManager) {
1813 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00001814 }
1815
1816 @Override
Svet Ganov48f10a22017-04-26 18:49:30 -07001817 public void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001818 final AutofillManager afm = mAfm.get();
1819 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001820 afm.post(() -> afm.setState(enabled, resetSession, resetClient));
Svet Ganov782043c2017-02-11 00:52:02 +00001821 }
1822 }
1823
1824 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001825 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001826 final AutofillManager afm = mAfm.get();
1827 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001828 afm.post(() -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00001829 }
1830 }
1831
1832 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001833 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
1834 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001835 final AutofillManager afm = mAfm.get();
1836 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001837 afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00001838 }
1839 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001840
1841 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001842 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1843 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001844 final AutofillManager afm = mAfm.get();
1845 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001846 afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
1847 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07001848 }
1849 }
1850
1851 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001852 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001853 final AutofillManager afm = mAfm.get();
1854 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001855 afm.post(() -> afm.requestHideFillUi(id));
Felipe Leme4753bb02017-03-22 20:24:00 -07001856 }
1857 }
1858
1859 @Override
Felipe Lemec7b45292017-09-19 09:06:20 -07001860 public void notifyNoFillUi(int sessionId, AutofillId id, boolean sessionFinished) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001861 final AutofillManager afm = mAfm.get();
1862 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001863 afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinished));
Felipe Lemee6010f22017-03-03 11:19:51 -08001864 }
1865 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07001866
1867 @Override
Felipe Lemec24a56a2017-08-03 14:27:57 -07001868 public void startIntentSender(IntentSender intentSender, Intent intent) {
Svet Ganovc3d1c852017-04-12 22:34:28 -07001869 final AutofillManager afm = mAfm.get();
1870 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001871 afm.post(() -> {
Svet Ganovc3d1c852017-04-12 22:34:28 -07001872 try {
Felipe Lemec24a56a2017-08-03 14:27:57 -07001873 afm.mContext.startIntentSender(intentSender, intent, 0, 0, 0);
Svet Ganovc3d1c852017-04-12 22:34:28 -07001874 } catch (IntentSender.SendIntentException e) {
1875 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
1876 }
1877 });
1878 }
1879 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001880
1881 @Override
Felipe Leme27e20222017-05-18 15:24:11 -07001882 public void setTrackedViews(int sessionId, AutofillId[] ids,
1883 boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001884 final AutofillManager afm = mAfm.get();
1885 if (afm != null) {
Felipe Leme9876a6f2017-05-30 15:47:28 -07001886 afm.post(() ->
Felipe Leme27e20222017-05-18 15:24:11 -07001887 afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds)
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001888 );
1889 }
1890 }
Felipe Lemec24a56a2017-08-03 14:27:57 -07001891
1892 @Override
1893 public void setSaveUiState(int sessionId, boolean shown) {
1894 final AutofillManager afm = mAfm.get();
1895 if (afm != null) {
Felipe Lemec7b45292017-09-19 09:06:20 -07001896 afm.post(() -> afm.setSaveUiState(sessionId, shown));
1897 }
1898 }
1899
1900 @Override
1901 public void setSessionFinished() {
1902 final AutofillManager afm = mAfm.get();
1903 if (afm != null) {
1904 afm.post(() -> afm.setSessionFinished());
Felipe Lemec24a56a2017-08-03 14:27:57 -07001905 }
1906 }
Svet Ganov782043c2017-02-11 00:52:02 +00001907 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001908}