Merge "Hide floating toolbar when user interacts with screen." into mnc-dev

This commit is contained in:
Abodunrinwa Toki
2015-05-19 14:06:44 +00:00
committed by Android (Google) Code Review
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 void setTitleOptionalHint(boolean);
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_PRIMARY = 0; // 0x0
}
@ -36507,6 +36509,7 @@ package android.view {
public class ViewConfiguration {
ctor public deprecated ViewConfiguration();
method public static android.view.ViewConfiguration get(android.content.Context);
method public static int getDefaultActionModeSnoozeTime();
method public static int getDoubleTapTimeout();
method public static deprecated int getEdgeSlop();
method public static deprecated int getFadingEdgeLength();

View File

@ -36706,6 +36706,8 @@ package android.view {
method public abstract void setTitle(int);
method public void setTitleOptionalHint(boolean);
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_PRIMARY = 0; // 0x0
}
@ -38770,6 +38772,7 @@ package android.view {
public class ViewConfiguration {
ctor public deprecated ViewConfiguration();
method public static android.view.ViewConfiguration get(android.content.Context);
method public static int getDefaultActionModeSnoozeTime();
method public static int getDoubleTapTimeout();
method public static deprecated int getEdgeSlop();
method public static deprecated int getFadingEdgeLength();

View File

@ -44,6 +44,12 @@ public abstract class ActionMode {
*/
public static final int TYPE_FLOATING = 1;
/**
* Default snooze time.
*/
public static final int SNOOZE_TIME_DEFAULT =
ViewConfiguration.getDefaultActionModeSnoozeTime();
private Object mTag;
private boolean mTitleOptionalHint;
private int mType = TYPE_PRIMARY;
@ -206,6 +212,19 @@ public abstract class ActionMode {
*/
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
* have its {@link Callback#onDestroyActionMode(ActionMode)} method called.

View File

@ -212,6 +212,11 @@ public class ViewConfiguration {
*/
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.
* These constants must match the definition in res/values/config.xml.
@ -731,6 +736,13 @@ public class ViewConfiguration {
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.
*

View File

@ -233,6 +233,24 @@ public class Editor {
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) {
mTextView = textView;
// 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(mHideFloatingToolbar);
mTextView.removeCallbacks(mShowFloatingToolbar);
destroyDisplayListsData();
if (mSpellChecker != null) {
@ -1169,6 +1190,8 @@ public class Editor {
}
void onTouchEvent(MotionEvent event) {
updateFloatingToolbarVisibility(event);
if (hasSelectionController()) {
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() {
mInBatchEditControllers = true;
final InputMethodState ims = mInputMethodState;
@ -3661,6 +3715,8 @@ public class Editor {
@Override
public boolean onTouchEvent(MotionEvent ev) {
updateFloatingToolbarVisibility(ev);
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
startTouchUpFilter(getCurrentCursorOffset());

View File

@ -30,6 +30,9 @@ import com.android.internal.widget.FloatingToolbar;
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 ActionMode.Callback2 mCallback;
private final MenuBuilder mMenu;
@ -38,12 +41,26 @@ public class FloatingActionMode extends ActionMode {
private final Rect mPreviousContentRectOnWindow;
private final int[] mViewPosition;
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 FloatingToolbarVisibilityHelper mFloatingToolbarVisibilityHelper;
public FloatingActionMode(
Context context, ActionMode.Callback2 callback, View originatingView) {
mContext = context;
mCallback = callback;
mContext = Preconditions.checkNotNull(context);
mCallback = Preconditions.checkNotNull(callback);
mMenu = new MenuBuilder(context).setDefaultShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM);
setType(ActionMode.TYPE_FLOATING);
@ -51,7 +68,8 @@ public class FloatingActionMode extends ActionMode {
mContentRectOnWindow = new Rect();
mPreviousContentRectOnWindow = new Rect();
mViewPosition = new int[2];
mOriginatingView = originatingView;
mOriginatingView = Preconditions.checkNotNull(originatingView);
mOriginatingView.getLocationInWindow(mViewPosition);
}
public void setFloatingToolbar(FloatingToolbar floatingToolbar) {
@ -63,6 +81,7 @@ public class FloatingActionMode extends ActionMode {
return mCallback.onActionItemClicked(FloatingActionMode.this, item);
}
});
mFloatingToolbarVisibilityHelper = new FloatingToolbarVisibilityHelper(mFloatingToolbar);
}
@Override
@ -82,7 +101,7 @@ public class FloatingActionMode extends ActionMode {
@Override
public void invalidate() {
Preconditions.checkNotNull(mFloatingToolbar);
checkToolbarInitialized();
mCallback.onPrepareActionMode(this, mMenu);
mFloatingToolbar.updateLayout();
invalidateContentRect();
@ -90,32 +109,57 @@ public class FloatingActionMode extends ActionMode {
@Override
public void invalidateContentRect() {
Preconditions.checkNotNull(mFloatingToolbar);
checkToolbarInitialized();
mCallback.onGetContentRect(this, mOriginatingView, mContentRect);
repositionToolbar();
}
public void updateViewLocationInWindow() {
Preconditions.checkNotNull(mFloatingToolbar);
checkToolbarInitialized();
mOriginatingView.getLocationInWindow(mViewPosition);
repositionToolbar();
}
private void repositionToolbar() {
checkToolbarInitialized();
mContentRectOnWindow.set(
mContentRect.left + mViewPosition[0],
mContentRect.top + mViewPosition[1],
mContentRect.right + mViewPosition[0],
mContentRect.bottom + mViewPosition[1]);
if (!mContentRectOnWindow.equals(mPreviousContentRectOnWindow)) {
if (!mPreviousContentRectOnWindow.isEmpty()) {
notifyContentRectMoving();
}
mFloatingToolbar.setContentRect(mContentRectOnWindow);
mFloatingToolbar.updateLayout();
}
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
public void finish() {
checkToolbarInitialized();
reset();
mCallback.onDestroyActionMode(this);
}
@ -144,4 +188,56 @@ public class FloatingActionMode extends ActionMode {
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();
}
}
}
}