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:
Jeff Brown
2013-04-09 17:46:25 -07:00
parent d7094ea29b
commit 678a1252b4
2 changed files with 561 additions and 569 deletions

View File

@@ -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