diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index c2cd895b5e3cc..927988f712832 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -520,7 +520,6 @@
800dp
- 4dp
+ 2dp
64dp
- 16dp
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 227d8b61764bb..571aa17304849 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2833,7 +2833,6 @@
-
diff --git a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
index e53165d4b567e..c68ac60bb26bb 100644
--- a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
+++ b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
@@ -17,277 +17,134 @@ package com.android.server.autofill;
import static com.android.server.autofill.Helper.DEBUG;
-import android.annotation.Nullable;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.os.IBinder;
import android.util.Slog;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
-import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
+import java.io.PrintWriter;
/**
* A window above the application that is smartly anchored to a rectangular region.
*/
-final class AnchoredWindow implements View.OnLayoutChangeListener, View.OnTouchListener {
+final class AnchoredWindow {
private static final String TAG = "AutoFill";
- private static final int NULL_HEIGHT = -1;
-
private final WindowManager mWm;
- private final IBinder mAppToken;
- private final View mContentView;
-
- private final View mWindowSizeListenerView;
- private final int mMinMargin;
-
- private int mLastHeight = NULL_HEIGHT;
- @Nullable
- private Rect mLastBounds;
- @Nullable
- private Rect mLastDisplayBounds;
+ private final View mRootView;
+ private final View mView;
+ private final int mWidth;
+ private final int mHeight;
+ private boolean mIsShowing = false;
/**
* Constructor.
*
- * @param wm window manager that draws the content on a window
- * @param appToken token to pass to window manager
- * @param contentView content of the window
+ * @param wm window manager that draws the view on a window
+ * @param view singleton view in the window
+ * @param width requested width of the view
+ * @param height requested height of the view
*/
- AnchoredWindow(WindowManager wm, IBinder appToken, View contentView) {
+ AnchoredWindow(WindowManager wm, View view, int width, int height) {
mWm = wm;
- mAppToken = appToken;
- mContentView = contentView;
-
- mContentView.addOnLayoutChangeListener(this);
-
- Context context = contentView.getContext();
-
- mWindowSizeListenerView = new FrameLayout(context);
- mWindowSizeListenerView.addOnLayoutChangeListener(this);
-
- mMinMargin = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.autofill_fill_min_margin);
+ mRootView = wrapView(view, width, height);
+ mView = view;
+ mWidth = width;
+ mHeight = height;
}
/**
* Shows the window.
*
- * @param bounds the region the window should be anchored to
+ * @param bounds the rectangular region this window should be anchored to
*/
void show(Rect bounds) {
- if (DEBUG) Slog.d(TAG, "show bounds=" + bounds);
+ final LayoutParams params = createBaseLayoutParams();
+ params.x = bounds.left;
+ params.y = bounds.bottom;
- if (!mWindowSizeListenerView.isAttachedToWindow()) {
- if (DEBUG) Slog.d(TAG, "adding mWindowSizeListenerView");
- LayoutParams params = createWindowLayoutParams(
- mAppToken,
- LayoutParams.FLAG_NOT_TOUCHABLE); // not touchable
- params.gravity = Gravity.LEFT | Gravity.TOP;
- params.x = 0;
- params.y = 0;
- params.width = LayoutParams.MATCH_PARENT;
- params.height = LayoutParams.MATCH_PARENT;
- mWm.addView(mWindowSizeListenerView, params);
+ if (!mIsShowing) {
+ if (DEBUG) Slog.d(TAG, "adding view " + mView);
+ mWm.addView(mRootView, params);
+ } else {
+ if (DEBUG) Slog.d(TAG, "updating view " + mView);
+ mWm.updateViewLayout(mRootView, params);
}
-
- updateBounds(bounds);
+ mIsShowing = true;
}
/**
* Hides the window.
*/
void hide() {
- if (DEBUG) Slog.d(TAG, "hide");
+ if (DEBUG) Slog.d(TAG, "removing view " + mView);
- mLastHeight = NULL_HEIGHT;
- mLastBounds = null;
- mLastDisplayBounds = null;
-
- if (mWindowSizeListenerView.isAttachedToWindow()) {
- if (DEBUG) Slog.d(TAG, "removing mWindowSizeListenerView");
- mWm.removeView(mWindowSizeListenerView);
- }
-
- if (mContentView.isAttachedToWindow()) {
- if (DEBUG) Slog.d(TAG, "removing mContentView");
- mContentView.setOnTouchListener(null);
- mWm.removeView(mContentView);
- }
- }
-
- @Override
- public void onLayoutChange(View view, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (view == mWindowSizeListenerView) {
- if (DEBUG) Slog.d(TAG, "onLayoutChange() for mWindowSizeListenerView");
- // mWindowSizeListenerView layout changed, get the size of the display bounds and update
- // the window.
- final Rect displayBounds = new Rect();
- view.getBoundsOnScreen(displayBounds);
- updateDisplayBounds(displayBounds);
- } else if (view == mContentView) {
- // mContentView layout changed, update the window in case its height changed.
- if (DEBUG) Slog.d(TAG, "onLayoutChange() for mContentView");
- updateHeight();
- }
- }
-
- // When the window is touched outside, hide the window.
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- if (view == mContentView && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- hide();
- return true;
- }
- return false;
- }
-
- private boolean updateHeight() {
- final Rect displayBounds = mLastDisplayBounds;
- if (displayBounds == null) {
- return false;
- }
-
- mContentView.measure(
- MeasureSpec.makeMeasureSpec(displayBounds.width(), MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(displayBounds.height(), MeasureSpec.AT_MOST));
- int height = mContentView.getMeasuredHeight();
- if (height != mLastHeight) {
- if (DEBUG) Slog.d(TAG, "update height=" + height);
- mLastHeight = height;
- update(height, mLastBounds, displayBounds);
- return true;
- } else {
- return false;
- }
- }
-
- private void updateBounds(Rect bounds) {
- if (!bounds.equals(mLastBounds)) {
- if (DEBUG) Slog.d(TAG, "update bounds=" + bounds);
- mLastBounds = bounds;
-
- update(mLastHeight, bounds, mLastDisplayBounds);
- }
- }
-
- private void updateDisplayBounds(Rect displayBounds) {
- if (!displayBounds.equals(mLastDisplayBounds)) {
- if (DEBUG) Slog.d(TAG, "update displayBounds=" + displayBounds);
- mLastDisplayBounds = displayBounds;
-
- if (!updateHeight()) {
- update(mLastHeight, mLastBounds, displayBounds);
- }
- }
- }
-
- // Updates the window if height, bounds, and displayBounds are not null.
- // Caller should ensure that something changed before calling.
- private void update(int height, @Nullable Rect bounds, @Nullable Rect displayBounds) {
- if (height == NULL_HEIGHT || bounds == null || displayBounds == null) {
- return;
- }
-
- if (DEBUG) Slog.d(TAG, "update height=" + height + ", bounds=" + bounds
- + ", displayBounds=" + displayBounds);
-
- final LayoutParams params = createWindowLayoutParams(mAppToken,
- LayoutParams.FLAG_NOT_TOUCH_MODAL // outside touches go to windows behind us
- | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); // outside touches trigger MotionEvent
- params.setTitle("AutoFill Fill"); // used for debugging
- updatePosition(params, height, mMinMargin, bounds, displayBounds);
- if (!mContentView.isAttachedToWindow()) {
- if (DEBUG) Slog.d(TAG, "adding mContentView");
- mWm.addView(mContentView, params);
- mContentView.setOnTouchListener(this);
- } else {
- if (DEBUG) Slog.d(TAG, "updating mContentView");
- mWm.updateViewLayout(mContentView, params);
+ if (mIsShowing) {
+ mWm.removeView(mRootView);
}
+ mIsShowing = false;
}
/**
- * Updates the position of the window by altering the {@link LayoutParams}.
- *
- *
The window can be anchored either above or below the bounds. Anchoring the window below
- * the bounds is preferred, if it fits. Otherwise, anchor the window on the side with more
- * space.
- *
- * @param params the params to update
- * @param height the requested height of the window
- * @param minMargin the minimum margin between the window and the display bounds
- * @param bounds the region the window should be anchored to
- * @param displayBounds the region in which the window may be displayed
+ * Wraps a view with a SelfRemovingView and sets its requested width and height.
*/
- private static void updatePosition(
- LayoutParams params,
- int height,
- int minMargin,
- Rect bounds,
- Rect displayBounds) {
- boolean below;
- int verticalSpace;
- final int verticalSpaceBelow = displayBounds.bottom - bounds.bottom - minMargin;
- if (height <= verticalSpaceBelow) {
- // Fits below bounds.
- below = true;
- verticalSpace = height;
- } else {
- final int verticalSpaceAbove = bounds.top - displayBounds.top - minMargin;
- if (height <= verticalSpaceAbove) {
- // Fits above bounds.
- below = false;
- verticalSpace = height;
- } else {
- // Pick above/below based on which has the most space.
- if (verticalSpaceBelow >= verticalSpaceAbove) {
- below = true;
- verticalSpace = verticalSpaceBelow;
- } else {
- below = false;
- verticalSpace = verticalSpaceAbove;
- }
- }
- }
-
- int gravity;
- int y;
- if (below) {
- if (DEBUG) Slog.d(TAG, "anchorBelow");
- gravity = Gravity.TOP | Gravity.LEFT;
- y = bounds.bottom - displayBounds.top;
- } else {
- if (DEBUG) Slog.d(TAG, "anchorAbove");
- gravity = Gravity.BOTTOM | Gravity.LEFT;
- y = displayBounds.bottom - bounds.top;
- }
-
- final int x = bounds.left - displayBounds.left;
-
- params.gravity = gravity;
- params.x = x;
- params.y = y;
- params.width = bounds.width();
- params.height = verticalSpace;
+ private View wrapView(View view, int width, int height) {
+ final ViewGroup viewGroup = new SelfRemovingView(view.getContext());
+ viewGroup.addView(view, new ViewGroup.LayoutParams(width, height));
+ return viewGroup;
}
- private static LayoutParams createWindowLayoutParams(IBinder appToken, int flags) {
+ private static LayoutParams createBaseLayoutParams() {
final LayoutParams params = new LayoutParams();
- params.token = appToken;
- params.type = LayoutParams.TYPE_PHONE;
+ // TODO(b/33197203): LayoutParams.TYPE_AUTOFILL
+ params.type = LayoutParams.TYPE_SYSTEM_ALERT;
params.flags =
- flags
- | LayoutParams.FLAG_NOT_FOCUSABLE // don't receive input events
- | LayoutParams.FLAG_ALT_FOCUSABLE_IM; // resize for soft input
- params.format = PixelFormat.TRANSLUCENT;
+ LayoutParams.SOFT_INPUT_STATE_UNCHANGED
+ | LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | LayoutParams.FLAG_NOT_FOCUSABLE
+ | LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+ params.gravity = Gravity.TOP | Gravity.LEFT;
+ params.width = LayoutParams.WRAP_CONTENT;
+ params.height = LayoutParams.WRAP_CONTENT;
return params;
}
+
+ @Override
+ public String toString() {
+ if (!DEBUG) return super.toString();
+
+ return "AnchoredWindow: [width=" + mWidth + ", height=" + mHeight + ", view=" + mView + "]";
+ }
+
+ void dump(PrintWriter pw) {
+ pw.println("Anchored Window");
+ final String prefix = " ";
+ pw.print(prefix); pw.print("width: "); pw.println(mWidth);
+ pw.print(prefix); pw.print("height: "); pw.println(mHeight);
+ pw.print(prefix); pw.print("visible: "); pw.println(mIsShowing);
+ }
+
+ /** FrameLayout that listens for touch events removes itself if the touch event is outside. */
+ private final class SelfRemovingView extends FrameLayout {
+ public SelfRemovingView(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ hide();
+ return true;
+ } else {
+ return super.onTouchEvent(event);
+ }
+ }
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
index 73f488d39f154..7d82bec4996c7 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -71,6 +71,8 @@ final class AutoFillUI {
private AnchoredWindow mFillWindow;
private DatasetPicker mFillView;
private ViewState mViewState;
+ private Rect mBounds;
+ private String mFilterText;
/**
* Custom snackbar UI used for saving autofill or other informational messages.
@@ -115,6 +117,8 @@ final class AutoFillUI {
}
mViewState = null;
+ mBounds = null;
+ mFilterText = null;
mFillView = null;
mFillWindow = null;
}
@@ -142,14 +146,23 @@ final class AutoFillUI {
mSession.autoFillApp(dataset);
hideFillUi();
});
- mFillWindow = new AnchoredWindow(mWm, mAppToken, mFillView);
+ mFillWindow = new AnchoredWindow(
+ mWm, mFillView, 800, ViewGroup.LayoutParams.WRAP_CONTENT);
- if (DEBUG) Slog.d(TAG, "showFillUi(): view changed");
+ if (DEBUG) Slog.d(TAG, "show FillUi");
}
- if (DEBUG) Slog.d(TAG, "showFillUi(): bounds=" + bounds + ", filterText=" + filterText);
- mFillView.update(filterText);
- mFillWindow.show(bounds);
+ if (!bounds.equals(mBounds)) {
+ if (DEBUG) Slog.d(TAG, "update FillUi bounds: " + mBounds);
+ mBounds = bounds;
+ mFillWindow.show(mBounds);
+ }
+
+ if (!filterText.equals(mFilterText)) {
+ if (DEBUG) Slog.d(TAG, "update FillUi filter text: " + mFilterText);
+ mFilterText = filterText;
+ mFillView.update(mFilterText);
+ }
}, 0);
}
@@ -238,6 +251,8 @@ final class AutoFillUI {
pw.print(prefix); pw.print("mSessionId: "); pw.println(mSession.mId);
pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar);
pw.print(prefix); pw.print("mViewState: "); pw.println(mViewState);
+ pw.print(prefix); pw.print("mBounds: "); pw.println(mBounds);
+ pw.print(prefix); pw.print("mFilterText: "); pw.println(mFilterText);
}
//similar to a snackbar, but can be a bit custom since it is more than just text. This will