blob: 68c74da6a7c4ef5bdf70b7d92168eb3a666b1057 [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;
Felipe Leme3461d3c2017-01-19 08:54:55 -080026import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000027import android.content.Intent;
28import android.content.IntentSender;
Felipe Leme3461d3c2017-01-19 08:54:55 -080029import android.graphics.Rect;
Felipe Leme4753bb02017-03-22 20:24:00 -070030import android.metrics.LogMaker;
Svet Ganov782043c2017-02-11 00:52:02 +000031import android.os.Bundle;
Svet Ganov782043c2017-02-11 00:52:02 +000032import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080033import android.os.RemoteException;
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -070034import android.service.autofill.AutofillService;
35import android.service.autofill.FillEventHistory;
Felipe Leme4753bb02017-03-22 20:24:00 -070036import android.util.ArrayMap;
Philip P. Moltmann494c3f52017-04-11 10:13:33 -070037import android.util.ArraySet;
Felipe Leme3461d3c2017-01-19 08:54:55 -080038import android.util.Log;
Felipe Leme4753bb02017-03-22 20:24:00 -070039import android.util.SparseArray;
Felipe Leme3461d3c2017-01-19 08:54:55 -080040import android.view.View;
41
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070042import com.android.internal.annotations.GuardedBy;
Felipe Leme4753bb02017-03-22 20:24:00 -070043import com.android.internal.logging.MetricsLogger;
44import com.android.internal.logging.nano.MetricsProto;
45
Felipe Lemee6010f22017-03-03 11:19:51 -080046import java.lang.annotation.Retention;
47import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000048import java.lang.ref.WeakReference;
Philip P. Moltmann134cee22017-05-06 11:28:38 -070049import java.util.ArrayList;
Svet Ganov782043c2017-02-11 00:52:02 +000050import java.util.List;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -070051import java.util.Objects;
Svet Ganov782043c2017-02-11 00:52:02 +000052
Felipe Leme3461d3c2017-01-19 08:54:55 -080053/**
Felipe Leme85d1c2d2017-04-21 08:56:04 -070054 * App entry point to the Autofill Framework.
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -070055 *
56 * <p>It is safe to call into this from any thread.
Felipe Leme3461d3c2017-01-19 08:54:55 -080057 */
Felipe Leme640f30a2017-03-06 15:44:06 -080058public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -080059
Felipe Leme640f30a2017-03-06 15:44:06 -080060 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -080061
Svet Ganov782043c2017-02-11 00:52:02 +000062 /**
63 * Intent extra: The assist structure which captures the filled screen.
Felipe Leme7320ca92017-03-29 15:09:54 -070064 *
Svet Ganov782043c2017-02-11 00:52:02 +000065 * <p>
66 * Type: {@link android.app.assist.AssistStructure}
Svet Ganov782043c2017-02-11 00:52:02 +000067 */
68 public static final String EXTRA_ASSIST_STRUCTURE =
69 "android.view.autofill.extra.ASSIST_STRUCTURE";
70
71 /**
72 * Intent extra: The result of an authentication operation. It is
73 * either a fully populated {@link android.service.autofill.FillResponse}
74 * or a fully populated {@link android.service.autofill.Dataset} if
75 * a response or a dataset is being authenticated respectively.
76 *
77 * <p>
78 * Type: {@link android.service.autofill.FillResponse} or a
79 * {@link android.service.autofill.Dataset}
Svet Ganov782043c2017-02-11 00:52:02 +000080 */
81 public static final String EXTRA_AUTHENTICATION_RESULT =
82 "android.view.autofill.extra.AUTHENTICATION_RESULT";
83
Felipe Leme7320ca92017-03-29 15:09:54 -070084 /**
85 * Intent extra: The optional extras provided by the
86 * {@link android.service.autofill.AutofillService}.
87 *
88 * <p>For example, when the service responds to a {@link
89 * android.service.autofill.FillCallback#onSuccess(android.service.autofill.FillResponse)} with
90 * a {@code FillResponse} that requires authentication, the Intent that launches the
91 * service authentication will contain the Bundle set by
Felipe Leme49e96962017-04-20 15:46:18 -070092 * {@link android.service.autofill.FillResponse.Builder#setClientState(Bundle)} on this extra.
Felipe Leme7320ca92017-03-29 15:09:54 -070093 *
94 * <p>
95 * Type: {@link android.os.Bundle}
96 */
Felipe Leme37a440fd2017-04-28 10:50:20 -070097 public static final String EXTRA_CLIENT_STATE =
Jeff Sharkey000ce802017-04-29 13:13:27 -060098 "android.view.autofill.extra.CLIENT_STATE";
Felipe Leme7320ca92017-03-29 15:09:54 -070099
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700100 static final String SESSION_ID_TAG = "android:sessionId";
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700101 static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
102
Felipe Leme0aa4c502017-04-26 12:36:01 -0700103 /** @hide */ public static final int ACTION_START_SESSION = 1;
104 /** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
105 /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
106 /** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800107
Felipe Leme9f9ee252017-04-27 13:56:22 -0700108
109 /** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
110 /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
111 /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
112
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700113 /** Which bits in an authentication id are used for the dataset id */
114 private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
115 /** How many bits in an authentication id are used for the dataset id */
116 private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16;
117 /** @hide The index for an undefined data set */
118 public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF;
119
120 /**
121 * Makes an authentication id from a request id and a dataset id.
122 *
123 * @param requestId The request id.
124 * @param datasetId The dataset id.
125 * @return The authentication id.
126 * @hide
127 */
128 public static int makeAuthenticationId(int requestId, int datasetId) {
129 return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT)
130 | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK);
131 }
132
133 /**
134 * Gets the request id from an authentication id.
135 *
136 * @param authRequestId The authentication id.
137 * @return The request id.
138 * @hide
139 */
140 public static int getRequestIdFromAuthenticationId(int authRequestId) {
141 return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT);
142 }
143
144 /**
145 * Gets the dataset id from an authentication id.
146 *
147 * @param authRequestId The authentication id.
148 * @return The dataset id.
149 * @hide
150 */
151 public static int getDatasetIdFromAuthenticationId(int authRequestId) {
152 return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK);
153 }
154
Felipe Leme4753bb02017-03-22 20:24:00 -0700155 private final MetricsLogger mMetricsLogger = new MetricsLogger();
Svet Ganov782043c2017-02-11 00:52:02 +0000156
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700157 /**
158 * There is currently no session running.
159 * {@hide}
160 */
161 public static final int NO_SESSION = Integer.MIN_VALUE;
162
Svet Ganov782043c2017-02-11 00:52:02 +0000163 private final IAutoFillManager mService;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700164
165 private final Object mLock = new Object();
166
167 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000168 private IAutoFillManagerClient mServiceClient;
169
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700170 @GuardedBy("mLock")
Felipe Lemee6010f22017-03-03 11:19:51 -0800171 private AutofillCallback mCallback;
172
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700173 private final Context mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000174
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700175 @GuardedBy("mLock")
176 private int mSessionId = NO_SESSION;
177
178 @GuardedBy("mLock")
Svet Ganov782043c2017-02-11 00:52:02 +0000179 private boolean mEnabled;
180
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700181 /** If a view changes to this mapping the autofill operation was successful */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700182 @GuardedBy("mLock")
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700183 @Nullable private ParcelableMap mLastAutofilledData;
184
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700185 /** If view tracking is enabled, contains the tracking state */
186 @GuardedBy("mLock")
187 @Nullable private TrackedViews mTrackedViews;
188
Svet Ganov782043c2017-02-11 00:52:02 +0000189 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -0800190 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000191 /**
Svet Ganov782043c2017-02-11 00:52:02 +0000192 * Asks the client to start an authentication flow.
193 *
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700194 * @param authenticationId A unique id of the authentication operation.
Svet Ganov782043c2017-02-11 00:52:02 +0000195 * @param intent The authentication intent.
196 * @param fillInIntent The authentication fill-in intent.
197 */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700198 void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
199 Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800200
201 /**
202 * Tells the client this manager has state to be reset.
203 */
Felipe Leme4753bb02017-03-22 20:24:00 -0700204 void autofillCallbackResetableStateAvailable();
205
206 /**
207 * Request showing the autofill UI.
208 *
209 * @param anchor The real view the UI needs to anchor to.
210 * @param width The width of the fill UI content.
211 * @param height The height of the fill UI content.
212 * @param virtualBounds The bounds of the virtual decendant of the anchor.
213 * @param presenter The presenter that controls the fill UI window.
214 * @return Whether the UI was shown.
215 */
216 boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
217 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
218
219 /**
220 * Request hiding the autofill UI.
221 *
222 * @return Whether the UI was hidden.
223 */
224 boolean autofillCallbackRequestHideFillUi();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700225
226 /**
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700227 * Checks if views are currently attached and visible.
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700228 *
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700229 * @return And array with {@code true} iff the view is attached or visible
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700230 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700231 @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700232
233 /**
234 * Checks is the client is currently visible as understood by autofill.
235 *
236 * @return {@code true} if the client is currently visible
237 */
238 boolean isVisibleForAutofill();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700239
240 /**
241 * Find views by traversing the hierarchies of the client.
242 *
243 * @param viewIds The accessibility ids of the views to find
244 *
245 * @return And array containing the views, or {@code null} if not found
246 */
247 @NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds);
Svet Ganov782043c2017-02-11 00:52:02 +0000248 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800249
250 /**
251 * @hide
252 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800253 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Lemebab851c2017-02-03 18:45:08 -0800254 mContext = context;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800255 mService = service;
256 }
257
258 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700259 * Restore state after activity lifecycle
260 *
261 * @param savedInstanceState The state to be restored
262 *
263 * {@hide}
264 */
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700265 public void onCreate(Bundle savedInstanceState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700266 if (!hasAutofillFeature()) {
267 return;
268 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700269 synchronized (mLock) {
270 mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
271
272 if (mSessionId != NO_SESSION) {
273 Log.w(TAG, "New session was started before onCreate()");
274 return;
275 }
276
277 mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
278
279 if (mSessionId != NO_SESSION) {
280 ensureServiceClientAddedIfNeededLocked();
281
282 final AutofillClient client = getClientLocked();
283 if (client != null) {
284 try {
285 final boolean sessionWasRestored = mService.restoreSession(mSessionId,
286 mContext.getActivityToken(), mServiceClient.asBinder());
287
288 if (!sessionWasRestored) {
289 Log.w(TAG, "Session " + mSessionId + " could not be restored");
290 mSessionId = NO_SESSION;
291 } else {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700292 if (sDebug) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700293 Log.d(TAG, "session " + mSessionId + " was restored");
294 }
295
296 client.autofillCallbackResetableStateAvailable();
297 }
298 } catch (RemoteException e) {
299 Log.e(TAG, "Could not figure out if there was an autofill session", e);
300 }
301 }
302 }
303 }
304 }
305
306 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700307 * Called once the client becomes visible.
308 *
309 * @see AutofillClient#isVisibleForAutofill()
310 *
311 * {@hide}
312 */
313 public void onVisibleForAutofill() {
314 synchronized (mLock) {
315 if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700316 mTrackedViews.onVisibleForAutofillLocked();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700317 }
318 }
319 }
320
321 /**
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700322 * Save state before activity lifecycle
323 *
324 * @param outState Place to store the state
325 *
326 * {@hide}
327 */
328 public void onSaveInstanceState(Bundle outState) {
Svet Ganov43574b02017-04-12 09:25:20 -0700329 if (!hasAutofillFeature()) {
330 return;
331 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700332 synchronized (mLock) {
333 if (mSessionId != NO_SESSION) {
334 outState.putInt(SESSION_ID_TAG, mSessionId);
335 }
336
337 if (mLastAutofilledData != null) {
338 outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
339 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700340 }
341 }
342
343 /**
344 * Checks whether autofill is enabled for the current user.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700345 *
346 * <p>Typically used to determine whether the option to explicitly request autofill should
347 * be offered - see {@link #requestAutofill(View)}.
348 *
349 * @return whether autofill is enabled for the current user.
350 */
351 public boolean isEnabled() {
Svet Ganov43574b02017-04-12 09:25:20 -0700352 if (!hasAutofillFeature()) {
353 return false;
354 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700355 synchronized (mLock) {
356 ensureServiceClientAddedIfNeededLocked();
357 return mEnabled;
358 }
Felipe Leme2ac463e2017-03-13 14:06:25 -0700359 }
360
361 /**
Philip P. Moltmanncc684ed2017-04-17 14:18:33 -0700362 * Should always be called from {@link AutofillService#getFillEventHistory()}.
363 *
364 * @hide
365 */
366 @Nullable public FillEventHistory getFillEventHistory() {
367 try {
368 return mService.getFillEventHistory();
369 } catch (RemoteException e) {
370 e.rethrowFromSystemServer();
371 return null;
372 }
373 }
374
375 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700376 * Explicitly requests a new autofill context.
377 *
378 * <p>Normally, the autofill context is automatically started when autofillable views are
379 * focused, but this method should be used in the cases where it must be explicitly requested,
380 * like a view that provides a contextual menu allowing users to autofill the activity.
381 *
382 * @param view view requesting the new autofill context.
383 */
384 public void requestAutofill(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700385 notifyViewEntered(view, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700386 }
387
388 /**
389 * Explicitly requests a new autofill context for virtual views.
390 *
391 * <p>Normally, the autofill context is automatically started when autofillable views are
392 * focused, but this method should be used in the cases where it must be explicitly requested,
393 * like a virtual view that provides a contextual menu allowing users to autofill the activity.
394 *
395 * @param view the {@link View} whose descendant is the virtual view.
396 * @param childId id identifying the virtual child inside the view.
397 * @param bounds child boundaries, relative to the top window.
398 */
399 public void requestAutofill(@NonNull View view, int childId, @NonNull Rect bounds) {
Felipe Lemed1146422017-04-26 10:17:05 -0700400 notifyViewEntered(view, childId, bounds, FLAG_MANUAL_REQUEST);
Felipe Leme2ac463e2017-03-13 14:06:25 -0700401 }
402
Felipe Leme2ac463e2017-03-13 14:06:25 -0700403 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700404 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800405 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700406 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800407 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700408 public void notifyViewEntered(@NonNull View view) {
Felipe Lemed1146422017-04-26 10:17:05 -0700409 notifyViewEntered(view, 0);
410 }
411
412 private void notifyViewEntered(@NonNull View view, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700413 if (!hasAutofillFeature()) {
414 return;
415 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700416 AutofillCallback callback = null;
417 synchronized (mLock) {
418 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000419
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700420 if (!mEnabled) {
421 if (mCallback != null) {
422 callback = mCallback;
423 }
424 } else {
425 final AutofillId id = getAutofillId(view);
426 final AutofillValue value = view.getAutofillValue();
427
428 if (mSessionId == NO_SESSION) {
429 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700430 startSessionLocked(id, null, value, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700431 } else {
432 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700433 updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700434 }
Felipe Leme24aae152017-03-15 12:33:01 -0700435 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800436 }
437
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700438 if (callback != null) {
439 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Svet Ganov782043c2017-02-11 00:52:02 +0000440 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800441 }
442
443 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700444 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800445 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700446 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800447 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700448 public void notifyViewExited(@NonNull View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700449 if (!hasAutofillFeature()) {
450 return;
451 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700452 synchronized (mLock) {
453 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800454
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700455 if (mEnabled && mSessionId != NO_SESSION) {
456 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800457
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700458 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700459 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700460 }
Philip P. Moltmann44611812017-02-23 12:52:46 -0800461 }
462 }
463
464 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700465 * Called when a {@link View view's} visibility changes.
466 *
467 * @param view {@link View} that was exited.
468 * @param isVisible visible if the view is visible in the view hierarchy.
469 *
470 * @hide
471 */
472 public void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
473 synchronized (mLock) {
474 if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
475 mTrackedViews.notifyViewVisibilityChange(view, isVisible);
476 }
477 }
478 }
479
480 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700481 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800482 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700483 * @param view the {@link View} whose descendant is the virtual view.
484 * @param childId id identifying the virtual child inside the view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800485 * @param bounds child boundaries, relative to the top window.
Felipe Lemebab851c2017-02-03 18:45:08 -0800486 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700487 public void notifyViewEntered(@NonNull View view, int childId, @NonNull Rect bounds) {
Felipe Lemed1146422017-04-26 10:17:05 -0700488 notifyViewEntered(view, childId, bounds, 0);
489 }
490
491 private void notifyViewEntered(View view, int childId, Rect bounds, int flags) {
Svet Ganov43574b02017-04-12 09:25:20 -0700492 if (!hasAutofillFeature()) {
493 return;
494 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700495 AutofillCallback callback = null;
496 synchronized (mLock) {
497 ensureServiceClientAddedIfNeededLocked();
Svet Ganov782043c2017-02-11 00:52:02 +0000498
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700499 if (!mEnabled) {
500 if (mCallback != null) {
501 callback = mCallback;
502 }
503 } else {
504 final AutofillId id = getAutofillId(view, childId);
505
506 if (mSessionId == NO_SESSION) {
507 // Starts new session.
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700508 startSessionLocked(id, bounds, null, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700509 } else {
510 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700511 updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700512 }
Felipe Leme24aae152017-03-15 12:33:01 -0700513 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800514 }
515
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700516 if (callback != null) {
517 callback.onAutofillEvent(view, childId,
518 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800519 }
520 }
521
522 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700523 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800524 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700525 * @param view the {@link View} whose descendant is the virtual view.
526 * @param childId id identifying the virtual child inside the view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800527 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700528 public void notifyViewExited(@NonNull View view, int childId) {
Svet Ganov43574b02017-04-12 09:25:20 -0700529 if (!hasAutofillFeature()) {
530 return;
531 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700532 synchronized (mLock) {
533 ensureServiceClientAddedIfNeededLocked();
Philip P. Moltmann44611812017-02-23 12:52:46 -0800534
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700535 if (mEnabled && mSessionId != NO_SESSION) {
536 final AutofillId id = getAutofillId(view, childId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800537
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700538 // Update focus on existing session.
Felipe Leme0aa4c502017-04-26 12:36:01 -0700539 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700540 }
Svet Ganov782043c2017-02-11 00:52:02 +0000541 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800542 }
543
544 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800545 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800546 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800547 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800548 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700549 public void notifyValueChanged(View view) {
Svet Ganov43574b02017-04-12 09:25:20 -0700550 if (!hasAutofillFeature()) {
551 return;
552 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700553 AutofillId id = null;
554 boolean valueWasRead = false;
555 AutofillValue value = null;
556
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700557 synchronized (mLock) {
558 // If the session is gone some fields might still be highlighted, hence we have to
559 // remove the isAutofilled property even if no sessions are active.
560 if (mLastAutofilledData == null) {
561 view.setAutofilled(false);
562 } else {
563 id = getAutofillId(view);
564 if (mLastAutofilledData.containsKey(id)) {
565 value = view.getAutofillValue();
566 valueWasRead = true;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700567
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700568 if (Objects.equals(mLastAutofilledData.get(id), value)) {
569 view.setAutofilled(true);
570 } else {
571 view.setAutofilled(false);
572 mLastAutofilledData.remove(id);
573 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700574 } else {
575 view.setAutofilled(false);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700576 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700577 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700578
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700579 if (!mEnabled || mSessionId == NO_SESSION) {
580 return;
581 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800582
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700583 if (id == null) {
584 id = getAutofillId(view);
585 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700586
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700587 if (!valueWasRead) {
588 value = view.getAutofillValue();
589 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700590
Felipe Leme0aa4c502017-04-26 12:36:01 -0700591 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700592 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800593 }
594
Felipe Lemebab851c2017-02-03 18:45:08 -0800595 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800596 * Called to indicate the value of an autofillable virtual {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800597 *
Felipe Leme2ac463e2017-03-13 14:06:25 -0700598 * @param view the {@link View} whose descendant is the virtual view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800599 * @param childId id identifying the virtual child inside the parent view.
600 * @param value new value of the child.
601 */
Felipe Leme81f01d92017-03-16 17:13:25 -0700602 public void notifyValueChanged(View view, int childId, AutofillValue value) {
Svet Ganov43574b02017-04-12 09:25:20 -0700603 if (!hasAutofillFeature()) {
604 return;
605 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700606 synchronized (mLock) {
607 if (!mEnabled || mSessionId == NO_SESSION) {
608 return;
609 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800610
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700611 final AutofillId id = getAutofillId(view, childId);
Felipe Leme0aa4c502017-04-26 12:36:01 -0700612 updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700613 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800614 }
615
616 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700617 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800618 *
619 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
620 * call this method after the form is submitted and another page is rendered.
621 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700622 public void commit() {
Svet Ganov43574b02017-04-12 09:25:20 -0700623 if (!hasAutofillFeature()) {
624 return;
625 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700626 synchronized (mLock) {
627 if (!mEnabled && mSessionId == NO_SESSION) {
628 return;
629 }
Svet Ganov782043c2017-02-11 00:52:02 +0000630
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700631 finishSessionLocked();
632 }
Felipe Leme0200d9e2017-01-24 15:10:26 -0800633 }
634
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700635 /**
636 * Called to indicate the current autofill context should be cancelled.
637 *
638 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
639 * call this method if the user does not post the form but moves to another form in this page.
640 */
641 public void cancel() {
Svet Ganov43574b02017-04-12 09:25:20 -0700642 if (!hasAutofillFeature()) {
643 return;
644 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700645 synchronized (mLock) {
646 if (!mEnabled && mSessionId == NO_SESSION) {
647 return;
648 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700649
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700650 cancelSessionLocked();
651 }
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700652 }
653
Svet Ganovf965b872017-04-28 16:34:02 -0700654 /** @hide */
655 public void disableOwnedAutofillServices() {
656 disableAutofillServices();
657 }
658
Svet Ganovf20a0372017-04-10 17:08:05 -0700659 /**
660 * If the app calling this API has enabled autofill services they
661 * will be disabled.
662 */
Svet Ganovf965b872017-04-28 16:34:02 -0700663 public void disableAutofillServices() {
Svet Ganov43574b02017-04-12 09:25:20 -0700664 if (!hasAutofillFeature()) {
665 return;
666 }
Svet Ganovf20a0372017-04-10 17:08:05 -0700667 try {
668 mService.disableOwnedAutofillServices(mContext.getUserId());
669 } catch (RemoteException e) {
670 throw e.rethrowFromSystemServer();
671 }
672 }
673
Felipe Lemedb041182017-04-21 17:33:38 -0700674 /**
675 * Returns {@code true} if the calling application provides a {@link AutofillService} that is
676 * enabled for the current user, or {@code false} otherwise.
677 */
678 public boolean hasEnabledAutofillServices() {
679 if (mService == null) return false;
680
681 try {
682 return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
683 } catch (RemoteException e) {
684 throw e.rethrowFromSystemServer();
685 }
686 }
687
688 /**
689 * Returns {@code true} if Autofill is supported for this user.
690 *
691 * <p>Autofill is typically supported, but it could be unsupported in cases like:
692 * <ol>
693 * <li>Low-end devices.
694 * <li>Device policy rules that forbid its usage.
695 * </ol>
696 */
697 public boolean isAutofillSupported() {
698 if (mService == null) return false;
699
700 try {
701 return mService.isServiceSupported(mContext.getUserId());
702 } catch (RemoteException e) {
703 throw e.rethrowFromSystemServer();
704 }
705 }
706
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700707 private AutofillClient getClientLocked() {
Felipe Leme640f30a2017-03-06 15:44:06 -0800708 if (mContext instanceof AutofillClient) {
709 return (AutofillClient) mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000710 }
Svet Ganov17db9dc2017-02-21 19:54:31 -0800711 return null;
Svet Ganov782043c2017-02-11 00:52:02 +0000712 }
713
714 /** @hide */
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700715 public void onAuthenticationResult(int authenticationId, Intent data) {
Svet Ganov43574b02017-04-12 09:25:20 -0700716 if (!hasAutofillFeature()) {
717 return;
718 }
Felipe Leme85d1c2d2017-04-21 08:56:04 -0700719 // TODO: the result code is being ignored, so this method is not reliably
Felipe Lemed633f072017-02-14 10:17:17 -0800720 // handling the cases where it's not RESULT_OK: it works fine if the service does not
721 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
722 // service set the extra and returned RESULT_CANCELED...
723
Felipe Leme9f9ee252017-04-27 13:56:22 -0700724 if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);
Felipe Lemed633f072017-02-14 10:17:17 -0800725
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700726 synchronized (mLock) {
727 if (mSessionId == NO_SESSION || data == null) {
728 return;
729 }
730 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
731 final Bundle responseData = new Bundle();
732 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
733 try {
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700734 mService.setAuthenticationResult(responseData, mSessionId, authenticationId,
735 mContext.getUserId());
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700736 } catch (RemoteException e) {
737 Log.e(TAG, "Error delivering authentication result", e);
738 }
Svet Ganov782043c2017-02-11 00:52:02 +0000739 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800740 }
741
Felipe Leme640f30a2017-03-06 15:44:06 -0800742 private static AutofillId getAutofillId(View view) {
743 return new AutofillId(view.getAccessibilityViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -0800744 }
745
Felipe Leme640f30a2017-03-06 15:44:06 -0800746 private static AutofillId getAutofillId(View parent, int childId) {
747 return new AutofillId(parent.getAccessibilityViewId(), childId);
Felipe Lemebab851c2017-02-03 18:45:08 -0800748 }
749
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700750 private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
751 @NonNull AutofillValue value, int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700752 if (sVerbose) {
753 Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Leme2ac463e2017-03-13 14:06:25 -0700754 + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -0800755 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800756
Felipe Lemebab851c2017-02-03 18:45:08 -0800757 try {
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700758 mSessionId = mService.startSession(mContext.getActivityToken(),
Felipe Lemee6010f22017-03-03 11:19:51 -0800759 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Philip P. Moltmann7b771162017-03-03 17:22:57 -0800760 mCallback != null, flags, mContext.getOpPackageName());
Felipe Leme0aa4c502017-04-26 12:36:01 -0700761 final AutofillClient client = getClientLocked();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800762 if (client != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700763 client.autofillCallbackResetableStateAvailable();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800764 }
Svet Ganov782043c2017-02-11 00:52:02 +0000765 } catch (RemoteException e) {
766 throw e.rethrowFromSystemServer();
767 }
768 }
769
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700770 private void finishSessionLocked() {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700771 if (sVerbose) Log.v(TAG, "finishSessionLocked()");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700772
Svet Ganov782043c2017-02-11 00:52:02 +0000773 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700774 mService.finishSession(mSessionId, mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -0800775 } catch (RemoteException e) {
776 throw e.rethrowFromSystemServer();
777 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700778
Philip P. Moltmann494c3f52017-04-11 10:13:33 -0700779 mTrackedViews = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700780 mSessionId = NO_SESSION;
Felipe Lemebab851c2017-02-03 18:45:08 -0800781 }
782
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700783 private void cancelSessionLocked() {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700784 if (sVerbose) Log.v(TAG, "cancelSessionLocked()");
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700785
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700786 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700787 mService.cancelSession(mSessionId, mContext.getUserId());
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700788 } catch (RemoteException e) {
789 throw e.rethrowFromSystemServer();
790 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700791
Svet Ganov48f10a22017-04-26 18:49:30 -0700792 resetSessionLocked();
793 }
794
795 private void resetSessionLocked() {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700796 mSessionId = NO_SESSION;
Svet Ganov48f10a22017-04-26 18:49:30 -0700797 mTrackedViews = null;
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700798 }
799
Felipe Leme0aa4c502017-04-26 12:36:01 -0700800 private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
801 int flags) {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700802 if (sVerbose && action != ACTION_VIEW_EXITED) {
803 Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
804 + ", value=" + value + ", action=" + action + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -0800805 }
Felipe Leme0ab53dc2017-02-23 08:33:18 -0800806
Felipe Leme3461d3c2017-01-19 08:54:55 -0800807 try {
Felipe Leme0aa4c502017-04-26 12:36:01 -0700808 mService.updateSession(mSessionId, id, bounds, value, action, flags,
809 mContext.getUserId());
Felipe Leme3461d3c2017-01-19 08:54:55 -0800810 } catch (RemoteException e) {
811 throw e.rethrowFromSystemServer();
812 }
813 }
Svet Ganov782043c2017-02-11 00:52:02 +0000814
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700815 private void ensureServiceClientAddedIfNeededLocked() {
816 if (getClientLocked() == null) {
Svet Ganov782043c2017-02-11 00:52:02 +0000817 return;
818 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700819
Svet Ganov782043c2017-02-11 00:52:02 +0000820 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -0800821 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +0000822 try {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700823 final int flags = mService.addClient(mServiceClient, mContext.getUserId());
824 mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
825 sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
826 sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
Svet Ganov782043c2017-02-11 00:52:02 +0000827 } catch (RemoteException e) {
828 throw e.rethrowFromSystemServer();
829 }
830 }
831 }
832
Felipe Lemee6010f22017-03-03 11:19:51 -0800833 /**
834 * Registers a {@link AutofillCallback} to receive autofill events.
835 *
836 * @param callback callback to receive events.
837 */
838 public void registerCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -0700839 if (!hasAutofillFeature()) {
840 return;
841 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700842 synchronized (mLock) {
843 if (callback == null) return;
Felipe Lemee6010f22017-03-03 11:19:51 -0800844
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700845 final boolean hadCallback = mCallback != null;
846 mCallback = callback;
Felipe Lemee6010f22017-03-03 11:19:51 -0800847
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700848 if (!hadCallback) {
849 try {
850 mService.setHasCallback(mSessionId, mContext.getUserId(), true);
851 } catch (RemoteException e) {
852 throw e.rethrowFromSystemServer();
853 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800854 }
855 }
856 }
857
858 /**
859 * Unregisters a {@link AutofillCallback} to receive autofill events.
860 *
861 * @param callback callback to stop receiving events.
862 */
863 public void unregisterCallback(@Nullable AutofillCallback callback) {
Svet Ganov43574b02017-04-12 09:25:20 -0700864 if (!hasAutofillFeature()) {
865 return;
866 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700867 synchronized (mLock) {
868 if (callback == null || mCallback == null || callback != mCallback) return;
Felipe Lemee6010f22017-03-03 11:19:51 -0800869
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700870 mCallback = null;
Felipe Lemee6010f22017-03-03 11:19:51 -0800871
Felipe Lemee6010f22017-03-03 11:19:51 -0800872 try {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700873 mService.setHasCallback(mSessionId, mContext.getUserId(), false);
Felipe Lemee6010f22017-03-03 11:19:51 -0800874 } catch (RemoteException e) {
875 throw e.rethrowFromSystemServer();
876 }
877 }
878 }
879
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700880 private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
881 Rect anchorBounds, IAutofillWindowPresenter presenter) {
882 final View anchor = findView(id);
Felipe Leme4753bb02017-03-22 20:24:00 -0700883 if (anchor == null) {
884 return;
885 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700886
887 AutofillCallback callback = null;
888 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700889 if (mSessionId == sessionId) {
890 AutofillClient client = getClientLocked();
891
892 if (client != null) {
893 if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
894 anchorBounds, presenter) && mCallback != null) {
895 callback = mCallback;
896 }
897 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700898 }
899 }
900
901 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -0700902 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700903 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -0700904 AutofillCallback.EVENT_INPUT_SHOWN);
905 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700906 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
Felipe Leme4753bb02017-03-22 20:24:00 -0700907 }
908 }
909 }
910
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700911 private void authenticate(int sessionId, int authenticationId, IntentSender intent,
912 Intent fillInIntent) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700913 synchronized (mLock) {
914 if (sessionId == mSessionId) {
915 AutofillClient client = getClientLocked();
916 if (client != null) {
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700917 client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700918 }
919 }
920 }
921 }
922
Svet Ganov48f10a22017-04-26 18:49:30 -0700923 private void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700924 synchronized (mLock) {
925 mEnabled = enabled;
Svet Ganov48f10a22017-04-26 18:49:30 -0700926 if (!mEnabled || resetSession) {
927 // Reset the session state
928 resetSessionLocked();
929 }
930 if (resetClient) {
931 // Reset connection to system
932 mServiceClient = null;
933 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700934 }
935 }
936
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700937 /**
938 * Sets a view as autofilled if the current value is the {code targetValue}.
939 *
940 * @param view The view that is to be autofilled
941 * @param targetValue The value we want to fill into view
942 */
943 private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
944 AutofillValue currentValue = view.getAutofillValue();
945 if (Objects.equals(currentValue, targetValue)) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700946 synchronized (mLock) {
947 if (mLastAutofilledData == null) {
948 mLastAutofilledData = new ParcelableMap(1);
949 }
950 mLastAutofilledData.put(getAutofillId(view), targetValue);
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700951 }
Philip P. Moltmannb42d1332017-03-27 09:55:30 -0700952 view.setAutofilled(true);
953 }
954 }
955
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700956 private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700957 synchronized (mLock) {
958 if (sessionId != mSessionId) {
959 return;
Felipe Leme4753bb02017-03-22 20:24:00 -0700960 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700961
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700962 final AutofillClient client = getClientLocked();
963 if (client == null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700964 return;
965 }
966
967 final int itemCount = ids.size();
968 int numApplied = 0;
969 ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700970 final View[] views = client.findViewsByAccessibilityIdTraversal(getViewIds(ids));
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700971
972 for (int i = 0; i < itemCount; i++) {
973 final AutofillId id = ids.get(i);
974 final AutofillValue value = values.get(i);
975 final int viewId = id.getViewId();
Philip P. Moltmann134cee22017-05-06 11:28:38 -0700976 final View view = views[i];
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700977 if (view == null) {
978 Log.w(TAG, "autofill(): no View with id " + viewId);
979 continue;
Felipe Leme4753bb02017-03-22 20:24:00 -0700980 }
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700981 if (id.isVirtual()) {
982 if (virtualValues == null) {
983 // Most likely there will be just one view with virtual children.
984 virtualValues = new ArrayMap<>(1);
985 }
986 SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
987 if (valuesByParent == null) {
988 // We don't know the size yet, but usually it will be just a few fields...
989 valuesByParent = new SparseArray<>(5);
990 virtualValues.put(view, valuesByParent);
991 }
992 valuesByParent.put(id.getVirtualChildId(), value);
993 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -0700994 // Mark the view as to be autofilled with 'value'
995 if (mLastAutofilledData == null) {
996 mLastAutofilledData = new ParcelableMap(itemCount - i);
997 }
998 mLastAutofilledData.put(id, value);
Philip P. Moltmanne048a652017-04-11 10:13:33 -0700999
1000 view.autofill(value);
1001
1002 // Set as autofilled if the values match now, e.g. when the value was updated
1003 // synchronously.
1004 // If autofill happens async, the view is set to autofilled in
1005 // notifyValueChanged.
1006 setAutofilledIfValuesIs(view, value);
1007
1008 numApplied++;
Philip P. Moltmannb42d1332017-03-27 09:55:30 -07001009 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001010 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001011
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001012 if (virtualValues != null) {
1013 for (int i = 0; i < virtualValues.size(); i++) {
1014 final View parent = virtualValues.keyAt(i);
1015 final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
1016 parent.autofill(childrenValues);
1017 numApplied += childrenValues.size();
1018 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001019 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001020
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001021 final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
1022 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
1023 log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED,
1024 numApplied);
1025 mMetricsLogger.write(log);
1026 }
Felipe Leme4753bb02017-03-22 20:24:00 -07001027 }
1028
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001029 /**
1030 * Set the tracked views.
1031 *
1032 * @param trackedIds The views to be tracked
1033 * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
1034 */
1035 private void setTrackedViews(int sessionId, List<AutofillId> trackedIds,
1036 boolean saveOnAllViewsInvisible) {
1037 synchronized (mLock) {
1038 if (mEnabled && mSessionId == sessionId) {
1039 if (saveOnAllViewsInvisible) {
1040 mTrackedViews = new TrackedViews(trackedIds);
1041 } else {
1042 mTrackedViews = null;
1043 }
1044 }
1045 }
1046 }
1047
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001048 private void requestHideFillUi(int sessionId, AutofillId id) {
1049 final View anchor = findView(id);
1050 if (anchor == null) {
1051 return;
1052 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001053
1054 AutofillCallback callback = null;
1055 synchronized (mLock) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001056 // We do not check the session id for two reasons:
1057 // 1. If local and remote session id are off sync the UI would be stuck shown
1058 // 2. There is a race between the user state being destroyed due the fill
1059 // service being uninstalled and the UI being dismissed.
1060 AutofillClient client = getClientLocked();
1061 if (client != null) {
1062 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
1063 callback = mCallback;
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001064 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001065 }
1066 }
1067
1068 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001069 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001070 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001071 AutofillCallback.EVENT_INPUT_HIDDEN);
1072 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001073 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
Felipe Leme4753bb02017-03-22 20:24:00 -07001074 }
1075 }
1076 }
1077
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001078 private void notifyNoFillUi(int sessionId, AutofillId id) {
1079 final View anchor = findView(id);
1080 if (anchor == null) {
1081 return;
1082 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001083
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001084 AutofillCallback callback = null;
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001085 synchronized (mLock) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001086 if (mSessionId == sessionId && getClientLocked() != null) {
1087 callback = mCallback;
1088 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001089 }
1090
1091 if (callback != null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001092 if (id.isVirtual()) {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001093 callback.onAutofillEvent(anchor, id.getVirtualChildId(),
Felipe Leme4753bb02017-03-22 20:24:00 -07001094 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
1095 } else {
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001096 callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
Felipe Leme4753bb02017-03-22 20:24:00 -07001097 }
Philip P. Moltmanneab62ba2017-03-20 10:55:43 -07001098
Felipe Leme4753bb02017-03-22 20:24:00 -07001099 }
1100 }
1101
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001102 /**
1103 * Get an array of viewIds from a List of {@link AutofillId}.
1104 *
1105 * @param autofillIds The autofill ids to convert
1106 *
1107 * @return The array of viewIds.
1108 */
1109 @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
1110 final int numIds = autofillIds.size();
1111 final int[] viewIds = new int[numIds];
1112 for (int i = 0; i < numIds; i++) {
1113 viewIds[i] = autofillIds.get(i).getViewId();
1114 }
1115
1116 return viewIds;
1117 }
1118
1119 /**
1120 * Find a single view by its id.
1121 *
1122 * @param autofillId The autofill id of the view
1123 *
1124 * @return The view or {@code null} if view was not found
1125 */
1126 private View findView(@NonNull AutofillId autofillId) {
1127 final AutofillClient client = getClientLocked();
1128
1129 if (client == null) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001130 return null;
Felipe Lemee6010f22017-03-03 11:19:51 -08001131 }
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001132
1133 return client.findViewsByAccessibilityIdTraversal(new int[]{autofillId.getViewId()})[0];
Felipe Lemee6010f22017-03-03 11:19:51 -08001134 }
1135
Felipe Lemebb810922017-04-25 15:54:06 -07001136 /** @hide */
1137 public boolean hasAutofillFeature() {
Svet Ganov43574b02017-04-12 09:25:20 -07001138 return mService != null;
1139 }
1140
Felipe Lemee6010f22017-03-03 11:19:51 -08001141 /**
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001142 * View tracking information. Once all tracked views become invisible the session is finished.
1143 */
1144 private class TrackedViews {
1145 /** Visible tracked views */
1146 @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
1147
1148 /** Invisible tracked views */
1149 @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
1150
1151 /**
1152 * Check if set is null or value is in set.
1153 *
1154 * @param set The set or null (== empty set)
1155 * @param value The value that might be in the set
1156 *
1157 * @return {@code true} iff set is not empty and value is in set
1158 */
1159 private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
1160 return set != null && set.contains(value);
1161 }
1162
1163 /**
1164 * Add a value to a set. If set is null, create a new set.
1165 *
1166 * @param set The set or null (== empty set)
1167 * @param valueToAdd The value to add
1168 *
1169 * @return The set including the new value. If set was {@code null}, a set containing only
1170 * the new value.
1171 */
1172 @NonNull
1173 private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
1174 if (set == null) {
1175 set = new ArraySet<>(1);
1176 }
1177
1178 set.add(valueToAdd);
1179
1180 return set;
1181 }
1182
1183 /**
1184 * Remove a value from a set.
1185 *
1186 * @param set The set or null (== empty set)
1187 * @param valueToRemove The value to remove
1188 *
1189 * @return The set without the removed value. {@code null} if set was null, or is empty
1190 * after removal.
1191 */
1192 @Nullable
1193 private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
1194 if (set == null) {
1195 return null;
1196 }
1197
1198 set.remove(valueToRemove);
1199
1200 if (set.isEmpty()) {
1201 return null;
1202 }
1203
1204 return set;
1205 }
1206
1207 /**
1208 * Set the tracked views.
1209 *
1210 * @param trackedIds The views to be tracked
1211 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001212 TrackedViews(List<AutofillId> trackedIds) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001213 mVisibleTrackedIds = null;
1214 mInvisibleTrackedIds = null;
1215
1216 AutofillClient client = getClientLocked();
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001217 if (trackedIds != null && client != null) {
1218 final boolean[] isVisible;
1219
1220 if (client.isVisibleForAutofill()) {
1221 isVisible = client.getViewVisibility(getViewIds(trackedIds));
1222 } else {
1223 // All false
1224 isVisible = new boolean[trackedIds.size()];
1225 }
1226
1227 final int numIds = trackedIds.size();
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001228 for (int i = 0; i < numIds; i++) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001229 final AutofillId id = trackedIds.get(i);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001230
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001231 if (isVisible[i]) {
Philip P. Moltmanne0e28712017-04-20 15:19:06 -07001232 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001233 } else {
1234 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1235 }
1236 }
1237 }
1238
Felipe Leme9f9ee252017-04-27 13:56:22 -07001239 if (sVerbose) {
1240 Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001241 + " mVisibleTrackedIds=" + mVisibleTrackedIds
1242 + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
1243 }
1244
1245 if (mVisibleTrackedIds == null) {
1246 finishSessionLocked();
1247 }
1248 }
1249
1250 /**
1251 * Called when a {@link View view's} visibility changes.
1252 *
1253 * @param view {@link View} that was exited.
1254 * @param isVisible visible if the view is visible in the view hierarchy.
1255 */
1256 void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
1257 AutofillId id = getAutofillId(view);
1258 AutofillClient client = getClientLocked();
1259
Felipe Leme9f9ee252017-04-27 13:56:22 -07001260 if (sDebug) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001261 Log.d(TAG, "notifyViewVisibilityChange(): id=" + id + " isVisible="
1262 + isVisible);
1263 }
1264
1265 if (client != null && client.isVisibleForAutofill()) {
1266 if (isVisible) {
1267 if (isInSet(mInvisibleTrackedIds, id)) {
1268 mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
1269 mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
1270 }
1271 } else {
1272 if (isInSet(mVisibleTrackedIds, id)) {
1273 mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
1274 mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
1275 }
1276 }
1277 }
1278
1279 if (mVisibleTrackedIds == null) {
1280 finishSessionLocked();
1281 }
1282 }
1283
1284 /**
1285 * Called once the client becomes visible.
1286 *
1287 * @see AutofillClient#isVisibleForAutofill()
1288 */
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001289 void onVisibleForAutofillLocked() {
1290 // The visibility of the views might have changed while the client was not be visible,
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001291 // hence update the visibility state for all views.
1292 AutofillClient client = getClientLocked();
1293 ArraySet<AutofillId> updatedVisibleTrackedIds = null;
1294 ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
1295 if (client != null) {
1296 if (mInvisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001297 final ArrayList<AutofillId> orderedInvisibleIds =
1298 new ArrayList<>(mInvisibleTrackedIds);
1299 final boolean[] isVisible = client.getViewVisibility(
1300 getViewIds(orderedInvisibleIds));
1301
1302 final int numInvisibleTrackedIds = orderedInvisibleIds.size();
1303 for (int i = 0; i < numInvisibleTrackedIds; i++) {
1304 final AutofillId id = orderedInvisibleIds.get(i);
1305 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001306 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1307
Felipe Leme9f9ee252017-04-27 13:56:22 -07001308 if (sDebug) {
1309 Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001310 }
1311 } else {
1312 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1313 }
1314 }
1315 }
1316
1317 if (mVisibleTrackedIds != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001318 final ArrayList<AutofillId> orderedVisibleIds =
1319 new ArrayList<>(mVisibleTrackedIds);
1320 final boolean[] isVisible = client.getViewVisibility(
1321 getViewIds(orderedVisibleIds));
1322
1323 final int numVisibleTrackedIds = orderedVisibleIds.size();
1324 for (int i = 0; i < numVisibleTrackedIds; i++) {
1325 final AutofillId id = orderedVisibleIds.get(i);
1326
1327 if (isVisible[i]) {
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001328 updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
1329 } else {
1330 updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
1331
Felipe Leme9f9ee252017-04-27 13:56:22 -07001332 if (sDebug) {
1333 Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001334 }
1335 }
1336 }
1337 }
1338
1339 mInvisibleTrackedIds = updatedInvisibleTrackedIds;
1340 mVisibleTrackedIds = updatedVisibleTrackedIds;
1341 }
1342
1343 if (mVisibleTrackedIds == null) {
1344 finishSessionLocked();
1345 }
1346 }
1347 }
1348
1349 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08001350 * Callback for auto-fill related events.
1351 *
1352 * <p>Typically used for applications that display their own "auto-complete" views, so they can
1353 * enable / disable such views when the auto-fill UI affordance is shown / hidden.
1354 */
1355 public abstract static class AutofillCallback {
1356
1357 /** @hide */
1358 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
1359 @Retention(RetentionPolicy.SOURCE)
1360 public @interface AutofillEventType {}
1361
1362 /**
1363 * The auto-fill input UI affordance associated with the view was shown.
1364 *
1365 * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
1366 * should be hidden upon receiving this event.
1367 */
1368 public static final int EVENT_INPUT_SHOWN = 1;
1369
1370 /**
1371 * The auto-fill input UI affordance associated with the view was hidden.
1372 *
1373 * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
1374 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
1375 */
1376 public static final int EVENT_INPUT_HIDDEN = 2;
1377
1378 /**
Felipe Leme24aae152017-03-15 12:33:01 -07001379 * The auto-fill input UI affordance associated with the view won't be shown because
1380 * autofill is not available.
1381 *
1382 * <p>If the view provides its own auto-complete UI affordance but was not displaying it
1383 * to avoid flickering, it could shown it upon receiving this event.
1384 */
1385 public static final int EVENT_INPUT_UNAVAILABLE = 3;
1386
1387 /**
Felipe Lemee6010f22017-03-03 11:19:51 -08001388 * Called after a change in the autofill state associated with a view.
1389 *
1390 * @param view view associated with the change.
1391 *
1392 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1393 */
Felipe Leme81f01d92017-03-16 17:13:25 -07001394 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
1395 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001396
1397 /**
1398 * Called after a change in the autofill state associated with a virtual view.
1399 *
1400 * @param view parent view associated with the change.
1401 * @param childId id identifying the virtual child inside the parent view.
1402 *
1403 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
1404 */
Felipe Leme81f01d92017-03-16 17:13:25 -07001405 public void onAutofillEvent(@NonNull View view, int childId, @AutofillEventType int event) {
1406 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001407 }
1408
Felipe Leme640f30a2017-03-06 15:44:06 -08001409 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
1410 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +00001411
Felipe Leme640f30a2017-03-06 15:44:06 -08001412 AutofillManagerClient(AutofillManager autofillManager) {
1413 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +00001414 }
1415
1416 @Override
Svet Ganov48f10a22017-04-26 18:49:30 -07001417 public void setState(boolean enabled, boolean resetSession, boolean resetClient) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001418 final AutofillManager afm = mAfm.get();
1419 if (afm != null) {
Svet Ganov48f10a22017-04-26 18:49:30 -07001420 afm.mContext.getMainThreadHandler().post(
1421 () -> afm.setState(enabled, resetSession, resetClient));
Svet Ganov782043c2017-02-11 00:52:02 +00001422 }
1423 }
1424
1425 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001426 public void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001427 final AutofillManager afm = mAfm.get();
1428 if (afm != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001429 afm.mContext.getMainThreadHandler().post(
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001430 () -> afm.autofill(sessionId, ids, values));
Svet Ganov782043c2017-02-11 00:52:02 +00001431 }
1432 }
1433
1434 @Override
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001435 public void authenticate(int sessionId, int authenticationId, IntentSender intent,
1436 Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001437 final AutofillManager afm = mAfm.get();
1438 if (afm != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001439 afm.mContext.getMainThreadHandler().post(
Svetoslav Ganova9379d02017-05-09 17:40:24 -07001440 () -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
Svet Ganov782043c2017-02-11 00:52:02 +00001441 }
1442 }
Felipe Lemee6010f22017-03-03 11:19:51 -08001443
1444 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001445 public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
1446 Rect anchorBounds, IAutofillWindowPresenter presenter) {
Felipe Leme640f30a2017-03-06 15:44:06 -08001447 final AutofillManager afm = mAfm.get();
1448 if (afm != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001449 afm.mContext.getMainThreadHandler().post(
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001450 () -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
1451 presenter));
Felipe Leme4753bb02017-03-22 20:24:00 -07001452 }
1453 }
1454
1455 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001456 public void requestHideFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001457 final AutofillManager afm = mAfm.get();
1458 if (afm != null) {
Philip P. Moltmanne048a652017-04-11 10:13:33 -07001459 afm.mContext.getMainThreadHandler().post(
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001460 () -> afm.requestHideFillUi(sessionId, id));
Felipe Leme4753bb02017-03-22 20:24:00 -07001461 }
1462 }
1463
1464 @Override
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001465 public void notifyNoFillUi(int sessionId, AutofillId id) {
Felipe Leme4753bb02017-03-22 20:24:00 -07001466 final AutofillManager afm = mAfm.get();
1467 if (afm != null) {
Philip P. Moltmann134cee22017-05-06 11:28:38 -07001468 afm.mContext.getMainThreadHandler().post(() -> afm.notifyNoFillUi(sessionId, id));
Felipe Lemee6010f22017-03-03 11:19:51 -08001469 }
1470 }
Svet Ganovc3d1c852017-04-12 22:34:28 -07001471
1472 @Override
1473 public void startIntentSender(IntentSender intentSender) {
1474 final AutofillManager afm = mAfm.get();
1475 if (afm != null) {
1476 afm.mContext.getMainThreadHandler().post(() -> {
1477 try {
1478 afm.mContext.startIntentSender(intentSender, null, 0, 0, 0);
1479 } catch (IntentSender.SendIntentException e) {
1480 Log.e(TAG, "startIntentSender() failed for intent:" + intentSender, e);
1481 }
1482 });
1483 }
1484 }
Philip P. Moltmann494c3f52017-04-11 10:13:33 -07001485
1486 @Override
1487 public void setTrackedViews(int sessionId, List<AutofillId> ids,
1488 boolean saveOnAllViewsInvisible) {
1489 final AutofillManager afm = mAfm.get();
1490 if (afm != null) {
1491 afm.mContext.getMainThreadHandler().post(
1492 () -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible)
1493 );
1494 }
1495 }
Svet Ganov782043c2017-02-11 00:52:02 +00001496 }
Felipe Leme3461d3c2017-01-19 08:54:55 -08001497}