summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYohei Yukawa <yukawa@google.com>2015-07-14 05:59:05 -0700
committerYohei Yukawa <yukawa@google.com>2015-07-14 05:59:05 -0700
commitbafc908304d49e8f7f7c5e52772c75da66e4daa3 (patch)
tree90556fb0473be16b8a91bcd8380ac1deff6273b3
parent9de5dcac9589d064414a2ff6f3b76dc40c1b8765 (diff)
downloadframeworks_base-bafc908304d49e8f7f7c5e52772c75da66e4daa3.zip
frameworks_base-bafc908304d49e8f7f7c5e52772c75da66e4daa3.tar.gz
frameworks_base-bafc908304d49e8f7f7c5e52772c75da66e4daa3.tar.bz2
Allow FloatingToolbar to be outside of the attached window.
Currently PopupWindow used for the floating toolbar specifies neither FLAG_LAYOUT_NO_LIMITS nor FLAG_LAYOUT_IN_SCREEN. As a result, the floating toolbar can overlap the selected text when the attached window does not have enough height. Here is the repro code. final TextView textView = new TextView(this); textView.setLayoutParams( new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); textView.setText("A test sentence."); textView.setTextIsSelectable(true); final AlertDialog dialog = new AlertDialog.Builder(this) .setView(textView) .create(); dialog.getWindow().setGravity(Gravity.BOTTOM) dialog.show(); If you tap a word in the dialog, the floating toolbar unintentionally overlaps the selected text due to the limited height of the AlertDialog. It also turns out that just calling PopupWindow.setClippingEnabled(false) to specify FLAG_LAYOUT_NO_LIMITS is not sufficient and ends up showing the toolbar on the NavBar because we have mistakenly compared bounds in window-local coordinates (e.g. FloatingActionModemContentRectOnWindow) with bounds in screen coordinates (e.g. FloatingActionMode#mScreenRect). Hence the confusion of window-local coordinates and screen coordinates in FloatingToolbar and FloatingToolbar also needs to be addresses. To summarize here are the notable changes in this CL: - Specify FLAG_LAYOUT_NO_LIMITS so that the floating toolbar can be placed outside of the attached window. (We do this with PopupWindow#setClippingEnabled) - Switch to use screen coordinates from window-local coordiantes in FloatingToolbar and FloatingActionMode because some system components such as WindowManager prefer screen coordinates. - Put -OnScreen suffix for Rect and Point variables as long as they are in screen coordinates. Bug: 22335001 Change-Id: I71a8d356e868dc7715b030ca1078da4ec39368c3
-rw-r--r--core/java/com/android/internal/view/FloatingActionMode.java78
-rw-r--r--core/java/com/android/internal/widget/FloatingToolbar.java87
2 files changed, 91 insertions, 74 deletions
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index ef2fef0..41628d0 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -41,13 +41,13 @@ public class FloatingActionMode extends ActionMode {
private final ActionMode.Callback2 mCallback;
private final MenuBuilder mMenu;
private final Rect mContentRect;
- private final Rect mContentRectOnWindow;
- private final Rect mPreviousContentRectOnWindow;
- private final int[] mViewPosition;
- private final int[] mPreviousViewPosition;
- private final int[] mRootViewPosition;
- private final Rect mViewRect;
- private final Rect mPreviousViewRect;
+ private final Rect mContentRectOnScreen;
+ private final Rect mPreviousContentRectOnScreen;
+ private final int[] mViewPositionOnScreen;
+ private final int[] mPreviousViewPositionOnScreen;
+ private final int[] mRootViewPositionOnScreen;
+ private final Rect mViewRectOnScreen;
+ private final Rect mPreviousViewRectOnScreen;
private final Rect mScreenRect;
private final View mOriginatingView;
private final int mBottomAllowance;
@@ -77,16 +77,16 @@ public class FloatingActionMode extends ActionMode {
MenuItem.SHOW_AS_ACTION_IF_ROOM);
setType(ActionMode.TYPE_FLOATING);
mContentRect = new Rect();
- mContentRectOnWindow = new Rect();
- mPreviousContentRectOnWindow = new Rect();
- mViewPosition = new int[2];
- mPreviousViewPosition = new int[2];
- mRootViewPosition = new int[2];
- mViewRect = new Rect();
- mPreviousViewRect = new Rect();
+ mContentRectOnScreen = new Rect();
+ mPreviousContentRectOnScreen = new Rect();
+ mViewPositionOnScreen = new int[2];
+ mPreviousViewPositionOnScreen = new int[2];
+ mRootViewPositionOnScreen = new int[2];
+ mViewRectOnScreen = new Rect();
+ mPreviousViewRectOnScreen = new Rect();
mScreenRect = new Rect();
mOriginatingView = Preconditions.checkNotNull(originatingView);
- mOriginatingView.getLocationInWindow(mViewPosition);
+ mOriginatingView.getLocationOnScreen(mViewPositionOnScreen);
// Allow the content rect to overshoot a little bit beyond the
// bottom view bound if necessary.
mBottomAllowance = context.getResources()
@@ -138,52 +138,53 @@ public class FloatingActionMode extends ActionMode {
public void updateViewLocationInWindow() {
checkToolbarInitialized();
- mOriginatingView.getLocationInWindow(mViewPosition);
- mOriginatingView.getRootView().getLocationInWindow(mRootViewPosition);
- mOriginatingView.getGlobalVisibleRect(mViewRect);
- mViewRect.offset(mRootViewPosition[0], mRootViewPosition[1]);
+ mOriginatingView.getLocationOnScreen(mViewPositionOnScreen);
+ mOriginatingView.getRootView().getLocationOnScreen(mRootViewPositionOnScreen);
+ mOriginatingView.getGlobalVisibleRect(mViewRectOnScreen);
+ mViewRectOnScreen.offset(mRootViewPositionOnScreen[0], mRootViewPositionOnScreen[1]);
- if (!Arrays.equals(mViewPosition, mPreviousViewPosition)
- || !mViewRect.equals(mPreviousViewRect)) {
+ if (!Arrays.equals(mViewPositionOnScreen, mPreviousViewPositionOnScreen)
+ || !mViewRectOnScreen.equals(mPreviousViewRectOnScreen)) {
repositionToolbar();
- mPreviousViewPosition[0] = mViewPosition[0];
- mPreviousViewPosition[1] = mViewPosition[1];
- mPreviousViewRect.set(mViewRect);
+ mPreviousViewPositionOnScreen[0] = mViewPositionOnScreen[0];
+ mPreviousViewPositionOnScreen[1] = mViewPositionOnScreen[1];
+ mPreviousViewRectOnScreen.set(mViewRectOnScreen);
}
}
private void repositionToolbar() {
checkToolbarInitialized();
- mContentRectOnWindow.set(mContentRect);
- mContentRectOnWindow.offset(mViewPosition[0], mViewPosition[1]);
+ mContentRectOnScreen.set(mContentRect);
+ mContentRectOnScreen.offset(mViewPositionOnScreen[0], mViewPositionOnScreen[1]);
if (isContentRectWithinBounds()) {
mFloatingToolbarVisibilityHelper.setOutOfBounds(false);
// Make sure that content rect is not out of the view's visible bounds.
- mContentRectOnWindow.set(
- Math.max(mContentRectOnWindow.left, mViewRect.left),
- Math.max(mContentRectOnWindow.top, mViewRect.top),
- Math.min(mContentRectOnWindow.right, mViewRect.right),
- Math.min(mContentRectOnWindow.bottom, mViewRect.bottom + mBottomAllowance));
-
- if (!mContentRectOnWindow.equals(mPreviousContentRectOnWindow)) {
+ mContentRectOnScreen.set(
+ Math.max(mContentRectOnScreen.left, mViewRectOnScreen.left),
+ Math.max(mContentRectOnScreen.top, mViewRectOnScreen.top),
+ Math.min(mContentRectOnScreen.right, mViewRectOnScreen.right),
+ Math.min(mContentRectOnScreen.bottom,
+ mViewRectOnScreen.bottom + mBottomAllowance));
+
+ if (!mContentRectOnScreen.equals(mPreviousContentRectOnScreen)) {
// Content rect is moving.
mOriginatingView.removeCallbacks(mMovingOff);
mFloatingToolbarVisibilityHelper.setMoving(true);
mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
mOriginatingView.postDelayed(mMovingOff, MOVING_HIDE_DELAY);
- mFloatingToolbar.setContentRect(mContentRectOnWindow);
+ mFloatingToolbar.setContentRect(mContentRectOnScreen);
mFloatingToolbar.updateLayout();
}
} else {
mFloatingToolbarVisibilityHelper.setOutOfBounds(true);
mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
- mContentRectOnWindow.setEmpty();
+ mContentRectOnScreen.setEmpty();
}
- mPreviousContentRectOnWindow.set(mContentRectOnWindow);
+ mPreviousContentRectOnScreen.set(mContentRectOnScreen);
}
private boolean isContentRectWithinBounds() {
@@ -193,8 +194,8 @@ public class FloatingActionMode extends ActionMode {
mContext.getResources().getDisplayMetrics().widthPixels,
mContext.getResources().getDisplayMetrics().heightPixels);
- return Rect.intersects(mContentRectOnWindow, mScreenRect)
- && Rect.intersects(mContentRectOnWindow, mViewRect);
+ return Rect.intersects(mContentRectOnScreen, mScreenRect)
+ && Rect.intersects(mContentRectOnScreen, mViewRectOnScreen);
}
@Override
@@ -269,7 +270,6 @@ public class FloatingActionMode extends ActionMode {
mOriginatingView.removeCallbacks(mHideOff);
}
-
/**
* A helper for showing/hiding the floating toolbar depending on certain states.
*/
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index a6e8034..b3f688b 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -285,6 +285,7 @@ public final class FloatingToolbar {
private final Context mContext;
private final View mParent;
+ private final int[] mParentPositionOnScreen = new int[2];
private final PopupWindow mPopupWindow;
private final ViewGroup mContentContainer;
private final int mMarginHorizontal;
@@ -337,8 +338,8 @@ public final class FloatingToolbar {
}
};
- private final Rect mViewPort = new Rect();
- private final Point mCoords = new Point();
+ private final Rect mViewPortOnScreen = new Rect();
+ private final Point mCoordsOnScreen = new Point();
private final Rect mTmpRect = new Rect();
private final Region mTouchableRegion = new Region();
@@ -428,8 +429,8 @@ public final class FloatingToolbar {
* Shows this popup at the specified coordinates.
* The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
*/
- public void show(Rect contentRect) {
- Preconditions.checkNotNull(contentRect);
+ public void show(Rect contentRectOnScreen) {
+ Preconditions.checkNotNull(contentRectOnScreen);
if (isShowing()) {
return;
@@ -447,9 +448,15 @@ public final class FloatingToolbar {
// The "show" animation will make this visible.
mContentContainer.setAlpha(0);
}
- refreshCoordinatesAndOverflowDirection(contentRect);
+ refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
preparePopupContent();
- mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoords.x, mCoords.y);
+ // We need to specify the offset relative to mParent.
+ // TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can
+ // specify the popup poision in screen coordinates.
+ mParent.getLocationOnScreen(mParentPositionOnScreen);
+ final int relativeX = mCoordsOnScreen.x - mParentPositionOnScreen[0];
+ final int relativeY = mCoordsOnScreen.y - mParentPositionOnScreen[1];
+ mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, relativeX, relativeY);
setTouchableSurfaceInsetsComputer();
runShowAnimation();
}
@@ -502,17 +509,23 @@ public final class FloatingToolbar {
* The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
* This is a no-op if this popup is not showing.
*/
- public void updateCoordinates(Rect contentRect) {
- Preconditions.checkNotNull(contentRect);
+ public void updateCoordinates(Rect contentRectOnScreen) {
+ Preconditions.checkNotNull(contentRectOnScreen);
if (!isShowing() || !mPopupWindow.isShowing()) {
return;
}
cancelOverflowAnimations();
- refreshCoordinatesAndOverflowDirection(contentRect);
+ refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
preparePopupContent();
- mPopupWindow.update(mCoords.x, mCoords.y, getWidth(), getHeight());
+ // We need to specify the offset relative to mParent.
+ // TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can
+ // specify the popup poision in screen coordinates.
+ mParent.getLocationOnScreen(mParentPositionOnScreen);
+ final int relativeX = mCoordsOnScreen.x - mParentPositionOnScreen[0];
+ final int relativeY = mCoordsOnScreen.y - mParentPositionOnScreen[1];
+ mPopupWindow.update(relativeX, relativeY, getWidth(), getHeight());
}
/**
@@ -536,47 +549,47 @@ public final class FloatingToolbar {
return mContext;
}
- private void refreshCoordinatesAndOverflowDirection(Rect contentRect) {
+ private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
refreshViewPort();
- int x = contentRect.centerX() - getWidth() / 2;
+ int x = contentRectOnScreen.centerX() - getWidth() / 2;
// Update x so that the toolbar isn't rendered behind the nav bar in landscape.
- x = Math.max(0, Math.min(x, mViewPort.right - getWidth()));
+ x = Math.max(0, Math.min(x, mViewPortOnScreen.right - getWidth()));
int y;
- int availableHeightAboveContent = contentRect.top - mViewPort.top;
- int availableHeightBelowContent = mViewPort.bottom - contentRect.bottom;
+ int availableHeightAboveContent = contentRectOnScreen.top - mViewPortOnScreen.top;
+ int availableHeightBelowContent = mViewPortOnScreen.bottom - contentRectOnScreen.bottom;
if (mOverflowPanel == null) { // There is no overflow.
if (availableHeightAboveContent >= getToolbarHeightWithVerticalMargin()) {
// There is enough space at the top of the content.
- y = contentRect.top - getToolbarHeightWithVerticalMargin();
+ y = contentRectOnScreen.top - getToolbarHeightWithVerticalMargin();
} else if (availableHeightBelowContent >= getToolbarHeightWithVerticalMargin()) {
// There is enough space at the bottom of the content.
- y = contentRect.bottom;
+ y = contentRectOnScreen.bottom;
} else if (availableHeightBelowContent >= getEstimatedToolbarHeight(mContext)) {
// Just enough space to fit the toolbar with no vertical margins.
- y = contentRect.bottom - mMarginVertical;
+ y = contentRectOnScreen.bottom - mMarginVertical;
} else {
// Not enough space. Prefer to position as high as possible.
y = Math.max(
- mViewPort.top,
- contentRect.top - getToolbarHeightWithVerticalMargin());
+ mViewPortOnScreen.top,
+ contentRectOnScreen.top - getToolbarHeightWithVerticalMargin());
}
} else { // There is an overflow.
int margin = 2 * mMarginVertical;
int minimumOverflowHeightWithMargin = mOverflowPanel.getMinimumHeight() + margin;
- int availableHeightThroughContentDown =
- mViewPort.bottom - contentRect.top + getToolbarHeightWithVerticalMargin();
- int availableHeightThroughContentUp =
- contentRect.bottom - mViewPort.top + getToolbarHeightWithVerticalMargin();
+ int availableHeightThroughContentDown = mViewPortOnScreen.bottom -
+ contentRectOnScreen.top + getToolbarHeightWithVerticalMargin();
+ int availableHeightThroughContentUp = contentRectOnScreen.bottom -
+ mViewPortOnScreen.top + getToolbarHeightWithVerticalMargin();
if (availableHeightAboveContent >= minimumOverflowHeightWithMargin) {
// There is enough space at the top of the content rect for the overflow.
// Position above and open upwards.
updateOverflowHeight(availableHeightAboveContent - margin);
- y = contentRect.top - getHeight();
+ y = contentRectOnScreen.top - getHeight();
mOverflowDirection = OVERFLOW_DIRECTION_UP;
} else if (availableHeightAboveContent >= getToolbarHeightWithVerticalMargin()
&& availableHeightThroughContentDown >= minimumOverflowHeightWithMargin) {
@@ -584,33 +597,34 @@ public final class FloatingToolbar {
// but not the overflow.
// Position above but open downwards.
updateOverflowHeight(availableHeightThroughContentDown - margin);
- y = contentRect.top - getToolbarHeightWithVerticalMargin();
+ y = contentRectOnScreen.top - getToolbarHeightWithVerticalMargin();
mOverflowDirection = OVERFLOW_DIRECTION_DOWN;
} else if (availableHeightBelowContent >= minimumOverflowHeightWithMargin) {
// There is enough space at the bottom of the content rect for the overflow.
// Position below and open downwards.
updateOverflowHeight(availableHeightBelowContent - margin);
- y = contentRect.bottom;
+ y = contentRectOnScreen.bottom;
mOverflowDirection = OVERFLOW_DIRECTION_DOWN;
} else if (availableHeightBelowContent >= getToolbarHeightWithVerticalMargin()
- && mViewPort.height() >= minimumOverflowHeightWithMargin) {
+ && mViewPortOnScreen.height() >= minimumOverflowHeightWithMargin) {
// There is enough space at the bottom of the content rect for the main panel
// but not the overflow.
// Position below but open upwards.
updateOverflowHeight(availableHeightThroughContentUp - margin);
- y = contentRect.bottom + getToolbarHeightWithVerticalMargin() - getHeight();
+ y = contentRectOnScreen.bottom + getToolbarHeightWithVerticalMargin() -
+ getHeight();
mOverflowDirection = OVERFLOW_DIRECTION_UP;
} else {
// Not enough space.
// Position at the top of the view port and open downwards.
- updateOverflowHeight(mViewPort.height() - margin);
- y = mViewPort.top;
+ updateOverflowHeight(mViewPortOnScreen.height() - margin);
+ y = mViewPortOnScreen.top;
mOverflowDirection = OVERFLOW_DIRECTION_DOWN;
}
mOverflowPanel.setOverflowDirection(mOverflowDirection);
}
- mCoords.set(x, y);
+ mCoordsOnScreen.set(x, y);
}
private int getToolbarHeightWithVerticalMargin() {
@@ -913,18 +927,18 @@ public final class FloatingToolbar {
private void refreshViewPort() {
- mParent.getWindowVisibleDisplayFrame(mViewPort);
+ mParent.getWindowVisibleDisplayFrame(mViewPortOnScreen);
}
private boolean viewPortHasChanged() {
mParent.getWindowVisibleDisplayFrame(mTmpRect);
- return !mTmpRect.equals(mViewPort);
+ return !mTmpRect.equals(mViewPortOnScreen);
}
private int getToolbarWidth(int suggestedWidth) {
int width = suggestedWidth;
refreshViewPort();
- int maximumWidth = mViewPort.width() - 2 * mParent.getResources()
+ int maximumWidth = mViewPortOnScreen.width() - 2 * mParent.getResources()
.getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
if (width <= 0) {
width = mParent.getResources()
@@ -1443,6 +1457,9 @@ public final class FloatingToolbar {
private static PopupWindow createPopupWindow(View content) {
ViewGroup popupContentHolder = new LinearLayout(content.getContext());
PopupWindow popupWindow = new PopupWindow(popupContentHolder);
+ // TODO: Use .setLayoutInScreenEnabled(true) instead of .setClippingEnabled(false)
+ // unless FLAG_LAYOUT_IN_SCREEN has any unintentional side-effects.
+ popupWindow.setClippingEnabled(false);
popupWindow.setWindowLayoutType(
WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
popupWindow.setAnimationStyle(0);