diff options
| -rw-r--r-- | api/current.txt | 2 | ||||
| -rw-r--r-- | core/java/android/app/Activity.java | 11 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 62 | ||||
| -rw-r--r-- | core/java/android/view/autofill/AutofillManager.java | 48 |
4 files changed, 113 insertions, 10 deletions
diff --git a/api/current.txt b/api/current.txt index 18a900c5bec8..57271d6946a4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -47617,6 +47617,7 @@ package android.view { method public void setAlpha(float); method public void setAnimation(android.view.animation.Animation); method public void setAutofillHints(java.lang.String...); + method public void setAutofillId(android.view.autofill.AutofillId); method public void setBackground(android.graphics.drawable.Drawable); method public void setBackgroundColor(int); method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable); @@ -49780,6 +49781,7 @@ package android.view.autofill { method public android.content.ComponentName getAutofillServiceComponentName(); method public java.util.List<java.lang.String> getAvailableFieldClassificationAlgorithms(); method public java.lang.String getDefaultFieldClassificationAlgorithm(); + method public android.view.autofill.AutofillId getNextAutofillId(); method public android.service.autofill.UserData getUserData(); method public java.lang.String getUserDataId(); method public boolean hasEnabledAutofillServices(); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index a45c50060e17..3ebe89f3a487 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1400,6 +1400,7 @@ public class Activity extends ContextThemeWrapper * * {@hide} */ + @Override public int getNextAutofillId() { if (mLastAutofillId == Integer.MAX_VALUE - 1) { mLastAutofillId = View.LAST_APP_AUTOFILL_ID; @@ -1411,6 +1412,14 @@ public class Activity extends ContextThemeWrapper } /** + * @hide + */ + @Override + public AutofillId autofillClientGetNextAutofillId() { + return new AutofillId(getNextAutofillId()); + } + + /** * Check whether this activity is running as part of a voice interaction with the user. * If true, it should perform its interaction with the user through the * {@link VoiceInteractor} returned by {@link #getVoiceInteractor}. @@ -7752,7 +7761,7 @@ public class Activity extends ContextThemeWrapper /** @hide */ @Override - public final boolean autofillIsCompatibilityModeEnabled() { + public final boolean autofillClientIsCompatibilityModeEnabled() { return isAutofillCompatibilityEnabled(); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 3c7624225eb9..f61b6528bd0e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8144,7 +8144,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Gets the unique identifier of this view in the screen, for autofill purposes. + * Gets the unique, logical identifier of this view in the activity, for autofill purposes. + * + * <p>The autofill id is created on demand, unless it is explicitly set by + * {@link #setAutofillId(AutofillId)}. + * + * <p>See {@link #setAutofillId(AutofillId)} for more info. * * @return The View's autofill id. */ @@ -8158,6 +8163,61 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Sets the unique, logical identifier of this view in the activity, for autofill purposes. + * + * <p>The autofill id is created on demand, and this method should only be called when a view is + * reused after {@link #dispatchProvideAutofillStructure(ViewStructure, int)} is called, as + * that method creates a snapshot of the view that is passed along to the autofill service. + * + * <p>This method is typically used when view subtrees are recycled to represent different + * content* —in this case, the autofill id can be saved before the view content is swapped + * out, and restored later when it's swapped back in. For example: + * + * <pre> + * EditText reusableView = ...; + * ViewGroup parentView = ...; + * AutofillManager afm = ...; + * + * // Swap out the view and change its contents + * AutofillId oldId = reusableView.getAutofillId(); + * CharSequence oldText = reusableView.getText(); + * parentView.removeView(reusableView); + * AutofillId newId = afm.getNextAutofillId(); + * reusableView.setText("New I am"); + * reusableView.setAutofillId(newId); + * parentView.addView(reusableView); + * + * // Later, swap the old content back in + * parentView.removeView(reusableView); + * reusableView.setAutofillId(oldId); + * reusableView.setText(oldText); + * parentView.addView(reusableView); + * </pre> + * + * @param id an autofill ID that is unique in the {@link android.app.Activity} hosting the view, + * or {@code null} to reset it. Usually it's an id previously allocated to another view (and + * obtained through {@link #getAutofillId()}), or a new value obtained through + * {@link AutofillManager#getNextAutofillId()}. + * + * @throws IllegalStateException if the view is already {@link #isAttachedToWindow() attached to + * a window}. + * + * @throws IllegalArgumentException if the id is an autofill id associated with a virtual view. + */ + public void setAutofillId(@Nullable AutofillId id) { + if (android.view.autofill.Helper.sVerbose) { + Log.v(VIEW_LOG_TAG, "setAutofill(): from " + mAutofillId + " to " + id); + } + if (isAttachedToWindow()) { + throw new IllegalStateException("Cannot set autofill id when view is attached"); + } + if (id.isVirtual()) { + throw new IllegalStateException("Cannot set autofill id assigned to virtual views"); + } + mAutofillId = id; + } + + /** * Describes the autofill type of this view, so an * {@link android.service.autofill.AutofillService} can create the proper {@link AutofillValue} * when autofilling the view. diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index dd9b69ffcb07..a4261ebe16a0 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -498,7 +498,17 @@ public final class AutofillManager { /** * @return Whether compatibility mode is enabled. */ - boolean autofillIsCompatibilityModeEnabled(); + boolean autofillClientIsCompatibilityModeEnabled(); + + /** + * Gets the next unique autofill ID. + * + * <p>Typically used to manage views whose content is recycled - see + * {@link View#setAutofillId(AutofillId)} for more info. + * + * @return An ID that is unique in the activity. + */ + @Nullable AutofillId autofillClientGetNextAutofillId(); } /** @@ -781,7 +791,7 @@ public final class AutofillManager { /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */ @GuardedBy("mLock") private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) { - final AutofillId id = getAutofillId(view); + final AutofillId id = view.getAutofillId(); if (shouldIgnoreViewEnteredLocked(id, flags)) return null; AutofillCallback callback = null; @@ -831,7 +841,7 @@ public final class AutofillManager { if (mEnabled && isActiveLocked()) { // dont notify exited when Activity is already in background if (!isClientDisablingEnterExitEvent()) { - final AutofillId id = getAutofillId(view); + final AutofillId id = view.getAutofillId(); // Update focus on existing session. updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0); @@ -874,6 +884,7 @@ public final class AutofillManager { if (mEnabled && isActiveLocked()) { final AutofillId id = virtual ? getAutofillId(view, virtualId) : view.getAutofillId(); + if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible); if (!isVisible && mFillableIds != null) { if (mFillableIds.contains(id)) { if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible"); @@ -882,6 +893,8 @@ public final class AutofillManager { } if (mTrackedViews != null) { mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible); + } else if (sVerbose) { + Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views"); } } } @@ -1012,7 +1025,7 @@ public final class AutofillManager { if (mLastAutofilledData == null) { view.setAutofilled(false); } else { - id = getAutofillId(view); + id = view.getAutofillId(); if (mLastAutofilledData.containsKey(id)) { value = view.getAutofillValue(); valueWasRead = true; @@ -1037,7 +1050,7 @@ public final class AutofillManager { } if (id == null) { - id = getAutofillId(view); + id = view.getAutofillId(); } if (!valueWasRead) { @@ -1437,8 +1450,27 @@ public final class AutofillManager { } } - private static AutofillId getAutofillId(View view) { - return new AutofillId(view.getAutofillViewId()); + /** + * Gets the next unique autofill ID for the activity context. + * + * <p>Typically used to manage views whose content is recycled - see + * {@link View#setAutofillId(AutofillId)} for more info. + * + * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in + * the {@link Context} associated with this {@link AutofillManager}. + */ + @Nullable + public AutofillId getNextAutofillId() { + final AutofillClient client = getClient(); + if (client == null) return null; + + final AutofillId id = client.autofillClientGetNextAutofillId(); + + if (id == null && sDebug) { + Log.d(TAG, "getNextAutofillId(): client " + client + " returned null"); + } + + return id; } private static AutofillId getAutofillId(View parent, int virtualId) { @@ -1739,7 +1771,7 @@ public final class AutofillManager { if (mLastAutofilledData == null) { mLastAutofilledData = new ParcelableMap(1); } - mLastAutofilledData.put(getAutofillId(view), targetValue); + mLastAutofilledData.put(view.getAutofillId(), targetValue); } view.setAutofilled(true); } |