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

Commit a5073ec6 authored by Feng Cao's avatar Feng Cao Committed by Android (Google) Code Review
Browse files

Merge "Use the same UI classes for augmented autofill as the standard android autofill."

parents 7dcdca64 158b6563
Loading
Loading
Loading
Loading
+27 −13
Original line number Original line Diff line number Diff line
@@ -42,6 +42,7 @@ import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAugmentedAutofillManagerClient;
import android.view.autofill.IAugmentedAutofillManagerClient;
import android.view.autofill.IAutofillWindowPresenter;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;


@@ -95,10 +96,10 @@ public abstract class AugmentedAutofillService extends Service {
        }
        }


        @Override
        @Override
        public void onDestroyFillWindowRequest(int sessionId) {
        public void onDestroyAllFillWindowsRequest() {
            mHandler.sendMessage(
            mHandler.sendMessage(
                    obtainMessage(AugmentedAutofillService::handleOnDestroyFillWindowRequest,
                    obtainMessage(AugmentedAutofillService::handleOnDestroyAllFillWindowsRequest,
                            AugmentedAutofillService.this, sessionId));
                            AugmentedAutofillService.this));
        }
        }
    };
    };


@@ -185,18 +186,21 @@ public abstract class AugmentedAutofillService extends Service {
                new FillCallback(proxy));
                new FillCallback(proxy));
    }
    }


    private void handleOnDestroyFillWindowRequest(@NonNull int sessionId) {
    private void handleOnDestroyAllFillWindowsRequest() {
        AutofillProxy proxy = null;
        if (mAutofillProxies != null) {
        if (mAutofillProxies != null) {
            proxy = mAutofillProxies.get(sessionId);
            final int size = mAutofillProxies.size();
        }
            for (int i = 0; i < size; i++) {
                final int sessionId = mAutofillProxies.keyAt(i);
                final AutofillProxy proxy = mAutofillProxies.valueAt(i);
                if (proxy == null) {
                if (proxy == null) {
                    // TODO(b/111330312): this might be fine, in which case we should logv it
                    // TODO(b/111330312): this might be fine, in which case we should logv it
                    Log.w(TAG, "No proxy for session " + sessionId);
                    Log.w(TAG, "No proxy for session " + sessionId);
                    return;
                    return;
                }
                }
                proxy.destroy();
                proxy.destroy();
        mAutofillProxies.remove(sessionId);
            }
            mAutofillProxies.clear();
        }
    }
    }


    private void handleOnUnbind() {
    private void handleOnUnbind() {
@@ -350,6 +354,16 @@ public abstract class AugmentedAutofillService extends Service {
            }
            }
        }
        }


        public void requestShowFillUi(int width, int height, Rect anchorBounds,
                IAutofillWindowPresenter presenter) throws RemoteException {
            mClient.requestShowFillUi(mSessionId, mFocusedId, width, height, anchorBounds,
                    presenter);
        }

        public void requestHideFillUi() throws RemoteException {
            mClient.requestHideFillUi(mSessionId, mFocusedId);
        }

        private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
        private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
            synchronized (mLock) {
            synchronized (mLock) {
                // TODO(b/111330312): should we close the popupwindow if the focused id changed?
                // TODO(b/111330312): should we close the popupwindow if the focused id changed?
+121 −62
Original line number Original line Diff line number Diff line
@@ -16,22 +16,25 @@
package android.service.autofill.augmented;
package android.service.autofill.augmented;


import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
import static android.service.autofill.augmented.AugmentedAutofillService.VERBOSE;

import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;


import android.annotation.LongDef;
import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.TestApi;
import android.app.Dialog;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
import android.service.autofill.augmented.PresentationParams.Area;
import android.service.autofill.augmented.PresentationParams.Area;
import android.util.Log;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager;
import android.view.autofill.IAutofillWindowPresenter;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;
@@ -71,7 +74,7 @@ public final class FillWindow implements AutoCloseable {
    /** Indicates the data being shown is a physical address */
    /** Indicates the data being shown is a physical address */
    public static final long FLAG_METADATA_ADDRESS = 0x1;
    public static final long FLAG_METADATA_ADDRESS = 0x1;


    // TODO(b/111330312): add moar flags
    // TODO(b/111330312): add more flags


    /** @hide */
    /** @hide */
    @LongDef(prefix = { "FLAG" }, value = {
    @LongDef(prefix = { "FLAG" }, value = {
@@ -83,8 +86,17 @@ public final class FillWindow implements AutoCloseable {
    private final Object mLock = new Object();
    private final Object mLock = new Object();
    private final CloseGuard mCloseGuard = CloseGuard.get();
    private final CloseGuard mCloseGuard = CloseGuard.get();


    private final @NonNull Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
    private final @NonNull FillWindowPresenter mFillWindowPresenter = new FillWindowPresenter();

    @GuardedBy("mLock")
    private WindowManager mWm;
    @GuardedBy("mLock")
    private View mFillView;
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private Dialog mDialog;
    private boolean mShowing;
    @GuardedBy("mLock")
    private Rect mBounds;


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private boolean mDestroyed;
    private boolean mDestroyed;
@@ -140,51 +152,28 @@ public final class FillWindow implements AutoCloseable {
            // window instead of destroying. In fact, it might be better to allocate a full window
            // window instead of destroying. In fact, it might be better to allocate a full window
            // initially, which is transparent (and let touches get through) everywhere but in the
            // initially, which is transparent (and let touches get through) everywhere but in the
            // rect boundaries.
            // rect boundaries.
            destroy();


            // TODO(b/111330312): make sure all touch events are handled, window is always closed,
            // TODO(b/111330312): make sure all touch events are handled, window is always closed,
            // etc.
            // etc.


            mDialog = new Dialog(rootView.getContext()) {
            mWm = rootView.getContext().getSystemService(WindowManager.class);
                @Override
            mFillView = rootView;
                public boolean onTouchEvent(MotionEvent event) {
            // Listen to the touch outside to destroy the window when typing is detected.
                    if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
            mFillView.setOnTouchListener(
                        FillWindow.this.destroy();
                    (view, motionEvent) -> {
                        if (motionEvent.getAction() == MotionEvent.ACTION_OUTSIDE) {
                            if (VERBOSE) Log.v(TAG, "Outside touch detected, hiding the window");
                            hide();
                        }
                        }
                        return false;
                        return false;
                    }
                    }
            };
            );
            mCloseGuard.open("destroy");
            mShowing = false;
            final Window window = mDialog.getWindow();
            mBounds = new Rect(area.getBounds());
            window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
            // Makes sure touch outside the dialog is received by the window behind the dialog.
            window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
            // Makes sure the touch outside the dialog is received by the dialog to dismiss it.
            window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
            // Makes sure keyboard shows up.
            window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);

            final int height = rect.bottom - rect.top;
            final int width = rect.right - rect.left;
            final WindowManager.LayoutParams windowParams = window.getAttributes();
            windowParams.gravity = Gravity.TOP | Gravity.LEFT;
            windowParams.y = rect.top + height;
            windowParams.height = height;
            windowParams.x = rect.left;
            windowParams.width = width;

            window.setAttributes(windowParams);
            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
            window.setBackgroundDrawableResource(android.R.color.transparent);

            mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
            final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
            mDialog.setContentView(rootView, diagParams);

            if (DEBUG) {
            if (DEBUG) {
                Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
                Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
            }
            }

            mDestroyed = false;
            mProxy.setFillWindow(this);
            mProxy.setFillWindow(this);
            return true;
            return true;
        }
        }
@@ -194,36 +183,87 @@ public final class FillWindow implements AutoCloseable {
    void show() {
    void show() {
        // TODO(b/111330312): check if updated first / throw exception
        // TODO(b/111330312): check if updated first / throw exception
        if (DEBUG) Log.d(TAG, "show()");
        if (DEBUG) Log.d(TAG, "show()");

        synchronized (mLock) {
        synchronized (mLock) {
            checkNotDestroyedLocked();
            checkNotDestroyedLocked();
            if (mDialog == null) {
            if (mWm == null || mFillView == null) {
                throw new IllegalStateException("update() not called yet, or already destroyed()");
                throw new IllegalStateException("update() not called yet, or already destroyed()");
            }
            }

            mDialog.show();
            if (mProxy != null) {
            if (mProxy != null) {
                try {
                    mProxy.requestShowFillUi(mBounds.right - mBounds.left,
                            mBounds.bottom - mBounds.top,
                            /*anchorBounds=*/ null, mFillWindowPresenter);
                } catch (RemoteException e) {
                    Log.w(TAG, "Error requesting to show fill window", e);
                }
                mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
                mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
            }
            }
        }
        }
    }
    }


    /**
     * Hides the window.
     *
     * <p>The window is not destroyed and can be shown again
     */
    private void hide() {
        if (DEBUG) Log.d(TAG, "hide()");
        synchronized (mLock) {
            checkNotDestroyedLocked();
            if (mWm == null || mFillView == null) {
                throw new IllegalStateException("update() not called yet, or already destroyed()");
            }
            if (mProxy != null && mShowing) {
                try {
                    mProxy.requestHideFillUi();
                } catch (RemoteException e) {
                    Log.w(TAG, "Error requesting to hide fill window", e);
                }
            }
        }
    }

    private void handleShow(WindowManager.LayoutParams p) {
        if (DEBUG) Log.d(TAG, "handleShow()");
        synchronized (mLock) {
            if (mWm != null && mFillView != null) {
                p.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
                if (!mShowing) {
                    mWm.addView(mFillView, p);
                    mShowing = true;
                } else {
                    mWm.updateViewLayout(mFillView, p);
                }
            }
        }
    }

    private void handleHide() {
        if (DEBUG) Log.d(TAG, "handleHide()");
        synchronized (mLock) {
            if (mWm != null && mFillView != null && mShowing) {
                mWm.removeView(mFillView);
                mShowing = false;
            }
        }
    }

    /**
    /**
     * Destroys the window.
     * Destroys the window.
     *
     *
     * <p>Once destroyed, this window cannot be used anymore
     * <p>Once destroyed, this window cannot be used anymore
     */
     */
    public void destroy() {
    public void destroy() {
        if (DEBUG) Log.d(TAG, "destroy(): mDestroyed=" + mDestroyed + " mDialog=" + mDialog);
        if (DEBUG) {

            Log.d(TAG,
        synchronized (this) {
                    "destroy(): mDestroyed=" + mDestroyed + " mShowing=" + mShowing + " mFillView="
            if (mDestroyed || mDialog == null) return;
                            + mFillView);

            mDialog.dismiss();
            mDialog = null;
            if (mProxy != null) {
                mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
        }
        }
        synchronized (mLock) {
            if (mDestroyed) return;
            hide();
            mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
            mDestroyed = true;
            mCloseGuard.close();
            mCloseGuard.close();
        }
        }
    }
    }
@@ -250,11 +290,15 @@ public final class FillWindow implements AutoCloseable {
    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
        synchronized (this) {
        synchronized (this) {
            pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
            pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
            if (mDialog != null) {
            if (mFillView != null) {
                pw.print(prefix); pw.print("dialog: ");
                pw.print(prefix); pw.print("fill window: ");
                pw.println(mDialog.isShowing() ? "shown" : "hidden");
                pw.println(mShowing ? "shown" : "hidden");
                pw.print(prefix); pw.print("window: ");
                pw.print(prefix); pw.print("fill view: ");
                pw.println(mDialog.getWindow().getAttributes());
                pw.println(mFillView);
                pw.print(prefix); pw.print("mBounds: ");
                pw.println(mBounds);
                pw.print(prefix); pw.print("mWm: ");
                pw.println(mWm);
            }
            }
        }
        }
    }
    }
@@ -264,4 +308,19 @@ public final class FillWindow implements AutoCloseable {
    public void close() throws Exception {
    public void close() throws Exception {
        destroy();
        destroy();
    }
    }

    private final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
        @Override
        public void show(WindowManager.LayoutParams p, Rect transitionEpicenter,
                boolean fitsSystemWindows, int layoutDirection) {
            if (DEBUG) Log.d(TAG, "FillWindowPresenter.show()");
            mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleShow, FillWindow.this, p));
        }

        @Override
        public void hide(Rect transitionEpicenter) {
            if (DEBUG) Log.d(TAG, "FillWindowPresenter.hide()");
            mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleHide, FillWindow.this));
        }
    }
}
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -36,5 +36,5 @@ oneway interface IAugmentedAutofillService {
                       in ComponentName activityComponent, in AutofillId focusedId,
                       in ComponentName activityComponent, in AutofillId focusedId,
                       in AutofillValue focusedValue, long requestTime, in IFillCallback callback);
                       in AutofillValue focusedValue, long requestTime, in IFillCallback callback);


    void onDestroyFillWindowRequest(int sessionId);
    void onDestroyAllFillWindowsRequest();
}
}
+18 −0
Original line number Original line Diff line number Diff line
@@ -2997,5 +2997,23 @@ public final class AutofillManager {
                afm.post(() -> afm.autofill(sessionId, ids, values));
                afm.post(() -> afm.autofill(sessionId, ids, values));
            }
            }
        }
        }

        @Override
        public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
                Rect anchorBounds, IAutofillWindowPresenter presenter) {
            final AutofillManager afm = mAfm.get();
            if (afm != null) {
                afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
                        presenter));
            }
        }

        @Override
        public void requestHideFillUi(int sessionId, AutofillId id) {
            final AutofillManager afm = mAfm.get();
            if (afm != null) {
                afm.post(() -> afm.requestHideFillUi(id, false));
            }
        }
    }
    }
}
}
+21 −2
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import java.util.List;
import android.graphics.Rect;
import android.graphics.Rect;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;


/**
/**
 * Object running in the application process and responsible to provide the functionalities
 * Object running in the application process and responsible to provide the functionalities
@@ -29,6 +30,24 @@ import android.view.autofill.AutofillValue;
 * @hide
 * @hide
 */
 */
interface IAugmentedAutofillManagerClient {
interface IAugmentedAutofillManagerClient {
    /**
      * Gets the coordinates of the input field view.
      */
    Rect getViewCoordinates(in AutofillId id);
    Rect getViewCoordinates(in AutofillId id);

    /**
     * Autofills the activity with the contents of the values.
     */
    void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
    void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);

    /**
      * Requests showing the fill UI.
      */
    void requestShowFillUi(int sessionId, in AutofillId id, int width, int height,
            in Rect anchorBounds, in IAutofillWindowPresenter presenter);

    /**
      * Requests hiding the fill UI.
      */
    void requestHideFillUi(int sessionId, in AutofillId id);
}
}
Loading