blob: b852aabe3b3defb0a4b627af3168fc37931c1bb7 [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 Lemed633f072017-02-14 10:17:17 -080019import static android.view.autofill.Helper.DEBUG;
Felipe Leme0ab53dc2017-02-23 08:33:18 -080020import static android.view.autofill.Helper.VERBOSE;
Felipe Lemed633f072017-02-14 10:17:17 -080021
Felipe Lemee6010f22017-03-03 11:19:51 -080022import android.annotation.IntDef;
Philip P. Moltmann44611812017-02-23 12:52:46 -080023import android.annotation.NonNull;
Felipe Lemee6010f22017-03-03 11:19:51 -080024import android.annotation.Nullable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080025import android.content.Context;
Svet Ganov782043c2017-02-11 00:52:02 +000026import android.content.Intent;
27import android.content.IntentSender;
Felipe Leme3461d3c2017-01-19 08:54:55 -080028import android.graphics.Rect;
Svet Ganov782043c2017-02-11 00:52:02 +000029import android.os.Bundle;
Svet Ganov28a2c7e2017-02-21 10:16:16 -080030import android.os.IBinder;
Svet Ganov782043c2017-02-11 00:52:02 +000031import android.os.Parcelable;
Felipe Leme3461d3c2017-01-19 08:54:55 -080032import android.os.RemoteException;
Felipe Leme3461d3c2017-01-19 08:54:55 -080033import android.util.Log;
34import android.view.View;
Felipe Lemee6010f22017-03-03 11:19:51 -080035import android.view.WindowManagerGlobal;
Felipe Leme3461d3c2017-01-19 08:54:55 -080036
Felipe Lemee6010f22017-03-03 11:19:51 -080037import java.lang.annotation.Retention;
38import java.lang.annotation.RetentionPolicy;
Svet Ganov782043c2017-02-11 00:52:02 +000039import java.lang.ref.WeakReference;
40import java.util.List;
41
Felipe Leme3461d3c2017-01-19 08:54:55 -080042/**
43 * App entry point to the AutoFill Framework.
44 */
45// TODO(b/33197203): improve this javadoc
Felipe Lemebab851c2017-02-03 18:45:08 -080046//TODO(b/33197203): restrict manager calls to activity
Felipe Leme640f30a2017-03-06 15:44:06 -080047public final class AutofillManager {
Felipe Leme3461d3c2017-01-19 08:54:55 -080048
Felipe Leme640f30a2017-03-06 15:44:06 -080049 private static final String TAG = "AutofillManager";
Felipe Leme3461d3c2017-01-19 08:54:55 -080050
Svet Ganov782043c2017-02-11 00:52:02 +000051 /**
52 * Intent extra: The assist structure which captures the filled screen.
53 * <p>
54 * Type: {@link android.app.assist.AssistStructure}
55 * </p>
56 */
57 public static final String EXTRA_ASSIST_STRUCTURE =
58 "android.view.autofill.extra.ASSIST_STRUCTURE";
59
60 /**
61 * Intent extra: The result of an authentication operation. It is
62 * either a fully populated {@link android.service.autofill.FillResponse}
63 * or a fully populated {@link android.service.autofill.Dataset} if
64 * a response or a dataset is being authenticated respectively.
65 *
66 * <p>
67 * Type: {@link android.service.autofill.FillResponse} or a
68 * {@link android.service.autofill.Dataset}
69 * </p>
70 */
71 public static final String EXTRA_AUTHENTICATION_RESULT =
72 "android.view.autofill.extra.AUTHENTICATION_RESULT";
73
Felipe Leme2ac463e2017-03-13 14:06:25 -070074 // Public flags start from the lowest bit
75 /**
76 * Indicates autofill was explicitly requested by the user.
77 */
78 public static final int FLAG_MANUAL_REQUEST = 0x1;
79
80 // Private flags start from the highest bit
81 /** @hide */ public static final int FLAG_START_SESSION = 0x80000000;
82 /** @hide */ public static final int FLAG_VIEW_ENTERED = 0x40000000;
83 /** @hide */ public static final int FLAG_VIEW_EXITED = 0x20000000;
84 /** @hide */ public static final int FLAG_VALUE_CHANGED = 0x10000000;
Felipe Leme3461d3c2017-01-19 08:54:55 -080085
Philip P. Moltmann7b771162017-03-03 17:22:57 -080086 @NonNull private final Rect mTempRect = new Rect();
Svet Ganov782043c2017-02-11 00:52:02 +000087
88 private final IAutoFillManager mService;
89 private IAutoFillManagerClient mServiceClient;
90
Felipe Lemee6010f22017-03-03 11:19:51 -080091 private AutofillCallback mCallback;
92
Svet Ganov782043c2017-02-11 00:52:02 +000093 private Context mContext;
94
Svet Ganov782043c2017-02-11 00:52:02 +000095 private boolean mHasSession;
96 private boolean mEnabled;
97
98 /** @hide */
Felipe Leme640f30a2017-03-06 15:44:06 -080099 public interface AutofillClient {
Svet Ganov782043c2017-02-11 00:52:02 +0000100 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800101 * Asks the client to perform an autofill.
Svet Ganov782043c2017-02-11 00:52:02 +0000102 *
Felipe Leme640f30a2017-03-06 15:44:06 -0800103 * @param ids The values to autofill
104 * @param values The values to autofill
Svet Ganov782043c2017-02-11 00:52:02 +0000105 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800106 void autofill(List<AutofillId> ids, List<AutofillValue> values);
Svet Ganov782043c2017-02-11 00:52:02 +0000107
108 /**
109 * Asks the client to start an authentication flow.
110 *
111 * @param intent The authentication intent.
112 * @param fillInIntent The authentication fill-in intent.
113 */
114 void authenticate(IntentSender intent, Intent fillInIntent);
Svet Ganov17db9dc2017-02-21 19:54:31 -0800115
116 /**
117 * Tells the client this manager has state to be reset.
118 */
119 void resetableStateAvailable();
Svet Ganov782043c2017-02-11 00:52:02 +0000120 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800121
122 /**
123 * @hide
124 */
Felipe Leme640f30a2017-03-06 15:44:06 -0800125 public AutofillManager(Context context, IAutoFillManager service) {
Felipe Lemebab851c2017-02-03 18:45:08 -0800126 mContext = context;
Felipe Leme3461d3c2017-01-19 08:54:55 -0800127 mService = service;
128 }
129
130 /**
Felipe Leme2ac463e2017-03-13 14:06:25 -0700131 * Checkes whether autofill is enabled for the current user.
132 *
133 * <p>Typically used to determine whether the option to explicitly request autofill should
134 * be offered - see {@link #requestAutofill(View)}.
135 *
136 * @return whether autofill is enabled for the current user.
137 */
138 public boolean isEnabled() {
139 ensureServiceClientAddedIfNeeded();
140 return mEnabled;
141 }
142
143 /**
144 * Explicitly requests a new autofill context.
145 *
146 * <p>Normally, the autofill context is automatically started when autofillable views are
147 * focused, but this method should be used in the cases where it must be explicitly requested,
148 * like a view that provides a contextual menu allowing users to autofill the activity.
149 *
150 * @param view view requesting the new autofill context.
151 */
152 public void requestAutofill(@NonNull View view) {
153 ensureServiceClientAddedIfNeeded();
154
155 if (!mEnabled) {
156 return;
157 }
158
159 final Rect bounds = mTempRect;
160 view.getBoundsOnScreen(bounds);
161 final AutofillId id = getAutofillId(view);
162 final AutofillValue value = view.getAutofillValue();
163
164 startSession(id, view.getWindowToken(), bounds, value, FLAG_MANUAL_REQUEST);
165 }
166
167 /**
168 * Explicitly requests a new autofill context for virtual views.
169 *
170 * <p>Normally, the autofill context is automatically started when autofillable views are
171 * focused, but this method should be used in the cases where it must be explicitly requested,
172 * like a virtual view that provides a contextual menu allowing users to autofill the activity.
173 *
174 * @param view the {@link View} whose descendant is the virtual view.
175 * @param childId id identifying the virtual child inside the view.
176 * @param bounds child boundaries, relative to the top window.
177 */
178 public void requestAutofill(@NonNull View view, int childId, @NonNull Rect bounds) {
179 ensureServiceClientAddedIfNeeded();
180
181 if (!mEnabled) {
182 return;
183 }
184
185 final AutofillId id = getAutofillId(view, childId);
186 startSession(id, view.getWindowToken(), bounds, null, FLAG_MANUAL_REQUEST);
187 }
188
189
190 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700191 * Called when a {@link View} that supports autofill is entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800192 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700193 * @param view {@link View} that was entered.
Felipe Leme3461d3c2017-01-19 08:54:55 -0800194 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700195 public void notifyViewEntered(@NonNull View view) {
Svet Ganov782043c2017-02-11 00:52:02 +0000196 ensureServiceClientAddedIfNeeded();
197
198 if (!mEnabled) {
Felipe Leme24aae152017-03-15 12:33:01 -0700199 if (mCallback != null) {
200 mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
201 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800202 return;
203 }
204
Svet Ganov782043c2017-02-11 00:52:02 +0000205 final Rect bounds = mTempRect;
Felipe Lemebd00fef2017-01-24 15:10:26 -0800206 view.getBoundsOnScreen(bounds);
Felipe Leme640f30a2017-03-06 15:44:06 -0800207 final AutofillId id = getAutofillId(view);
208 final AutofillValue value = view.getAutofillValue();
Svet Ganov782043c2017-02-11 00:52:02 +0000209
210 if (!mHasSession) {
Philip P. Moltmann44611812017-02-23 12:52:46 -0800211 // Starts new session.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700212 startSession(id, view.getWindowToken(), bounds, value, 0);
Svet Ganov782043c2017-02-11 00:52:02 +0000213 } else {
214 // Update focus on existing session.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700215 updateSession(id, bounds, value, FLAG_VIEW_ENTERED);
Svet Ganov782043c2017-02-11 00:52:02 +0000216 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800217 }
218
219 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700220 * Called when a {@link View} that supports autofill is exited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800221 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700222 * @param view {@link View} that was exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800223 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700224 public void notifyViewExited(@NonNull View view) {
Philip P. Moltmann44611812017-02-23 12:52:46 -0800225 ensureServiceClientAddedIfNeeded();
226
227 if (mEnabled && mHasSession) {
Felipe Leme640f30a2017-03-06 15:44:06 -0800228 final AutofillId id = getAutofillId(view);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800229
230 // Update focus on existing session.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700231 updateSession(id, null, null, FLAG_VIEW_EXITED);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800232 }
233 }
234
235 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700236 * Called when a virtual view that supports autofill is entered.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800237 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700238 * @param view the {@link View} whose descendant is the virtual view.
239 * @param childId id identifying the virtual child inside the view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800240 * @param bounds child boundaries, relative to the top window.
Felipe Lemebab851c2017-02-03 18:45:08 -0800241 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700242 public void notifyVirtualViewEntered(@NonNull View view, int childId,
Philip P. Moltmann44611812017-02-23 12:52:46 -0800243 @NonNull Rect bounds) {
Svet Ganov782043c2017-02-11 00:52:02 +0000244 ensureServiceClientAddedIfNeeded();
245
246 if (!mEnabled) {
Felipe Leme24aae152017-03-15 12:33:01 -0700247 if (mCallback != null) {
248 mCallback.onAutofillEventVirtual(view, childId,
249 AutofillCallback.EVENT_INPUT_UNAVAILABLE);
250 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800251 return;
252 }
253
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700254 final AutofillId id = getAutofillId(view, childId);
Svet Ganov782043c2017-02-11 00:52:02 +0000255
256 if (!mHasSession) {
Philip P. Moltmann44611812017-02-23 12:52:46 -0800257 // Starts new session.
Felipe Leme2ac463e2017-03-13 14:06:25 -0700258 startSession(id, view.getWindowToken(), bounds, null, 0);
Svet Ganov782043c2017-02-11 00:52:02 +0000259 } else {
260 // Update focus on existing session.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700261 updateSession(id, bounds, null, FLAG_VIEW_ENTERED);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800262 }
263 }
264
265 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700266 * Called when a virtual view that supports autofill is exited.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800267 *
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700268 * @param view the {@link View} whose descendant is the virtual view.
269 * @param childId id identifying the virtual child inside the view.
Philip P. Moltmann44611812017-02-23 12:52:46 -0800270 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700271 public void notifyVirtualViewExited(@NonNull View view, int childId) {
Philip P. Moltmann44611812017-02-23 12:52:46 -0800272 ensureServiceClientAddedIfNeeded();
273
274 if (mEnabled && mHasSession) {
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700275 final AutofillId id = getAutofillId(view, childId);
Philip P. Moltmann44611812017-02-23 12:52:46 -0800276
277 // Update focus on existing session.
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700278 updateSession(id, null, null, FLAG_VIEW_EXITED);
Svet Ganov782043c2017-02-11 00:52:02 +0000279 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800280 }
281
282 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800283 * Called to indicate the value of an autofillable {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800284 *
Philip P. Moltmann44611812017-02-23 12:52:46 -0800285 * @param view view whose value changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800286 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700287 public void notifyValueChanged(View view) {
Svet Ganov782043c2017-02-11 00:52:02 +0000288 if (!mEnabled || !mHasSession) {
289 return;
290 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800291
Felipe Leme640f30a2017-03-06 15:44:06 -0800292 final AutofillId id = getAutofillId(view);
293 final AutofillValue value = view.getAutofillValue();
Felipe Lemebab851c2017-02-03 18:45:08 -0800294 updateSession(id, null, value, FLAG_VALUE_CHANGED);
295 }
296
297
298 /**
Felipe Leme640f30a2017-03-06 15:44:06 -0800299 * Called to indicate the value of an autofillable virtual {@link View} changed.
Felipe Lemebab851c2017-02-03 18:45:08 -0800300 *
Felipe Leme2ac463e2017-03-13 14:06:25 -0700301 * @param view the {@link View} whose descendant is the virtual view.
Felipe Lemebab851c2017-02-03 18:45:08 -0800302 * @param childId id identifying the virtual child inside the parent view.
303 * @param value new value of the child.
304 */
Felipe Leme2ac463e2017-03-13 14:06:25 -0700305 public void notifyVirtualValueChanged(View view, int childId, AutofillValue value) {
Svet Ganov782043c2017-02-11 00:52:02 +0000306 if (!mEnabled || !mHasSession) {
307 return;
308 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800309
Felipe Leme2ac463e2017-03-13 14:06:25 -0700310 final AutofillId id = getAutofillId(view, childId);
Felipe Lemebab851c2017-02-03 18:45:08 -0800311 updateSession(id, null, value, FLAG_VALUE_CHANGED);
312 }
313
314 /**
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700315 * Called to indicate the current autofill context should be commited.
Felipe Lemebab851c2017-02-03 18:45:08 -0800316 *
317 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
318 * call this method after the form is submitted and another page is rendered.
319 */
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700320 public void commit() {
Svet Ganov782043c2017-02-11 00:52:02 +0000321 if (!mEnabled && !mHasSession) {
Felipe Lemebab851c2017-02-03 18:45:08 -0800322 return;
323 }
Svet Ganov782043c2017-02-11 00:52:02 +0000324
325 finishSession();
Felipe Leme0200d9e2017-01-24 15:10:26 -0800326 }
327
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700328 /**
329 * Called to indicate the current autofill context should be cancelled.
330 *
331 * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
332 * call this method if the user does not post the form but moves to another form in this page.
333 */
334 public void cancel() {
335 if (!mEnabled && !mHasSession) {
336 return;
337 }
338
339 cancelSession();
340 }
341
Felipe Leme640f30a2017-03-06 15:44:06 -0800342 private AutofillClient getClient() {
343 if (mContext instanceof AutofillClient) {
344 return (AutofillClient) mContext;
Svet Ganov782043c2017-02-11 00:52:02 +0000345 }
Svet Ganov17db9dc2017-02-21 19:54:31 -0800346 return null;
Svet Ganov782043c2017-02-11 00:52:02 +0000347 }
348
349 /** @hide */
350 public void onAuthenticationResult(Intent data) {
Felipe Lemed633f072017-02-14 10:17:17 -0800351 // TODO(b/33197203): the result code is being ignored, so this method is not reliably
352 // handling the cases where it's not RESULT_OK: it works fine if the service does not
353 // set the EXTRA_AUTHENTICATION_RESULT extra, but it could cause weird results if the
354 // service set the extra and returned RESULT_CANCELED...
355
356 if (DEBUG) Log.d(TAG, "onAuthenticationResult(): d=" + data);
357
Svet Ganov782043c2017-02-11 00:52:02 +0000358 if (data == null) {
359 return;
360 }
Felipe Lemed633f072017-02-14 10:17:17 -0800361 final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
362 final Bundle responseData = new Bundle();
Svet Ganov782043c2017-02-11 00:52:02 +0000363 responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
364 try {
365 mService.setAuthenticationResult(responseData,
366 mContext.getActivityToken(), mContext.getUserId());
367 } catch (RemoteException e) {
368 Log.e(TAG, "Error delivering authentication result", e);
369 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800370 }
371
Felipe Leme640f30a2017-03-06 15:44:06 -0800372
373 private static AutofillId getAutofillId(View view) {
374 return new AutofillId(view.getAccessibilityViewId());
Felipe Leme0200d9e2017-01-24 15:10:26 -0800375 }
376
Felipe Leme640f30a2017-03-06 15:44:06 -0800377 private static AutofillId getAutofillId(View parent, int childId) {
378 return new AutofillId(parent.getAccessibilityViewId(), childId);
Felipe Lemebab851c2017-02-03 18:45:08 -0800379 }
380
Philip P. Moltmann7b771162017-03-03 17:22:57 -0800381 private void startSession(@NonNull AutofillId id, @NonNull IBinder windowToken,
382 @NonNull Rect bounds, @NonNull AutofillValue value, int flags) {
Svet Ganov782043c2017-02-11 00:52:02 +0000383 if (DEBUG) {
Felipe Leme2ac463e2017-03-13 14:06:25 -0700384 Log.d(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
385 + ", flags=" + flags);
Felipe Lemebab851c2017-02-03 18:45:08 -0800386 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800387
Felipe Lemebab851c2017-02-03 18:45:08 -0800388 try {
Svet Ganov28a2c7e2017-02-21 10:16:16 -0800389 mService.startSession(mContext.getActivityToken(), windowToken,
Felipe Lemee6010f22017-03-03 11:19:51 -0800390 mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
Philip P. Moltmann7b771162017-03-03 17:22:57 -0800391 mCallback != null, flags, mContext.getOpPackageName());
392 AutofillClient client = getClient();
Svet Ganov17db9dc2017-02-21 19:54:31 -0800393 if (client != null) {
394 client.resetableStateAvailable();
395 }
Svet Ganov782043c2017-02-11 00:52:02 +0000396 mHasSession = true;
Svet Ganov782043c2017-02-11 00:52:02 +0000397 } catch (RemoteException e) {
398 throw e.rethrowFromSystemServer();
399 }
400 }
401
402 private void finishSession() {
403 if (DEBUG) {
Felipe Leme0ab53dc2017-02-23 08:33:18 -0800404 Log.d(TAG, "finishSession()");
Svet Ganov782043c2017-02-11 00:52:02 +0000405 }
406 mHasSession = false;
407 try {
408 mService.finishSession(mContext.getActivityToken(), mContext.getUserId());
Felipe Lemebab851c2017-02-03 18:45:08 -0800409 } catch (RemoteException e) {
410 throw e.rethrowFromSystemServer();
411 }
412 }
413
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700414 private void cancelSession() {
415 if (DEBUG) {
416 Log.d(TAG, "cancelSession()");
417 }
418 mHasSession = false;
419 try {
420 mService.cancelSession(mContext.getActivityToken(), mContext.getUserId());
421 } catch (RemoteException e) {
422 throw e.rethrowFromSystemServer();
423 }
424 }
425
Felipe Leme640f30a2017-03-06 15:44:06 -0800426 private void updateSession(AutofillId id, Rect bounds, AutofillValue value, int flags) {
Svet Ganov782043c2017-02-11 00:52:02 +0000427 if (DEBUG) {
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700428 if (VERBOSE || (flags & FLAG_VIEW_EXITED) != 0) {
Felipe Leme0ab53dc2017-02-23 08:33:18 -0800429 Log.d(TAG, "updateSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
Felipe Leme640f30a2017-03-06 15:44:06 -0800430 + ", flags=" + flags);
Felipe Leme0ab53dc2017-02-23 08:33:18 -0800431 }
Felipe Lemebab851c2017-02-03 18:45:08 -0800432 }
Felipe Leme0ab53dc2017-02-23 08:33:18 -0800433
Felipe Leme3461d3c2017-01-19 08:54:55 -0800434 try {
Svet Ganov782043c2017-02-11 00:52:02 +0000435 mService.updateSession(mContext.getActivityToken(), id, bounds, value, flags,
436 mContext.getUserId());
Felipe Leme3461d3c2017-01-19 08:54:55 -0800437 } catch (RemoteException e) {
438 throw e.rethrowFromSystemServer();
439 }
440 }
Svet Ganov782043c2017-02-11 00:52:02 +0000441
442 private void ensureServiceClientAddedIfNeeded() {
443 if (getClient() == null) {
444 return;
445 }
446 if (mServiceClient == null) {
Felipe Leme640f30a2017-03-06 15:44:06 -0800447 mServiceClient = new AutofillManagerClient(this);
Svet Ganov782043c2017-02-11 00:52:02 +0000448 try {
449 mEnabled = mService.addClient(mServiceClient, mContext.getUserId());
450 } catch (RemoteException e) {
451 throw e.rethrowFromSystemServer();
452 }
453 }
454 }
455
Felipe Lemee6010f22017-03-03 11:19:51 -0800456 /**
457 * Registers a {@link AutofillCallback} to receive autofill events.
458 *
459 * @param callback callback to receive events.
460 */
461 public void registerCallback(@Nullable AutofillCallback callback) {
462 if (callback == null) return;
463
464 final boolean hadCallback = mCallback != null;
465 mCallback = callback;
466
467 if (mHasSession && !hadCallback) {
468 try {
469 mService.setHasCallback(mContext.getActivityToken(), mContext.getUserId(), true);
470 } catch (RemoteException e) {
471 throw e.rethrowFromSystemServer();
472 }
473 }
474 }
475
476 /**
477 * Unregisters a {@link AutofillCallback} to receive autofill events.
478 *
479 * @param callback callback to stop receiving events.
480 */
481 public void unregisterCallback(@Nullable AutofillCallback callback) {
482 if (callback == null || mCallback == null || callback != mCallback) return;
483
484 mCallback = null;
485
486 if (mHasSession) {
487 try {
488 mService.setHasCallback(mContext.getActivityToken(), mContext.getUserId(), false);
489 } catch (RemoteException e) {
490 throw e.rethrowFromSystemServer();
491 }
492 }
493 }
494
Felipe Leme640f30a2017-03-06 15:44:06 -0800495 private void onAutofillEvent(IBinder windowToken, AutofillId id, int event) {
Felipe Lemee6010f22017-03-03 11:19:51 -0800496 if (mCallback == null) return;
497 if (id == null) {
498 Log.w(TAG, "onAutofillEvent(): no id for event " + event);
499 return;
500 }
501
502 final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
503 if (root == null) {
504 Log.w(TAG, "onAutofillEvent() for " + id + ": root view gone");
505 return;
506 }
507 final View view = root.findViewByAccessibilityIdTraversal(id.getViewId());
508 if (view == null) {
509 Log.w(TAG, "onAutofillEvent() for " + id + ": view gone");
510 return;
511 }
512 if (id.isVirtual()) {
513 mCallback.onAutofillEventVirtual(view, id.getVirtualChildId(), event);
514 } else {
515 mCallback.onAutofillEvent(view, event);
516 }
517 }
518
519 /**
520 * Callback for auto-fill related events.
521 *
522 * <p>Typically used for applications that display their own "auto-complete" views, so they can
523 * enable / disable such views when the auto-fill UI affordance is shown / hidden.
524 */
525 public abstract static class AutofillCallback {
526
527 /** @hide */
528 @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
529 @Retention(RetentionPolicy.SOURCE)
530 public @interface AutofillEventType {}
531
532 /**
533 * The auto-fill input UI affordance associated with the view was shown.
534 *
535 * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
536 * should be hidden upon receiving this event.
537 */
538 public static final int EVENT_INPUT_SHOWN = 1;
539
540 /**
541 * The auto-fill input UI affordance associated with the view was hidden.
542 *
543 * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
544 * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
545 */
546 public static final int EVENT_INPUT_HIDDEN = 2;
547
548 /**
Felipe Leme24aae152017-03-15 12:33:01 -0700549 * The auto-fill input UI affordance associated with the view won't be shown because
550 * autofill is not available.
551 *
552 * <p>If the view provides its own auto-complete UI affordance but was not displaying it
553 * to avoid flickering, it could shown it upon receiving this event.
554 */
555 public static final int EVENT_INPUT_UNAVAILABLE = 3;
556
557 /**
Felipe Lemee6010f22017-03-03 11:19:51 -0800558 * Called after a change in the autofill state associated with a view.
559 *
560 * @param view view associated with the change.
561 *
562 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
563 */
564 public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {}
565
566 /**
567 * Called after a change in the autofill state associated with a virtual view.
568 *
569 * @param view parent view associated with the change.
570 * @param childId id identifying the virtual child inside the parent view.
571 *
572 * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
573 */
574 public void onAutofillEventVirtual(@NonNull View view, int childId,
575 @AutofillEventType int event) {}
576 }
577
Felipe Leme640f30a2017-03-06 15:44:06 -0800578 private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
579 private final WeakReference<AutofillManager> mAfm;
Svet Ganov782043c2017-02-11 00:52:02 +0000580
Felipe Leme640f30a2017-03-06 15:44:06 -0800581 AutofillManagerClient(AutofillManager autofillManager) {
582 mAfm = new WeakReference<>(autofillManager);
Svet Ganov782043c2017-02-11 00:52:02 +0000583 }
584
585 @Override
586 public void setState(boolean enabled) {
Felipe Leme640f30a2017-03-06 15:44:06 -0800587 final AutofillManager afm = mAfm.get();
588 if (afm != null) {
589 afm.mContext.getMainThreadHandler().post(() -> afm.mEnabled = enabled);
Svet Ganov782043c2017-02-11 00:52:02 +0000590 }
591 }
592
593 @Override
Felipe Leme640f30a2017-03-06 15:44:06 -0800594 public void autofill(List<AutofillId> ids, List<AutofillValue> values) {
Svet Ganov782043c2017-02-11 00:52:02 +0000595 // TODO(b/33197203): must keep the dataset so subsequent calls pass the same
596 // dataset.extras to service
Felipe Leme640f30a2017-03-06 15:44:06 -0800597 final AutofillManager afm = mAfm.get();
598 if (afm != null) {
599 afm.mContext.getMainThreadHandler().post(() -> {
600 if (afm.getClient() != null) {
601 afm.getClient().autofill(ids, values);
Svet Ganov782043c2017-02-11 00:52:02 +0000602 }
603 });
604 }
605 }
606
607 @Override
608 public void authenticate(IntentSender intent, Intent fillInIntent) {
Felipe Leme640f30a2017-03-06 15:44:06 -0800609 final AutofillManager afm = mAfm.get();
610 if (afm != null) {
611 afm.mContext.getMainThreadHandler().post(() -> {
612 if (afm.getClient() != null) {
613 afm.getClient().authenticate(intent, fillInIntent);
Svet Ganov782043c2017-02-11 00:52:02 +0000614 }
615 });
616 }
617 }
Felipe Lemee6010f22017-03-03 11:19:51 -0800618
619 @Override
Felipe Leme640f30a2017-03-06 15:44:06 -0800620 public void onAutofillEvent(IBinder windowToken, AutofillId id, int event) {
621 final AutofillManager afm = mAfm.get();
622 if (afm != null) {
623 afm.mContext.getMainThreadHandler().post(() -> {
624 if (afm.getClient() != null) {
625 afm.onAutofillEvent(windowToken, id, event);
Felipe Lemee6010f22017-03-03 11:19:51 -0800626 }
627 });
628 }
629 }
Svet Ganov782043c2017-02-11 00:52:02 +0000630 }
Felipe Leme3461d3c2017-01-19 08:54:55 -0800631}