Revert "Improve AnchoredWindow with smart positioning logic."

This reverts commit eb3c934439.

Change-Id: I6af63525bf72dc9d0dd5a215421fb513e7e635be
This commit is contained in:
Svetoslav Ganov
2017-02-06 04:00:34 +00:00
parent eb3c934439
commit 7b25218f5a
4 changed files with 102 additions and 232 deletions

View File

@@ -520,7 +520,6 @@
<dimen name="item_touch_helper_swipe_escape_max_velocity">800dp</dimen>
<!-- The elevation of AutoFill fill window-->
<dimen name="autofill_fill_elevation">4dp</dimen>
<dimen name="autofill_fill_elevation">2dp</dimen>
<dimen name="autofill_fill_item_height">64dp</dimen>
<dimen name="autofill_fill_min_margin">16dp</dimen>
</resources>

View File

@@ -2833,7 +2833,6 @@
<!-- com.android.server.autofill -->
<java-symbol type="dimen" name="autofill_fill_elevation" />
<java-symbol type="dimen" name="autofill_fill_item_height" />
<java-symbol type="dimen" name="autofill_fill_min_margin" />
<java-symbol type="layout" name="autofill_save"/>
<java-symbol type="id" name="autofill_save_title" />
<java-symbol type="id" name="autofill_save_no" />

View File

@@ -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}.
*
* <p>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);
}
}
}
}

View File

@@ -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