summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2011-05-25 14:42:01 -0700
committerAndroid Git Automerger <android-git-automerger@android.com>2011-05-25 14:42:01 -0700
commit4e3ba25cc718bbc6db0a332c4105debc4b3552d3 (patch)
tree41ea26bf7a101268ac614d362393b7bd2705f4fb /libs
parentcfb046dead49568de6f6808a697e0508ef39a3a6 (diff)
parent4bd89fb00f319c5d6d4f17b39fd4c0b3dc827ad1 (diff)
downloadframeworks_base-4e3ba25cc718bbc6db0a332c4105debc4b3552d3.zip
frameworks_base-4e3ba25cc718bbc6db0a332c4105debc4b3552d3.tar.gz
frameworks_base-4e3ba25cc718bbc6db0a332c4105debc4b3552d3.tar.bz2
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)
Diffstat (limited to 'libs')
-rw-r--r--libs/ui/Input.cpp154
-rw-r--r--libs/ui/InputTransport.cpp4
2 files changed, 156 insertions, 2 deletions
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 19d590a..0f13879 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -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() {
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index 5c57a76..9d1b8b9 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -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;