Provided an AutofillCallback API.
Some custom views - like WebView - might have their own auto-complete mechanism, so we need to provide a way for them to know when the auto-fill UI is shown or hidden. Fixes: 35948429 Test: CtsAutoFillServiceTestCases (with new tests) pass Test: m update-api Test: manual verification Change-Id: I5682a3b9645d5d077a4a2446e79256d6f77b4b5a
This commit is contained in:
@@ -19,7 +19,9 @@ package android.view.autofill;
|
||||
import static android.view.autofill.Helper.DEBUG;
|
||||
import static android.view.autofill.Helper.VERBOSE;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
@@ -30,7 +32,10 @@ import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
@@ -76,6 +81,8 @@ public final class AutoFillManager {
|
||||
private final IAutoFillManager mService;
|
||||
private IAutoFillManagerClient mServiceClient;
|
||||
|
||||
private AutofillCallback mCallback;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private boolean mHasSession;
|
||||
@@ -276,11 +283,11 @@ public final class AutoFillManager {
|
||||
}
|
||||
}
|
||||
|
||||
private AutoFillId getAutoFillId(View view) {
|
||||
private static AutoFillId getAutoFillId(View view) {
|
||||
return new AutoFillId(view.getAccessibilityViewId());
|
||||
}
|
||||
|
||||
private AutoFillId getAutoFillId(View parent, int childId) {
|
||||
private static AutoFillId getAutoFillId(View parent, int childId) {
|
||||
return new AutoFillId(parent.getAccessibilityViewId(), childId);
|
||||
}
|
||||
|
||||
@@ -289,10 +296,12 @@ public final class AutoFillManager {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value);
|
||||
}
|
||||
|
||||
try {
|
||||
mService.startSession(mContext.getActivityToken(), windowToken,
|
||||
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId());
|
||||
AutoFillClient client = getClient();
|
||||
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
|
||||
mCallback != null);
|
||||
final AutoFillClient client = getClient();
|
||||
if (client != null) {
|
||||
client.resetableStateAvailable();
|
||||
}
|
||||
@@ -344,6 +353,119 @@ public final class AutoFillManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link AutofillCallback} to receive autofill events.
|
||||
*
|
||||
* @param callback callback to receive events.
|
||||
*/
|
||||
public void registerCallback(@Nullable AutofillCallback callback) {
|
||||
if (callback == null) return;
|
||||
|
||||
final boolean hadCallback = mCallback != null;
|
||||
mCallback = callback;
|
||||
|
||||
if (mHasSession && !hadCallback) {
|
||||
try {
|
||||
mService.setHasCallback(mContext.getActivityToken(), mContext.getUserId(), true);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a {@link AutofillCallback} to receive autofill events.
|
||||
*
|
||||
* @param callback callback to stop receiving events.
|
||||
*/
|
||||
public void unregisterCallback(@Nullable AutofillCallback callback) {
|
||||
if (callback == null || mCallback == null || callback != mCallback) return;
|
||||
|
||||
mCallback = null;
|
||||
|
||||
if (mHasSession) {
|
||||
try {
|
||||
mService.setHasCallback(mContext.getActivityToken(), mContext.getUserId(), false);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onAutofillEvent(IBinder windowToken, AutoFillId id, int event) {
|
||||
if (mCallback == null) return;
|
||||
if (id == null) {
|
||||
Log.w(TAG, "onAutofillEvent(): no id for event " + event);
|
||||
return;
|
||||
}
|
||||
|
||||
final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
|
||||
if (root == null) {
|
||||
Log.w(TAG, "onAutofillEvent() for " + id + ": root view gone");
|
||||
return;
|
||||
}
|
||||
final View view = root.findViewByAccessibilityIdTraversal(id.getViewId());
|
||||
if (view == null) {
|
||||
Log.w(TAG, "onAutofillEvent() for " + id + ": view gone");
|
||||
return;
|
||||
}
|
||||
if (id.isVirtual()) {
|
||||
mCallback.onAutofillEventVirtual(view, id.getVirtualChildId(), event);
|
||||
} else {
|
||||
mCallback.onAutofillEvent(view, event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for auto-fill related events.
|
||||
*
|
||||
* <p>Typically used for applications that display their own "auto-complete" views, so they can
|
||||
* enable / disable such views when the auto-fill UI affordance is shown / hidden.
|
||||
*/
|
||||
public abstract static class AutofillCallback {
|
||||
|
||||
/** @hide */
|
||||
@IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface AutofillEventType {}
|
||||
|
||||
/**
|
||||
* The auto-fill input UI affordance associated with the view was shown.
|
||||
*
|
||||
* <p>If the view provides its own auto-complete UI affordance and its currently shown, it
|
||||
* should be hidden upon receiving this event.
|
||||
*/
|
||||
public static final int EVENT_INPUT_SHOWN = 1;
|
||||
|
||||
/**
|
||||
* The auto-fill input UI affordance associated with the view was hidden.
|
||||
*
|
||||
* <p>If the view provides its own auto-complete UI affordance that was hidden upon a
|
||||
* {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
|
||||
*/
|
||||
public static final int EVENT_INPUT_HIDDEN = 2;
|
||||
|
||||
/**
|
||||
* Called after a change in the autofill state associated with a view.
|
||||
*
|
||||
* @param view view associated with the change.
|
||||
*
|
||||
* @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
|
||||
*/
|
||||
public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {}
|
||||
|
||||
/**
|
||||
* Called after a change in the autofill state associated with a virtual view.
|
||||
*
|
||||
* @param view parent view associated with the change.
|
||||
* @param childId id identifying the virtual child inside the parent view.
|
||||
*
|
||||
* @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
|
||||
*/
|
||||
public void onAutofillEventVirtual(@NonNull View view, int childId,
|
||||
@AutofillEventType int event) {}
|
||||
}
|
||||
|
||||
private static final class AutoFillManagerClient extends IAutoFillManagerClient.Stub {
|
||||
private final WeakReference<AutoFillManager> mAutoFillManager;
|
||||
|
||||
@@ -385,5 +507,17 @@ public final class AutoFillManager {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutofillEvent(IBinder windowToken, AutoFillId id, int event) {
|
||||
final AutoFillManager autoFillManager = mAutoFillManager.get();
|
||||
if (autoFillManager != null) {
|
||||
autoFillManager.mContext.getMainThreadHandler().post(() -> {
|
||||
if (autoFillManager.getClient() != null) {
|
||||
autoFillManager.onAutofillEvent(windowToken, id, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,12 @@ import android.view.autofill.IAutoFillManagerClient;
|
||||
interface IAutoFillManager {
|
||||
boolean addClient(in IAutoFillManagerClient client, int userId);
|
||||
oneway void startSession(in IBinder activityToken, IBinder windowToken, in IBinder appCallback,
|
||||
in AutoFillId autoFillId, in Rect bounds, in AutoFillValue value, int userId);
|
||||
in AutoFillId autoFillId, in Rect bounds, in AutoFillValue value, int userId,
|
||||
boolean hasCallback);
|
||||
oneway void updateSession(in IBinder activityToken, in AutoFillId id, in Rect bounds,
|
||||
in AutoFillValue value, int flags, int userId);
|
||||
oneway void finishSession(in IBinder activityToken, int userId);
|
||||
oneway void setAuthenticationResult(in Bundle data,
|
||||
in IBinder activityToken, int userId);
|
||||
oneway void setHasCallback(in IBinder activityToken, int userId, boolean hasIt);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.os.IBinder;
|
||||
import android.view.autofill.AutoFillId;
|
||||
import android.view.autofill.AutoFillValue;
|
||||
|
||||
@@ -43,4 +44,9 @@ oneway interface IAutoFillManagerClient {
|
||||
* Authenticates a fill response or a data set.
|
||||
*/
|
||||
void authenticate(in IntentSender intent, in Intent fillInIntent);
|
||||
|
||||
/**
|
||||
* Notifies the client when the auto-fill UI changed.
|
||||
*/
|
||||
void onAutofillEvent(in IBinder windowToken, in AutoFillId id, int event);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user