Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d4356fdf authored by Dake Gu's avatar Dake Gu Committed by Android (Google) Code Review
Browse files

Merge "autofill: support dpad/keyboard"

parents ed87e536 67decfa7
Loading
Loading
Loading
Loading
+52 −7
Original line number Diff line number Diff line
@@ -857,6 +857,7 @@ public class Activity extends ContextThemeWrapper
    private boolean mHasCurrentPermissionsRequest;

    private boolean mAutoFillResetNeeded;
    private boolean mAutoFillIgnoreFirstResumePause;

    /** The last autofill id that was returned from {@link #getNextAutofillId()} */
    private int mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
@@ -1253,10 +1254,7 @@ public class Activity extends ContextThemeWrapper
        getApplication().dispatchActivityStarted(this);

        if (mAutoFillResetNeeded) {
            AutofillManager afm = getAutofillManager();
            if (afm != null) {
                afm.onVisibleForAutofill();
            }
            getAutofillManager().onVisibleForAutofill();
        }
    }

@@ -1320,6 +1318,20 @@ public class Activity extends ContextThemeWrapper
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
        getApplication().dispatchActivityResumed(this);
        mActivityTransitionState.onResume(this, isTopOfTask());
        if (mAutoFillResetNeeded) {
            if (!mAutoFillIgnoreFirstResumePause) {
                View focus = getCurrentFocus();
                if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
                    // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
                    // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
                    // window visibility after recreation is INVISIBLE in onResume() and next frame
                    // ViewRootImpl.performTraversals() changes window visibility to VISIBLE.
                    // So we cannot call View.notifyEnterOrExited() which will do nothing
                    // when View.isVisibleToUser() is false.
                    getAutofillManager().notifyViewEntered(focus);
                }
            }
        }
        mCalled = true;
    }

@@ -1681,6 +1693,19 @@ public class Activity extends ContextThemeWrapper
    protected void onPause() {
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
        getApplication().dispatchActivityPaused(this);
        if (mAutoFillResetNeeded) {
            if (!mAutoFillIgnoreFirstResumePause) {
                if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this);
                View focus = getCurrentFocus();
                if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
                    getAutofillManager().notifyViewExited(focus);
                }
            } else {
                // reset after first pause()
                if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill got first pause " + this);
                mAutoFillIgnoreFirstResumePause = false;
            }
        }
        mCalled = true;
    }

@@ -1871,6 +1896,10 @@ public class Activity extends ContextThemeWrapper
        mTranslucentCallback = null;
        mCalled = true;

        if (mAutoFillResetNeeded) {
            getAutofillManager().onInvisibleForAutofill();
        }

        if (isFinishing()) {
            if (mAutoFillResetNeeded) {
                getAutofillManager().onActivityFinished();
@@ -6266,7 +6295,7 @@ public class Activity extends ContextThemeWrapper

        mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);

        final AutofillManager afm = getAutofillManager();
        final AutofillManager afm = mAutofillManager;
        if (afm != null) {
            afm.dump(prefix, writer);
        } else {
@@ -7119,13 +7148,23 @@ public class Activity extends ContextThemeWrapper
        }
    }

    final void performResume() {
    final void performResume(boolean followedByPause) {
        performRestart(true /* start */);

        mFragments.execPendingActions();

        mLastNonConfigurationInstances = null;

        if (mAutoFillResetNeeded) {
            // When Activity is destroyed in paused state, and relaunch activity, there will be
            // extra onResume and onPause event,  ignore the first onResume and onPause.
            // see ActivityThread.handleRelaunchActivity()
            mAutoFillIgnoreFirstResumePause = followedByPause;
            if (mAutoFillIgnoreFirstResumePause && DEBUG_LIFECYCLE) {
                Slog.v(TAG, "autofill will ignore first pause when relaunching " + this);
            }
        }

        mCalled = false;
        // mResumed is set by the instrumentation
        mInstrumentation.callActivityOnResume(this);
@@ -7310,7 +7349,7 @@ public class Activity extends ContextThemeWrapper
            }
        } else if (who.startsWith(AUTO_FILL_AUTH_WHO_PREFIX)) {
            Intent resultData = (resultCode == Activity.RESULT_OK) ? data : null;
            getAutofillManager().onAuthenticationResult(requestCode, resultData);
            getAutofillManager().onAuthenticationResult(requestCode, resultData, getCurrentFocus());
        } else {
            Fragment frag = mFragments.findFragmentByWho(who);
            if (frag != null) {
@@ -7584,6 +7623,12 @@ public class Activity extends ContextThemeWrapper
        return !mStopped;
    }

    /** @hide */
    @Override
    public boolean isDisablingEnterExitEventForAutofill() {
        return mAutoFillIgnoreFirstResumePause || !mResumed;
    }

    /**
     * If set to true, this indicates to the system that it should never take a
     * screenshot of the activity to be used as a representation while it is not in a started state.
+3 −3
Original line number Diff line number Diff line
@@ -3096,7 +3096,7 @@ public final class ActivityThread extends ClientTransactionHandler {
        checkAndBlockForNetworkAccess();
        deliverNewIntents(r, intents);
        if (resumed) {
            r.activity.performResume();
            r.activity.performResume(false);
            r.activity.mTemporaryPause = false;
        }

@@ -3718,7 +3718,7 @@ public final class ActivityThread extends ClientTransactionHandler {
                    deliverResults(r, r.pendingResults);
                    r.pendingResults = null;
                }
                r.activity.performResume();
                r.activity.performResume(r.startsNotResumed);

                synchronized (mResourcesManager) {
                    // If there is a pending local relaunch that was requested when the activity was
@@ -4437,7 +4437,7 @@ public final class ActivityThread extends ClientTransactionHandler {
            checkAndBlockForNetworkAccess();
            deliverResults(r, results);
            if (resumed) {
                r.activity.performResume();
                r.activity.performResume(false);
                r.activity.mTemporaryPause = false;
            }
        }
+13 −6
Original line number Diff line number Diff line
@@ -7232,20 +7232,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
    }
    private void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
        if (isAutofillable() && isAttachedToWindow()) {
    /** @hide */
    public void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
        if (canNotifyAutofillEnterExitEvent()) {
            AutofillManager afm = getAutofillManager();
            if (afm != null) {
                if (enter && hasWindowFocus() && isFocused()) {
                if (enter && isFocused()) {
                    // We have not been laid out yet, hence cannot evaluate
                    // whether this view is visible to the user, we will do
                    // the evaluation once layout is complete.
                    if (!isLaidOut()) {
                        mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
                    } else if (isVisibleToUser()) {
                        // TODO This is a potential problem that View gets focus before it's visible
                        // to User. Ideally View should handle the event when isVisibleToUser()
                        // becomes true where it should issue notifyViewEntered().
                        afm.notifyViewEntered(this);
                    }
                } else if (!hasWindowFocus() || !isFocused()) {
                } else if (!isFocused()) {
                    afm.notifyViewExited(this);
                }
            }
@@ -8317,6 +8321,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                && getAutofillViewId() > LAST_APP_AUTOFILL_ID;
    }
    /** @hide */
    public boolean canNotifyAutofillEnterExitEvent() {
        return isAutofillable() && isAttachedToWindow();
    }
    private void populateVirtualStructure(ViewStructure structure,
            AccessibilityNodeProvider provider, AccessibilityNodeInfo info) {
        structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
@@ -12451,8 +12460,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            imm.focusIn(this);
        }
        notifyEnterOrExitForAutoFillIfNeeded(hasWindowFocus);
        refreshDrawableState();
    }
+121 −36
Original line number Diff line number Diff line
@@ -341,6 +341,10 @@ public final class AutofillManager {
    @GuardedBy("mLock")
    @Nullable private AutofillId mSaveTriggerId;

    /** set to true when onInvisibleForAutofill is called, used by onAuthenticationResult */
    @GuardedBy("mLock")
    private boolean mOnInvisibleCalled;

    /** If set, session is commited when the activity is finished; otherwise session is canceled. */
    @GuardedBy("mLock")
    private boolean mSaveOnFinish;
@@ -396,6 +400,11 @@ public final class AutofillManager {
         */
        boolean isVisibleForAutofill();

        /**
         * Client might disable enter/exit event e.g. when activity is paused.
         */
        boolean isDisablingEnterExitEventForAutofill();

        /**
         * Finds views by traversing the hierarchies of the client.
         *
@@ -498,6 +507,19 @@ public final class AutofillManager {
        }
    }

    /**
     * Called once the client becomes invisible.
     *
     * @see AutofillClient#isVisibleForAutofill()
     *
     * {@hide}
     */
    public void onInvisibleForAutofill() {
        synchronized (mLock) {
            mOnInvisibleCalled = true;
        }
    }

    /**
     * Save state before activity lifecycle
     *
@@ -623,13 +645,35 @@ public final class AutofillManager {
        return false;
    }

    private boolean isClientVisibleForAutofillLocked() {
        final AutofillClient client = getClient();
        return client != null && client.isVisibleForAutofill();
    }

    private boolean isClientDisablingEnterExitEvent() {
        final AutofillClient client = getClient();
        return client != null && client.isDisablingEnterExitEventForAutofill();
    }

    private void notifyViewEntered(@NonNull View view, int flags) {
        if (!hasAutofillFeature()) {
            return;
        }
        AutofillCallback callback = null;
        AutofillCallback callback;
        synchronized (mLock) {
            if (shouldIgnoreViewEnteredLocked(view, flags)) return;
            callback = notifyViewEnteredLocked(view, flags);
        }

        if (callback != null) {
            mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
        }
    }

    /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
    private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
        if (shouldIgnoreViewEnteredLocked(view, flags)) return null;

        AutofillCallback callback = null;

        ensureServiceClientAddedIfNeededLocked();

@@ -638,6 +682,8 @@ public final class AutofillManager {
                callback = mCallback;
            }
        } else {
            // don't notify entered when Activity is already in background
            if (!isClientDisablingEnterExitEvent()) {
                final AutofillId id = getAutofillId(view);
                final AutofillValue value = view.getAutofillValue();

@@ -650,10 +696,7 @@ public final class AutofillManager {
                }
            }
        }

        if (callback != null) {
            mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
        }
        return callback;
    }

    /**
@@ -666,9 +709,16 @@ public final class AutofillManager {
            return;
        }
        synchronized (mLock) {
            notifyViewExitedLocked(view);
        }
    }

    void notifyViewExitedLocked(@NonNull View view) {
        ensureServiceClientAddedIfNeededLocked();

        if (mEnabled && isActiveLocked()) {
            // dont notify exited when Activity is already in background
            if (!isClientDisablingEnterExitEvent()) {
                final AutofillId id = getAutofillId(view);

                // Update focus on existing session.
@@ -719,7 +769,7 @@ public final class AutofillManager {
                    }
                }
                if (mTrackedViews != null) {
                    mTrackedViews.notifyViewVisibilityChanged(id, isVisible);
                    mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
                }
            }
        }
@@ -752,9 +802,22 @@ public final class AutofillManager {
        if (!hasAutofillFeature()) {
            return;
        }
        AutofillCallback callback = null;
        AutofillCallback callback;
        synchronized (mLock) {
            if (shouldIgnoreViewEnteredLocked(view, flags)) return;
            callback = notifyViewEnteredLocked(view, virtualId, bounds, flags);
        }

        if (callback != null) {
            callback.onAutofillEvent(view, virtualId,
                    AutofillCallback.EVENT_INPUT_UNAVAILABLE);
        }
    }

    /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
    private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
                                                     int flags) {
        AutofillCallback callback = null;
        if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;

        ensureServiceClientAddedIfNeededLocked();

@@ -763,6 +826,8 @@ public final class AutofillManager {
                callback = mCallback;
            }
        } else {
            // don't notify entered when Activity is already in background
            if (!isClientDisablingEnterExitEvent()) {
                final AutofillId id = getAutofillId(view, virtualId);

                if (!isActiveLocked()) {
@@ -774,11 +839,7 @@ public final class AutofillManager {
                }
            }
        }

        if (callback != null) {
            callback.onAutofillEvent(view, virtualId,
                    AutofillCallback.EVENT_INPUT_UNAVAILABLE);
        }
        return callback;
    }

    /**
@@ -792,9 +853,16 @@ public final class AutofillManager {
            return;
        }
        synchronized (mLock) {
            notifyViewExitedLocked(view, virtualId);
        }
    }

    private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
        ensureServiceClientAddedIfNeededLocked();

        if (mEnabled && isActiveLocked()) {
            // don't notify exited when Activity is already in background
            if (!isClientDisablingEnterExitEvent()) {
                final AutofillId id = getAutofillId(view, virtualId);

                // Update focus on existing session.
@@ -1155,7 +1223,7 @@ public final class AutofillManager {
    }

    /** @hide */
    public void onAuthenticationResult(int authenticationId, Intent data) {
    public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
        if (!hasAutofillFeature()) {
            return;
        }
@@ -1167,9 +1235,24 @@ public final class AutofillManager {
        if (sDebug) Log.d(TAG, "onAuthenticationResult(): d=" + data);

        synchronized (mLock) {
            if (!isActiveLocked() || data == null) {
            if (!isActiveLocked()) {
                return;
            }
            // If authenticate activity closes itself during onCreate(), there is no onStop/onStart
            // of app activity.  We enforce enter event to re-show fill ui in such case.
            // CTS example:
            //     LoginActivityTest#testDatasetAuthTwoFieldsUserCancelsFirstAttempt
            //     LoginActivityTest#testFillResponseAuthBothFieldsUserCancelsFirstAttempt
            if (!mOnInvisibleCalled && focusView != null
                    && focusView.canNotifyAutofillEnterExitEvent()) {
                notifyViewExitedLocked(focusView);
                notifyViewEnteredLocked(focusView, 0);
            }
            if (data == null) {
                // data is set to null when result is not RESULT_OK
                return;
            }

            final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
            final Bundle responseData = new Bundle();
            responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
@@ -1402,6 +1485,9 @@ public final class AutofillManager {
            if (sessionId == mSessionId) {
                final AutofillClient client = getClient();
                if (client != null) {
                    // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
                    // before onAuthenticationResult()
                    mOnInvisibleCalled = false;
                    client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
                }
            }
@@ -1767,6 +1853,7 @@ public final class AutofillManager {
        pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
        pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
        pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
        pw.print(pfx); pw.print("onInvisibleCalled "); pw.println(mOnInvisibleCalled);
        pw.print(pfx); pw.print("last autofilled data: "); pw.println(mLastAutofilledData);
        pw.print(pfx); pw.print("tracked views: ");
        if (mTrackedViews == null) {
@@ -1937,15 +2024,13 @@ public final class AutofillManager {
         * @param id the id of the view/virtual view whose visibility changed.
         * @param isVisible visible if the view is visible in the view hierarchy.
         */
        void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
            AutofillClient client = getClient();

        void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
            if (sDebug) {
                Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
                        + isVisible);
            }

            if (client != null && client.isVisibleForAutofill()) {
            if (isClientVisibleForAutofillLocked()) {
                if (isVisible) {
                    if (isInSet(mInvisibleTrackedIds, id)) {
                        mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
+3 −1
Original line number Diff line number Diff line
@@ -78,8 +78,10 @@ public class AutofillPopupWindow extends PopupWindow {
    public AutofillPopupWindow(@NonNull IAutofillWindowPresenter presenter) {
        mWindowPresenter = new WindowPresenter(presenter);

        setTouchModal(false);
        setOutsideTouchable(true);
        setInputMethodMode(INPUT_METHOD_NEEDED);
        setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
        setFocusable(true);
    }

    @Override
Loading