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:
Felipe Leme
2017-03-03 11:19:51 -08:00
parent eb7ca5ca3a
commit e6010f2fb4
9 changed files with 248 additions and 19 deletions

View File

@@ -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);
}
});
}
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}