Adds fill request APIs for the client suggestions

Bug: 172024354
Test: atest CtsAutoFillServiceTestCases
Change-Id: I9d60361afb853568f92b88c9c1929b28195df836
This commit is contained in:
TYM Tsai
2020-12-25 17:55:19 +08:00
parent 2f74b666a3
commit dee5a2b902
5 changed files with 180 additions and 0 deletions

View File

@@ -50786,6 +50786,7 @@ package android.view.autofill {
public final class AutofillManager {
method public void cancel();
method public void clearAutofillRequestCallback();
method public void commit();
method public void disableAutofillServices();
method @Nullable public android.content.ComponentName getAutofillServiceComponentName();
@@ -50811,6 +50812,7 @@ package android.view.autofill {
method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
method public void requestAutofill(@NonNull android.view.View);
method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
method public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback);
method public void setUserData(@Nullable android.service.autofill.UserData);
method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
@@ -50828,6 +50830,10 @@ package android.view.autofill {
field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
}
public interface AutofillRequestCallback {
method public void onFillRequest(@Nullable android.view.inputmethod.InlineSuggestionsRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
}
public final class AutofillValue implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutofillValue forDate(long);

View File

@@ -96,6 +96,8 @@ public final class FillRequest implements Parcelable {
*/
public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10;
// The flag value 0x20 has been defined in AutofillManager.
/** @hide */
public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;

View File

@@ -25,6 +25,7 @@ import static android.view.autofill.Helper.sVerbose;
import static android.view.autofill.Helper.toList;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -45,16 +46,21 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.AutofillService;
import android.service.autofill.FillCallback;
import android.service.autofill.FillEventHistory;
import android.service.autofill.IFillCallback;
import android.service.autofill.UserData;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -74,6 +80,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
@@ -99,6 +106,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import sun.misc.Cleaner;
@@ -167,6 +175,12 @@ import sun.misc.Cleaner;
* shows an autofill save UI if the value of savable views have changed. If the user selects the
* option to Save, the current value of the views is then sent to the autofill service.
*
* <p>There is another choice for the application to provide it's datasets to the Autofill framework
* by setting an {@link AutofillRequestCallback} through
* {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use
* its callback instead of the default {@link AutofillService}. See
* {@link AutofillRequestCallback} for more details.
*
* <h3 id="additional-notes">Additional notes</h3>
*
* <p>It is safe to call <code>AutofillManager</code> methods from any thread.
@@ -280,6 +294,7 @@ public final class AutofillManager {
/** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
/** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
/** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
/** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20;
// NOTE: flag below is used by the session start receiver only, hence it can have values above
/** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
@@ -580,6 +595,11 @@ public final class AutofillManager {
@GuardedBy("mLock")
private boolean mEnabledForAugmentedAutofillOnly;
@GuardedBy("mLock")
@Nullable private AutofillRequestCallback mAutofillRequestCallback;
@GuardedBy("mLock")
@Nullable private Executor mRequestCallbackExecutor;
/** @hide */
public interface AutofillClient {
/**
@@ -1824,6 +1844,32 @@ public final class AutofillManager {
return new AutofillId(parent.getAutofillViewId(), virtualId);
}
/**
* Sets the client's suggestions callback for autofill.
*
* @see AutofillRequestCallback
*
* @param executor specifies the thread upon which the callbacks will be invoked.
* @param callback which handles autofill request to provide client's suggestions.
*/
public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull AutofillRequestCallback callback) {
synchronized (mLock) {
mRequestCallbackExecutor = executor;
mAutofillRequestCallback = callback;
}
}
/**
* clears the client's suggestions callback for autofill.
*/
public void clearAutofillRequestCallback() {
synchronized (mLock) {
mRequestCallbackExecutor = null;
mAutofillRequestCallback = null;
}
}
@GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
@@ -1884,6 +1930,13 @@ public final class AutofillManager {
}
}
if (mAutofillRequestCallback != null) {
if (sDebug) {
Log.d(TAG, "startSession with the client suggestions provider");
}
flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS;
}
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, componentName,
@@ -2233,6 +2286,28 @@ public final class AutofillManager {
}
}
private void onFillRequest(InlineSuggestionsRequest request,
CancellationSignal cancellationSignal, FillCallback callback) {
final AutofillRequestCallback autofillRequestCallback;
final Executor executor;
synchronized (mLock) {
autofillRequestCallback = mAutofillRequestCallback;
executor = mRequestCallbackExecutor;
}
if (autofillRequestCallback != null && executor != null) {
final long ident = Binder.clearCallingIdentity();
try {
executor.execute(() ->
autofillRequestCallback.onFillRequest(
request, cancellationSignal, callback));
} finally {
Binder.restoreCallingIdentity(ident);
}
} else {
callback.onSuccess(null);
}
}
/** @hide */
public static final int SET_STATE_FLAG_ENABLED = 0x01;
/** @hide */
@@ -3612,6 +3687,23 @@ public final class AutofillManager {
afm.post(() -> afm.requestShowSoftInput(id));
}
}
@Override
public void requestFillFromClient(int id, InlineSuggestionsRequest request,
IFillCallback callback) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
ICancellationSignal transport = CancellationSignal.createTransport();
try {
callback.onCancellable(transport);
} catch (RemoteException e) {
Slog.w(TAG, "Error requesting a cancellation", e);
}
afm.onFillRequest(request, CancellationSignal.fromTransport(transport),
new FillCallback(callback, id));
}
}
}
private static final class AugmentedAutofillManagerClient

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2020 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.view.autofill;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.CancellationSignal;
import android.service.autofill.FillCallback;
import android.view.inputmethod.InlineSuggestionsRequest;
/**
* <p>This class is used to provide some input suggestions to the Autofill framework.
*
* <P>When the user is requested to input something, Autofill will try to query input suggestions
* for the user choosing. If the application want to provide some internal input suggestions,
* implements this callback and register via
* {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor,
* AutofillRequestCallback)}. Autofill will callback the
* {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request
* input suggestions.
*
* <P>To make sure the callback to take effect, must register before the autofill session starts.
* If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current
* session, and then the callback will be used at the next restarted session.
*
* <P>To create a {@link android.service.autofill.FillResponse}, application should fetch
* {@link AutofillId}s from its view structure. Below is an example:
* <pre class="prettyprint">
* AutofillId usernameId = findViewById(R.id.username).getAutofillId();
* AutofillId passwordId = findViewById(R.id.password).getAutofillId();
* </pre>
* To learn more about creating a {@link android.service.autofill.FillResponse}, read
* <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>.
*
* <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond
* a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill
* request with the default {@link android.service.autofill.AutofillService}. Or clear the callback
* from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the
* client would like to keep no suggestions for the field, respond with an empty
* {@link android.service.autofill.FillResponse} which has no dataset.
*
* <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or
* the keyboard may choose to block your app from the inline strip.
*/
public interface AutofillRequestCallback {
/**
* Called by the Android system to decide if a screen can be autofilled by the callback.
*
* @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if
* currently inline suggestions are supported and can be displayed.
* @param cancellationSignal signal for observing cancellation requests. The system will use
* this to notify you that the fill result is no longer needed and you should stop
* handling this fill request in order to save resources.
* @param callback object used to notify the result of the request.
*/
void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
@NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
}

View File

@@ -24,9 +24,11 @@ import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Rect;
import android.os.IBinder;
import android.service.autofill.IFillCallback;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.KeyEvent;
import com.android.internal.os.IResultReceiver;
@@ -140,4 +142,10 @@ oneway interface IAutoFillManagerClient {
* Requests to show the soft input method if the focus is on the given id.
*/
void requestShowSoftInput(in AutofillId id);
/**
* Requests to determine if a screen can be autofilled by the client app.
*/
void requestFillFromClient(int id, in InlineSuggestionsRequest request,
in IFillCallback callback);
}