Merge "[Fill Dialog Improvements] Implement Fill Dialog improvements" into main
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1de0474..60e528c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,6 +26,7 @@
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.service.autofill.Flags.relayoutFix;
import static android.view.ContentInfo.SOURCE_AUTOFILL;
import static android.view.autofill.Helper.sDebug;
@@ -787,6 +788,11 @@
private AutofillStateFingerprint mAutofillStateFingerprint;
+ /**
+ * Whether improveFillDialog feature is enabled or not.
+ */
+ private boolean mImproveFillDialogEnabled;
+
/** @hide */
public interface AutofillClient {
/**
@@ -1017,6 +1023,17 @@
mRelayoutFix = relayoutFix() && AutofillFeatureFlags.enableRelayoutFixes();
mRelativePositionForRelayout = AutofillFeatureFlags.enableRelativeLocationForRelayout();
mIsCredmanIntegrationEnabled = Flags.autofillCredmanIntegration();
+ mImproveFillDialogEnabled =
+ improveFillDialogAconfig() && AutofillFeatureFlags.isImproveFillDialogEnabled();
+ }
+
+ /**
+ * Whether improvement to fill dialog is enabled.
+ *
+ * @hide
+ */
+ public boolean isImproveFillDialogEnabled() {
+ return mImproveFillDialogEnabled;
}
/**
@@ -1679,6 +1696,11 @@
private void notifyViewReadyInner(AutofillId id, @Nullable String[] autofillHints,
boolean isCredmanRequested) {
+ if (isImproveFillDialogEnabled() && !isCredmanRequested) {
+ // We do not want to send pre-trigger request.
+ // TODO(b/377868687): verify if we can remove the flow for isCredmanRequested too.
+ return;
+ }
if (sDebug) {
Log.d(TAG, "notifyViewReadyInner:" + id);
}
@@ -2046,6 +2068,34 @@
}
/**
+ * Notify autofill system that IME animation has started
+ * @param startTimeMs start time as measured by SystemClock.elapsedRealtime()
+ */
+ void notifyImeAnimationStart(long startTimeMs) {
+ try {
+ mService.notifyImeAnimationStart(mSessionId, startTimeMs, mContext.getUserId());
+ } catch (RemoteException e) {
+ // The failure could be a consequence of something going wrong on the
+ // server side. Just log the exception and move-on.
+ Log.w(TAG, "notifyImeAnimationStart(): RemoteException caught but ignored " + e);
+ }
+ }
+
+ /**
+ * Notify autofill system that IME animation has ended
+ * @param endTimeMs end time as measured by SystemClock.elapsedRealtime()
+ */
+ void notifyImeAnimationEnd(long endTimeMs) {
+ try {
+ mService.notifyImeAnimationEnd(mSessionId, endTimeMs, mContext.getUserId());
+ } catch (RemoteException e) {
+ // The failure could be a consequence of something going wrong on the
+ // server side. Just log the exception and move-on.
+ Log.w(TAG, "notifyImeAnimationStart(): RemoteException caught but ignored " + e);
+ }
+ }
+
+ /**
* Called when a virtual view that supports autofill is exited.
*
* @param view the virtual view parent.
@@ -4050,6 +4100,10 @@
@FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
@Deprecated
public boolean showAutofillDialog(@NonNull View view) {
+ if (isImproveFillDialogEnabled()) {
+ Log.i(TAG, "showAutofillDialog() return false due to improve fill dialog");
+ return false;
+ }
Objects.requireNonNull(view);
if (shouldShowAutofillDialog(view, view.getAutofillId())) {
mShowAutofillDialogCalled = true;
@@ -4093,6 +4147,10 @@
@FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
@Deprecated
public boolean showAutofillDialog(@NonNull View view, int virtualId) {
+ if (isImproveFillDialogEnabled()) {
+ Log.i(TAG, "showAutofillDialog() return false due to improve fill dialog");
+ return false;
+ }
Objects.requireNonNull(view);
if (shouldShowAutofillDialog(view, getAutofillId(view, virtualId))) {
mShowAutofillDialogCalled = true;
@@ -4117,7 +4175,7 @@
return false;
}
- if (getImeStateFlag(view) == FLAG_IME_SHOWING) {
+ if (getImeStateFlag(view) == FLAG_IME_SHOWING && !isImproveFillDialogEnabled()) {
// IME is showing
return false;
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index f67405f..28f8577 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -70,4 +70,6 @@
void notifyNotExpiringResponseDuringAuth(int sessionId, int userId);
void notifyViewEnteredIgnoredDuringAuthCount(int sessionId, int userId);
void setAutofillIdsAttemptedForRefill(int sessionId, in List<AutofillId> ids, int userId);
+ void notifyImeAnimationStart(int sessionId, long startTimeMs, int userId);
+ void notifyImeAnimationEnd(int sessionId, long endTimeMs, int userId);
}
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 5e1b147..9c83757 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -9,6 +9,16 @@
}
flag {
+ name: "improve_fill_dialog_aconfig"
+ namespace: "autofill"
+ description: "Improvements for Fill Dialog. Guard DeviceConfig rollout "
+ bug: "382493181"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "fill_fields_from_current_session_only"
namespace: "autofill"
description: "Only fill autofill fields that are part of the current session."
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 219b788..5e7e557 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -354,6 +354,13 @@
}
}
+ private void handleOnInputMethodStartInputView() {
+ synchronized (mLock) {
+ mUiCallback.onInputMethodStartInputView();
+ handleOnReceiveImeStatusUpdated(true, true);
+ }
+ }
+
/**
* Handles the IME session status received from the IME.
*
@@ -437,8 +444,8 @@
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
session.mHandler.sendMessage(obtainMessage(
- AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
- session, true, true));
+ AutofillInlineSuggestionsRequestSession::handleOnInputMethodStartInputView,
+ session));
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 259ea14..cba8c66 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -2139,6 +2139,32 @@
}
@Override
+ public void notifyImeAnimationStart(int sessionId, long startTimeMs, int userId) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ service.notifyImeAnimationStart(sessionId, startTimeMs, getCallingUid());
+ } else if (sVerbose) {
+ Slog.v(TAG, "notifyImeAnimationStart(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int userId) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service =
+ peekServiceForUserWithLocalBinderIdentityLocked(userId);
+ if (service != null) {
+ service.notifyImeAnimationEnd(sessionId, endTimeMs, getCallingUid());
+ } else if (sVerbose) {
+ Slog.v(TAG, "notifyImeAnimationEnd(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 5cf96bf..0fa43ac 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -823,6 +823,36 @@
}
@GuardedBy("mLock")
+ public void notifyImeAnimationStart(int sessionId, long startTimeMs, int uid) {
+ if (!isEnabledLocked()) {
+ Slog.wtf(TAG, "Service not enabled");
+ return;
+ }
+ final Session session = mSessions.get(sessionId);
+ if (session == null || uid != session.uid) {
+ Slog.v(TAG, "notifyImeAnimationStart(): no session for "
+ + sessionId + "(" + uid + ")");
+ return;
+ }
+ session.notifyImeAnimationStart(startTimeMs);
+ }
+
+ @GuardedBy("mLock")
+ public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int uid) {
+ if (!isEnabledLocked()) {
+ Slog.wtf(TAG, "Service not enabled");
+ return;
+ }
+ final Session session = mSessions.get(sessionId);
+ if (session == null || uid != session.uid) {
+ Slog.v(TAG, "notifyImeAnimationEnd(): no session for "
+ + sessionId + "(" + uid + ")");
+ return;
+ }
+ session.notifyImeAnimationEnd(endTimeMs);
+ }
+
+ @GuardedBy("mLock")
@Override // from PerUserSystemService
protected void handlePackageUpdateLocked(@NonNull String packageName) {
final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 6b227d7..9c6e4741 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -40,6 +40,7 @@
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
import static android.service.autofill.Flags.highlightAutofillSingleField;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
@@ -50,6 +51,7 @@
import static android.view.autofill.AutofillManager.EXTRA_AUTOFILL_REQUEST_ID;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
+
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_EXPLICITLY_REQUESTED;
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_NORMAL_TRIGGER;
@@ -184,6 +186,7 @@
import android.view.autofill.IAutofillWindowPresenter;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.RemoteViews;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -195,6 +198,7 @@
import com.android.server.autofill.ui.PendingUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -248,6 +252,8 @@
private static final int DEFAULT__FILL_REQUEST_ID_SNAPSHOT = -2;
private static final int DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT = -2;
+ private static final long DEFAULT_UNASSIGNED_TIME = -1;
+
static final String SESSION_ID_KEY = "autofill_session_id";
static final String REQUEST_ID_KEY = "autofill_request_id";
@@ -292,6 +298,31 @@
@Retention(RetentionPolicy.SOURCE)
@interface SessionState {}
+ /**
+ * Indicates fill dialog will not be shown.
+ */
+ private static final int SHOW_FILL_DIALOG_NO = 0;
+
+ /**
+ * Indicates fill dialog is shown.
+ */
+ private static final int SHOW_FILL_DIALOG_YES = 1;
+
+ /**
+ * Indicates fill dialog can be shown, but we need to wait.
+ * For eg, if the IME animation is happening, we need for it to complete before fill dialog can
+ * be shown.
+ */
+ private static final int SHOW_FILL_DIALOG_WAIT = 2;
+
+ @IntDef(prefix = { "SHOW_FILL_DIALOG_" }, value = {
+ SHOW_FILL_DIALOG_NO,
+ SHOW_FILL_DIALOG_YES,
+ SHOW_FILL_DIALOG_WAIT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface ShowFillDialogState{}
+
@GuardedBy("mLock")
private final SessionFlags mSessionFlags;
@@ -576,6 +607,47 @@
private boolean mIgnoreViewStateResetToEmpty;
+ /**
+ * Whether improveFillDialog feature is enabled or not.
+ * Configured via device config flag and aconfig flag.
+ */
+ private final boolean mImproveFillDialogEnabled;
+
+ /**
+ * Timeout, after which, fill dialog won't be shown.
+ * Configured via device config flag.
+ */
+ private final long mFillDialogTimeoutMs;
+
+ /**
+ * Time to wait after ime Animation ends before showing up fill dialog.
+ * Configured via device config flag.
+ */
+ private final long mFillDialogMinWaitAfterImeAnimationMs;
+
+ /**
+ * Indicates the need to wait for ime animation to end before showing up fill dialog.
+ * This is set when we receive notification of ime animation start.
+ * Focussing on one input field from another wouldn't cause ime to re-animate. So this will let
+ * us know whether we need to wait for ime animation finish notification.
+ */
+ private boolean mWaitForImeAnimation;
+
+ /**
+ * A runnable set to run when there is a need to wait for ime animation to end before showing
+ * up fill dialog. This is set only if the fill response has been received, and the response
+ * is eligible for showing up fill dialog, but the ime animation hasn't completed. This allows
+ * for this runnable to be scheduled/run when the ime animation ends, in order to show fill
+ * dialog.
+ */
+ private Runnable mFillDialogRunnable;
+
+ private long mImeAnimationFinishTimeMs = DEFAULT_UNASSIGNED_TIME;
+
+ private long mImeAnimationStartTimeMs = DEFAULT_UNASSIGNED_TIME;
+
+ private long mLastInputStartTime = DEFAULT_UNASSIGNED_TIME;
+
/*
* Id of the previous view that was entered. Once set, it would only be replaced by non-null
* view ids.
@@ -1493,6 +1565,12 @@
// Now request the assist structure data.
requestAssistStructureLocked(requestId, flags);
+ if (mImproveFillDialogEnabled) {
+ // New request has been sent, so re-enable fill dialog.
+ // Fill dialog is eligible to be shown after each Fill request.
+ enableFillDialog();
+ }
+
return Optional.of(requestId);
}
@@ -1657,6 +1735,11 @@
mSaveEventLogger = SaveEventLogger.forSessionId(sessionId, mLatencyBaseTime);
mIsPrimaryCredential = isPrimaryCredential;
mIgnoreViewStateResetToEmpty = AutofillFeatureFlags.shouldIgnoreViewStateResetToEmpty();
+ mImproveFillDialogEnabled =
+ improveFillDialogAconfig() && AutofillFeatureFlags.isImproveFillDialogEnabled();
+ mFillDialogTimeoutMs = AutofillFeatureFlags.getFillDialogTimeoutMs();
+ mFillDialogMinWaitAfterImeAnimationMs =
+ AutofillFeatureFlags.getFillDialogMinWaitAfterImeAnimationtEndMs();
synchronized (mLock) {
mSessionFlags = new SessionFlags();
@@ -1682,6 +1765,13 @@
public void notifyInlineUiHidden(AutofillId autofillId) {
notifyFillUiHidden(autofillId);
}
+
+ @Override
+ public void onInputMethodStartInputView() {
+ // TODO(b/377868687): This method isn't called when IME doesn't
+ // support inline suggestion. Handle those cases as well.
+ onInputMethodStart();
+ }
});
mMetricsLogger.write(
@@ -3044,6 +3134,12 @@
}
}
+ private void onInputMethodStart() {
+ synchronized (mLock) {
+ mLastInputStartTime = SystemClock.elapsedRealtime();
+ }
+ }
+
private void doStartIntentSender(IntentSender intentSender, Intent intent) {
try {
synchronized (mLock) {
@@ -5407,6 +5503,15 @@
}
}
+ private void resetImeAnimationState() {
+ synchronized (mLock) {
+ mWaitForImeAnimation = false;
+ mImeAnimationStartTimeMs = DEFAULT_UNASSIGNED_TIME;
+ mImeAnimationFinishTimeMs = DEFAULT_UNASSIGNED_TIME;
+ mLastInputStartTime = DEFAULT_UNASSIGNED_TIME;
+ }
+ }
+
@Override
public void onFillReady(
@NonNull FillResponse response,
@@ -5452,18 +5557,24 @@
final AutofillId[] ids = response.getFillDialogTriggerIds();
if (ids != null && ArrayUtils.contains(ids, filledId)) {
- if (requestShowFillDialog(response, filledId, filterText, flags)) {
+ @ShowFillDialogState int fillDialogState =
+ requestShowFillDialog(response, filledId, filterText, flags);
+ if (fillDialogState == SHOW_FILL_DIALOG_YES) {
synchronized (mLock) {
final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
}
- // Just show fill dialog once, so disabled after shown.
+ // Just show fill dialog once per fill request, so disabled after shown.
// Note: Cannot disable before requestShowFillDialog() because the method
- // need to check whether fill dialog enabled.
+ // need to check whether fill dialog is enabled.
setFillDialogDisabled();
+ resetImeAnimationState();
return;
- } else {
+ } else if (fillDialogState == SHOW_FILL_DIALOG_NO) {
+ resetImeAnimationState();
setFillDialogDisabled();
+ } else { // SHOW_FILL_DIALOG_WAIT
+ return;
}
}
@@ -5559,7 +5670,20 @@
}
}
+ private void enableFillDialog() {
+ if (sVerbose) {
+ Slog.v(TAG, "Enabling Fill Dialog....");
+ }
+ synchronized (mLock) {
+ mSessionFlags.mFillDialogDisabled = false;
+ }
+ notifyClientFillDialogTriggerIds(null);
+ }
+
private void setFillDialogDisabled() {
+ if (sVerbose) {
+ Slog.v(TAG, "Disabling Fill Dialog.");
+ }
synchronized (mLock) {
mSessionFlags.mFillDialogDisabled = true;
}
@@ -5577,24 +5701,28 @@
}
}
- private boolean requestShowFillDialog(
+ private @ShowFillDialogState int requestShowFillDialog(
FillResponse response, AutofillId filledId, String filterText, int flags) {
if (!isFillDialogUiEnabled()) {
+ // TODO(b/377868687): The above check includes credman fields. We may want to show
+ // credman fields again.
// Unsupported fill dialog UI
- if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled");
- return false;
+ if (sDebug) Log.w(TAG, "requestShowFillDialog(): fill dialog is disabled");
+ return SHOW_FILL_DIALOG_NO;
}
- if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) {
- // IME is showing, fallback to normal suggestions UI
- if (sDebug) Log.w(TAG, "requestShowFillDialog: IME is showing");
- return false;
- }
+ if (!mImproveFillDialogEnabled) {
+ if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) {
+ // IME is showing, fallback to normal suggestions UI
+ if (sDebug) Log.w(TAG, "requestShowFillDialog(): IME is showing");
+ return SHOW_FILL_DIALOG_NO;
+ }
- if (mInlineSessionController.isImeShowing()) {
- // IME is showing, fallback to normal suggestions UI
- // Note: only work when inline suggestions supported
- return false;
+ if (mInlineSessionController.isImeShowing()) {
+ // IME is showing, fallback to normal suggestions UI
+ // Note: only work when inline suggestions supported
+ return SHOW_FILL_DIALOG_NO;
+ }
}
synchronized (mLock) {
@@ -5602,29 +5730,84 @@
|| !ArrayUtils.contains(mLastFillDialogTriggerIds, filledId)) {
// Last fill dialog triggered ids are changed.
if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed.");
- return false;
+ return SHOW_FILL_DIALOG_NO;
+ }
+
+ if (mImproveFillDialogEnabled && mInlineSessionController.isImeShowing()) {
+ long currentTimestampMs = SystemClock.elapsedRealtime();
+ long durationMs = currentTimestampMs - mLastInputStartTime;
+ if (sVerbose) {
+ Log.d(TAG, "IME is showing. Checking for elapsed time ");
+ Log.d(TAG, "IME is showing. Timestamps start: " + mLastInputStartTime
+ + " current: " + currentTimestampMs + " duration: " + durationMs
+ + " mFillDialogTimeoutMs: " + mFillDialogTimeoutMs);
+ }
+
+ // Following situations can arise wrt IME animation.
+ // 1. No animation happening (eg IME already animated). In that case,
+ // mWaitForImeAnimation should be false. This is possible if the IME is already up
+ // on a field, but the user focusses on another field. Under such condition,
+ // since IME has already animated, there won't be another animation. However,
+ // onInputStartInputView is still called.
+ // 2. Animation is still proceeding. We should wait for animation to finish,
+ // and then proceed.
+ // 3. Animation is complete.
+ if (mWaitForImeAnimation) {
+ // we need to wait for animation to happen. We can't return from here yet.
+ // This is the situation #2 described above.
+ Log.d(TAG, "Waiting for ime animation to complete before showing fill dialog");
+ mFillDialogRunnable = createFillDialogEvalRunnable(
+ response, filledId, filterText, flags);
+ return SHOW_FILL_DIALOG_WAIT;
+ }
+
+ // Incorporate situations 1 & 3 discussed above. We calculate the duration from the
+ // max of start input time or the ime finish time
+ long effectiveDuration = currentTimestampMs
+ - Math.max(mLastInputStartTime, mImeAnimationFinishTimeMs);
+ if (effectiveDuration >= mFillDialogTimeoutMs) {
+ Log.d(TAG, "Fill dialog not shown since IME has been up for more time than "
+ + mFillDialogTimeoutMs + "ms");
+ return SHOW_FILL_DIALOG_NO;
+ } else if (effectiveDuration < mFillDialogMinWaitAfterImeAnimationMs) {
+ // we need to wait for some time after animation ends
+ Runnable runnable = createFillDialogEvalRunnable(
+ response, filledId, filterText, flags);
+ mHandler.postDelayed(runnable,
+ mFillDialogMinWaitAfterImeAnimationMs - effectiveDuration);
+ return SHOW_FILL_DIALOG_WAIT;
+ }
}
}
+ showFillDialog(response, filledId, filterText);
+ return SHOW_FILL_DIALOG_YES;
+ }
+
+ private Runnable createFillDialogEvalRunnable(
+ @NonNull FillResponse response,
+ @NonNull AutofillId filledId,
+ String filterText,
+ int flags) {
+ return () -> {
+ synchronized (mLock) {
+ AutofillValue value = AutofillValue.forText(filterText);
+ onFillReady(response, filledId, value, flags);
+ }
+ };
+ }
+
+ private void showFillDialog(FillResponse response, AutofillId filledId, String filterText) {
Drawable serviceIcon = null;
+ PresentationStatsEventLogger logger = null;
synchronized (mLock) {
serviceIcon = getServiceIcon(response);
+ logger = mPresentationStatsEventLogger;
}
- getUiForShowing()
- .showFillDialog(
- filledId,
- response,
- filterText,
- mService.getServicePackageName(),
- mComponentName,
- serviceIcon,
- this,
- id,
- mCompatMode,
- mPresentationStatsEventLogger,
- mLock);
- return true;
+ getUiForShowing().showFillDialog(filledId, response, filterText,
+ mService.getServicePackageName(), mComponentName, serviceIcon, this,
+ id, mCompatMode, logger, mLock);
}
/**
@@ -7689,6 +7872,30 @@
mSessionState = STATE_REMOVED;
}
+ @GuardedBy("mLock")
+ public void notifyImeAnimationStart(long startTimeMs) {
+ mImeAnimationStartTimeMs = startTimeMs;
+ mWaitForImeAnimation = true;
+ }
+
+ @GuardedBy("mLock")
+ public void notifyImeAnimationEnd(long endTimeMs) {
+ mImeAnimationFinishTimeMs = endTimeMs;
+ // Make sure to use mRunnable with synchronized
+ if (mFillDialogRunnable != null) {
+ if (sVerbose) {
+ Log.v(TAG, "Ime animation ended, starting fill dialog.");
+ }
+ mHandler.postDelayed(mFillDialogRunnable, mFillDialogMinWaitAfterImeAnimationMs);
+ mFillDialogRunnable = null;
+ } else {
+ if (sVerbose) {
+ Log.v(TAG, "Ime animation ended, no runnable present.");
+ }
+ }
+ mWaitForImeAnimation = false;
+ }
+
void onPendingSaveUi(int operation, @NonNull IBinder token) {
getUiForShowing().onPendingSaveUi(operation, token);
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
index ffc80ee..7287bdd 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -409,5 +409,10 @@
* Callback to notify inline ui is hidden.
*/
void notifyInlineUiHidden(@NonNull AutofillId autofillId);
+
+ /**
+ * Callback to notify input method started.
+ */
+ void onInputMethodStartInputView();
}
}