am 785c0a02: am 1f21d2a8: Merge "Hide floating toolbar when user interacts with screen." into mnc-dev

* commit '785c0a02c0016fc6c1981bd3e509948d1c1cb227':
  Hide floating toolbar when user interacts with screen.
This commit is contained in:
Abodunrinwa Toki
2015-05-19 14:28:57 +00:00
committed by Android Git Automerger
6 changed files with 195 additions and 6 deletions

View File

@@ -34443,6 +34443,8 @@ package android.view {
method public abstract void setTitle(int); method public abstract void setTitle(int);
method public void setTitleOptionalHint(boolean); method public void setTitleOptionalHint(boolean);
method public void setType(int); method public void setType(int);
method public void snooze(int);
field public static final int SNOOZE_TIME_DEFAULT;
field public static final int TYPE_FLOATING = 1; // 0x1 field public static final int TYPE_FLOATING = 1; // 0x1
field public static final int TYPE_PRIMARY = 0; // 0x0 field public static final int TYPE_PRIMARY = 0; // 0x0
} }
@@ -36507,6 +36509,7 @@ package android.view {
public class ViewConfiguration { public class ViewConfiguration {
ctor public deprecated ViewConfiguration(); ctor public deprecated ViewConfiguration();
method public static android.view.ViewConfiguration get(android.content.Context); method public static android.view.ViewConfiguration get(android.content.Context);
method public static int getDefaultActionModeSnoozeTime();
method public static int getDoubleTapTimeout(); method public static int getDoubleTapTimeout();
method public static deprecated int getEdgeSlop(); method public static deprecated int getEdgeSlop();
method public static deprecated int getFadingEdgeLength(); method public static deprecated int getFadingEdgeLength();

View File

@@ -36706,6 +36706,8 @@ package android.view {
method public abstract void setTitle(int); method public abstract void setTitle(int);
method public void setTitleOptionalHint(boolean); method public void setTitleOptionalHint(boolean);
method public void setType(int); method public void setType(int);
method public void snooze(int);
field public static final int SNOOZE_TIME_DEFAULT;
field public static final int TYPE_FLOATING = 1; // 0x1 field public static final int TYPE_FLOATING = 1; // 0x1
field public static final int TYPE_PRIMARY = 0; // 0x0 field public static final int TYPE_PRIMARY = 0; // 0x0
} }
@@ -38770,6 +38772,7 @@ package android.view {
public class ViewConfiguration { public class ViewConfiguration {
ctor public deprecated ViewConfiguration(); ctor public deprecated ViewConfiguration();
method public static android.view.ViewConfiguration get(android.content.Context); method public static android.view.ViewConfiguration get(android.content.Context);
method public static int getDefaultActionModeSnoozeTime();
method public static int getDoubleTapTimeout(); method public static int getDoubleTapTimeout();
method public static deprecated int getEdgeSlop(); method public static deprecated int getEdgeSlop();
method public static deprecated int getFadingEdgeLength(); method public static deprecated int getFadingEdgeLength();

View File

@@ -44,6 +44,12 @@ public abstract class ActionMode {
*/ */
public static final int TYPE_FLOATING = 1; public static final int TYPE_FLOATING = 1;
/**
* Default snooze time.
*/
public static final int SNOOZE_TIME_DEFAULT =
ViewConfiguration.getDefaultActionModeSnoozeTime();
private Object mTag; private Object mTag;
private boolean mTitleOptionalHint; private boolean mTitleOptionalHint;
private int mType = TYPE_PRIMARY; private int mType = TYPE_PRIMARY;
@@ -206,6 +212,19 @@ public abstract class ActionMode {
*/ */
public void invalidateContentRect() {} public void invalidateContentRect() {}
/**
* Hide the action mode view from obstructing the content below for a short period.
* This only makes sense for action modes that support dynamic positioning on the screen.
* If this method is called again before the snooze time expires, the later snooze will
* cancel the former and then take effect.
* NOTE that there is an internal limit to how long the mode can be snoozed for. It's typically
* about a few seconds.
*
* @param snoozeTime The number of milliseconds to snooze for.
* @see #SNOOZE_TIME_DEFAULT
*/
public void snooze(int snoozeTime) {}
/** /**
* Finish and close this action mode. The action mode's {@link ActionMode.Callback} will * Finish and close this action mode. The action mode's {@link ActionMode.Callback} will
* have its {@link Callback#onDestroyActionMode(ActionMode)} method called. * have its {@link Callback#onDestroyActionMode(ActionMode)} method called.

View File

@@ -212,6 +212,11 @@ public class ViewConfiguration {
*/ */
private static final int OVERFLING_DISTANCE = 6; private static final int OVERFLING_DISTANCE = 6;
/**
* Default time to snooze an action mode for.
*/
private static final int ACTION_MODE_SNOOZE_TIME_DEFAULT = 2000;
/** /**
* Configuration values for overriding {@link #hasPermanentMenuKey()} behavior. * Configuration values for overriding {@link #hasPermanentMenuKey()} behavior.
* These constants must match the definition in res/values/config.xml. * These constants must match the definition in res/values/config.xml.
@@ -731,6 +736,13 @@ public class ViewConfiguration {
return SCROLL_FRICTION; return SCROLL_FRICTION;
} }
/**
* @return the default duration in milliseconds for {@link ActionMode#snooze(int)}.
*/
public static int getDefaultActionModeSnoozeTime() {
return ACTION_MODE_SNOOZE_TIME_DEFAULT;
}
/** /**
* Report if the device has a permanent menu key available to the user. * Report if the device has a permanent menu key available to the user.
* *

View File

@@ -233,6 +233,24 @@ public class Editor {
final CursorAnchorInfoNotifier mCursorAnchorInfoNotifier = new CursorAnchorInfoNotifier(); final CursorAnchorInfoNotifier mCursorAnchorInfoNotifier = new CursorAnchorInfoNotifier();
private final Runnable mHideFloatingToolbar = new Runnable() {
@Override
public void run() {
if (mSelectionActionMode != null) {
mSelectionActionMode.snooze(ActionMode.SNOOZE_TIME_DEFAULT);
}
}
};
private final Runnable mShowFloatingToolbar = new Runnable() {
@Override
public void run() {
if (mSelectionActionMode != null) {
mSelectionActionMode.snooze(0); // snooze off.
}
}
};
Editor(TextView textView) { Editor(TextView textView) {
mTextView = textView; mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end. // Synchronize the filter list, which places the undo input filter at the end.
@@ -358,6 +376,9 @@ public class Editor {
mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
} }
mTextView.removeCallbacks(mHideFloatingToolbar);
mTextView.removeCallbacks(mShowFloatingToolbar);
destroyDisplayListsData(); destroyDisplayListsData();
if (mSpellChecker != null) { if (mSpellChecker != null) {
@@ -1169,6 +1190,8 @@ public class Editor {
} }
void onTouchEvent(MotionEvent event) { void onTouchEvent(MotionEvent event) {
updateFloatingToolbarVisibility(event);
if (hasSelectionController()) { if (hasSelectionController()) {
getSelectionController().onTouchEvent(event); getSelectionController().onTouchEvent(event);
} }
@@ -1189,6 +1212,37 @@ public class Editor {
} }
} }
private void updateFloatingToolbarVisibility(MotionEvent event) {
if (mSelectionActionMode != null) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
hideFloatingToolbar();
break;
case MotionEvent.ACTION_UP: // fall through
case MotionEvent.ACTION_CANCEL:
showFloatingToolbar();
}
}
}
private void hideFloatingToolbar() {
if (mSelectionActionMode != null) {
mTextView.removeCallbacks(mShowFloatingToolbar);
// Delay the "hide" a little bit just in case a "show" will happen almost immediately.
mTextView.postDelayed(mHideFloatingToolbar, 100);
}
}
private void showFloatingToolbar() {
if (mSelectionActionMode != null) {
mTextView.removeCallbacks(mHideFloatingToolbar);
// Delay "show" so it doesn't interfere with click confirmations
// or double-clicks that could "dismiss" the floating toolbar.
int delay = ViewConfiguration.getDoubleTapTimeout();
mTextView.postDelayed(mShowFloatingToolbar, delay);
}
}
public void beginBatchEdit() { public void beginBatchEdit() {
mInBatchEditControllers = true; mInBatchEditControllers = true;
final InputMethodState ims = mInputMethodState; final InputMethodState ims = mInputMethodState;
@@ -3661,6 +3715,8 @@ public class Editor {
@Override @Override
public boolean onTouchEvent(MotionEvent ev) { public boolean onTouchEvent(MotionEvent ev) {
updateFloatingToolbarVisibility(ev);
switch (ev.getActionMasked()) { switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: { case MotionEvent.ACTION_DOWN: {
startTouchUpFilter(getCurrentCursorOffset()); startTouchUpFilter(getCurrentCursorOffset());

View File

@@ -30,6 +30,9 @@ import com.android.internal.widget.FloatingToolbar;
public class FloatingActionMode extends ActionMode { public class FloatingActionMode extends ActionMode {
private static final int MAX_SNOOZE_TIME = 3000;
private static final int MOVING_HIDE_DELAY = 300;
private final Context mContext; private final Context mContext;
private final ActionMode.Callback2 mCallback; private final ActionMode.Callback2 mCallback;
private final MenuBuilder mMenu; private final MenuBuilder mMenu;
@@ -38,12 +41,26 @@ public class FloatingActionMode extends ActionMode {
private final Rect mPreviousContentRectOnWindow; private final Rect mPreviousContentRectOnWindow;
private final int[] mViewPosition; private final int[] mViewPosition;
private final View mOriginatingView; private final View mOriginatingView;
private final Runnable mMovingOff = new Runnable() {
public void run() {
mFloatingToolbarVisibilityHelper.setMoving(false);
}
};
private final Runnable mSnoozeOff = new Runnable() {
public void run() {
mFloatingToolbarVisibilityHelper.setSnoozed(false);
}
};
private FloatingToolbar mFloatingToolbar; private FloatingToolbar mFloatingToolbar;
private FloatingToolbarVisibilityHelper mFloatingToolbarVisibilityHelper;
public FloatingActionMode( public FloatingActionMode(
Context context, ActionMode.Callback2 callback, View originatingView) { Context context, ActionMode.Callback2 callback, View originatingView) {
mContext = context; mContext = Preconditions.checkNotNull(context);
mCallback = callback; mCallback = Preconditions.checkNotNull(callback);
mMenu = new MenuBuilder(context).setDefaultShowAsAction( mMenu = new MenuBuilder(context).setDefaultShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM); MenuItem.SHOW_AS_ACTION_IF_ROOM);
setType(ActionMode.TYPE_FLOATING); setType(ActionMode.TYPE_FLOATING);
@@ -51,7 +68,8 @@ public class FloatingActionMode extends ActionMode {
mContentRectOnWindow = new Rect(); mContentRectOnWindow = new Rect();
mPreviousContentRectOnWindow = new Rect(); mPreviousContentRectOnWindow = new Rect();
mViewPosition = new int[2]; mViewPosition = new int[2];
mOriginatingView = originatingView; mOriginatingView = Preconditions.checkNotNull(originatingView);
mOriginatingView.getLocationInWindow(mViewPosition);
} }
public void setFloatingToolbar(FloatingToolbar floatingToolbar) { public void setFloatingToolbar(FloatingToolbar floatingToolbar) {
@@ -63,6 +81,7 @@ public class FloatingActionMode extends ActionMode {
return mCallback.onActionItemClicked(FloatingActionMode.this, item); return mCallback.onActionItemClicked(FloatingActionMode.this, item);
} }
}); });
mFloatingToolbarVisibilityHelper = new FloatingToolbarVisibilityHelper(mFloatingToolbar);
} }
@Override @Override
@@ -82,7 +101,7 @@ public class FloatingActionMode extends ActionMode {
@Override @Override
public void invalidate() { public void invalidate() {
Preconditions.checkNotNull(mFloatingToolbar); checkToolbarInitialized();
mCallback.onPrepareActionMode(this, mMenu); mCallback.onPrepareActionMode(this, mMenu);
mFloatingToolbar.updateLayout(); mFloatingToolbar.updateLayout();
invalidateContentRect(); invalidateContentRect();
@@ -90,32 +109,57 @@ public class FloatingActionMode extends ActionMode {
@Override @Override
public void invalidateContentRect() { public void invalidateContentRect() {
Preconditions.checkNotNull(mFloatingToolbar); checkToolbarInitialized();
mCallback.onGetContentRect(this, mOriginatingView, mContentRect); mCallback.onGetContentRect(this, mOriginatingView, mContentRect);
repositionToolbar(); repositionToolbar();
} }
public void updateViewLocationInWindow() { public void updateViewLocationInWindow() {
Preconditions.checkNotNull(mFloatingToolbar); checkToolbarInitialized();
mOriginatingView.getLocationInWindow(mViewPosition); mOriginatingView.getLocationInWindow(mViewPosition);
repositionToolbar(); repositionToolbar();
} }
private void repositionToolbar() { private void repositionToolbar() {
checkToolbarInitialized();
mContentRectOnWindow.set( mContentRectOnWindow.set(
mContentRect.left + mViewPosition[0], mContentRect.left + mViewPosition[0],
mContentRect.top + mViewPosition[1], mContentRect.top + mViewPosition[1],
mContentRect.right + mViewPosition[0], mContentRect.right + mViewPosition[0],
mContentRect.bottom + mViewPosition[1]); mContentRect.bottom + mViewPosition[1]);
if (!mContentRectOnWindow.equals(mPreviousContentRectOnWindow)) { if (!mContentRectOnWindow.equals(mPreviousContentRectOnWindow)) {
if (!mPreviousContentRectOnWindow.isEmpty()) {
notifyContentRectMoving();
}
mFloatingToolbar.setContentRect(mContentRectOnWindow); mFloatingToolbar.setContentRect(mContentRectOnWindow);
mFloatingToolbar.updateLayout(); mFloatingToolbar.updateLayout();
} }
mPreviousContentRectOnWindow.set(mContentRectOnWindow); mPreviousContentRectOnWindow.set(mContentRectOnWindow);
} }
private void notifyContentRectMoving() {
mOriginatingView.removeCallbacks(mMovingOff);
mFloatingToolbarVisibilityHelper.setMoving(true);
mOriginatingView.postDelayed(mMovingOff, MOVING_HIDE_DELAY);
}
@Override
public void snooze(int snoozeTime) {
checkToolbarInitialized();
snoozeTime = Math.min(MAX_SNOOZE_TIME, snoozeTime);
mOriginatingView.removeCallbacks(mSnoozeOff);
if (snoozeTime <= 0) {
mSnoozeOff.run();
} else {
mFloatingToolbarVisibilityHelper.setSnoozed(true);
mOriginatingView.postDelayed(mSnoozeOff, snoozeTime);
}
}
@Override @Override
public void finish() { public void finish() {
checkToolbarInitialized();
reset();
mCallback.onDestroyActionMode(this); mCallback.onDestroyActionMode(this);
} }
@@ -144,4 +188,56 @@ public class FloatingActionMode extends ActionMode {
return new MenuInflater(mContext); return new MenuInflater(mContext);
} }
/**
* @throws IlllegalStateException
*/
private void checkToolbarInitialized() {
Preconditions.checkState(mFloatingToolbar != null);
Preconditions.checkState(mFloatingToolbarVisibilityHelper != null);
}
private void reset() {
mOriginatingView.removeCallbacks(mMovingOff);
mOriginatingView.removeCallbacks(mSnoozeOff);
}
/**
* A helper that shows/hides the floating toolbar depending on certain states.
*/
private static final class FloatingToolbarVisibilityHelper {
private final FloatingToolbar mToolbar;
private boolean mSnoozed;
private boolean mMoving;
private boolean mOutOfBounds;
public FloatingToolbarVisibilityHelper(FloatingToolbar toolbar) {
mToolbar = Preconditions.checkNotNull(toolbar);
}
public void setSnoozed(boolean snoozed) {
mSnoozed = snoozed;
updateToolbarVisibility();
}
public void setMoving(boolean moving) {
mMoving = moving;
updateToolbarVisibility();
}
public void setOutOfBounds(boolean outOfBounds) {
mOutOfBounds = outOfBounds;
updateToolbarVisibility();
}
private void updateToolbarVisibility() {
if (mSnoozed || mMoving || mOutOfBounds) {
mToolbar.hide();
} else if (mToolbar.isHidden()) {
mToolbar.show();
}
}
}
} }