Add suuport for splitting touch events across windows.
This feature is currently used to enable dragging the start and end selection handles of a TextView at the same time. Could be used for other things later. Deleted some dead code in ArrowKeyMovementMethod and CursorControllers. Change-Id: I930accd97ca1ca1917aab8a807db2c950fc7b409
This commit is contained in:
@@ -23,19 +23,12 @@ import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.CursorController;
|
||||
|
||||
// XXX this doesn't extend MetaKeyKeyListener because the signatures
|
||||
// don't match. Need to figure that out. Meanwhile the meta keys
|
||||
// won't work in fields that don't take input.
|
||||
|
||||
public class ArrowKeyMovementMethod implements MovementMethod {
|
||||
/**
|
||||
* An optional controller for the cursor.
|
||||
* Use {@link #setCursorController(CursorController)} to set this field.
|
||||
*/
|
||||
private CursorController mCursorController;
|
||||
|
||||
private boolean isCap(Spannable buffer) {
|
||||
return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
|
||||
(MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0));
|
||||
@@ -192,21 +185,10 @@ public class ArrowKeyMovementMethod implements MovementMethod {
|
||||
}
|
||||
|
||||
public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) {
|
||||
if (mCursorController != null) {
|
||||
mCursorController.hide();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
|
||||
if (mCursorController != null) {
|
||||
return onTouchEventCursor(widget, buffer, event);
|
||||
} else {
|
||||
return onTouchEventStandard(widget, buffer, event);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onTouchEventStandard(TextView widget, Spannable buffer, MotionEvent event) {
|
||||
int initialScrollX = -1, initialScrollY = -1;
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
initialScrollX = Touch.getInitialScrollX(widget, buffer);
|
||||
@@ -278,49 +260,6 @@ public class ArrowKeyMovementMethod implements MovementMethod {
|
||||
return handled;
|
||||
}
|
||||
|
||||
private boolean onTouchEventCursor(TextView widget, Spannable buffer, MotionEvent event) {
|
||||
if (widget.isFocused() && !widget.didTouchFocusSelect()) {
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
widget.cancelLongPress();
|
||||
|
||||
// Offset the current touch position (from controller to cursor)
|
||||
final float x = event.getX() + mCursorController.getOffsetX();
|
||||
final float y = event.getY() + mCursorController.getOffsetY();
|
||||
mCursorController.updatePosition((int) x, (int) y);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mCursorController = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the cursor controller.
|
||||
*
|
||||
* When set, this object can be used to handle touch events, that can be translated into cursor
|
||||
* updates.
|
||||
*
|
||||
* {@link MotionEvent#ACTION_MOVE} events will call back the
|
||||
* {@link CursorController#updatePosition(int, int)} controller's method, passing the current
|
||||
* finger coordinates (offset by {@link CursorController#getOffsetX()} and
|
||||
* {@link CursorController#getOffsetY()}) as parameters.
|
||||
*
|
||||
* When the gesture is finished (on a {@link MotionEvent#ACTION_UP} or
|
||||
* {@link MotionEvent#ACTION_CANCEL} event), the controller is reset to null.
|
||||
*
|
||||
* @param cursorController A cursor controller implementation
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setCursorController(CursorController cursorController) {
|
||||
mCursorController = cursorController;
|
||||
}
|
||||
|
||||
public boolean canSelectArbitrarily() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -583,6 +583,19 @@ public interface WindowManager extends ViewManager {
|
||||
* also been set.
|
||||
*/
|
||||
public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
|
||||
|
||||
/** Window flag: when set the window will accept for touch events
|
||||
* outside of its bounds to be sent to other windows that also
|
||||
* support split touch. When this flag is not set, the first pointer
|
||||
* that goes down determines the window to which all subsequent touches
|
||||
* go until all pointers go up. When this flag is set, each pointer
|
||||
* (not necessarily the first) that goes down determines the window
|
||||
* to which all subsequent touches of that pointer will go until that
|
||||
* pointer goes up thereby enabling touches with multiple pointers
|
||||
* to be split across multiple windows.
|
||||
*
|
||||
* {@hide} */
|
||||
public static final int FLAG_SPLIT_TOUCH = 0x00800000;
|
||||
|
||||
/** Window flag: *sigh* The lock screen wants to continue running its
|
||||
* animation while it is fading. A kind-of hack to allow this. Maybe
|
||||
|
||||
@@ -85,6 +85,7 @@ public class PopupWindow {
|
||||
private boolean mTouchable = true;
|
||||
private boolean mOutsideTouchable = false;
|
||||
private boolean mClippingEnabled = true;
|
||||
private boolean mSplitTouchEnabled;
|
||||
|
||||
private OnTouchListener mTouchInterceptor;
|
||||
|
||||
@@ -564,6 +565,36 @@ public class PopupWindow {
|
||||
mClippingEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Indicates whether the popup window supports splitting touches.</p>
|
||||
*
|
||||
* @return true if the touch splitting is enabled, false otherwise
|
||||
*
|
||||
* @see #setSplitTouchEnabled(boolean)
|
||||
* @hide
|
||||
*/
|
||||
public boolean isSplitTouchEnabled() {
|
||||
return mSplitTouchEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Allows the popup window to split touches across other windows that also
|
||||
* support split touch. When this flag is not set, the first pointer
|
||||
* that goes down determines the window to which all subsequent touches
|
||||
* go until all pointers go up. When this flag is set, each pointer
|
||||
* (not necessarily the first) that goes down determines the window
|
||||
* to which all subsequent touches of that pointer will go until that
|
||||
* pointer goes up thereby enabling touches with multiple pointers
|
||||
* to be split across multiple windows.</p>
|
||||
*
|
||||
* @param enabled true if the split touches should be enabled, false otherwise
|
||||
* @see #isSplitTouchEnabled()
|
||||
* @hide
|
||||
*/
|
||||
public void setSplitTouchEnabled(boolean enabled) {
|
||||
mSplitTouchEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Change the width and height measure specs that are given to the
|
||||
* window manager by the popup. By default these are 0, meaning that
|
||||
@@ -889,6 +920,9 @@ public class PopupWindow {
|
||||
if (!mClippingEnabled) {
|
||||
curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
|
||||
}
|
||||
if (mSplitTouchEnabled) {
|
||||
curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
|
||||
}
|
||||
return curFlags;
|
||||
}
|
||||
|
||||
|
||||
@@ -7601,16 +7601,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
|
||||
/**
|
||||
* A CursorController instance can be used to control a cursor in the text.
|
||||
*
|
||||
* It can be passed to an {@link ArrowKeyMovementMethod} which can intercepts events
|
||||
* and send them to this object instead of the cursor.
|
||||
*
|
||||
* It is not used outside of {@link TextView}.
|
||||
* @hide
|
||||
*/
|
||||
public interface CursorController {
|
||||
/* Cursor fade-out animation duration, in milliseconds. */
|
||||
static final int FADE_OUT_DURATION = 400;
|
||||
|
||||
private interface CursorController {
|
||||
/**
|
||||
* Makes the cursor controller visible on screen. Will be drawn by {@link #draw(Canvas)}.
|
||||
* See also {@link #hide()}.
|
||||
@@ -7631,22 +7625,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
/**
|
||||
* Update the controller's position.
|
||||
*/
|
||||
public void updatePosition(int x, int y);
|
||||
public void updatePosition(HandleView handle, int x, int y);
|
||||
|
||||
public void updatePosition();
|
||||
|
||||
/**
|
||||
* The controller and the cursor's positions can be link by a fixed offset,
|
||||
* computed when the controller is touched, and then maintained as it moves
|
||||
* @return Horizontal offset between the controller and the cursor.
|
||||
*/
|
||||
public float getOffsetX();
|
||||
|
||||
/**
|
||||
* @return Vertical offset between the controller and the cursor.
|
||||
*/
|
||||
public float getOffsetY();
|
||||
|
||||
/**
|
||||
* This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
|
||||
* a chance to become active and/or visible.
|
||||
@@ -7670,6 +7652,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
mDrawable = handle;
|
||||
mContainer = new PopupWindow(TextView.this.mContext, null,
|
||||
com.android.internal.R.attr.textSelectHandleWindowStyle);
|
||||
mContainer.setSplitTouchEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -7768,7 +7751,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
TextView.this.getLocationOnScreen(coords);
|
||||
final int x = (int) (rawX - coords[0] + 0.5f);
|
||||
final int y = (int) (rawY - coords[1] + 0.5f);
|
||||
mController.updatePosition(x, y);
|
||||
mController.updatePosition(this, x, y);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
@@ -7802,13 +7785,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
}
|
||||
|
||||
class InsertionPointCursorController implements CursorController {
|
||||
private class InsertionPointCursorController implements CursorController {
|
||||
private static final int DELAY_BEFORE_FADE_OUT = 4100;
|
||||
|
||||
// The cursor controller image
|
||||
private final HandleView mHandle;
|
||||
// Offset between finger hot point on cursor controller and actual cursor
|
||||
private float mOffsetX, mOffsetY;
|
||||
|
||||
private final Runnable mHider = new Runnable() {
|
||||
public void run() {
|
||||
@@ -7841,7 +7822,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
return mHandle.isShowing();
|
||||
}
|
||||
|
||||
public void updatePosition(int x, int y) {
|
||||
public void updatePosition(HandleView handle, int x, int y) {
|
||||
final int previousOffset = getSelectionStart();
|
||||
int offset = getHysteresisOffset(x, y, previousOffset);
|
||||
|
||||
@@ -7865,24 +7846,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
mHandle.positionAtCursor(offset, true);
|
||||
}
|
||||
|
||||
public float getOffsetX() {
|
||||
return mOffsetX;
|
||||
}
|
||||
|
||||
public float getOffsetY() {
|
||||
return mOffsetY;
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class SelectionModifierCursorController implements CursorController {
|
||||
private class SelectionModifierCursorController implements CursorController {
|
||||
// The cursor controller images
|
||||
private HandleView mStartHandle, mEndHandle;
|
||||
// Offset between finger hot point on active cursor controller and actual cursor
|
||||
private float mOffsetX, mOffsetY;
|
||||
// The offsets of that last touch down event. Remembered to start selection there.
|
||||
private int mMinTouchOffset, mMaxTouchOffset;
|
||||
// Whether selection anchors are active
|
||||
@@ -7916,15 +7887,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
hide();
|
||||
}
|
||||
|
||||
public void updatePosition(int x, int y) {
|
||||
public void updatePosition(HandleView handle, int x, int y) {
|
||||
int selectionStart = getSelectionStart();
|
||||
int selectionEnd = getSelectionEnd();
|
||||
|
||||
final int previousOffset = mStartHandle.isDragging() ? selectionStart : selectionEnd;
|
||||
final int previousOffset = handle == mStartHandle ? selectionStart : selectionEnd;
|
||||
int offset = getHysteresisOffset(x, y, previousOffset);
|
||||
|
||||
// Handle the case where start and end are swapped, making sure start <= end
|
||||
if (mStartHandle.isDragging()) {
|
||||
if (handle == mStartHandle) {
|
||||
if (offset <= selectionEnd) {
|
||||
if (selectionStart == offset) {
|
||||
return; // no change, no need to redraw;
|
||||
@@ -8021,14 +7992,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
return mMaxTouchOffset;
|
||||
}
|
||||
|
||||
public float getOffsetX() {
|
||||
return mOffsetX;
|
||||
}
|
||||
|
||||
public float getOffsetY() {
|
||||
return mOffsetY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff this controller is currently used to move the selection start.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user