From 485150ebcf0237d55aa460ba7a2954d9f69499ac Mon Sep 17 00:00:00 2001 From: TYM Tsai Date: Fri, 30 Dec 2022 01:34:36 +0800 Subject: [PATCH] Add API for virtual view is ready to user When the view is ready to the user, notify autofill can do something if needed before the user start to input. And then autofill start to prepare suggestions for some features, e.g. a prefill request for the fill dialog. Bug: 258077433 Test: atest android.autofillservice.cts.dialog Change-Id: I0c6c964fb158c2da35b3470528d92d0d5bf74aa7 --- core/api/current.txt | 11 ++ .../view/autofill/AutofillManager.java | 52 ++++++- .../view/autofill/VirtualViewFillInfo.java | 141 ++++++++++++++++++ 3 files changed, 198 insertions(+), 6 deletions(-) create mode 100644 core/java/android/view/autofill/VirtualViewFillInfo.java diff --git a/core/api/current.txt b/core/api/current.txt index 0ad7d80ea508d..16d56077e7e84 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -54194,6 +54194,7 @@ package android.view.autofill { method public void notifyViewExited(@NonNull android.view.View, int); method public void notifyViewVisibilityChanged(@NonNull android.view.View, boolean); method public void notifyViewVisibilityChanged(@NonNull android.view.View, int, boolean); + method public void notifyVirtualViewsReady(@NonNull android.view.View, @NonNull android.util.SparseArray); method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback); method public void requestAutofill(@NonNull android.view.View); method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect); @@ -54240,6 +54241,16 @@ package android.view.autofill { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } + public final class VirtualViewFillInfo { + method @Nullable public String[] getAutofillHints(); + } + + public static final class VirtualViewFillInfo.Builder { + ctor public VirtualViewFillInfo.Builder(); + method @NonNull public android.view.autofill.VirtualViewFillInfo build(); + method @NonNull public android.view.autofill.VirtualViewFillInfo.Builder setAutofillHints(@NonNull java.lang.String...); + } + } package android.view.contentcapture { diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 58e7a70b155c1..756952319b355 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1087,6 +1087,42 @@ public final class AutofillManager { notifyViewEntered(view, 0); } + /** + * Called when the virtual views are ready to the user for autofill. + * + * This method is used to notify autofill system the views are ready to the user. And then + * Autofill can do initialization if needed before the user starts to input. For example, do + * a pre-fill request for the + * fill dialog. + * + * @param view the host view that holds a virtual view hierarchy. + * @param infos extra information for the virtual views. The key is virtual id which represents + * the virtual view in the host view. + * + * @throws IllegalArgumentException if the {@code infos} was empty + */ + public void notifyVirtualViewsReady( + @NonNull View view, @NonNull SparseArray infos) { + Objects.requireNonNull(infos); + if (infos.size() == 0) { + throw new IllegalArgumentException("No VirtualViewInfo found"); + } + if (AutofillFeatureFlags.isFillDialogDisabledForCredentialManager() + && view.isCredential()) { + if (sDebug) { + Log.d(TAG, "Ignoring Fill Dialog request since important for credMan:" + + view.getAutofillId().toString()); + } + return; + } + for (int i = 0; i < infos.size(); i++) { + final VirtualViewFillInfo info = infos.valueAt(i); + final int virtualId = infos.indexOfKey(i); + notifyViewReadyInner(getAutofillId(view, virtualId), + (info == null) ? null : info.getAutofillHints()); + } + } + /** * The {@link AutofillFeatureFlags#DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED} is {@code true} or * the view have the allowed autofill hints, performs a fill request to know there is any field @@ -1098,9 +1134,6 @@ public final class AutofillManager { if (sDebug) { Log.d(TAG, "notifyViewEnteredForFillDialog:" + v.getAutofillId()); } - if (!hasAutofillFeature()) { - return; - } if (AutofillFeatureFlags.isFillDialogDisabledForCredentialManager() && v.isCredential()) { if (sDebug) { @@ -1109,6 +1142,13 @@ public final class AutofillManager { } return; } + notifyViewReadyInner(v.getAutofillId(), v.getAutofillHints()); + } + + private void notifyViewReadyInner(AutofillId id, String[] autofillHints) { + if (!hasAutofillFeature()) { + return; + } synchronized (mLock) { if (mTrackedViews != null) { @@ -1116,7 +1156,7 @@ public final class AutofillManager { // different pages but in the same Activity. We need to reset the // mIsFillRequested flag to allow asking for a new FillRequest when // user switches to other page - mTrackedViews.checkViewState(v.getAutofillId()); + mTrackedViews.checkViewState(id); } } @@ -1126,9 +1166,9 @@ public final class AutofillManager { } if (mIsFillDialogEnabled - || ArrayUtils.containsAny(v.getAutofillHints(), mFillDialogEnabledHints)) { + || ArrayUtils.containsAny(autofillHints, mFillDialogEnabledHints)) { if (sDebug) { - Log.d(TAG, "Trigger fill request at view entered"); + Log.d(TAG, "Trigger fill request when the view is ready."); } int flags = FLAG_SUPPORTS_FILL_DIALOG; diff --git a/core/java/android/view/autofill/VirtualViewFillInfo.java b/core/java/android/view/autofill/VirtualViewFillInfo.java new file mode 100644 index 0000000000000..4bec5a4a0960f --- /dev/null +++ b/core/java/android/view/autofill/VirtualViewFillInfo.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view.autofill; + + +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.view.View; + +import com.android.internal.util.DataClass; + + +/** + * Information for the virtual view to the autofill framework. + */ +@DataClass(genBuilder = true) +public final class VirtualViewFillInfo { + + /** + * Autofill hints of the virtual view. + * + * @see View#setAutofillHints(String...) + */ + @Nullable + @SuppressLint("NullableCollection") + private String[] mAutofillHints; + + private static String[] defaultAutofillHints() { + return null; + } + + + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/autofill/VirtualViewFillInfo.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ VirtualViewFillInfo( + @Nullable @SuppressLint("NullableCollection") String[] autofillHints) { + this.mAutofillHints = autofillHints; + com.android.internal.util.AnnotationValidations.validate( + SuppressLint.class, null, mAutofillHints, + "value", "NullableCollection"); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * Autofill hints of the virtual view. + * + * @see View#setAutofillHints(String...) + */ + @DataClass.Generated.Member + public @Nullable @SuppressLint("NullableCollection") String[] getAutofillHints() { + return mAutofillHints; + } + + /** + * A builder for {@link VirtualViewFillInfo} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private @Nullable @SuppressLint("NullableCollection") String[] mAutofillHints; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } + + /** + * Autofill hints of the virtual view. + * + * @see View#setAutofillHints(String...) + */ + @DataClass.Generated.Member + public @android.annotation.NonNull Builder setAutofillHints(@SuppressLint("NullableCollection") @android.annotation.NonNull String... value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mAutofillHints = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @android.annotation.NonNull VirtualViewFillInfo build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mAutofillHints = defaultAutofillHints(); + } + VirtualViewFillInfo o = new VirtualViewFillInfo( + mAutofillHints); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x2) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1674023010954L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/core/java/android/view/autofill/VirtualViewFillInfo.java", + inputSignatures = "private @android.annotation.Nullable @android.annotation.SuppressLint java.lang.String[] mAutofillHints\nprivate static java.lang.String[] defaultAutofillHints()\nclass VirtualViewFillInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +}