Loading Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -326,6 +326,7 @@ LOCAL_SRC_FILES += \ core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \ core/java/android/view/autofill/IAutoFillManager.aidl \ core/java/android/view/autofill/IAutoFillManagerClient.aidl \ core/java/android/view/autofill/IAutofillWindowPresenter.aidl \ core/java/android/view/IApplicationToken.aidl \ core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \ core/java/android/view/IDockedStackListener.aidl \ Loading core/java/android/app/Activity.java +44 −56 Original line number Diff line number Diff line Loading @@ -17,9 +17,12 @@ package android.app; import android.metrics.LogMaker; import android.graphics.Rect; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillPopupWindow; import android.view.autofill.AutofillValue; import android.view.autofill.IAutofillWindowPresenter; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ToolbarActionBar; Loading Loading @@ -768,7 +771,6 @@ public class Activity extends ContextThemeWrapper /*package*/ Configuration mCurrentConfig; private SearchManager mSearchManager; private MenuInflater mMenuInflater; private final MetricsLogger mMetricsLogger = new MetricsLogger(); static final class NonConfigurationInstances { Object activity; Loading Loading @@ -850,6 +852,8 @@ public class Activity extends ContextThemeWrapper private boolean mAutoFillResetNeeded; private AutofillPopupWindow mAutofillPopupWindow; private static native String getDlWarning(); /** Return the intent that started this activity. */ Loading Loading @@ -7190,72 +7194,56 @@ public class Activity extends ContextThemeWrapper /** @hide */ @Override public void autofill(List<AutofillId> ids, List<AutofillValue> values) { final View root = getWindow().getDecorView(); final int itemCount = ids.size(); int numApplied = 0; ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null; for (int i = 0; i < itemCount; i++) { final AutofillId id = ids.get(i); final AutofillValue value = values.get(i); final int viewId = id.getViewId(); final View view = root.findViewByAccessibilityIdTraversal(viewId); if (view == null) { Log.w(TAG, "autofill(): no View with id " + viewId); continue; } if (id.isVirtual()) { final int parentId = id.getViewId(); if (virtualValues == null) { // Most likely there will be just one view with virtual children. virtualValues = new ArrayMap<>(1); } SparseArray<AutofillValue> valuesByParent = virtualValues.get(view); if (valuesByParent == null) { // We don't know the size yet, but usually it will be just a few fields... valuesByParent = new SparseArray<>(5); virtualValues.put(view, valuesByParent); } valuesByParent.put(id.getVirtualChildId(), value); } else { if (view.autofill(value)) { numApplied++; } } } if (virtualValues != null) { for (int i = 0; i < virtualValues.size(); i++) { final View parent = virtualValues.keyAt(i); final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i); if (parent.autofill(childrenValues)) { numApplied += childrenValues.size(); } final public void autofillCallbackAuthenticate(IntentSender intent, Intent fillInIntent) { try { startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX, 0, fillInIntent, 0, 0, null); } catch (IntentSender.SendIntentException e) { Log.e(TAG, "authenticate() failed for intent:" + intent, e); } } final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED); log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount); log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied); mMetricsLogger.write(log); /** @hide */ @Override final public void autofillCallbackResetableStateAvailable() { mAutoFillResetNeeded = true; } /** @hide */ @Override public void authenticate(IntentSender intent, Intent fillInIntent) { try { startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX, 0, fillInIntent, 0, 0, null); } catch (IntentSender.SendIntentException e) { Log.e(TAG, "authenticate() failed for intent:" + intent, e); final public boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height, @Nullable Rect anchorBounds, IAutofillWindowPresenter presenter) { final Rect actualAnchorBounds = new Rect(); anchor.getBoundsOnScreen(actualAnchorBounds); final int offsetX = (anchorBounds != null) ? anchorBounds.left - actualAnchorBounds.left : 0; int offsetY = (anchorBounds != null) ? anchorBounds.top - actualAnchorBounds.top : 0; final boolean wasShowing; if (mAutofillPopupWindow == null) { wasShowing = false; mAutofillPopupWindow = new AutofillPopupWindow(presenter); } else { wasShowing = mAutofillPopupWindow.isShowing(); } mAutofillPopupWindow.update(anchor, offsetX, offsetY, width, height, anchorBounds, actualAnchorBounds); return !wasShowing && mAutofillPopupWindow.isShowing(); } /** @hide */ @Override public void resetableStateAvailable() { mAutoFillResetNeeded = true; final public boolean autofillCallbackRequestHideFillUi() { if (mAutofillPopupWindow == null) { return false; } mAutofillPopupWindow.dismiss(); mAutofillPopupWindow = null; return true; } /** Loading core/java/android/view/autofill/AutofillManager.java +161 −37 Original line number Diff line number Diff line Loading @@ -26,14 +26,20 @@ import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.graphics.Rect; import android.metrics.LogMaker; import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; import android.view.View; import android.view.WindowManagerGlobal; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; Loading Loading @@ -83,7 +89,7 @@ public final class AutofillManager { /** @hide */ public static final int FLAG_VIEW_EXITED = 0x20000000; /** @hide */ public static final int FLAG_VALUE_CHANGED = 0x10000000; @NonNull private final Rect mTempRect = new Rect(); private final MetricsLogger mMetricsLogger = new MetricsLogger(); private final IAutoFillManager mService; private IAutoFillManagerClient mServiceClient; Loading @@ -97,26 +103,38 @@ public final class AutofillManager { /** @hide */ public interface AutofillClient { /** * Asks the client to perform an autofill. * * @param ids The values to autofill * @param values The values to autofill */ void autofill(List<AutofillId> ids, List<AutofillValue> values); /** * Asks the client to start an authentication flow. * * @param intent The authentication intent. * @param fillInIntent The authentication fill-in intent. */ void authenticate(IntentSender intent, Intent fillInIntent); void autofillCallbackAuthenticate(IntentSender intent, Intent fillInIntent); /** * Tells the client this manager has state to be reset. */ void resetableStateAvailable(); void autofillCallbackResetableStateAvailable(); /** * Request showing the autofill UI. * * @param anchor The real view the UI needs to anchor to. * @param width The width of the fill UI content. * @param height The height of the fill UI content. * @param virtualBounds The bounds of the virtual decendant of the anchor. * @param presenter The presenter that controls the fill UI window. * @return Whether the UI was shown. */ boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height, @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter); /** * Request hiding the autofill UI. * * @return Whether the UI was hidden. */ boolean autofillCallbackRequestHideFillUi(); } /** Loading Loading @@ -156,12 +174,10 @@ public final class AutofillManager { return; } final Rect bounds = mTempRect; view.getBoundsOnScreen(bounds); final AutofillId id = getAutofillId(view); final AutofillValue value = view.getAutofillValue(); startSession(id, view.getWindowToken(), bounds, value, FLAG_MANUAL_REQUEST); startSession(id, view.getWindowToken(), null, value, FLAG_MANUAL_REQUEST); } /** Loading Loading @@ -202,17 +218,15 @@ public final class AutofillManager { return; } final Rect bounds = mTempRect; view.getBoundsOnScreen(bounds); final AutofillId id = getAutofillId(view); final AutofillValue value = view.getAutofillValue(); if (!mHasSession) { // Starts new session. startSession(id, view.getWindowToken(), bounds, value, 0); startSession(id, view.getWindowToken(), null, value, 0); } else { // Update focus on existing session. updateSession(id, bounds, value, FLAG_VIEW_ENTERED); updateSession(id, null, value, FLAG_VIEW_ENTERED); } } Loading Loading @@ -389,7 +403,7 @@ public final class AutofillManager { mCallback != null, flags, mContext.getOpPackageName()); AutofillClient client = getClient(); if (client != null) { client.resetableStateAvailable(); client.autofillCallbackResetableStateAvailable(); } mHasSession = true; } catch (RemoteException e) { Loading Loading @@ -490,28 +504,114 @@ public final class AutofillManager { } } 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); private void requestShowFillUi(IBinder windowToken, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter) { final View anchor = findAchorView(windowToken, id); if (anchor == null) { return; } if (getClient().autofillCallbackRequestShowFillUi(anchor, width, height, anchorBounds, presenter) && mCallback != null) { if (id.isVirtual()) { mCallback.onAutofillEvent(anchor, id.getVirtualChildId(), AutofillCallback.EVENT_INPUT_SHOWN); } else { mCallback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN); } } } private void handleAutofill(IBinder windowToken, List<AutofillId> ids, List<AutofillValue> values) { 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()); final int itemCount = ids.size(); int numApplied = 0; ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null; for (int i = 0; i < itemCount; i++) { final AutofillId id = ids.get(i); final AutofillValue value = values.get(i); final int viewId = id.getViewId(); final View view = root.findViewByAccessibilityIdTraversal(viewId); if (view == null) { Log.w(TAG, "onAutofillEvent() for " + id + ": view gone"); return; Log.w(TAG, "autofill(): no View with id " + viewId); continue; } if (id.isVirtual()) { if (virtualValues == null) { // Most likely there will be just one view with virtual children. virtualValues = new ArrayMap<>(1); } SparseArray<AutofillValue> valuesByParent = virtualValues.get(view); if (valuesByParent == null) { // We don't know the size yet, but usually it will be just a few fields... valuesByParent = new SparseArray<>(5); virtualValues.put(view, valuesByParent); } valuesByParent.put(id.getVirtualChildId(), value); } else { if (view.autofill(value)) { numApplied++; } } } if (virtualValues != null) { for (int i = 0; i < virtualValues.size(); i++) { final View parent = virtualValues.keyAt(i); final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i); if (parent.autofill(childrenValues)) { numApplied += childrenValues.size(); } } } final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED); log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount); log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied); mMetricsLogger.write(log); } private void requestHideFillUi(IBinder windowToken, AutofillId id) { if (getClient().autofillCallbackRequestHideFillUi() && mCallback != null) { final View anchor = findAchorView(windowToken, id); if (id.isVirtual()) { mCallback.onAutofillEvent(anchor, id.getVirtualChildId(), AutofillCallback.EVENT_INPUT_HIDDEN); } else { mCallback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN); } } } private void notifyNoFillUi(IBinder windowToken, AutofillId id) { if (mCallback != null) { final View anchor = findAchorView(windowToken, id); if (id.isVirtual()) { mCallback.onAutofillEvent(view, id.getVirtualChildId(), event); mCallback.onAutofillEvent(anchor, id.getVirtualChildId(), AutofillCallback.EVENT_INPUT_UNAVAILABLE); } else { mCallback.onAutofillEvent(view, event); mCallback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE); } } } private View findAchorView(IBinder windowToken, AutofillId id) { final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken); if (root == null) { Log.w(TAG, "no window with token " + windowToken); return null; } final View view = root.findViewByAccessibilityIdTraversal(id.getViewId()); if (view == null) { Log.w(TAG, "no view with id " + id); return null; } return view; } /** Loading Loading @@ -590,38 +690,62 @@ public final class AutofillManager { } @Override public void autofill(List<AutofillId> ids, List<AutofillValue> values) { public void autofill(IBinder windowToken, List<AutofillId> ids, List<AutofillValue> values) { // TODO(b/33197203): must keep the dataset so subsequent calls pass the same // dataset.extras to service final AutofillManager afm = mAfm.get(); if (afm != null) { afm.mContext.getMainThreadHandler().post(() -> afm.handleAutofill(windowToken, ids, values)); } } @Override public void authenticate(IntentSender intent, Intent fillInIntent) { final AutofillManager afm = mAfm.get(); if (afm != null) { afm.mContext.getMainThreadHandler().post(() -> { if (afm.getClient() != null) { afm.getClient().autofill(ids, values); afm.getClient().autofillCallbackAuthenticate(intent, fillInIntent); } }); } } @Override public void authenticate(IntentSender intent, Intent fillInIntent) { public void requestShowFillUi(IBinder windowToken, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter) { final AutofillManager afm = mAfm.get(); if (afm != null) { afm.mContext.getMainThreadHandler().post(() -> { if (afm.getClient() != null) { afm.requestShowFillUi(windowToken, id, width, height, anchorBounds, presenter); } }); } } @Override public void requestHideFillUi(IBinder windowToken, AutofillId id) { final AutofillManager afm = mAfm.get(); if (afm != null) { afm.mContext.getMainThreadHandler().post(() -> { if (afm.getClient() != null) { afm.getClient().authenticate(intent, fillInIntent); afm.requestHideFillUi(windowToken, id); } }); } } @Override public void onAutofillEvent(IBinder windowToken, AutofillId id, int event) { public void notifyNoFillUi(IBinder windowToken, AutofillId id) { final AutofillManager afm = mAfm.get(); if (afm != null) { afm.mContext.getMainThreadHandler().post(() -> { if (afm.getClient() != null) { afm.onAutofillEvent(windowToken, id, event); afm.notifyNoFillUi(windowToken, id); } }); } Loading core/java/android/view/autofill/AutofillPopupWindow.java 0 → 100644 +260 −0 File added.Preview size limit exceeded, changes collapsed. Show changes core/java/android/view/autofill/IAutoFillManagerClient.aidl +16 −3 Original line number Diff line number Diff line Loading @@ -20,9 +20,11 @@ import java.util.List; import android.content.Intent; import android.content.IntentSender; import android.graphics.Rect; import android.os.IBinder; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import android.view.autofill.IAutofillWindowPresenter; /** * Object running in the application process and responsible for autofilling it. Loading @@ -38,7 +40,7 @@ oneway interface IAutoFillManagerClient { /** * Autofills the activity with the contents of a dataset. */ void autofill(in List<AutofillId> ids, in List<AutofillValue> values); void autofill(in IBinder windowToken, in List<AutofillId> ids, in List<AutofillValue> values); /** * Authenticates a fill response or a data set. Loading @@ -46,7 +48,18 @@ oneway interface IAutoFillManagerClient { void authenticate(in IntentSender intent, in Intent fillInIntent); /** * Notifies the client when the auto-fill UI changed. * Requests showing the fill UI. */ void onAutofillEvent(in IBinder windowToken, in AutofillId id, int event); void requestShowFillUi(in IBinder windowToken, in AutofillId id, int width, int height, in Rect anchorBounds, in IAutofillWindowPresenter presenter); /** * Requests hiding the fill UI. */ void requestHideFillUi(in IBinder windowToken, in AutofillId id); /** * Nitifies no fill UI will be shown. */ void notifyNoFillUi(in IBinder windowToken, in AutofillId id); } Loading
Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -326,6 +326,7 @@ LOCAL_SRC_FILES += \ core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \ core/java/android/view/autofill/IAutoFillManager.aidl \ core/java/android/view/autofill/IAutoFillManagerClient.aidl \ core/java/android/view/autofill/IAutofillWindowPresenter.aidl \ core/java/android/view/IApplicationToken.aidl \ core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \ core/java/android/view/IDockedStackListener.aidl \ Loading
core/java/android/app/Activity.java +44 −56 Original line number Diff line number Diff line Loading @@ -17,9 +17,12 @@ package android.app; import android.metrics.LogMaker; import android.graphics.Rect; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillPopupWindow; import android.view.autofill.AutofillValue; import android.view.autofill.IAutofillWindowPresenter; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ToolbarActionBar; Loading Loading @@ -768,7 +771,6 @@ public class Activity extends ContextThemeWrapper /*package*/ Configuration mCurrentConfig; private SearchManager mSearchManager; private MenuInflater mMenuInflater; private final MetricsLogger mMetricsLogger = new MetricsLogger(); static final class NonConfigurationInstances { Object activity; Loading Loading @@ -850,6 +852,8 @@ public class Activity extends ContextThemeWrapper private boolean mAutoFillResetNeeded; private AutofillPopupWindow mAutofillPopupWindow; private static native String getDlWarning(); /** Return the intent that started this activity. */ Loading Loading @@ -7190,72 +7194,56 @@ public class Activity extends ContextThemeWrapper /** @hide */ @Override public void autofill(List<AutofillId> ids, List<AutofillValue> values) { final View root = getWindow().getDecorView(); final int itemCount = ids.size(); int numApplied = 0; ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null; for (int i = 0; i < itemCount; i++) { final AutofillId id = ids.get(i); final AutofillValue value = values.get(i); final int viewId = id.getViewId(); final View view = root.findViewByAccessibilityIdTraversal(viewId); if (view == null) { Log.w(TAG, "autofill(): no View with id " + viewId); continue; } if (id.isVirtual()) { final int parentId = id.getViewId(); if (virtualValues == null) { // Most likely there will be just one view with virtual children. virtualValues = new ArrayMap<>(1); } SparseArray<AutofillValue> valuesByParent = virtualValues.get(view); if (valuesByParent == null) { // We don't know the size yet, but usually it will be just a few fields... valuesByParent = new SparseArray<>(5); virtualValues.put(view, valuesByParent); } valuesByParent.put(id.getVirtualChildId(), value); } else { if (view.autofill(value)) { numApplied++; } } } if (virtualValues != null) { for (int i = 0; i < virtualValues.size(); i++) { final View parent = virtualValues.keyAt(i); final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i); if (parent.autofill(childrenValues)) { numApplied += childrenValues.size(); } final public void autofillCallbackAuthenticate(IntentSender intent, Intent fillInIntent) { try { startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX, 0, fillInIntent, 0, 0, null); } catch (IntentSender.SendIntentException e) { Log.e(TAG, "authenticate() failed for intent:" + intent, e); } } final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED); log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount); log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied); mMetricsLogger.write(log); /** @hide */ @Override final public void autofillCallbackResetableStateAvailable() { mAutoFillResetNeeded = true; } /** @hide */ @Override public void authenticate(IntentSender intent, Intent fillInIntent) { try { startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX, 0, fillInIntent, 0, 0, null); } catch (IntentSender.SendIntentException e) { Log.e(TAG, "authenticate() failed for intent:" + intent, e); final public boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height, @Nullable Rect anchorBounds, IAutofillWindowPresenter presenter) { final Rect actualAnchorBounds = new Rect(); anchor.getBoundsOnScreen(actualAnchorBounds); final int offsetX = (anchorBounds != null) ? anchorBounds.left - actualAnchorBounds.left : 0; int offsetY = (anchorBounds != null) ? anchorBounds.top - actualAnchorBounds.top : 0; final boolean wasShowing; if (mAutofillPopupWindow == null) { wasShowing = false; mAutofillPopupWindow = new AutofillPopupWindow(presenter); } else { wasShowing = mAutofillPopupWindow.isShowing(); } mAutofillPopupWindow.update(anchor, offsetX, offsetY, width, height, anchorBounds, actualAnchorBounds); return !wasShowing && mAutofillPopupWindow.isShowing(); } /** @hide */ @Override public void resetableStateAvailable() { mAutoFillResetNeeded = true; final public boolean autofillCallbackRequestHideFillUi() { if (mAutofillPopupWindow == null) { return false; } mAutofillPopupWindow.dismiss(); mAutofillPopupWindow = null; return true; } /** Loading
core/java/android/view/autofill/AutofillManager.java +161 −37 Original line number Diff line number Diff line Loading @@ -26,14 +26,20 @@ import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.graphics.Rect; import android.metrics.LogMaker; import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; import android.view.View; import android.view.WindowManagerGlobal; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; Loading Loading @@ -83,7 +89,7 @@ public final class AutofillManager { /** @hide */ public static final int FLAG_VIEW_EXITED = 0x20000000; /** @hide */ public static final int FLAG_VALUE_CHANGED = 0x10000000; @NonNull private final Rect mTempRect = new Rect(); private final MetricsLogger mMetricsLogger = new MetricsLogger(); private final IAutoFillManager mService; private IAutoFillManagerClient mServiceClient; Loading @@ -97,26 +103,38 @@ public final class AutofillManager { /** @hide */ public interface AutofillClient { /** * Asks the client to perform an autofill. * * @param ids The values to autofill * @param values The values to autofill */ void autofill(List<AutofillId> ids, List<AutofillValue> values); /** * Asks the client to start an authentication flow. * * @param intent The authentication intent. * @param fillInIntent The authentication fill-in intent. */ void authenticate(IntentSender intent, Intent fillInIntent); void autofillCallbackAuthenticate(IntentSender intent, Intent fillInIntent); /** * Tells the client this manager has state to be reset. */ void resetableStateAvailable(); void autofillCallbackResetableStateAvailable(); /** * Request showing the autofill UI. * * @param anchor The real view the UI needs to anchor to. * @param width The width of the fill UI content. * @param height The height of the fill UI content. * @param virtualBounds The bounds of the virtual decendant of the anchor. * @param presenter The presenter that controls the fill UI window. * @return Whether the UI was shown. */ boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height, @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter); /** * Request hiding the autofill UI. * * @return Whether the UI was hidden. */ boolean autofillCallbackRequestHideFillUi(); } /** Loading Loading @@ -156,12 +174,10 @@ public final class AutofillManager { return; } final Rect bounds = mTempRect; view.getBoundsOnScreen(bounds); final AutofillId id = getAutofillId(view); final AutofillValue value = view.getAutofillValue(); startSession(id, view.getWindowToken(), bounds, value, FLAG_MANUAL_REQUEST); startSession(id, view.getWindowToken(), null, value, FLAG_MANUAL_REQUEST); } /** Loading Loading @@ -202,17 +218,15 @@ public final class AutofillManager { return; } final Rect bounds = mTempRect; view.getBoundsOnScreen(bounds); final AutofillId id = getAutofillId(view); final AutofillValue value = view.getAutofillValue(); if (!mHasSession) { // Starts new session. startSession(id, view.getWindowToken(), bounds, value, 0); startSession(id, view.getWindowToken(), null, value, 0); } else { // Update focus on existing session. updateSession(id, bounds, value, FLAG_VIEW_ENTERED); updateSession(id, null, value, FLAG_VIEW_ENTERED); } } Loading Loading @@ -389,7 +403,7 @@ public final class AutofillManager { mCallback != null, flags, mContext.getOpPackageName()); AutofillClient client = getClient(); if (client != null) { client.resetableStateAvailable(); client.autofillCallbackResetableStateAvailable(); } mHasSession = true; } catch (RemoteException e) { Loading Loading @@ -490,28 +504,114 @@ public final class AutofillManager { } } 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); private void requestShowFillUi(IBinder windowToken, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter) { final View anchor = findAchorView(windowToken, id); if (anchor == null) { return; } if (getClient().autofillCallbackRequestShowFillUi(anchor, width, height, anchorBounds, presenter) && mCallback != null) { if (id.isVirtual()) { mCallback.onAutofillEvent(anchor, id.getVirtualChildId(), AutofillCallback.EVENT_INPUT_SHOWN); } else { mCallback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN); } } } private void handleAutofill(IBinder windowToken, List<AutofillId> ids, List<AutofillValue> values) { 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()); final int itemCount = ids.size(); int numApplied = 0; ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null; for (int i = 0; i < itemCount; i++) { final AutofillId id = ids.get(i); final AutofillValue value = values.get(i); final int viewId = id.getViewId(); final View view = root.findViewByAccessibilityIdTraversal(viewId); if (view == null) { Log.w(TAG, "onAutofillEvent() for " + id + ": view gone"); return; Log.w(TAG, "autofill(): no View with id " + viewId); continue; } if (id.isVirtual()) { if (virtualValues == null) { // Most likely there will be just one view with virtual children. virtualValues = new ArrayMap<>(1); } SparseArray<AutofillValue> valuesByParent = virtualValues.get(view); if (valuesByParent == null) { // We don't know the size yet, but usually it will be just a few fields... valuesByParent = new SparseArray<>(5); virtualValues.put(view, valuesByParent); } valuesByParent.put(id.getVirtualChildId(), value); } else { if (view.autofill(value)) { numApplied++; } } } if (virtualValues != null) { for (int i = 0; i < virtualValues.size(); i++) { final View parent = virtualValues.keyAt(i); final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i); if (parent.autofill(childrenValues)) { numApplied += childrenValues.size(); } } } final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED); log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount); log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied); mMetricsLogger.write(log); } private void requestHideFillUi(IBinder windowToken, AutofillId id) { if (getClient().autofillCallbackRequestHideFillUi() && mCallback != null) { final View anchor = findAchorView(windowToken, id); if (id.isVirtual()) { mCallback.onAutofillEvent(anchor, id.getVirtualChildId(), AutofillCallback.EVENT_INPUT_HIDDEN); } else { mCallback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN); } } } private void notifyNoFillUi(IBinder windowToken, AutofillId id) { if (mCallback != null) { final View anchor = findAchorView(windowToken, id); if (id.isVirtual()) { mCallback.onAutofillEvent(view, id.getVirtualChildId(), event); mCallback.onAutofillEvent(anchor, id.getVirtualChildId(), AutofillCallback.EVENT_INPUT_UNAVAILABLE); } else { mCallback.onAutofillEvent(view, event); mCallback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE); } } } private View findAchorView(IBinder windowToken, AutofillId id) { final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken); if (root == null) { Log.w(TAG, "no window with token " + windowToken); return null; } final View view = root.findViewByAccessibilityIdTraversal(id.getViewId()); if (view == null) { Log.w(TAG, "no view with id " + id); return null; } return view; } /** Loading Loading @@ -590,38 +690,62 @@ public final class AutofillManager { } @Override public void autofill(List<AutofillId> ids, List<AutofillValue> values) { public void autofill(IBinder windowToken, List<AutofillId> ids, List<AutofillValue> values) { // TODO(b/33197203): must keep the dataset so subsequent calls pass the same // dataset.extras to service final AutofillManager afm = mAfm.get(); if (afm != null) { afm.mContext.getMainThreadHandler().post(() -> afm.handleAutofill(windowToken, ids, values)); } } @Override public void authenticate(IntentSender intent, Intent fillInIntent) { final AutofillManager afm = mAfm.get(); if (afm != null) { afm.mContext.getMainThreadHandler().post(() -> { if (afm.getClient() != null) { afm.getClient().autofill(ids, values); afm.getClient().autofillCallbackAuthenticate(intent, fillInIntent); } }); } } @Override public void authenticate(IntentSender intent, Intent fillInIntent) { public void requestShowFillUi(IBinder windowToken, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter) { final AutofillManager afm = mAfm.get(); if (afm != null) { afm.mContext.getMainThreadHandler().post(() -> { if (afm.getClient() != null) { afm.requestShowFillUi(windowToken, id, width, height, anchorBounds, presenter); } }); } } @Override public void requestHideFillUi(IBinder windowToken, AutofillId id) { final AutofillManager afm = mAfm.get(); if (afm != null) { afm.mContext.getMainThreadHandler().post(() -> { if (afm.getClient() != null) { afm.getClient().authenticate(intent, fillInIntent); afm.requestHideFillUi(windowToken, id); } }); } } @Override public void onAutofillEvent(IBinder windowToken, AutofillId id, int event) { public void notifyNoFillUi(IBinder windowToken, AutofillId id) { final AutofillManager afm = mAfm.get(); if (afm != null) { afm.mContext.getMainThreadHandler().post(() -> { if (afm.getClient() != null) { afm.onAutofillEvent(windowToken, id, event); afm.notifyNoFillUi(windowToken, id); } }); } Loading
core/java/android/view/autofill/AutofillPopupWindow.java 0 → 100644 +260 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
core/java/android/view/autofill/IAutoFillManagerClient.aidl +16 −3 Original line number Diff line number Diff line Loading @@ -20,9 +20,11 @@ import java.util.List; import android.content.Intent; import android.content.IntentSender; import android.graphics.Rect; import android.os.IBinder; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; import android.view.autofill.IAutofillWindowPresenter; /** * Object running in the application process and responsible for autofilling it. Loading @@ -38,7 +40,7 @@ oneway interface IAutoFillManagerClient { /** * Autofills the activity with the contents of a dataset. */ void autofill(in List<AutofillId> ids, in List<AutofillValue> values); void autofill(in IBinder windowToken, in List<AutofillId> ids, in List<AutofillValue> values); /** * Authenticates a fill response or a data set. Loading @@ -46,7 +48,18 @@ oneway interface IAutoFillManagerClient { void authenticate(in IntentSender intent, in Intent fillInIntent); /** * Notifies the client when the auto-fill UI changed. * Requests showing the fill UI. */ void onAutofillEvent(in IBinder windowToken, in AutofillId id, int event); void requestShowFillUi(in IBinder windowToken, in AutofillId id, int width, int height, in Rect anchorBounds, in IAutofillWindowPresenter presenter); /** * Requests hiding the fill UI. */ void requestHideFillUi(in IBinder windowToken, in AutofillId id); /** * Nitifies no fill UI will be shown. */ void notifyNoFillUi(in IBinder windowToken, in AutofillId id); }