diff options
9 files changed, 205 insertions, 81 deletions
| diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index 01169612a201..aaba85bd36a7 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -42,6 +42,7 @@ import android.util.TimeUtils;  import android.view.autofill.AutofillId;  import android.view.autofill.AutofillValue;  import android.view.autofill.IAugmentedAutofillManagerClient; +import android.view.autofill.IAutofillWindowPresenter;  import com.android.internal.annotations.GuardedBy; @@ -95,10 +96,10 @@ public abstract class AugmentedAutofillService extends Service {          }          @Override -        public void onDestroyFillWindowRequest(int sessionId) { +        public void onDestroyAllFillWindowsRequest() {              mHandler.sendMessage( -                    obtainMessage(AugmentedAutofillService::handleOnDestroyFillWindowRequest, -                            AugmentedAutofillService.this, sessionId)); +                    obtainMessage(AugmentedAutofillService::handleOnDestroyAllFillWindowsRequest, +                            AugmentedAutofillService.this));          }      }; @@ -185,18 +186,21 @@ public abstract class AugmentedAutofillService extends Service {                  new FillCallback(proxy));      } -    private void handleOnDestroyFillWindowRequest(@NonNull int sessionId) { -        AutofillProxy proxy = null; +    private void handleOnDestroyAllFillWindowsRequest() {          if (mAutofillProxies != null) { -            proxy = mAutofillProxies.get(sessionId); -        } -        if (proxy == null) { -            // TODO(b/111330312): this might be fine, in which case we should logv it -            Log.w(TAG, "No proxy for session " + sessionId); -            return; +            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) { +                    // TODO(b/111330312): this might be fine, in which case we should logv it +                    Log.w(TAG, "No proxy for session " + sessionId); +                    return; +                } +                proxy.destroy(); +            } +            mAutofillProxies.clear();          } -        proxy.destroy(); -        mAutofillProxies.remove(sessionId);      }      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) {              synchronized (mLock) {                  // TODO(b/111330312): should we close the popupwindow if the focused id changed? diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java index 33b88e42edb7..51b0f01af6ae 100644 --- a/core/java/android/service/autofill/augmented/FillWindow.java +++ b/core/java/android/service/autofill/augmented/FillWindow.java @@ -16,22 +16,25 @@  package android.service.autofill.augmented;  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.NonNull;  import android.annotation.SystemApi;  import android.annotation.TestApi; -import android.app.Dialog;  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.PresentationParams.Area;  import android.util.Log; -import android.view.Gravity;  import android.view.MotionEvent;  import android.view.View; -import android.view.ViewGroup; -import android.view.Window;  import android.view.WindowManager; +import android.view.autofill.IAutofillWindowPresenter;  import com.android.internal.annotations.GuardedBy;  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 */      public static final long FLAG_METADATA_ADDRESS = 0x1; -    // TODO(b/111330312): add moar flags +    // TODO(b/111330312): add more flags      /** @hide */      @LongDef(prefix = { "FLAG" }, value = { @@ -83,8 +86,17 @@ public final class FillWindow implements AutoCloseable {      private final Object mLock = new Object();      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 Dialog mDialog; +    private View mFillView; +    @GuardedBy("mLock") +    private boolean mShowing; +    @GuardedBy("mLock") +    private Rect mBounds;      @GuardedBy("mLock")      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              // initially, which is transparent (and let touches get through) everywhere but in the              // rect boundaries. -            destroy();              // TODO(b/111330312): make sure all touch events are handled, window is always closed,              // etc. -            mDialog = new Dialog(rootView.getContext()) { -                @Override -                public boolean onTouchEvent(MotionEvent event) { -                    if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { -                        FillWindow.this.destroy(); +            mWm = rootView.getContext().getSystemService(WindowManager.class); +            mFillView = rootView; +            // Listen to the touch outside to destroy the window when typing is detected. +            mFillView.setOnTouchListener( +                    (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"); -            final Window window = mDialog.getWindow(); -            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); - +            ); +            mShowing = false; +            mBounds = new Rect(area.getBounds());              if (DEBUG) {                  Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);              } - +            mDestroyed = false;              mProxy.setFillWindow(this);              return true;          } @@ -194,36 +183,87 @@ public final class FillWindow implements AutoCloseable {      void show() {          // TODO(b/111330312): check if updated first / throw exception          if (DEBUG) Log.d(TAG, "show()"); -          synchronized (mLock) {              checkNotDestroyedLocked(); -            if (mDialog == null) { +            if (mWm == null || mFillView == null) {                  throw new IllegalStateException("update() not called yet, or already destroyed()");              } - -            mDialog.show();              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);              }          }      }      /** -     * Destroys the window. +     * Hides the window.       * -     * <p>Once destroyed, this window cannot be used anymore +     * <p>The window is not destroyed and can be shown again       */ -    public void destroy() { -        if (DEBUG) Log.d(TAG, "destroy(): mDestroyed=" + mDestroyed + " mDialog=" + mDialog); +    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); +                } +            } +        } +    } -        synchronized (this) { -            if (mDestroyed || mDialog == null) return; +    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); +                } +            } +        } +    } -            mDialog.dismiss(); -            mDialog = null; -            if (mProxy != null) { -                mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED); +    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. +     * +     * <p>Once destroyed, this window cannot be used anymore +     */ +    public void destroy() { +        if (DEBUG) { +            Log.d(TAG, +                    "destroy(): mDestroyed=" + mDestroyed + " mShowing=" + mShowing + " mFillView=" +                            + mFillView); +        } +        synchronized (mLock) { +            if (mDestroyed) return; +            hide(); +            mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED); +            mDestroyed = true;              mCloseGuard.close();          }      } @@ -250,11 +290,15 @@ public final class FillWindow implements AutoCloseable {      public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {          synchronized (this) {              pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed); -            if (mDialog != null) { -                pw.print(prefix); pw.print("dialog: "); -                pw.println(mDialog.isShowing() ? "shown" : "hidden"); -                pw.print(prefix); pw.print("window: "); -                pw.println(mDialog.getWindow().getAttributes()); +            if (mFillView != null) { +                pw.print(prefix); pw.print("fill window: "); +                pw.println(mShowing ? "shown" : "hidden"); +                pw.print(prefix); pw.print("fill view: "); +                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 {          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)); +        } +    }  } diff --git a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl index b3ac2da1c17e..fb6912ac6752 100644 --- a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl +++ b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl @@ -36,5 +36,5 @@ oneway interface IAugmentedAutofillService {                         in ComponentName activityComponent, in AutofillId focusedId,                         in AutofillValue focusedValue, long requestTime, in IFillCallback callback); -    void onDestroyFillWindowRequest(int sessionId); +    void onDestroyAllFillWindowsRequest();  } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 93941d0d6edb..888a4c57751e 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -2997,5 +2997,23 @@ public final class AutofillManager {                  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)); +            } +        }      }  } diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl index 67cd0bf87b99..140507c80ed0 100644 --- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl +++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl @@ -21,6 +21,7 @@ import java.util.List;  import android.graphics.Rect;  import android.view.autofill.AutofillId;  import android.view.autofill.AutofillValue; +import android.view.autofill.IAutofillWindowPresenter;  /**   * Object running in the application process and responsible to provide the functionalities @@ -29,6 +30,24 @@ import android.view.autofill.AutofillValue;   * @hide   */  interface IAugmentedAutofillManagerClient { -   Rect getViewCoordinates(in AutofillId id); -   void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values); +    /** +      * Gets the coordinates of the input field view. +      */ +    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); + +    /** +      * 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);  } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 24fd7b9ae71d..ec6d20dd5c6a 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -701,6 +701,11 @@ public final class AutofillManagerService          public void onBackKeyPressed() {              if (sDebug) Slog.d(TAG, "onBackKeyPressed()");              mUi.hideAll(null); +            synchronized (mLock) { +                final AutofillManagerServiceImpl service = +                        getServiceForUserLocked(UserHandle.getCallingUserId()); +                service.onBackKeyPressed(); +            }          }          @Override diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index a6bb049602a0..d037b081cd4d 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -187,6 +187,15 @@ final class AutofillManagerServiceImpl      }      @GuardedBy("mLock") +    void onBackKeyPressed() { +        final RemoteAugmentedAutofillService remoteService = +                getRemoteAugmentedAutofillServiceLocked(); +        if (remoteService != null) { +            remoteService.onDestroyAutofillWindowsRequest(); +        } +    } + +    @GuardedBy("mLock")      @Override // from PerUserSystemService      protected boolean updateLocked(boolean disabled) {          destroySessionsLocked(); diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index a8ff9b0d5a3f..5d8d8fa46d3f 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -109,8 +109,8 @@ final class RemoteAugmentedAutofillService      /**       * Called by {@link Session} when it's time to destroy all augmented autofill requests.       */ -    public void onDestroyAutofillWindowsRequest(int sessionId) { -        scheduleAsyncRequest((s) -> s.onDestroyFillWindowRequest(sessionId)); +    public void onDestroyAutofillWindowsRequest() { +        scheduleAsyncRequest((s) -> s.onDestroyAllFillWindowsRequest());      }      // TODO(b/111330312): inline into PendingAutofillRequest if it doesn't have any other subclass diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index cf4963c1bebf..a5ef21afc23e 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2619,7 +2619,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                  currentValue);          if (mAugmentedAutofillDestroyer == null) { -            mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest(id); +            mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();          }          return mAugmentedAutofillDestroyer;      } |