Merge "Make TextView do not show IME when fill dialog popup" into tm-dev
This commit is contained in:
commit
e6ae224102
@ -105,6 +105,12 @@ public final class FillRequest implements Parcelable {
|
||||
*/
|
||||
public static final @RequestFlags int FLAG_SUPPORTS_FILL_DIALOG = 0x40;
|
||||
|
||||
/**
|
||||
* Indicates the ime is showing while request coming.
|
||||
* @hide
|
||||
*/
|
||||
public static final @RequestFlags int FLAG_IME_SHOWING = 0x80;
|
||||
|
||||
/** @hide */
|
||||
public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
|
||||
|
||||
@ -201,7 +207,8 @@ public final class FillRequest implements Parcelable {
|
||||
FLAG_COMPATIBILITY_MODE_REQUEST,
|
||||
FLAG_PASSWORD_INPUT_TYPE,
|
||||
FLAG_VIEW_NOT_FOCUSED,
|
||||
FLAG_SUPPORTS_FILL_DIALOG
|
||||
FLAG_SUPPORTS_FILL_DIALOG,
|
||||
FLAG_IME_SHOWING
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@DataClass.Generated.Member
|
||||
@ -227,6 +234,8 @@ public final class FillRequest implements Parcelable {
|
||||
return "FLAG_VIEW_NOT_FOCUSED";
|
||||
case FLAG_SUPPORTS_FILL_DIALOG:
|
||||
return "FLAG_SUPPORTS_FILL_DIALOG";
|
||||
case FLAG_IME_SHOWING:
|
||||
return "FLAG_IME_SHOWING";
|
||||
default: return Integer.toHexString(value);
|
||||
}
|
||||
}
|
||||
@ -302,7 +311,8 @@ public final class FillRequest implements Parcelable {
|
||||
| FLAG_COMPATIBILITY_MODE_REQUEST
|
||||
| FLAG_PASSWORD_INPUT_TYPE
|
||||
| FLAG_VIEW_NOT_FOCUSED
|
||||
| FLAG_SUPPORTS_FILL_DIALOG);
|
||||
| FLAG_SUPPORTS_FILL_DIALOG
|
||||
| FLAG_IME_SHOWING);
|
||||
this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
|
||||
this.mDelayedFillIntentSender = delayedFillIntentSender;
|
||||
|
||||
@ -462,7 +472,8 @@ public final class FillRequest implements Parcelable {
|
||||
| FLAG_COMPATIBILITY_MODE_REQUEST
|
||||
| FLAG_PASSWORD_INPUT_TYPE
|
||||
| FLAG_VIEW_NOT_FOCUSED
|
||||
| FLAG_SUPPORTS_FILL_DIALOG);
|
||||
| FLAG_SUPPORTS_FILL_DIALOG
|
||||
| FLAG_IME_SHOWING);
|
||||
this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
|
||||
this.mDelayedFillIntentSender = delayedFillIntentSender;
|
||||
|
||||
@ -484,10 +495,10 @@ public final class FillRequest implements Parcelable {
|
||||
};
|
||||
|
||||
@DataClass.Generated(
|
||||
time = 1647644111186L,
|
||||
time = 1647856966565L,
|
||||
codegenVersion = "1.0.23",
|
||||
sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
|
||||
inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
|
||||
inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package android.view.autofill;
|
||||
|
||||
import static android.service.autofill.FillRequest.FLAG_IME_SHOWING;
|
||||
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
|
||||
import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
|
||||
import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
|
||||
@ -70,6 +71,7 @@ import android.view.ContentInfo;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewRootImpl;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
@ -647,6 +649,9 @@ public final class AutofillManager {
|
||||
|
||||
private final boolean mIsFillDialogEnabled;
|
||||
|
||||
// Indicates whether called the showAutofillDialog() method.
|
||||
private boolean mShowAutofillDialogCalled = false;
|
||||
|
||||
/** @hide */
|
||||
public interface AutofillClient {
|
||||
/**
|
||||
@ -1103,6 +1108,14 @@ public final class AutofillManager {
|
||||
notifyViewEntered(view, flags);
|
||||
}
|
||||
|
||||
private int getImeStateFlag(View v) {
|
||||
final WindowInsets rootWindowInsets = v.getRootWindowInsets();
|
||||
if (rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsets.Type.ime())) {
|
||||
return FLAG_IME_SHOWING;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
|
||||
if (isDisabledByServiceLocked()) {
|
||||
@ -1182,6 +1195,8 @@ public final class AutofillManager {
|
||||
flags |= FLAG_PASSWORD_INPUT_TYPE;
|
||||
}
|
||||
|
||||
flags |= getImeStateFlag(view);
|
||||
|
||||
if (!isActiveLocked()) {
|
||||
// Starts new session.
|
||||
startSessionLocked(id, null, value, flags);
|
||||
@ -1358,6 +1373,8 @@ public final class AutofillManager {
|
||||
flags |= FLAG_PASSWORD_INPUT_TYPE;
|
||||
}
|
||||
|
||||
flags |= getImeStateFlag(view);
|
||||
|
||||
if (!isActiveLocked()) {
|
||||
// Starts new session.
|
||||
startSessionLocked(id, bounds, null, flags);
|
||||
@ -1473,7 +1490,7 @@ public final class AutofillManager {
|
||||
value = view.getAutofillValue();
|
||||
}
|
||||
|
||||
updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
|
||||
updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, getImeStateFlag(view));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1498,7 +1515,7 @@ public final class AutofillManager {
|
||||
}
|
||||
|
||||
final AutofillId id = getAutofillId(view, virtualId);
|
||||
updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
|
||||
updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, getImeStateFlag(view));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2057,6 +2074,8 @@ public final class AutofillManager {
|
||||
mIdShownFillUi = null;
|
||||
mIsFillRequested = false;
|
||||
mRequireAutofill = false;
|
||||
mShowAutofillDialogCalled = false;
|
||||
mFillDialogTriggerIds = null;
|
||||
if (resetEnteredIds) {
|
||||
mEnteredIds = null;
|
||||
}
|
||||
@ -3051,8 +3070,9 @@ public final class AutofillManager {
|
||||
* dialog-style UI</a> are available for {@code view}, shows a dialog allowing the user to
|
||||
* select a suggestion and returns {@code true}.
|
||||
* <p>
|
||||
* The dialog may not be available if the autofill service does not support it, or if the
|
||||
* autofill request has not returned a response yet.
|
||||
* The dialog may not be shown if the autofill service does not support it, if the autofill
|
||||
* request has not returned a response yet, if the dialog was shown previously, or if the
|
||||
* input method is already shown.
|
||||
* <p>
|
||||
* It is recommended apps to call this method the first time a user focuses on
|
||||
* an autofill-able form, and to avoid showing the input method if the dialog is shown. If
|
||||
@ -3068,9 +3088,16 @@ public final class AutofillManager {
|
||||
// TODO(b/210926084): Consider whether to include the one-time show logic within this method.
|
||||
public boolean showAutofillDialog(@NonNull View view) {
|
||||
Objects.requireNonNull(view);
|
||||
if (shouldShowAutofillDialog(view.getAutofillId())) {
|
||||
// If the id matches a trigger id, this will trigger the fill dialog.
|
||||
notifyViewEntered(view);
|
||||
if (shouldShowAutofillDialog(view, view.getAutofillId())) {
|
||||
mShowAutofillDialogCalled = true;
|
||||
final WeakReference<View> wrView = new WeakReference<>(view);
|
||||
// The id matches a trigger id, this will trigger the fill dialog.
|
||||
post(() -> {
|
||||
final View v = wrView.get();
|
||||
if (v != null) {
|
||||
notifyViewEntered(v);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -3099,18 +3126,33 @@ public final class AutofillManager {
|
||||
*/
|
||||
public boolean showAutofillDialog(@NonNull View view, int virtualId) {
|
||||
Objects.requireNonNull(view);
|
||||
if (shouldShowAutofillDialog(getAutofillId(view, virtualId))) {
|
||||
// If the id matches a trigger id, this will trigger the fill dialog.
|
||||
notifyViewEntered(view, virtualId, /* bounds= */ null, /* flags= */ 0);
|
||||
if (shouldShowAutofillDialog(view, getAutofillId(view, virtualId))) {
|
||||
mShowAutofillDialogCalled = true;
|
||||
final WeakReference<View> wrView = new WeakReference<>(view);
|
||||
// The id matches a trigger id, this will trigger the fill dialog.
|
||||
post(() -> {
|
||||
final View v = wrView.get();
|
||||
if (v != null) {
|
||||
notifyViewEntered(v, virtualId, /* bounds= */ null, /* flags= */ 0);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean shouldShowAutofillDialog(AutofillId id) {
|
||||
if (!hasFillDialogUiFeature() || mFillDialogTriggerIds == null) {
|
||||
private boolean shouldShowAutofillDialog(View view, AutofillId id) {
|
||||
if (!hasFillDialogUiFeature()
|
||||
|| mShowAutofillDialogCalled
|
||||
|| mFillDialogTriggerIds == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getImeStateFlag(view) == FLAG_IME_SHOWING) {
|
||||
// IME is showing
|
||||
return false;
|
||||
}
|
||||
|
||||
final int size = mFillDialogTriggerIds.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
AutofillId fillId = mFillDialogTriggerIds.get(i);
|
||||
|
@ -11458,7 +11458,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
// Show the IME, except when selecting in read-only text.
|
||||
final InputMethodManager imm = getInputMethodManager();
|
||||
viewClicked(imm);
|
||||
if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
|
||||
if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null
|
||||
&& !showAutofillDialog()) {
|
||||
imm.showSoftInput(this, 0);
|
||||
}
|
||||
|
||||
@ -11476,6 +11477,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
return superResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* The fill dialog UI is a more conspicuous and efficient interface than dropdown UI.
|
||||
* If autofill suggestions are available when the user clicks on a field that supports filling
|
||||
* the dialog UI, Autofill will pop up a fill dialog. The dialog will take up a larger area
|
||||
* to display the datasets, so it is easy for users to pay attention to the datasets and
|
||||
* selecting a dataset. The autofill dialog is shown as the bottom sheet, the better
|
||||
* experience is not to show the IME if there is a fill dialog.
|
||||
*/
|
||||
private boolean showAutofillDialog() {
|
||||
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
|
||||
if (afm != null) {
|
||||
return afm.showAutofillDialog(this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
if (mMovement != null && mText instanceof Spannable && mLayout != null) {
|
||||
|
@ -3182,7 +3182,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
||||
|
||||
@Override
|
||||
public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId,
|
||||
@Nullable AutofillValue value) {
|
||||
@Nullable AutofillValue value, int flags) {
|
||||
synchronized (mLock) {
|
||||
if (mDestroyed) {
|
||||
Slog.w(TAG, "Call to Session#onFillReady() rejected - session: "
|
||||
@ -3207,7 +3207,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestShowFillDialog(response, filledId, filterText)) {
|
||||
if (requestShowFillDialog(response, filledId, filterText, flags)) {
|
||||
synchronized (mLock) {
|
||||
final ViewState currentView = mViewStates.get(mCurrentViewId);
|
||||
currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
|
||||
@ -3312,12 +3312,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
|
||||
}
|
||||
|
||||
private boolean requestShowFillDialog(FillResponse response,
|
||||
AutofillId filledId, String filterText) {
|
||||
AutofillId filledId, String filterText, int flags) {
|
||||
if (!isFillDialogUiEnabled()) {
|
||||
// Unsupported fill dialog UI
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) {
|
||||
// IME is showing, fallback to normal suggestions UI
|
||||
return false;
|
||||
}
|
||||
|
||||
final AutofillId[] ids = response.getFillDialogTriggerIds();
|
||||
if (ids == null || !ArrayUtils.contains(ids, filledId)) {
|
||||
return false;
|
||||
|
@ -43,7 +43,7 @@ final class ViewState {
|
||||
* Called when the fill UI is ready to be shown for this view.
|
||||
*/
|
||||
void onFillReady(@NonNull FillResponse fillResponse, @NonNull AutofillId focusedId,
|
||||
@Nullable AutofillValue value);
|
||||
@Nullable AutofillValue value, int flags);
|
||||
}
|
||||
|
||||
private static final String TAG = "ViewState";
|
||||
@ -202,7 +202,7 @@ final class ViewState {
|
||||
|
||||
/**
|
||||
* Calls {@link
|
||||
* Listener#onFillReady(FillResponse, AutofillId, AutofillValue)} if the
|
||||
* Listener#onFillReady(FillResponse, AutofillId, AutofillValue, int)} if the
|
||||
* fill UI is ready to be displayed (i.e. when response and bounds are set).
|
||||
*/
|
||||
void maybeCallOnFillReady(int flags) {
|
||||
@ -213,7 +213,7 @@ final class ViewState {
|
||||
// First try the current response associated with this View.
|
||||
if (mResponse != null) {
|
||||
if (mResponse.getDatasets() != null || mResponse.getAuthentication() != null) {
|
||||
mListener.onFillReady(mResponse, this.id, mCurrentValue);
|
||||
mListener.onFillReady(mResponse, this.id, mCurrentValue, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user