Split up the event synthesis code by source.
The goal is to better encapsulate this code to make it easier to maintain and to facilitate some upcoming changes. Some of the variables have been renamed but the logic is unchanged. Bug: 8583760 Change-Id: I45501f7dabebcb938e42c386291d615d088a4c8c
This commit is contained in:
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class creates DPAD events from TouchNavigation events.
|
||||
*
|
||||
* @see ViewRootImpl
|
||||
*/
|
||||
|
||||
//TODO: Make this class an internal class of ViewRootImpl.java
|
||||
class SimulatedDpad {
|
||||
|
||||
private static final String TAG = "SimulatedDpad";
|
||||
|
||||
// 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;
|
||||
// Where the cutoff is for determining an edge swipe
|
||||
private static final float EDGE_SWIPE_THRESHOLD = 0.9f;
|
||||
private static final int MSG_FLICK = 313;
|
||||
// TODO: Pass touch slop from the input device
|
||||
private static final int TOUCH_SLOP = 30;
|
||||
// The position of the previous TouchNavigation event
|
||||
private float mLastTouchNavigationXPosition;
|
||||
private float mLastTouchNavigationYPosition;
|
||||
// Where the Touch Navigation was initially pressed
|
||||
private float mTouchNavigationEnterXPosition;
|
||||
private float mTouchNavigationEnterYPosition;
|
||||
// When the most recent ACTION_HOVER_ENTER occurred
|
||||
private long mLastTouchNavigationStartTimeMs = 0;
|
||||
// When the most recent direction key was sent
|
||||
private long mLastTouchNavigationKeySendTimeMs = 0;
|
||||
// When the most recent touch event of any type occurred
|
||||
private long mLastTouchNavigationEventTimeMs = 0;
|
||||
// Did the swipe begin in a valid region
|
||||
private boolean mEdgeSwipePossible;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
// 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
|
||||
private boolean mAlwaysInTapRegion = true;
|
||||
|
||||
// Information from the most recent event.
|
||||
// Used to determine what device sent the event during a fling.
|
||||
private int mLastSource;
|
||||
private int mLastMetaState;
|
||||
private int mLastDeviceId;
|
||||
|
||||
// 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 SimulatedDpad(Context context) {
|
||||
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 = TOUCH_SLOP;
|
||||
mTouchSlopSquared = mTouchSlop * mTouchSlop;
|
||||
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
private final Handler mHandler = new Handler(true /*async*/) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_FLICK: {
|
||||
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, mLastMetaState,
|
||||
mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
|
||||
viewroot.enqueueInputEvent(new KeyEvent(time, time,
|
||||
KeyEvent.ACTION_UP, msg.arg2, 0, mLastMetaState,
|
||||
mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
|
||||
|
||||
// Increase the delay by the decay factor and resend
|
||||
final int delay = (int) Math.ceil(mFlickDecay * msg.arg1);
|
||||
if (delay <= mMaxRepeatDelay) {
|
||||
Message msgCopy = Message.obtain(msg);
|
||||
msgCopy.arg1 = delay;
|
||||
msgCopy.setAsynchronous(true);
|
||||
mHandler.sendMessageDelayed(msgCopy, delay);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void updateTouchNavigation(ViewRootImpl viewroot, MotionEvent event,
|
||||
boolean synthesizeNewKeys) {
|
||||
if (!synthesizeNewKeys) {
|
||||
mHandler.removeMessages(MSG_FLICK);
|
||||
}
|
||||
InputDevice device = event.getDevice();
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
// Store what time the TouchNavigation event occurred
|
||||
final long time = SystemClock.uptimeMillis();
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mLastTouchNavigationStartTimeMs = time;
|
||||
mAlwaysInTapRegion = true;
|
||||
mTouchNavigationEnterXPosition = event.getX();
|
||||
mTouchNavigationEnterYPosition = event.getY();
|
||||
mAccumulatedX = 0;
|
||||
mAccumulatedY = 0;
|
||||
mLastMoveX = 0;
|
||||
mLastMoveY = 0;
|
||||
if (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
|
||||
* EDGE_SWIPE_THRESHOLD < event.getY()) {
|
||||
// Did the swipe begin in a valid region
|
||||
mEdgeSwipePossible = true;
|
||||
}
|
||||
// Clear any flings
|
||||
if (synthesizeNewKeys) {
|
||||
mHandler.removeMessages(MSG_FLICK);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// Determine whether the move is slop or an intentional move
|
||||
float deltaX = event.getX() - mTouchNavigationEnterXPosition;
|
||||
float deltaY = event.getY() - mTouchNavigationEnterYPosition;
|
||||
if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY) {
|
||||
mAlwaysInTapRegion = false;
|
||||
}
|
||||
// Checks if the swipe has crossed the midpoint
|
||||
// and if our swipe gesture is complete
|
||||
if (event.getY() < (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
|
||||
* .5) && mEdgeSwipePossible) {
|
||||
mEdgeSwipePossible = false;
|
||||
|
||||
Intent intent =
|
||||
((SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE))
|
||||
.getAssistIntent(mContext, false, UserHandle.USER_CURRENT_OR_SELF);
|
||||
if (intent != null) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
try {
|
||||
mContext.startActivity(intent);
|
||||
} catch (ActivityNotFoundException e){
|
||||
Log.e(TAG, "Could not start search activity");
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Could not find a search activity");
|
||||
}
|
||||
}
|
||||
// Find the difference in position between the two most recent
|
||||
// TouchNavigation events
|
||||
mLastMoveX = event.getX() - mLastTouchNavigationXPosition;
|
||||
mLastMoveY = event.getY() - mLastTouchNavigationYPosition;
|
||||
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;
|
||||
if (synthesizeNewKeys) {
|
||||
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 - mLastTouchNavigationKeySendTimeMs) / repeatCount;
|
||||
mLastTouchNavigationKeySendTimeMs = time;
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (time - mLastTouchNavigationStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
|
||||
if (synthesizeNewKeys) {
|
||||
viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
|
||||
time, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0,
|
||||
event.getMetaState(), event.getDeviceId(), 0,
|
||||
KeyEvent.FLAG_FALLBACK, event.getSource()));
|
||||
viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
|
||||
time, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0,
|
||||
event.getMetaState(), event.getDeviceId(), 0,
|
||||
KeyEvent.FLAG_FALLBACK, event.getSource()));
|
||||
}
|
||||
} else {
|
||||
float xMoveSquared = mLastMoveX * mLastMoveX;
|
||||
float yMoveSquared = mLastMoveY * mLastMoveY;
|
||||
// Determine whether the last gesture was a fling.
|
||||
if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared &&
|
||||
time - mLastTouchNavigationEventTimeMs <= MAX_TAP_TIME &&
|
||||
mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) {
|
||||
mLastDeviceId = event.getDeviceId();
|
||||
mLastSource = event.getSource();
|
||||
mLastMetaState = event.getMetaState();
|
||||
|
||||
if (synthesizeNewKeys) {
|
||||
Message message = Message.obtain(mHandler, MSG_FLICK,
|
||||
mKeySendRateMs, mLastKeySent, viewroot);
|
||||
message.setAsynchronous(true);
|
||||
mHandler.sendMessageDelayed(message, mKeySendRateMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
mEdgeSwipePossible = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Store touch event position and time
|
||||
mLastTouchNavigationEventTimeMs = time;
|
||||
mLastTouchNavigationXPosition = event.getX();
|
||||
mLastTouchNavigationYPosition = event.getY();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user