Merge "Synthesizing D-pad event directly from motion events. Includes fling." into jb-mr1-aah-dev
This commit is contained in:
@@ -16,90 +16,216 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Handler.Callback;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
|
||||
/**
|
||||
* This class creates trackball events from touchpad events.
|
||||
*
|
||||
* @see ViewRootImpl
|
||||
*/
|
||||
class SimulatedTrackball {
|
||||
|
||||
//The position of the previous touchpad event
|
||||
// Maximum difference in milliseconds between the down and up of a touch
|
||||
// event for it to be considered a tap
|
||||
// TODO:Read this value from a configuration file
|
||||
private static final int MAX_TAP_TIME = 250;
|
||||
private static final int FLICK_MSG_ID = 313;
|
||||
|
||||
// The position of the previous touchpad event
|
||||
private float mLastTouchpadXPosition;
|
||||
private float mLastTouchpadYPosition;
|
||||
//Where the touchpad was initially pressed
|
||||
// Where the touchpad was initially pressed
|
||||
private float mTouchpadEnterXPosition;
|
||||
private float mTouchpadEnterYPosition;
|
||||
//When the last touchpad event occurred
|
||||
// When the most recent ACTION_HOVER_ENTER occurred
|
||||
private long mLastTouchPadStartTimeMs = 0;
|
||||
// When the most recent direction key was sent
|
||||
private long mLastTouchPadKeySendTimeMs = 0;
|
||||
// When the most recent touch event of any type occurred
|
||||
private long mLastTouchPadEventTimeMs = 0;
|
||||
|
||||
//Change in position allowed during tap events
|
||||
// How quickly keys were sent;
|
||||
private int mKeySendRateMs = 0;
|
||||
private int mLastKeySent;
|
||||
// Last movement in device screen pixels
|
||||
private float mLastMoveX = 0;
|
||||
private float mLastMoveY = 0;
|
||||
// Offset from the initial touch. Gets reset as direction keys are sent.
|
||||
private float mAccumulatedX;
|
||||
private float mAccumulatedY;
|
||||
|
||||
// Change in position allowed during tap events
|
||||
private float mTouchSlop;
|
||||
private float mTouchSlopSquared;
|
||||
//Has the TouchSlop constraint been invalidated
|
||||
// Has the TouchSlop constraint been invalidated
|
||||
private boolean mAlwaysInTapRegion = true;
|
||||
|
||||
//Maximum difference in milliseconds between the down and up of a touch event
|
||||
//for it to be considered a tap
|
||||
//TODO:Read this value from a config file
|
||||
private static final int MAX_TAP_TIME = 250;
|
||||
// Most recent event. Used to determine what device sent the event.
|
||||
private MotionEvent mRecentEvent;
|
||||
|
||||
public SimulatedTrackball(){
|
||||
// TODO: Currently using screen dimensions tuned to a Galaxy Nexus, need to
|
||||
// read this from a config file instead
|
||||
private int mDistancePerTick;
|
||||
private int mDistancePerTickSquared;
|
||||
// Highest rate that the flinged events can occur at before dying out
|
||||
private int mMaxRepeatDelay;
|
||||
// The square of the minimum distance needed for a flick to register
|
||||
private int mMinFlickDistanceSquared;
|
||||
// How quickly the repeated events die off
|
||||
private float mFlickDecay;
|
||||
|
||||
public SimulatedTrackball() {
|
||||
mDistancePerTick = SystemProperties.getInt("persist.vr_dist_tick", 64);
|
||||
mDistancePerTickSquared = mDistancePerTick * mDistancePerTick;
|
||||
mMaxRepeatDelay = SystemProperties.getInt("persist.vr_repeat_delay", 300);
|
||||
mMinFlickDistanceSquared = SystemProperties.getInt("persist.vr_min_flick", 20);
|
||||
mMinFlickDistanceSquared *= mMinFlickDistanceSquared;
|
||||
mFlickDecay = Float.parseFloat(SystemProperties.get(
|
||||
"persist.sys.vr_flick_decay", "1.3"));
|
||||
mTouchSlop = ViewConfiguration.getTouchSlop();
|
||||
mTouchSlopSquared = mTouchSlop * mTouchSlop;
|
||||
}
|
||||
|
||||
public void updateTrackballDirection(ViewRootImpl viewroot, MotionEvent event){
|
||||
//Store what time the touchpad event occurred
|
||||
final long time = event.getEventTime();
|
||||
MotionEvent trackballEvent;
|
||||
private final Handler mHandler = new Handler(new Callback() {
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
if (msg.what != FLICK_MSG_ID)
|
||||
return false;
|
||||
|
||||
final long time = SystemClock.uptimeMillis();
|
||||
ViewRootImpl viewroot = (ViewRootImpl) msg.obj;
|
||||
// Send the key
|
||||
viewroot.enqueueInputEvent(new KeyEvent(time, time,
|
||||
KeyEvent.ACTION_DOWN, msg.arg2, 0, mRecentEvent.getMetaState(),
|
||||
mRecentEvent.getDeviceId(), 0,
|
||||
KeyEvent.FLAG_FALLBACK, mRecentEvent.getSource()));
|
||||
viewroot.enqueueInputEvent(new KeyEvent(time, time,
|
||||
KeyEvent.ACTION_UP, msg.arg2, 0, mRecentEvent.getMetaState(),
|
||||
mRecentEvent.getDeviceId(), 0,
|
||||
KeyEvent.FLAG_FALLBACK, mRecentEvent.getSource()));
|
||||
Message msgCopy = Message.obtain(msg);
|
||||
// Increase the delay by the decay factor
|
||||
msgCopy.arg1 = (int) Math.ceil(mFlickDecay * msgCopy.arg1);
|
||||
if (msgCopy.arg1 <= mMaxRepeatDelay) {
|
||||
// Send the key again in arg1 milliseconds
|
||||
mHandler.sendMessageDelayed(msgCopy, msgCopy.arg1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
public void updateTrackballDirection(ViewRootImpl viewroot, MotionEvent event) {
|
||||
// Store what time the touchpad event occurred
|
||||
final long time = SystemClock.uptimeMillis();
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_HOVER_ENTER:
|
||||
mLastTouchPadStartTimeMs = time;
|
||||
mAlwaysInTapRegion = true;
|
||||
mTouchpadEnterXPosition = event.getX();
|
||||
mTouchpadEnterYPosition = event.getY();
|
||||
mAccumulatedX = 0;
|
||||
mAccumulatedY = 0;
|
||||
mLastMoveX = 0;
|
||||
mLastMoveY = 0;
|
||||
// Clear any flings
|
||||
mHandler.removeMessages(0);
|
||||
break;
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
//Find the difference in position between the two most recent touchpad events
|
||||
float deltaX = event.getX() - mLastTouchpadXPosition;
|
||||
float deltaY = event.getY() - mLastTouchpadYPosition;
|
||||
|
||||
//TODO: Get simulated trackball configuration parameters
|
||||
//Create a trackball event from recorded touchpad event data
|
||||
trackballEvent = MotionEvent.obtain(mLastTouchPadStartTimeMs, time,
|
||||
MotionEvent.ACTION_MOVE, deltaX / 50,
|
||||
deltaY / 50, 0, 0, event.getMetaState(),
|
||||
10f, 10f, event.getDeviceId(), 0);
|
||||
trackballEvent.setSource(InputDevice.SOURCE_CLASS_TRACKBALL);
|
||||
//Add the new event to event queue
|
||||
viewroot.enqueueInputEvent(trackballEvent);
|
||||
|
||||
deltaX = event.getX() - mTouchpadEnterXPosition;
|
||||
deltaY = event.getY() - mTouchpadEnterYPosition;
|
||||
if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY){
|
||||
// Determine whether the move is slop or an intentional move
|
||||
float deltaX = event.getX() - mTouchpadEnterXPosition;
|
||||
float deltaY = event.getY() - mTouchpadEnterYPosition;
|
||||
if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY) {
|
||||
mAlwaysInTapRegion = false;
|
||||
}
|
||||
|
||||
// Find the difference in position between the two most recent
|
||||
// touchpad events
|
||||
mLastMoveX = event.getX() - mLastTouchpadXPosition;
|
||||
mLastMoveY = event.getY() - mLastTouchpadYPosition;
|
||||
mAccumulatedX += mLastMoveX;
|
||||
mAccumulatedY += mLastMoveY;
|
||||
float mAccumulatedXSquared = mAccumulatedX * mAccumulatedX;
|
||||
float mAccumulatedYSquared = mAccumulatedY * mAccumulatedY;
|
||||
// Determine if we've moved far enough to send a key press
|
||||
if (mAccumulatedXSquared > mDistancePerTickSquared ||
|
||||
mAccumulatedYSquared > mDistancePerTickSquared) {
|
||||
float dominantAxis;
|
||||
float sign;
|
||||
boolean isXAxis;
|
||||
int key;
|
||||
int repeatCount = 0;
|
||||
// Determine dominant axis
|
||||
if (mAccumulatedXSquared > mAccumulatedYSquared) {
|
||||
dominantAxis = mAccumulatedX;
|
||||
isXAxis = true;
|
||||
} else {
|
||||
dominantAxis = mAccumulatedY;
|
||||
isXAxis = false;
|
||||
}
|
||||
// Determine sign of axis
|
||||
sign = (dominantAxis > 0) ? 1 : -1;
|
||||
// Determine key to send
|
||||
if (isXAxis) {
|
||||
key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_RIGHT :
|
||||
KeyEvent.KEYCODE_DPAD_LEFT;
|
||||
} else {
|
||||
key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
|
||||
}
|
||||
// Send key until maximum distance constraint is satisfied
|
||||
while (dominantAxis * dominantAxis > mDistancePerTickSquared) {
|
||||
repeatCount++;
|
||||
dominantAxis -= sign * mDistancePerTick;
|
||||
viewroot.enqueueInputEvent(new KeyEvent(time, time,
|
||||
KeyEvent.ACTION_DOWN, key, 0, event.getMetaState(),
|
||||
event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK, event.getSource()));
|
||||
viewroot.enqueueInputEvent(new KeyEvent(time, time,
|
||||
KeyEvent.ACTION_UP, key, 0, event.getMetaState(),
|
||||
event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK, event.getSource()));
|
||||
}
|
||||
// Save new axis values
|
||||
mAccumulatedX = isXAxis ? dominantAxis : 0;
|
||||
mAccumulatedY = isXAxis ? 0 : dominantAxis;
|
||||
|
||||
mLastKeySent = key;
|
||||
mKeySendRateMs = (int) ((time - mLastTouchPadKeySendTimeMs) / repeatCount);
|
||||
mLastTouchPadKeySendTimeMs = time;
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_HOVER_EXIT:
|
||||
if (time-mLastTouchPadStartTimeMs<MAX_TAP_TIME && mAlwaysInTapRegion){
|
||||
//Trackball Down
|
||||
trackballEvent = MotionEvent.obtain(mLastTouchPadStartTimeMs, time,
|
||||
MotionEvent.ACTION_DOWN, 0, 0, 0, 0, event.getMetaState(),
|
||||
10f, 10f, event.getDeviceId(), 0);
|
||||
if (time - mLastTouchPadStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
|
||||
// Trackball Down
|
||||
MotionEvent trackballEvent = MotionEvent.obtain(mLastTouchPadStartTimeMs, time,
|
||||
MotionEvent.ACTION_DOWN, 0, 0, 0, 0, event.getMetaState(),
|
||||
10f, 10f, event.getDeviceId(), 0);
|
||||
trackballEvent.setSource(InputDevice.SOURCE_CLASS_TRACKBALL);
|
||||
//Add the new event to event queue
|
||||
viewroot.enqueueInputEvent(trackballEvent);
|
||||
|
||||
//Trackball Release
|
||||
// Trackball Release
|
||||
trackballEvent = MotionEvent.obtain(mLastTouchPadStartTimeMs, time,
|
||||
MotionEvent.ACTION_UP, 0, 0, 0, 0, event.getMetaState(),
|
||||
10f, 10f, event.getDeviceId(), 0);
|
||||
MotionEvent.ACTION_UP, 0, 0, 0, 0, event.getMetaState(),
|
||||
10f, 10f, event.getDeviceId(), 0);
|
||||
trackballEvent.setSource(InputDevice.SOURCE_CLASS_TRACKBALL);
|
||||
//Add the new event to event queue
|
||||
viewroot.enqueueInputEvent(trackballEvent);
|
||||
} else {
|
||||
float xMoveSquared = mLastMoveX * mLastMoveX;
|
||||
float yMoveSquared = mLastMoveY * mLastMoveY;
|
||||
// Determine whether the last gesture was a fling.
|
||||
if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared &&
|
||||
time - mLastTouchPadEventTimeMs <= MAX_TAP_TIME &&
|
||||
mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) {
|
||||
Message message = Message.obtain(mHandler, FLICK_MSG_ID,
|
||||
mKeySendRateMs, mLastKeySent, viewroot);
|
||||
mRecentEvent = event;
|
||||
mHandler.sendMessageDelayed(message, mKeySendRateMs);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
//Store touch event position
|
||||
// Store touch event position and time
|
||||
mLastTouchPadEventTimeMs = time;
|
||||
mLastTouchpadXPosition = event.getX();
|
||||
mLastTouchpadYPosition = event.getY();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user