summaryrefslogtreecommitdiff
path: root/core/java/android
diff options
context:
space:
mode:
authorFelipe Leme <felipeal@google.com>2016-10-25 14:57:11 -0700
committerFelipe Leme <felipeal@google.com>2016-11-11 16:02:55 -0800
commit29a5b0d0f1cc7fd6cbfe97c816b8a687d9e438cc (patch)
tree25f9c559ec8ae1ee3c9ecab0a9dc1982d5b50e8d /core/java/android
parent7519e166983e48988957e67679ef79b9661a2b34 (diff)
Added a callback for AutoFillService.
So far AutoFillService only received the assist data from framework; in this CL, it also offers a method where the auto-fill provider can send the auto-fill data back to framework. The workflow is: - AFMSI calls a new AM method (requestAutoFillData(), instead of requestAssistContextExtras()). - The assist receiver is located in the app, not on system service. - AM uses a new request type (ASSIST_CONTEXT_AUTOFILL) to request the assist data to the activity. - ViewStructure has a new setAutoFillId() method which is used to set an unique id for the view. - View uses the accessibility id to implement the auto-fill id. - When the activity fullfills the request, it creates an IAutoFillCallback remote object - that will be used to set the auto-fill fields - and returns it in the assist bundle (using the VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK key). - The app-visible AutoFillService class offers an onFillRequest() method, which contains the assist data and a FillCallback used to handle it. BUG: 31001899 Test: manually built and ran it Change-Id: I3d208c14e81022dc96dd03f38bbe25a778b24a67
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/Activity.java71
-rw-r--r--core/java/android/app/ActivityManager.java3
-rw-r--r--core/java/android/app/ActivityManagerNative.java2
-rw-r--r--core/java/android/app/ActivityThread.java32
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/assist/AssistStructure.java26
-rw-r--r--core/java/android/service/autofill/AutoFillService.java101
-rw-r--r--core/java/android/service/autofill/FillCallback.java146
-rw-r--r--core/java/android/service/autofill/FillableInputField.java99
-rw-r--r--core/java/android/service/autofill/IAutoFillCallback.aidl27
-rw-r--r--core/java/android/service/autofill/IAutoFillManagerService.aidl26
-rw-r--r--core/java/android/service/autofill/IAutoFillService.aidl10
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java2
-rw-r--r--core/java/android/view/View.java5
-rw-r--r--core/java/android/view/ViewStructure.java3
15 files changed, 465 insertions, 90 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index b38133901b30..6f53f13662be 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -53,7 +53,6 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
-import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.net.Uri;
@@ -70,6 +69,9 @@ import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.service.autofill.FillableInputField;
+import android.service.autofill.AutoFillService;
+import android.service.autofill.IAutoFillCallback;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -90,8 +92,6 @@ import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
import android.view.DragAndDropPermissions;
import android.view.DragEvent;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
@@ -113,9 +113,11 @@ import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
+import android.widget.EditText;
import android.widget.Toast;
import android.widget.Toolbar;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
@@ -802,11 +804,13 @@ public class Activity extends ContextThemeWrapper
private boolean mReleased;
private boolean mUpdated;
}
- private final ArrayList<ManagedCursor> mManagedCursors =
- new ArrayList<ManagedCursor>();
- // protected by synchronized (this)
+ @GuardedBy("mManagedCursors")
+ private final ArrayList<ManagedCursor> mManagedCursors = new ArrayList<>();
+
+ @GuardedBy("this")
int mResultCode = RESULT_CANCELED;
+ @GuardedBy("this")
Intent mResultData = null;
private TranslucentConversionListener mTranslucentCallback;
@@ -837,6 +841,9 @@ public class Activity extends ContextThemeWrapper
private boolean mHasCurrentPermissionsRequest;
private boolean mEatKeyUpEvent;
+ @GuardedBy("this")
+ private IAutoFillCallback mAutoFillCallback;
+
private static native String getDlWarning();
/** Return the intent that started this activity. */
@@ -1688,6 +1695,53 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Lazily gets the {@code IAutoFillCallback} for this activitity.
+ *
+ * <p>This callback is used by the {@link AutoFillService} app to auto-fill the activity fields.
+ */
+ IAutoFillCallback getAutoFillCallback() {
+ synchronized (this) {
+ if (mAutoFillCallback == null) {
+ mAutoFillCallback = new IAutoFillCallback.Stub() {
+ @Override
+ public void autofill(@SuppressWarnings("rawtypes") List fields)
+ throws RemoteException {
+ runOnUiThread(() -> {
+ final View root = getWindow().getDecorView().getRootView();
+ for (Object field : fields) {
+ if (!(field instanceof FillableInputField)) {
+ Slog.w(TAG, "autofill(): invalid type " + field.getClass());
+ continue;
+ }
+ FillableInputField autoFillField = (FillableInputField) field;
+ final int viewId = autoFillField.getId();
+ final View view = root.findViewByAccessibilityIdTraversal(viewId);
+ // TODO: should handle other types of view as well, but that will
+ // require:
+ // - a new interface like AutoFillable
+ // - a way for the views to define the type of the autofield value
+ if ((view instanceof EditText)) {
+ ((EditText) view).setText(autoFillField.getValue());
+ }
+ }
+ });
+ }
+
+ @Override
+ public void showError(String message) {
+ runOnUiThread(() -> {
+ // TODO: temporary show a toast until it uses the Snack bar.
+ Toast.makeText(Activity.this, "Auto-fill request failed: " + message,
+ Toast.LENGTH_LONG).show();
+ });
+ }
+ };
+ }
+ }
+ return mAutoFillCallback;
+ }
+
+ /**
* Request the Keyboard Shortcuts screen to show up. This will trigger
* {@link #onProvideKeyboardShortcuts} to retrieve the shortcuts for the foreground activity.
*/
@@ -5974,6 +6028,11 @@ public class Activity extends ContextThemeWrapper
getWindow().peekDecorView().getViewRootImpl().dump(prefix, fd, writer, args);
}
+ if (mAutoFillCallback != null) {
+ writer.print(prefix); writer.print("mAutoFillCallback: " );
+ writer.println(mAutoFillCallback);
+ }
+
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4a39e4a4cb50..c55d3b8208d7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -447,6 +447,9 @@ public class ActivityManager {
/** @hide requestType for assist context: generate full AssistStructure. */
public static final int ASSIST_CONTEXT_FULL = 1;
+ /** @hide requestType for assist context: generate full AssistStructure for auto-fill. */
+ public static final int ASSIST_CONTEXT_AUTOFILL = 2;
+
/** @hide Flag for registerUidObserver: report changes in process state. */
public static final int UID_OBSERVER_PROCSTATE = 1<<0;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 199fda4cd09c..cabdd4b6ac00 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -109,4 +109,4 @@ public abstract class ActivityManagerNative {
static public void noteAlarmFinish(PendingIntent ps, int sourceUid, String tag) {
ActivityManager.noteAlarmFinish(ps, sourceUid, tag);
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a3414f458ef3..d41a7e8102de 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -88,6 +88,8 @@ import android.provider.Downloads;
import android.provider.Settings;
import android.security.NetworkSecurityPolicy;
import android.security.net.config.NetworkSecurityConfigProvider;
+import android.service.autofill.IAutoFillCallback;
+import android.service.voice.VoiceInteractionSession;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
@@ -2881,16 +2883,24 @@ public final class ActivityThread {
mLastAssistStructures.remove(i);
}
}
+ // Filling for auto-fill has a few differences:
+ // - it does not need an AssistContent
+ // - it does not call onProvideAssistData()
+ // - it needs an IAutoFillCallback
+ boolean forAutofill = cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTOFILL;
+
Bundle data = new Bundle();
AssistStructure structure = null;
- AssistContent content = new AssistContent();
+ AssistContent content = forAutofill ? null : new AssistContent();
ActivityClientRecord r = mActivities.get(cmd.activityToken);
Uri referrer = null;
if (r != null) {
r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data);
- r.activity.onProvideAssistData(data);
+ if (!forAutofill) {
+ r.activity.onProvideAssistData(data);
+ }
referrer = r.activity.onProvideReferrer();
- if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
+ if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutofill) {
structure = new AssistStructure(r.activity);
Intent activityIntent = r.activity.getIntent();
if (activityIntent != null && (r.window == null ||
@@ -2900,11 +2910,21 @@ public final class ActivityThread {
intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
intent.removeUnsafeExtras();
- content.setDefaultIntent(intent);
+ if (forAutofill) {
+ IAutoFillCallback autoFillCallback = r.activity.getAutoFillCallback();
+ data.putBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK,
+ autoFillCallback.asBinder());
+ } else {
+ content.setDefaultIntent(intent);
+ }
} else {
- content.setDefaultIntent(new Intent());
+ if (!forAutofill) {
+ content.setDefaultIntent(new Intent());
+ }
+ }
+ if (!forAutofill) {
+ r.activity.onProvideAssistContent(content);
}
- r.activity.onProvideAssistContent(content);
}
}
if (structure == null) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 9fe34c0e39bc..d63d37be9f2d 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -564,6 +564,8 @@ interface IActivityManager {
void moveStackToDisplay(int stackId, int displayId) = 403;
void enterPictureInPictureModeWithAspectRatio(in IBinder token, float aspectRatio) = 404;
void setPictureInPictureAspectRatio(in IBinder token, float aspectRatio) = 405;
+ boolean requestAutoFillData(in IResultReceiver receiver, in Bundle receiverExtras,
+ in IBinder activityToken) = 406;
// Please keep these transaction codes the same -- they are also
// sent by C++ code. when a new method is added, use the next available transaction id.
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 6b720c0a22f6..a6f23666cc6d 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -515,6 +515,7 @@ public class AssistStructure implements Parcelable {
String mIdPackage;
String mIdType;
String mIdEntry;
+ int mAutoFillId = View.NO_ID;
int mX;
int mY;
int mScrollX;
@@ -539,6 +540,7 @@ public class AssistStructure implements Parcelable {
static final int FLAGS_ACTIVATED = 0x00002000;
static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
+ static final int FLAGS_HAS_AUTO_FILL_ID = 0x80000000;
static final int FLAGS_HAS_MATRIX = 0x40000000;
static final int FLAGS_HAS_ALPHA = 0x20000000;
static final int FLAGS_HAS_ELEVATION = 0x10000000;
@@ -582,6 +584,9 @@ public class AssistStructure implements Parcelable {
}
}
}
+ if ((flags&FLAGS_HAS_AUTO_FILL_ID) != 0) {
+ mAutoFillId = in.readInt();
+ }
if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
mX = in.readInt();
mY = in.readInt();
@@ -637,6 +642,9 @@ public class AssistStructure implements Parcelable {
if (mId != View.NO_ID) {
flags |= FLAGS_HAS_ID;
}
+ if (mAutoFillId != View.NO_ID) {
+ flags |= FLAGS_HAS_AUTO_FILL_ID;
+ }
if ((mX&~0x7fff) != 0 || (mY&~0x7fff) != 0
|| (mWidth&~0x7fff) != 0 | (mHeight&~0x7fff) != 0) {
flags |= FLAGS_HAS_LARGE_COORDS;
@@ -681,6 +689,9 @@ public class AssistStructure implements Parcelable {
}
}
}
+ if ((flags&FLAGS_HAS_AUTO_FILL_ID) != 0) {
+ out.writeInt(mAutoFillId);
+ }
if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
out.writeInt(mX);
out.writeInt(mY);
@@ -751,6 +762,16 @@ public class AssistStructure implements Parcelable {
}
/**
+ * Returns the id that can be used to auto-fill the view.
+ *
+ * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not
+ * for assist.
+ */
+ public int getAutoFillId() {
+ return mAutoFillId;
+ }
+
+ /**
* Returns the left edge of this view, in pixels, relative to the left edge of its parent.
*/
public int getLeft() {
@@ -1322,6 +1343,11 @@ public class AssistStructure implements Parcelable {
public Rect getTempRect() {
return mAssist.mTmpRect;
}
+
+ @Override
+ public void setAutoFillId(int autoFillId) {
+ mNode.mAutoFillId = autoFillId;
+ }
}
/** @hide */
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index 14ce009bedc6..83b2065f44fe 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -15,27 +15,37 @@
*/
package android.service.autofill;
+import android.annotation.IntDef;
import android.annotation.SdkConstant;
+import android.app.Activity;
import android.app.Service;
import android.app.assist.AssistStructure;
import android.content.Intent;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
+import android.service.voice.VoiceInteractionSession;
import android.util.Log;
import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Top-level service of the current auto-fill service for a given user.
+ *
+ * <p>Apps providing auto-fill capabilities must extend this service.
*/
-// TODO: expand documentation
public abstract class AutoFillService extends Service {
- private static final String TAG = "AutoFillService";
- private static final boolean DEBUG = true; // TODO: set to false once stable
+ static final String TAG = "AutoFillService";
+ static final boolean DEBUG = true; // TODO: set to false once stable
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -47,11 +57,23 @@ public abstract class AutoFillService extends Service {
public static final String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
private static final int MSG_READY = 1;
- private static final int MSG_NEW_SESSION = 2;
- private static final int MSG_SESSION_FINISHED = 3;
- private static final int MSG_SHUTDOWN = 4;
+ private static final int MSG_AUTO_FILL = 2;
+ private static final int MSG_SHUTDOWN = 3;
- // TODO: add metadata?
+ private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) throws RemoteException {
+ final AssistStructure structure = resultData
+ .getParcelable(VoiceInteractionSession.KEY_STRUCTURE);
+
+ final IBinder binder = resultData
+ .getBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK);
+
+ mHandlerCaller
+ .obtainMessageOO(MSG_AUTO_FILL, structure, binder).sendToTarget();
+ }
+
+ };
private final IAutoFillService mInterface = new IAutoFillService.Stub() {
@Override
@@ -60,15 +82,8 @@ public abstract class AutoFillService extends Service {
}
@Override
- public void newSession(String token, Bundle data, int flags,
- AssistStructure structure) {
- mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOOO(MSG_NEW_SESSION,
- flags, token, data, structure));
- }
-
- @Override
- public void finishSession(String token) {
- mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_SESSION_FINISHED, token));
+ public IResultReceiver getAssistReceiver() {
+ return mAssistReceiver;
}
@Override
@@ -85,17 +100,11 @@ public abstract class AutoFillService extends Service {
case MSG_READY: {
onReady();
break;
- } case MSG_NEW_SESSION: {
+ } case MSG_AUTO_FILL: {
final SomeArgs args = (SomeArgs) msg.obj;
- final int flags = args.argi1;
- final String token = (String) args.arg1;
- final Bundle data = (Bundle) args.arg2;
- final AssistStructure assistStructure = (AssistStructure) args.arg3;
- onNewSession(token, data, flags, assistStructure);
- break;
- } case MSG_SESSION_FINISHED: {
- final String token = (String) msg.obj;
- onSessionFinished(token);
+ final AssistStructure structure = (AssistStructure) args.arg1;
+ final IBinder binder = (IBinder) args.arg2;
+ autoFillActivity(structure, binder);
break;
} case MSG_SHUTDOWN: {
onShutdown();
@@ -121,6 +130,7 @@ public abstract class AutoFillService extends Service {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
return mInterface.asBinder();
}
+ Log.w(TAG, "Tried to bind to wrong intent: " + intent);
return null;
}
@@ -129,41 +139,26 @@ public abstract class AutoFillService extends Service {
* to receive interaction from it.
*
* <p>You should generally do initialization here rather than in {@link #onCreate}.
- *
- * <p>Sub-classes should call it first, since it sets the reference to the sytem-server service.
*/
- // TODO: rename to onConnected() / add onDisconnected()?
+ // TODO: rename to onConnect() / update javadoc
public void onReady() {
if (DEBUG) Log.d(TAG, "onReady()");
}
/**
- * Called to receive data from the application that the user was requested auto-fill for.
+ * Handles an auto-fill request.
*
- * @param token unique token identifying the auto-fill session, it should be used when providing
- * the auto-filled fields.
- * @param data Arbitrary data supplied by the app through
- * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
- * May be {@code null} if data has been disabled by the user or device policy.
- * @param startFlags currently always 0.
- * @param structure If available, the structure definition of all windows currently
- * displayed by the app. May be {@code null} if auto-fill data has been disabled by the user
- * or device policy; will be an empty stub if the application has disabled auto-fill
- * by marking its window as secure.
+ * @param structure {@link Activity}'s view structure .
+ * @param cancellationSignal signal for observing cancel requests.
+ * @param callback object used to fulllfill the request.
*/
- @SuppressWarnings("unused")
- // TODO: take the factory approach where this method return a session, and move the callback
- // methods (like autofill()) to the session.
- public void onNewSession(String token, Bundle data, int startFlags, AssistStructure structure) {
- if (DEBUG) Log.d(TAG, "onNewSession(): token=" + token);
- }
+ public abstract void onFillRequest(AssistStructure structure,
+ CancellationSignal cancellationSignal, FillCallback callback);
- /**
- * Called when an auto-fill session is finished.
- */
- @SuppressWarnings("unused")
- public void onSessionFinished(String token) {
- if (DEBUG) Log.d(TAG, "onSessionFinished(): token=" + token);
+ private void autoFillActivity(AssistStructure structure, IBinder binder) {
+ final FillCallback callback = new FillCallback(binder);
+ // TODO: hook up the cancelationSignal
+ onFillRequest(structure, new CancellationSignal(), callback);
}
/**
@@ -172,7 +167,9 @@ public abstract class AutoFillService extends Service {
*
* <p> At this point this service may no longer be an active {@link AutoFillService}.
*/
+ // TODO: rename to onDisconnected() / update javadoc
public void onShutdown() {
if (DEBUG) Log.d(TAG, "onShutdown()");
}
+
}
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
new file mode 100644
index 000000000000..bdcc93b0397c
--- /dev/null
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import static android.service.autofill.AutoFillService.DEBUG;
+import static android.service.autofill.AutoFillService.TAG;
+
+import android.app.Activity;
+import android.app.assist.AssistStructure.ViewNode;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Handles auto-fill requests from the {@link AutoFillService} into the {@link Activity} being
+ * auto-filled.
+ */
+public final class FillCallback {
+
+ private final IAutoFillCallback mCallback;
+
+ /** @hide */
+ FillCallback(IBinder binder) {
+ mCallback = IAutoFillCallback.Stub.asInterface(binder);
+ }
+
+ /**
+ * Auto-fills the {@link Activity}.
+ *
+ * @throws RuntimeException if an error occurred while auto-filling it.
+ */
+ public void onSuccess(FillData data) {
+ if (DEBUG) Log.d(TAG, "onSuccess(): data=" + data);
+
+ Preconditions.checkArgument(data != null, "data cannot be null");
+
+ try {
+ mCallback.autofill(data.asList());
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Notifies the activity that the auto-fill request failed.
+ */
+ public void onFailure(CharSequence message) {
+ if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
+
+ Preconditions.checkArgument(message != null, "message cannot be null");
+
+ try {
+ mCallback.showError(message.toString());
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Data used to fill the fields of an {@link Activity}.
+ *
+ * <p>This class is immutable.
+ */
+ public static final class FillData {
+
+ private final List<FillableInputField> mList;
+
+ private FillData(Builder builder) {
+ final int size = builder.mFields.size();
+ final List<FillableInputField> list = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ list.add(builder.mFields.valueAt(i));
+ }
+ mList = Collections.unmodifiableList(list);
+ // TODO: use FastImmutableArraySet or a similar structure instead?
+ }
+
+ /**
+ * Gets the response as a {@code List} so it can be used in a binder call.
+ */
+ List<FillableInputField> asList() {
+ return mList;
+ }
+
+ @Override
+ public String toString() {
+ return "[AutoFillResponse: " + mList + "]";
+ }
+
+ /**
+ * Builder for {@link FillData} objects.
+ *
+ * <p>Typical usage:
+ *
+ * <pre class="prettyprint">
+ * FillCallback.FillData data = new FillCallback.FillData.Builder()
+ * .setTextField(id1, "value 1")
+ * .setTextField(id2, "value 2")
+ * .build()
+ * </pre>
+ */
+ public static class Builder {
+ private final SparseArray<FillableInputField> mFields = new SparseArray<>();
+
+ /**
+ * Auto-fills a text field.
+ *
+ * @param id view id as returned by {@link ViewNode#getAutoFillId()}.
+ * @param text text to be auto-filled.
+ * @return same builder so it can be chained.
+ */
+ public Builder setTextField(int id, String text) {
+ mFields.put(id, FillableInputField.forText(id, text));
+ return this;
+ }
+
+ /**
+ * Builds a new {@link FillData} instance.
+ */
+ public FillData build() {
+ return new FillData(this);
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/autofill/FillableInputField.java b/core/java/android/service/autofill/FillableInputField.java
new file mode 100644
index 000000000000..62950b40e5e1
--- /dev/null
+++ b/core/java/android/service/autofill/FillableInputField.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.app.assist.AssistStructure.ViewNode;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a view field that can be auto-filled.
+ *
+ * <p>Currently only text-fields are supported, so the value of the field can be obtained through
+ * {@link #getValue()}.
+ *
+ * @hide
+ */
+public final class FillableInputField implements Parcelable {
+
+ private final int mId;
+ private final String mValue;
+
+ private FillableInputField(int id, String value) {
+ mId = id;
+ mValue = value;
+ }
+
+ private FillableInputField(Parcel parcel) {
+ mId = parcel.readInt();
+ mValue = parcel.readString();
+ }
+
+ /**
+ * Gets the view id as returned by {@link ViewNode#getAutoFillId()}.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Gets the value of this field.
+ */
+ public String getValue() {
+ return mValue;
+
+ }
+
+ @Override
+ public String toString() {
+ return "[AutoFillField: " + mId + "=" + mValue + "]";
+ }
+
+ /**
+ * Creates an {@code AutoFillField} for a text field.
+ *
+ * @param id view id as returned by {@link ViewNode#getAutoFillId()}.
+ * @param text value to be auto-filled.
+ */
+ public static FillableInputField forText(int id, String text) {
+ return new FillableInputField(id, text);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mId);
+ parcel.writeString(mValue);
+ }
+
+ public static final Parcelable.Creator<FillableInputField> CREATOR =
+ new Parcelable.Creator<FillableInputField>() {
+ @Override
+ public FillableInputField createFromParcel(Parcel source) {
+ return new FillableInputField(source);
+ }
+
+ @Override
+ public FillableInputField[] newArray(int size) {
+ return new FillableInputField[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/IAutoFillCallback.aidl b/core/java/android/service/autofill/IAutoFillCallback.aidl
new file mode 100644
index 000000000000..db8ef96976a2
--- /dev/null
+++ b/core/java/android/service/autofill/IAutoFillCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+interface IAutoFillCallback {
+ void autofill(in List values);
+ void showError(String message);
+}
diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl
index 2c0623413be6..a91841b98453 100644
--- a/core/java/android/service/autofill/IAutoFillManagerService.aidl
+++ b/core/java/android/service/autofill/IAutoFillManagerService.aidl
@@ -19,36 +19,20 @@ package android.service.autofill;
import android.os.Bundle;
/**
- * Intermediator between apps being auto-filled and auto-fill service implementations.
+ * Mediator between apps being auto-filled and auto-fill service implementations.
*
* {@hide}
*/
interface IAutoFillManagerService {
/**
- * Starts an auto-fill session for the top activities for a given user.
- *
- * It's used to start a new session from system affordances.
+ * Request auto-fill on the top activity of a given user.
*
* @param userId user handle.
- * @param args the bundle to pass as arguments to the voice interaction session.
- * @param flags flags indicating optional session behavior.
* @param activityToken optional token of activity that needs to be on top.
*
- * @return session token, or null if session was not created (for example, if the activity's
- * user does not have an auto-fill service associated with).
- */
- // TODO: pass callback providing an onAutoFill() method
- String startSession(int userId, in Bundle args, int flags, IBinder activityToken);
-
- /**
- * Finishes an auto-fill session.
- *
- * @param userId user handle.
- * @param token session token.
- *
- * @return true if session existed and was finished.
+ * @return whether the request succeeded (for example, if the activity's
+ * user does not have an auto-fill service associated with, it will return false).
*/
- boolean finishSession(int userId, String token);
-
+ boolean requestAutoFill(int userId, IBinder activityToken);
}
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index 73d8d5db5004..dca3c700c0a9 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -16,15 +16,17 @@
package android.service.autofill;
-import android.os.Bundle;
import android.app.assist.AssistStructure;
+import android.os.Bundle;
+import android.service.autofill.IAutoFillCallback;
+import com.android.internal.os.IResultReceiver;
/**
* @hide
*/
-oneway interface IAutoFillService {
+interface IAutoFillService {
+ // TODO: rename to onConnected() / onDisconnected()
void ready();
- void newSession(String token, in Bundle data, int flags, in AssistStructure structure);
- void finishSession(String token);
void shutdown();
+ IResultReceiver getAssistReceiver();
}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index e9bbc2de26cc..12aed251904f 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -119,6 +119,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
public static final String KEY_CONTENT = "content";
/** @hide */
public static final String KEY_RECEIVER_EXTRAS = "receiverExtras";
+ /** @hide */
+ public static final String KEY_AUTO_FILL_CALLBACK = "autoFillCallback";
final Context mContext;
final HandlerCaller mHandlerCaller;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 441f3302994f..02a85216cc20 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6703,6 +6703,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
structure.setId(id, null, null, null);
}
+
+ // The auto-fill id needs to be unique, but its value doesn't matter, so it's better to
+ // reuse the accessibility id to save space.
+ structure.setAutoFillId(getAccessibilityViewId());
+
structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop);
if (!hasIdentityMatrix()) {
structure.setTransformation(getMatrix());
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 2e4ba74159f9..e9ff9d0edba6 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -275,4 +275,7 @@ public abstract class ViewStructure {
/** @hide */
public abstract Rect getTempRect();
+
+ /** @hide */
+ public abstract void setAutoFillId(int autoFillId);
}