diff options
| -rw-r--r-- | api/current.txt | 10 | ||||
| -rw-r--r-- | api/system-current.txt | 10 | ||||
| -rw-r--r-- | api/test-current.txt | 10 | ||||
| -rw-r--r-- | core/java/android/view/autofill/AutoFillManager.java | 142 | ||||
| -rw-r--r-- | core/java/android/view/autofill/IAutoFillManager.aidl | 4 | ||||
| -rw-r--r-- | core/java/android/view/autofill/IAutoFillManagerClient.aidl | 6 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/AutoFillManagerService.java | 13 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java | 56 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java | 16 |
9 files changed, 248 insertions, 19 deletions
diff --git a/api/current.txt b/api/current.txt index 1a5ea853b365..6a62fdea1853 100644 --- a/api/current.txt +++ b/api/current.txt @@ -47452,17 +47452,27 @@ package android.view.autofill { } public final class AutoFillManager { + method public void registerCallback(android.view.autofill.AutoFillManager.AutofillCallback); method public void reset(); method public void startAutoFillRequest(android.view.View); method public void startAutoFillRequestOnVirtualView(android.view.View, int, android.graphics.Rect); method public void stopAutoFillRequest(android.view.View); method public void stopAutoFillRequestOnVirtualView(android.view.View, int); + method public void unregisterCallback(android.view.autofill.AutoFillManager.AutofillCallback); method public void valueChanged(android.view.View); method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue); field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE"; field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT"; } + public static abstract class AutoFillManager.AutofillCallback { + ctor public AutoFillManager.AutofillCallback(); + method public void onAutofillEvent(android.view.View, int); + method public void onAutofillEventVirtual(android.view.View, int, int); + field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2 + field public static final int EVENT_INPUT_SHOWN = 1; // 0x1 + } + public final class AutoFillType implements android.os.Parcelable { method public int describeContents(); method public static android.view.autofill.AutoFillType forDate(); diff --git a/api/system-current.txt b/api/system-current.txt index b5c20e52e913..2c0713dfa017 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -50915,17 +50915,27 @@ package android.view.autofill { } public final class AutoFillManager { + method public void registerCallback(android.view.autofill.AutoFillManager.AutofillCallback); method public void reset(); method public void startAutoFillRequest(android.view.View); method public void startAutoFillRequestOnVirtualView(android.view.View, int, android.graphics.Rect); method public void stopAutoFillRequest(android.view.View); method public void stopAutoFillRequestOnVirtualView(android.view.View, int); + method public void unregisterCallback(android.view.autofill.AutoFillManager.AutofillCallback); method public void valueChanged(android.view.View); method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue); field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE"; field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT"; } + public static abstract class AutoFillManager.AutofillCallback { + ctor public AutoFillManager.AutofillCallback(); + method public void onAutofillEvent(android.view.View, int); + method public void onAutofillEventVirtual(android.view.View, int, int); + field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2 + field public static final int EVENT_INPUT_SHOWN = 1; // 0x1 + } + public final class AutoFillType implements android.os.Parcelable { method public int describeContents(); method public static android.view.autofill.AutoFillType forDate(); diff --git a/api/test-current.txt b/api/test-current.txt index 57cc998ffb5d..fc96532ba383 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -47821,17 +47821,27 @@ package android.view.autofill { } public final class AutoFillManager { + method public void registerCallback(android.view.autofill.AutoFillManager.AutofillCallback); method public void reset(); method public void startAutoFillRequest(android.view.View); method public void startAutoFillRequestOnVirtualView(android.view.View, int, android.graphics.Rect); method public void stopAutoFillRequest(android.view.View); method public void stopAutoFillRequestOnVirtualView(android.view.View, int); + method public void unregisterCallback(android.view.autofill.AutoFillManager.AutofillCallback); method public void valueChanged(android.view.View); method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue); field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE"; field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT"; } + public static abstract class AutoFillManager.AutofillCallback { + ctor public AutoFillManager.AutofillCallback(); + method public void onAutofillEvent(android.view.View, int); + method public void onAutofillEventVirtual(android.view.View, int, int); + field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2 + field public static final int EVENT_INPUT_SHOWN = 1; // 0x1 + } + public final class AutoFillType implements android.os.Parcelable { method public int describeContents(); method public static android.view.autofill.AutoFillType forDate(); diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java index e8325e89f39a..8beaf4e423cb 100644 --- a/core/java/android/view/autofill/AutoFillManager.java +++ b/core/java/android/view/autofill/AutoFillManager.java @@ -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); + } + }); + } + } } } diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index d054e979ddb7..b36c0f15d58a 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -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); } diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index 45f363d32301..9eef7d0a6e69 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -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); } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java index 40491e91ea82..e943c4c388f5 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java @@ -305,14 +305,23 @@ public final class AutoFillManagerService extends SystemService { } @Override + public void setHasCallback(IBinder activityToken, int userId, boolean hasIt) { + synchronized (mLock) { + final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId); + service.setHasCallback(activityToken, hasIt); + } + } + + @Override public void startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback, - AutoFillId autoFillId, Rect bounds, AutoFillValue value, int userId) { + AutoFillId autoFillId, Rect bounds, AutoFillValue value, int userId, + boolean hasCallback) { // TODO(b/33197203): make sure it's called by resumed / focused activity synchronized (mLock) { final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId); service.startSessionLocked(activityToken, windowToken, appCallback, - autoFillId, bounds, value); + autoFillId, bounds, value, hasCallback); } } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java index 5e852f1738f1..c4d06488f7b5 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java @@ -274,15 +274,25 @@ final class AutoFillManagerServiceImpl { } } + void setHasCallback(IBinder activityToken, boolean hasIt) { + if (!hasService()) { + return; + } + final Session session = mSessions.get(activityToken); + if (session != null) { + session.setHasCallback(hasIt); + } + } + void startSessionLocked(IBinder activityToken, IBinder windowToken, IBinder appCallbackToken, - AutoFillId autoFillId, Rect bounds, AutoFillValue value) { + AutoFillId autoFillId, Rect bounds, AutoFillValue value, boolean hasCallback) { if (!hasService()) { return; } final String historyItem = "s=" + mInfo.getServiceInfo().packageName + " u=" + mUserId + " a=" + activityToken - + " i=" + autoFillId + " b=" + bounds; + + " i=" + autoFillId + " b=" + bounds + " hc=" + hasCallback; mRequestsHistory.log(historyItem); // TODO(b/33197203): Handle partitioning @@ -293,7 +303,7 @@ final class AutoFillManagerServiceImpl { } final Session newSession = createSessionByTokenLocked(activityToken, - windowToken, appCallbackToken); + windowToken, appCallbackToken, hasCallback); newSession.updateLocked(autoFillId, bounds, value, FLAG_START_SESSION); } @@ -312,9 +322,9 @@ final class AutoFillManagerServiceImpl { } private Session createSessionByTokenLocked(IBinder activityToken, IBinder windowToken, - IBinder appCallbackToken) { + IBinder appCallbackToken, boolean hasCallback) { final Session newSession = new Session(mContext, activityToken, - windowToken, appCallbackToken); + windowToken, appCallbackToken, hasCallback); mSessions.put(activityToken, newSession); /* @@ -599,12 +609,18 @@ final class AutoFillManagerServiceImpl { @GuardedBy("mLock") private AssistStructure mStructure; + /** + * Whether the client has an {@link android.view.autofill.AutoFillManager.AutofillCallback}. + */ + private boolean mHasCallback; + private Session(Context context, IBinder activityToken, IBinder windowToken, - IBinder client) { + IBinder client, boolean hasCallback) { mRemoteFillService = new RemoteFillService(context, mInfo.getServiceInfo().getComponentName(), mUserId, this); mActivityToken = activityToken; mWindowToken = windowToken; + mHasCallback = hasCallback; mClient = IAutoFillManagerClient.Stub.asInterface(client); try { @@ -712,6 +728,14 @@ final class AutoFillManagerServiceImpl { .sendToTarget(); } + // AutoFillUiCallback + @Override + public void onEvent(AutoFillId id, int event) { + mHandlerCaller.getHandler().post(() -> { + notifyChangeToClient(id, event); + }); + } + public void setAuthenticationResultLocked(Bundle data) { if (mCurrentResponse == null || data == null) { removeSelf(); @@ -731,6 +755,10 @@ final class AutoFillManagerServiceImpl { } } + public void setHasCallback(boolean hasIt) { + mHasCallback = hasIt; + } + /** * Show the save UI, when session can be saved. */ @@ -814,6 +842,7 @@ final class AutoFillManagerServiceImpl { node.updateAutoFillValue(value); } + // Sanitize structure before it's sent to service. mStructure.sanitizeForParceling(false); if (VERBOSE) { @@ -871,7 +900,7 @@ final class AutoFillManagerServiceImpl { if ((flags & FLAG_FOCUS_GAINED) != 0) { // Remove the UI if the ViewState has changed. if (mCurrentViewState != viewState) { - mUi.hideFillUi(); + mUi.hideFillUi(mCurrentViewState != null ? mCurrentViewState.mId : null); mCurrentViewState = viewState; } @@ -888,7 +917,7 @@ final class AutoFillManagerServiceImpl { if ((flags & FLAG_FOCUS_LOST) != 0) { if (mCurrentViewState == viewState) { - mUi.hideFillUi(); + mUi.hideFillUi(viewState.mId); mCurrentViewState = null; } return; @@ -912,6 +941,15 @@ final class AutoFillManagerServiceImpl { getUiForShowing().showFillUi(filledId, response, bounds, filterText); } + private void notifyChangeToClient(AutoFillId id, int event) { + if (!mHasCallback) return; + try { + mClient.onAutofillEvent(mWindowToken, id, event); + } catch (RemoteException e) { + Slog.e(TAG, "Error notifying client on change: id=" + id + ", event=" + event, e); + } + } + private void processResponseLocked(FillResponse response) { if (DEBUG) { Slog.d(TAG, "processResponseLocked(auth=" + response.getAuthentication() @@ -993,7 +1031,7 @@ final class AutoFillManagerServiceImpl { pw.println("null"); } } - + pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback); mRemoteFillService.dump(prefix, pw); } diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index f00d6c58d206..c86a8828d0b1 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -15,6 +15,9 @@ */ package com.android.server.autofill.ui; +import static android.view.autofill.AutoFillManager.AutofillCallback.EVENT_INPUT_HIDDEN; +import static android.view.autofill.AutoFillManager.AutofillCallback.EVENT_INPUT_SHOWN; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -26,8 +29,8 @@ import android.service.autofill.Dataset; import android.service.autofill.FillResponse; import android.service.autofill.SaveInfo; import android.text.TextUtils; -import android.util.Slog; import android.text.format.DateUtils; +import android.util.Slog; import android.view.autofill.AutoFillId; import android.widget.Toast; @@ -62,6 +65,7 @@ public final class AutoFillUI { void authenticate(@NonNull IntentSender intent); void fill(@NonNull Dataset dataset); void save(); + void onEvent(AutoFillId id, int event); } public AutoFillUI(@NonNull Context context) { @@ -97,8 +101,13 @@ public final class AutoFillUI { /** * Hides the fill UI. */ - public void hideFillUi() { - mHandler.post(this::hideFillUiUiThread); + public void hideFillUi(AutoFillId id) { + mHandler.post(() -> { + hideFillUiUiThread(); + if (mCallback != null) { + mCallback.onEvent(id, EVENT_INPUT_HIDDEN); + } + }); } /** @@ -175,6 +184,7 @@ public final class AutoFillUI { // TODO(b/33197203): add MetricsLogger call } }); + mCallback.onEvent(focusedId, EVENT_INPUT_SHOWN); }); } |