am 4bd89fb0: am 82e4373e: Merge "Use touch pad gestures to manipulate the pointer. (DO NOT MERGE)" into honeycomb-mr2
* commit '4bd89fb00f319c5d6d4f17b39fd4c0b3dc827ad1': Use touch pad gestures to manipulate the pointer. (DO NOT MERGE)
This commit is contained in:
commit
446b400910
@ -27,6 +27,7 @@
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/BitSet.h>
|
||||
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
class SkMatrix;
|
||||
@ -210,6 +211,13 @@ struct PointerCoords {
|
||||
status_t writeToParcel(Parcel* parcel) const;
|
||||
#endif
|
||||
|
||||
bool operator==(const PointerCoords& other) const;
|
||||
inline bool operator!=(const PointerCoords& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void copyFrom(const PointerCoords& other);
|
||||
|
||||
private:
|
||||
void tooManyAxes(int axis);
|
||||
};
|
||||
@ -544,6 +552,53 @@ private:
|
||||
MotionEvent mMotionEvent;
|
||||
};
|
||||
|
||||
/*
|
||||
* Calculates the velocity of pointer motions over time.
|
||||
* Uses essentially the same algorithm as android.view.VelocityTracker.
|
||||
*/
|
||||
class VelocityTracker {
|
||||
public:
|
||||
struct Position {
|
||||
float x, y;
|
||||
};
|
||||
|
||||
VelocityTracker();
|
||||
|
||||
// Resets the velocity tracker state.
|
||||
void clear();
|
||||
|
||||
// Adds movement information for a set of pointers.
|
||||
// The idBits bitfield specifies the pointer ids of the pointers whose positions
|
||||
// are included in the movement.
|
||||
// The positions array contains position information for each pointer in order by
|
||||
// increasing id. Its size should be equal to the number of one bits in idBits.
|
||||
void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
|
||||
|
||||
// Gets the velocity of the specified pointer id in position units per second.
|
||||
// Returns false and sets the velocity components to zero if there is no movement
|
||||
// information for the pointer.
|
||||
bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
|
||||
|
||||
private:
|
||||
// Number of samples to keep.
|
||||
static const uint32_t HISTORY_SIZE = 10;
|
||||
|
||||
// Oldest sample to consider when calculating the velocity.
|
||||
static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms
|
||||
|
||||
// The minimum duration between samples when estimating velocity.
|
||||
static const nsecs_t MIN_DURATION = 5 * 1000000; // 5 ms
|
||||
|
||||
struct Movement {
|
||||
nsecs_t eventTime;
|
||||
BitSet32 idBits;
|
||||
Position positions[MAX_POINTERS];
|
||||
};
|
||||
|
||||
uint32_t mIndex;
|
||||
Movement mMovements[HISTORY_SIZE];
|
||||
};
|
||||
|
||||
/*
|
||||
* Describes the characteristics and capabilities of an input device.
|
||||
*/
|
||||
|
@ -61,6 +61,12 @@ struct BitSet32 {
|
||||
// Result is undefined if all bits are marked.
|
||||
inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }
|
||||
|
||||
// Gets the index of the specified bit in the set, which is the number of
|
||||
// marked bits that appear before the specified bit.
|
||||
inline uint32_t getIndexOfBit(uint32_t n) const {
|
||||
return __builtin_popcount(value & ~(0xffffffffUL >> n));
|
||||
}
|
||||
|
||||
inline bool operator== (const BitSet32& other) const { return value == other.value; }
|
||||
inline bool operator!= (const BitSet32& other) const { return value != other.value; }
|
||||
};
|
||||
|
@ -7,8 +7,12 @@
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
// Log debug messages about keymap probing.
|
||||
#define DEBUG_PROBE 0
|
||||
|
||||
// Log debug messages about velocity tracking.
|
||||
#define DEBUG_VELOCITY 0
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
@ -347,6 +351,27 @@ void PointerCoords::tooManyAxes(int axis) {
|
||||
"cannot contain more than %d axis values.", axis, int(MAX_AXES));
|
||||
}
|
||||
|
||||
bool PointerCoords::operator==(const PointerCoords& other) const {
|
||||
if (bits != other.bits) {
|
||||
return false;
|
||||
}
|
||||
uint32_t count = __builtin_popcountll(bits);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (values[i] != other.values[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PointerCoords::copyFrom(const PointerCoords& other) {
|
||||
bits = other.bits;
|
||||
uint32_t count = __builtin_popcountll(bits);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
values[i] = other.values[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- MotionEvent ---
|
||||
|
||||
@ -633,6 +658,135 @@ bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
|
||||
}
|
||||
|
||||
|
||||
// --- VelocityTracker ---
|
||||
|
||||
VelocityTracker::VelocityTracker() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void VelocityTracker::clear() {
|
||||
mIndex = 0;
|
||||
mMovements[0].idBits.clear();
|
||||
}
|
||||
|
||||
void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
|
||||
if (++mIndex == HISTORY_SIZE) {
|
||||
mIndex = 0;
|
||||
}
|
||||
Movement& movement = mMovements[mIndex];
|
||||
movement.eventTime = eventTime;
|
||||
movement.idBits = idBits;
|
||||
uint32_t count = idBits.count();
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
movement.positions[i] = positions[i];
|
||||
}
|
||||
|
||||
#if DEBUG_VELOCITY
|
||||
LOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x", eventTime, idBits.value);
|
||||
for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
|
||||
uint32_t id = iterBits.firstMarkedBit();
|
||||
uint32_t index = idBits.getIndexOfBit(id);
|
||||
iterBits.clearBit(id);
|
||||
float vx, vy;
|
||||
bool available = getVelocity(id, &vx, &vy);
|
||||
if (available) {
|
||||
LOGD(" %d: position (%0.3f, %0.3f), velocity (%0.3f, %0.3f), speed %0.3f",
|
||||
id, positions[index].x, positions[index].y, vx, vy, sqrtf(vx * vx + vy * vy));
|
||||
} else {
|
||||
assert(vx == 0 && vy == 0);
|
||||
LOGD(" %d: position (%0.3f, %0.3f), velocity not available",
|
||||
id, positions[index].x, positions[index].y);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
|
||||
const Movement& newestMovement = mMovements[mIndex];
|
||||
if (newestMovement.idBits.hasBit(id)) {
|
||||
// Find the oldest sample that contains the pointer and that is not older than MAX_AGE.
|
||||
nsecs_t minTime = newestMovement.eventTime - MAX_AGE;
|
||||
uint32_t oldestIndex = mIndex;
|
||||
uint32_t numTouches = 1;
|
||||
do {
|
||||
uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
|
||||
const Movement& nextOldestMovement = mMovements[nextOldestIndex];
|
||||
if (!nextOldestMovement.idBits.hasBit(id)
|
||||
|| nextOldestMovement.eventTime < minTime) {
|
||||
break;
|
||||
}
|
||||
oldestIndex = nextOldestIndex;
|
||||
} while (++numTouches < HISTORY_SIZE);
|
||||
|
||||
// If we have a lot of samples, skip the last received sample since it is
|
||||
// probably pretty noisy compared to the sum of all of the traces already acquired.
|
||||
//
|
||||
// NOTE: This condition exists in the android.view.VelocityTracker and imposes a
|
||||
// bias against the most recent data.
|
||||
if (numTouches > 3) {
|
||||
numTouches -= 1;
|
||||
}
|
||||
|
||||
// Calculate an exponentially weighted moving average of the velocity at different
|
||||
// points in time measured relative to the oldest samples. This is essentially
|
||||
// an IIR filter.
|
||||
//
|
||||
// One problem with this algorithm is that the sample data may be poorly conditioned.
|
||||
// Sometimes samples arrive very close together in time which can cause us to
|
||||
// overestimate the velocity at that time point. Most samples might be measured
|
||||
// 16ms apart but some consecutive samples could be only 0.5sm apart due to
|
||||
// the way they are reported by the hardware or driver (sometimes in bursts or with
|
||||
// significant jitter). The instantaneous velocity for those samples 0.5ms apart will
|
||||
// be calculated to be 32 times what it should have been.
|
||||
// To work around this effect, we impose a minimum duration on the samples.
|
||||
//
|
||||
// FIXME: Samples close together in time can have an disproportionately large
|
||||
// impact on the result because all samples are equally weighted. The average should
|
||||
// instead take the time factor into account.
|
||||
//
|
||||
// FIXME: The minimum duration condition does not exist in
|
||||
// android.view.VelocityTracker yet. It is less important there because sample times
|
||||
// are truncated to the millisecond so back to back samples will often appear to be
|
||||
// zero milliseconds apart and will be ignored if they are the oldest ones.
|
||||
float accumVx = 0;
|
||||
float accumVy = 0;
|
||||
uint32_t index = oldestIndex;
|
||||
uint32_t samplesUsed = 0;
|
||||
const Movement& oldestMovement = mMovements[oldestIndex];
|
||||
const Position& oldestPosition =
|
||||
oldestMovement.positions[oldestMovement.idBits.getIndexOfBit(id)];
|
||||
while (numTouches-- > 1) {
|
||||
if (++index == HISTORY_SIZE) {
|
||||
index = 0;
|
||||
}
|
||||
const Movement& movement = mMovements[index];
|
||||
nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
|
||||
if (duration > MIN_DURATION) {
|
||||
const Position& position = movement.positions[movement.idBits.getIndexOfBit(id)];
|
||||
float scale = 1000000000.0f / duration; // one over time delta in seconds
|
||||
float vx = (position.x - oldestPosition.x) * scale;
|
||||
float vy = (position.y - oldestPosition.y) * scale;
|
||||
accumVx = accumVx == 0 ? vx : (accumVx + vx) * 0.5f;
|
||||
accumVy = accumVy == 0 ? vy : (accumVy + vy) * 0.5f;
|
||||
samplesUsed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we used at least one sample.
|
||||
if (samplesUsed != 0) {
|
||||
*outVx = accumVx;
|
||||
*outVy = accumVy;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No data available for this pointer.
|
||||
*outVx = 0;
|
||||
*outVy = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// --- InputDeviceInfo ---
|
||||
|
||||
InputDeviceInfo::InputDeviceInfo() {
|
||||
|
@ -406,7 +406,7 @@ status_t InputPublisher::publishMotionEvent(
|
||||
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
mSharedMessage->motion.pointerIds[i] = pointerIds[i];
|
||||
mSharedMessage->motion.sampleData[0].coords[i] = pointerCoords[i];
|
||||
mSharedMessage->motion.sampleData[0].coords[i].copyFrom(pointerCoords[i]);
|
||||
}
|
||||
|
||||
// Cache essential information about the motion event to ensure that a malicious consumer
|
||||
@ -475,7 +475,7 @@ status_t InputPublisher::appendMotionSample(
|
||||
|
||||
mMotionEventSampleDataTail->eventTime = eventTime;
|
||||
for (size_t i = 0; i < mMotionEventPointerCount; i++) {
|
||||
mMotionEventSampleDataTail->coords[i] = pointerCoords[i];
|
||||
mMotionEventSampleDataTail->coords[i].copyFrom(pointerCoords[i]);
|
||||
}
|
||||
mMotionEventSampleDataTail = newTail;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user