Add new axes for joysticks and mouse wheels.

Added API on InputDevice to query the set of axes available.
Added API on KeyEvent and MotionEvent to convert keycodes and axes
to symbolic name strings for diagnostic purposes.
Added API on KeyEvent to query if a given key code is a gamepad button.
Added a new "axis" element to key layout files to specify the
mapping between raw absolute axis values and motion axis ids.
Expanded the axis bitfield to 64bits to allow for future growth.
Modified the Makefile for keyboard prebuilts to run the keymap
validation tool during the build.
Added layouts for two game controllers.
Added default actions for game pad button keys.
Added more tests.
Fixed a bunch of bugs.

Change-Id: I73f9166c3b3c5bcf4970845b58088ad467525525
This commit is contained in:
Jeff Brown 2011-02-19 01:08:02 -08:00
parent d647fdcb9c
commit 3ea4de826d
8 changed files with 489 additions and 177 deletions

View File

@ -170,10 +170,10 @@ struct InputConfiguration {
* Pointer coordinate data.
*/
struct PointerCoords {
enum { MAX_AXES = 15 }; // 15 so that sizeof(PointerCoords) == 16 * 4 == 64
enum { MAX_AXES = 14 }; // 14 so that sizeof(PointerCoords) == 64
// Bitfield of axes that are present in this structure.
uint32_t bits; // 32bits are enough for now, can raise to 64bit when needed
uint64_t bits;
// Values of axes that are stored in this structure packed in order by axis id
// for each axis that is present in the structure according to 'bits'.
@ -183,41 +183,9 @@ struct PointerCoords {
bits = 0;
}
inline float getAxisValue(int32_t axis) const {
uint32_t axisBit = 1 << axis;
if (!(bits & axisBit)) {
return 0;
}
uint32_t index = __builtin_popcount(bits & (axisBit - 1));
return values[index];
}
inline status_t setAxisValue(int32_t axis, float value) {
uint32_t axisBit = 1 << axis;
uint32_t index = __builtin_popcount(bits & (axisBit - 1));
if (!(bits & axisBit)) {
uint32_t count = __builtin_popcount(bits);
if (count >= MAX_AXES) {
tooManyAxes(axis);
return NO_MEMORY;
}
bits |= axisBit;
for (uint32_t i = count; i > index; i--) {
values[i] = values[i - 1];
}
}
values[index] = value;
return OK;
}
inline float* editAxisValue(int32_t axis) {
uint32_t axisBit = 1 << axis;
if (!(bits & axisBit)) {
return NULL;
}
uint32_t index = __builtin_popcount(bits & (axisBit - 1));
return &values[index];
}
float getAxisValue(int32_t axis) const;
status_t setAxisValue(int32_t axis, float value);
float* editAxisValue(int32_t axis);
#ifdef HAVE_ANDROID_OS
status_t readFromParcel(Parcel* parcel);

View File

@ -25,7 +25,7 @@
namespace android {
/**
* Describes a mapping from keyboard scan codes to Android key codes.
* Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes.
*/
class KeyLayoutMap {
public:
@ -33,8 +33,10 @@ public:
static status_t load(const String8& filename, KeyLayoutMap** outMap);
status_t map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const;
status_t findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
status_t mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const;
status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
status_t mapAxis(int32_t scanCode, int32_t* axis) const;
private:
struct Key {
@ -42,7 +44,8 @@ private:
uint32_t flags;
};
KeyedVector<int32_t,Key> mKeys;
KeyedVector<int32_t, Key> mKeys;
KeyedVector<int32_t, int32_t> mAxes;
KeyLayoutMap();
@ -57,6 +60,7 @@ private:
private:
status_t parseKey();
status_t parseAxis();
};
};

View File

@ -110,6 +110,18 @@ extern int32_t getKeyCodeByLabel(const char* label);
*/
extern uint32_t getKeyFlagByLabel(const char* label);
/**
* Gets a axis by its short form label, eg. "X".
* Returns -1 if unknown.
*/
extern int32_t getAxisByLabel(const char* label);
/**
* Gets a axis label by its id.
* Returns NULL if unknown.
*/
extern const char* getAxisLabel(int32_t axisId);
/**
* Updates a meta state field when a key is pressed or released.
*/

View File

@ -250,4 +250,47 @@ static const KeycodeLabel FLAGS[] = {
{ NULL, 0 }
};
static const KeycodeLabel AXES[] = {
{ "X", 0 },
{ "Y", 1 },
{ "PRESSURE", 2 },
{ "SIZE", 3 },
{ "TOUCH_MAJOR", 4 },
{ "TOUCH_MINOR", 5 },
{ "TOOL_MAJOR", 6 },
{ "TOOL_MINOR", 7 },
{ "ORIENTATION", 8 },
{ "VSCROLL", 9 },
{ "HSCROLL", 10 },
{ "Z", 11 },
{ "RX", 12 },
{ "RY", 13 },
{ "RZ", 14 },
{ "HAT_X", 15 },
{ "HAT_Y", 16 },
{ "LTRIGGER", 17 },
{ "RTRIGGER", 18 },
{ "GENERIC_1", 32 },
{ "GENERIC_2", 33 },
{ "GENERIC_3", 34 },
{ "GENERIC_4", 35 },
{ "GENERIC_5", 36 },
{ "GENERIC_6", 37 },
{ "GENERIC_7", 38 },
{ "GENERIC_8", 39 },
{ "GENERIC_9", 40 },
{ "GENERIC_10", 41 },
{ "GENERIC_11", 42 },
{ "GENERIC_12", 43 },
{ "GENERIC_13", 44 },
{ "GENERIC_14", 45 },
{ "GENERIC_15", 46 },
{ "GENERIC_16", 47 },
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
{ NULL, -1 }
};
#endif // _UI_KEYCODE_LABELS_H

View File

@ -250,11 +250,59 @@ void KeyEvent::initialize(const KeyEvent& from) {
// --- PointerCoords ---
float PointerCoords::getAxisValue(int32_t axis) const {
if (axis < 0 || axis > 63) {
return 0;
}
uint64_t axisBit = 1LL << axis;
if (!(bits & axisBit)) {
return 0;
}
uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
return values[index];
}
status_t PointerCoords::setAxisValue(int32_t axis, float value) {
if (axis < 0 || axis > 63) {
return NAME_NOT_FOUND;
}
uint64_t axisBit = 1LL << axis;
uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
if (!(bits & axisBit)) {
uint32_t count = __builtin_popcountll(bits);
if (count >= MAX_AXES) {
tooManyAxes(axis);
return NO_MEMORY;
}
bits |= axisBit;
for (uint32_t i = count; i > index; i--) {
values[i] = values[i - 1];
}
}
values[index] = value;
return OK;
}
float* PointerCoords::editAxisValue(int32_t axis) {
if (axis < 0 || axis > 63) {
return NULL;
}
uint64_t axisBit = 1LL << axis;
if (!(bits & axisBit)) {
return NULL;
}
uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
return &values[index];
}
#ifdef HAVE_ANDROID_OS
status_t PointerCoords::readFromParcel(Parcel* parcel) {
bits = parcel->readInt32();
bits = parcel->readInt64();
uint32_t count = __builtin_popcount(bits);
uint32_t count = __builtin_popcountll(bits);
if (count > MAX_AXES) {
return BAD_VALUE;
}
@ -266,9 +314,9 @@ status_t PointerCoords::readFromParcel(Parcel* parcel) {
}
status_t PointerCoords::writeToParcel(Parcel* parcel) const {
parcel->writeInt32(bits);
parcel->writeInt64(bits);
uint32_t count = __builtin_popcount(bits);
uint32_t count = __builtin_popcountll(bits);
for (uint32_t i = 0; i < count; i++) {
parcel->writeInt32(values[i]);
}

View File

@ -82,11 +82,11 @@ status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) {
return status;
}
status_t KeyLayoutMap::map(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const {
status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const {
ssize_t index = mKeys.indexOfKey(scanCode);
if (index < 0) {
#if DEBUG_MAPPING
LOGD("map: scanCode=%d ~ Failed.", scanCode);
LOGD("mapKey: scanCode=%d ~ Failed.", scanCode);
#endif
*keyCode = AKEYCODE_UNKNOWN;
*flags = 0;
@ -98,12 +98,12 @@ status_t KeyLayoutMap::map(int32_t scanCode, int32_t* keyCode, uint32_t* flags)
*flags = k.flags;
#if DEBUG_MAPPING
LOGD("map: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags);
LOGD("mapKey: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags);
#endif
return NO_ERROR;
}
status_t KeyLayoutMap::findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const {
const size_t N = mKeys.size();
for (size_t i=0; i<N; i++) {
if (mKeys.valueAt(i).keyCode == keyCode) {
@ -113,6 +113,25 @@ status_t KeyLayoutMap::findScanCodes(int32_t keyCode, Vector<int32_t>* outScanCo
return NO_ERROR;
}
status_t KeyLayoutMap::mapAxis(int32_t scanCode, int32_t* axis) const {
ssize_t index = mAxes.indexOfKey(scanCode);
if (index < 0) {
#if DEBUG_MAPPING
LOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
#endif
*axis = -1;
return NAME_NOT_FOUND;
}
*axis = mAxes.valueAt(index);
#if DEBUG_MAPPING
LOGD("mapAxis: scanCode=%d ~ Result axis=%d.", scanCode, *axis);
#endif
return NO_ERROR;
}
// --- KeyLayoutMap::Parser ---
KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) :
@ -137,6 +156,10 @@ status_t KeyLayoutMap::Parser::parse() {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseKey();
if (status) return status;
} else if (keywordToken == "axis") {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseAxis();
if (status) return status;
} else {
LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
keywordToken.string());
@ -162,12 +185,12 @@ status_t KeyLayoutMap::Parser::parseKey() {
char* end;
int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
if (*end) {
LOGE("%s: Expected scan code number, got '%s'.", mTokenizer->getLocation().string(),
LOGE("%s: Expected key scan code number, got '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
}
if (mMap->mKeys.indexOfKey(scanCode) >= 0) {
LOGE("%s: Duplicate entry for scan code '%s'.", mTokenizer->getLocation().string(),
LOGE("%s: Duplicate entry for key scan code '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
}
@ -189,12 +212,12 @@ status_t KeyLayoutMap::Parser::parseKey() {
String8 flagToken = mTokenizer->nextToken(WHITESPACE);
uint32_t flag = getKeyFlagByLabel(flagToken.string());
if (!flag) {
LOGE("%s: Expected flag label, got '%s'.", mTokenizer->getLocation().string(),
LOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
flagToken.string());
return BAD_VALUE;
}
if (flags & flag) {
LOGE("%s: Duplicate flag '%s'.", mTokenizer->getLocation().string(),
LOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
flagToken.string());
return BAD_VALUE;
}
@ -211,4 +234,35 @@ status_t KeyLayoutMap::Parser::parseKey() {
return NO_ERROR;
}
status_t KeyLayoutMap::Parser::parseAxis() {
String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
char* end;
int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
if (*end) {
LOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
}
if (mMap->mAxes.indexOfKey(scanCode) >= 0) {
LOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 axisToken = mTokenizer->nextToken(WHITESPACE);
int32_t axis = getAxisByLabel(axisToken.string());
if (axis < 0) {
LOGE("%s: Expected axis label, got '%s'.", mTokenizer->getLocation().string(),
axisToken.string());
return BAD_VALUE;
}
#if DEBUG_PARSER
LOGD("Parsed axis: scanCode=%d, axis=%d.", scanCode, axis);
#endif
mMap->mAxes.add(scanCode, axis);
return NO_ERROR;
}
};

View File

@ -217,7 +217,7 @@ status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFil
return NAME_NOT_FOUND;
}
static int lookupLabel(const char* literal, const KeycodeLabel *list) {
static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) {
while (list->literal) {
if (strcmp(literal, list->literal) == 0) {
return list->value;
@ -227,12 +227,30 @@ static int lookupLabel(const char* literal, const KeycodeLabel *list) {
return list->value;
}
static const char* lookupLabelByValue(int value, const KeycodeLabel *list) {
while (list->literal) {
if (list->value == value) {
return list->literal;
}
list++;
}
return NULL;
}
int32_t getKeyCodeByLabel(const char* label) {
return int32_t(lookupLabel(label, KEYCODES));
return int32_t(lookupValueByLabel(label, KEYCODES));
}
uint32_t getKeyFlagByLabel(const char* label) {
return uint32_t(lookupLabel(label, FLAGS));
return uint32_t(lookupValueByLabel(label, FLAGS));
}
int32_t getAxisByLabel(const char* label) {
return int32_t(lookupValueByLabel(label, AXES));
}
const char* getAxisLabel(int32_t axisId) {
return lookupLabelByValue(axisId, AXES);
}
static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {

View File

@ -18,6 +18,9 @@
#include <gtest/gtest.h>
#include <binder/Parcel.h>
#include <math.h>
#include <SkMatrix.h>
namespace android {
class BaseTest : public testing::Test {
@ -35,7 +38,7 @@ TEST_F(PointerCoordsTest, ClearSetsBitsToZero) {
PointerCoords coords;
coords.clear();
ASSERT_EQ(0U, coords.bits);
ASSERT_EQ(0ULL, coords.bits);
}
TEST_F(PointerCoordsTest, AxisValues) {
@ -54,7 +57,7 @@ TEST_F(PointerCoordsTest, AxisValues) {
// Set first axis.
ASSERT_EQ(OK, coords.setAxisValue(1, 5));
ASSERT_EQ(0x00000002U, coords.bits);
ASSERT_EQ(0x00000002ULL, coords.bits);
ASSERT_EQ(5, coords.values[0]);
ASSERT_EQ(0, coords.getAxisValue(0))
@ -64,7 +67,7 @@ TEST_F(PointerCoordsTest, AxisValues) {
// Set an axis with a higher id than all others. (appending value at the end)
ASSERT_EQ(OK, coords.setAxisValue(3, 2));
ASSERT_EQ(0x0000000aU, coords.bits);
ASSERT_EQ(0x0000000aULL, coords.bits);
ASSERT_EQ(5, coords.values[0]);
ASSERT_EQ(2, coords.values[1]);
@ -79,7 +82,7 @@ TEST_F(PointerCoordsTest, AxisValues) {
// Set an axis with an id lower than all others. (prepending value at beginning)
ASSERT_EQ(OK, coords.setAxisValue(0, 4));
ASSERT_EQ(0x0000000bU, coords.bits);
ASSERT_EQ(0x0000000bULL, coords.bits);
ASSERT_EQ(4, coords.values[0]);
ASSERT_EQ(5, coords.values[1]);
ASSERT_EQ(2, coords.values[2]);
@ -104,7 +107,7 @@ TEST_F(PointerCoordsTest, AxisValues) {
// Set an axis with an id between the others. (inserting value in the middle)
ASSERT_EQ(OK, coords.setAxisValue(2, 1));
ASSERT_EQ(0x0000000fU, coords.bits);
ASSERT_EQ(0x0000000fULL, coords.bits);
ASSERT_EQ(4, coords.values[0]);
ASSERT_EQ(7, coords.values[1]);
ASSERT_EQ(1, coords.values[2]);
@ -121,7 +124,7 @@ TEST_F(PointerCoordsTest, AxisValues) {
// Set an existing axis value in place.
ASSERT_EQ(OK, coords.setAxisValue(1, 6));
ASSERT_EQ(0x0000000fU, coords.bits);
ASSERT_EQ(0x0000000fULL, coords.bits);
ASSERT_EQ(4, coords.values[0]);
ASSERT_EQ(6, coords.values[1]);
ASSERT_EQ(1, coords.values[2]);
@ -140,15 +143,15 @@ TEST_F(PointerCoordsTest, AxisValues) {
for (size_t axis = 4; axis < PointerCoords::MAX_AXES; axis++) {
ASSERT_EQ(OK, coords.setAxisValue(axis, axis));
}
ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcount(coords.bits));
ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits));
// Try to set one more axis beyond maximum number.
// Ensure bits are unchanged.
ASSERT_EQ(NO_MEMORY, coords.setAxisValue(PointerCoords::MAX_AXES, 100));
ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcount(coords.bits));
ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits));
}
TEST_F(PointerCoordsTest, ReadAndWriteParcel) {
TEST_F(PointerCoordsTest, Parcel) {
Parcel parcel;
PointerCoords inCoords;
@ -160,7 +163,7 @@ TEST_F(PointerCoordsTest, ReadAndWriteParcel) {
parcel.setDataPosition(0);
outCoords.readFromParcel(&parcel);
ASSERT_EQ(0U, outCoords.bits);
ASSERT_EQ(0ULL, outCoords.bits);
// Round trip with some values.
parcel.freeData();
@ -213,16 +216,22 @@ TEST_F(KeyEventTest, Properties) {
// --- MotionEventTest ---
class MotionEventTest : public BaseTest {
protected:
static const nsecs_t ARBITRARY_DOWN_TIME;
static const nsecs_t ARBITRARY_EVENT_TIME;
static const float X_OFFSET;
static const float Y_OFFSET;
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
TEST_F(MotionEventTest, Properties) {
MotionEvent event;
const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1;
const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2;
const float MotionEventTest::X_OFFSET = 1.0f;
const float MotionEventTest::Y_OFFSET = 1.1f;
// Initialize, add samples and get properties.
const nsecs_t ARBITRARY_DOWN_TIME = 1;
const nsecs_t ARBITRARY_EVENT_TIME = 2;
const float X_OFFSET = 1.0f;
const float Y_OFFSET = 1.1f;
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
int32_t pointerIds[] = { 1, 2 };
PointerCoords pointerCoords[2];
pointerCoords[0].clear();
@ -245,7 +254,7 @@ TEST_F(MotionEventTest, Properties) {
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28);
event.initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE,
event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE,
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON,
X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
@ -270,7 +279,7 @@ TEST_F(MotionEventTest, Properties) {
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128);
event.addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211);
@ -290,128 +299,284 @@ TEST_F(MotionEventTest, Properties) {
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227);
pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228);
event.addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords);
}
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event.getType());
ASSERT_EQ(2, event.getDeviceId());
ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event.getSource());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event.getAction());
ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event.getFlags());
ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event.getEdgeFlags());
ASSERT_EQ(AMETA_ALT_ON, event.getMetaState());
ASSERT_EQ(X_OFFSET, event.getXOffset());
ASSERT_EQ(Y_OFFSET, event.getYOffset());
ASSERT_EQ(2.0f, event.getXPrecision());
ASSERT_EQ(2.1f, event.getYPrecision());
ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime());
void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) {
// Check properties.
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
ASSERT_EQ(2, event->getDeviceId());
ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
ASSERT_EQ(X_OFFSET, event->getXOffset());
ASSERT_EQ(Y_OFFSET, event->getYOffset());
ASSERT_EQ(2.0f, event->getXPrecision());
ASSERT_EQ(2.1f, event->getYPrecision());
ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime());
ASSERT_EQ(2U, event.getPointerCount());
ASSERT_EQ(1, event.getPointerId(0));
ASSERT_EQ(2, event.getPointerId(1));
ASSERT_EQ(2U, event->getPointerCount());
ASSERT_EQ(1, event->getPointerId(0));
ASSERT_EQ(2, event->getPointerId(1));
ASSERT_EQ(2U, event.getHistorySize());
ASSERT_EQ(2U, event->getHistorySize());
// Get data.
ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getHistoricalEventTime(0));
ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event.getHistoricalEventTime(1));
ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event.getEventTime());
// Check data.
ASSERT_EQ(ARBITRARY_EVENT_TIME, event->getHistoricalEventTime(0));
ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
ASSERT_EQ(11, event.getHistoricalRawPointerCoords(0, 0)->
ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
ASSERT_EQ(21, event.getHistoricalRawPointerCoords(1, 0)->
ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
ASSERT_EQ(111, event.getHistoricalRawPointerCoords(0, 1)->
ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
ASSERT_EQ(121, event.getHistoricalRawPointerCoords(1, 1)->
ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
ASSERT_EQ(211, event.getRawPointerCoords(0)->
ASSERT_EQ(211, event->getRawPointerCoords(0)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
ASSERT_EQ(221, event.getRawPointerCoords(1)->
ASSERT_EQ(221, event->getRawPointerCoords(1)->
getAxisValue(AMOTION_EVENT_AXIS_Y));
ASSERT_EQ(11, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
ASSERT_EQ(21, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
ASSERT_EQ(111, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
ASSERT_EQ(121, event.getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
ASSERT_EQ(211, event.getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
ASSERT_EQ(221, event.getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
ASSERT_EQ(10, event.getHistoricalRawX(0, 0));
ASSERT_EQ(20, event.getHistoricalRawX(1, 0));
ASSERT_EQ(110, event.getHistoricalRawX(0, 1));
ASSERT_EQ(120, event.getHistoricalRawX(1, 1));
ASSERT_EQ(210, event.getRawX(0));
ASSERT_EQ(220, event.getRawX(1));
ASSERT_EQ(10, event->getHistoricalRawX(0, 0));
ASSERT_EQ(20, event->getHistoricalRawX(1, 0));
ASSERT_EQ(110, event->getHistoricalRawX(0, 1));
ASSERT_EQ(120, event->getHistoricalRawX(1, 1));
ASSERT_EQ(210, event->getRawX(0));
ASSERT_EQ(220, event->getRawX(1));
ASSERT_EQ(11, event.getHistoricalRawY(0, 0));
ASSERT_EQ(21, event.getHistoricalRawY(1, 0));
ASSERT_EQ(111, event.getHistoricalRawY(0, 1));
ASSERT_EQ(121, event.getHistoricalRawY(1, 1));
ASSERT_EQ(211, event.getRawY(0));
ASSERT_EQ(221, event.getRawY(1));
ASSERT_EQ(11, event->getHistoricalRawY(0, 0));
ASSERT_EQ(21, event->getHistoricalRawY(1, 0));
ASSERT_EQ(111, event->getHistoricalRawY(0, 1));
ASSERT_EQ(121, event->getHistoricalRawY(1, 1));
ASSERT_EQ(211, event->getRawY(0));
ASSERT_EQ(221, event->getRawY(1));
ASSERT_EQ(X_OFFSET + 10, event.getHistoricalX(0, 0));
ASSERT_EQ(X_OFFSET + 20, event.getHistoricalX(1, 0));
ASSERT_EQ(X_OFFSET + 110, event.getHistoricalX(0, 1));
ASSERT_EQ(X_OFFSET + 120, event.getHistoricalX(1, 1));
ASSERT_EQ(X_OFFSET + 210, event.getX(0));
ASSERT_EQ(X_OFFSET + 220, event.getX(1));
ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0));
ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0));
ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1));
ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1));
ASSERT_EQ(X_OFFSET + 210, event->getX(0));
ASSERT_EQ(X_OFFSET + 220, event->getX(1));
ASSERT_EQ(Y_OFFSET + 11, event.getHistoricalY(0, 0));
ASSERT_EQ(Y_OFFSET + 21, event.getHistoricalY(1, 0));
ASSERT_EQ(Y_OFFSET + 111, event.getHistoricalY(0, 1));
ASSERT_EQ(Y_OFFSET + 121, event.getHistoricalY(1, 1));
ASSERT_EQ(Y_OFFSET + 211, event.getY(0));
ASSERT_EQ(Y_OFFSET + 221, event.getY(1));
ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0));
ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0));
ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1));
ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1));
ASSERT_EQ(Y_OFFSET + 211, event->getY(0));
ASSERT_EQ(Y_OFFSET + 221, event->getY(1));
ASSERT_EQ(12, event.getHistoricalPressure(0, 0));
ASSERT_EQ(22, event.getHistoricalPressure(1, 0));
ASSERT_EQ(112, event.getHistoricalPressure(0, 1));
ASSERT_EQ(122, event.getHistoricalPressure(1, 1));
ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
ASSERT_EQ(112, event->getHistoricalPressure(0, 1));
ASSERT_EQ(122, event->getHistoricalPressure(1, 1));
ASSERT_EQ(212, event->getPressure(0));
ASSERT_EQ(222, event->getPressure(1));
ASSERT_EQ(13, event->getHistoricalSize(0, 0));
ASSERT_EQ(23, event->getHistoricalSize(1, 0));
ASSERT_EQ(113, event->getHistoricalSize(0, 1));
ASSERT_EQ(123, event->getHistoricalSize(1, 1));
ASSERT_EQ(213, event->getSize(0));
ASSERT_EQ(223, event->getSize(1));
ASSERT_EQ(14, event->getHistoricalTouchMajor(0, 0));
ASSERT_EQ(24, event->getHistoricalTouchMajor(1, 0));
ASSERT_EQ(114, event->getHistoricalTouchMajor(0, 1));
ASSERT_EQ(124, event->getHistoricalTouchMajor(1, 1));
ASSERT_EQ(214, event->getTouchMajor(0));
ASSERT_EQ(224, event->getTouchMajor(1));
ASSERT_EQ(15, event->getHistoricalTouchMinor(0, 0));
ASSERT_EQ(25, event->getHistoricalTouchMinor(1, 0));
ASSERT_EQ(115, event->getHistoricalTouchMinor(0, 1));
ASSERT_EQ(125, event->getHistoricalTouchMinor(1, 1));
ASSERT_EQ(215, event->getTouchMinor(0));
ASSERT_EQ(225, event->getTouchMinor(1));
ASSERT_EQ(16, event->getHistoricalToolMajor(0, 0));
ASSERT_EQ(26, event->getHistoricalToolMajor(1, 0));
ASSERT_EQ(116, event->getHistoricalToolMajor(0, 1));
ASSERT_EQ(126, event->getHistoricalToolMajor(1, 1));
ASSERT_EQ(216, event->getToolMajor(0));
ASSERT_EQ(226, event->getToolMajor(1));
ASSERT_EQ(17, event->getHistoricalToolMinor(0, 0));
ASSERT_EQ(27, event->getHistoricalToolMinor(1, 0));
ASSERT_EQ(117, event->getHistoricalToolMinor(0, 1));
ASSERT_EQ(127, event->getHistoricalToolMinor(1, 1));
ASSERT_EQ(217, event->getToolMinor(0));
ASSERT_EQ(227, event->getToolMinor(1));
ASSERT_EQ(18, event->getHistoricalOrientation(0, 0));
ASSERT_EQ(28, event->getHistoricalOrientation(1, 0));
ASSERT_EQ(118, event->getHistoricalOrientation(0, 1));
ASSERT_EQ(128, event->getHistoricalOrientation(1, 1));
ASSERT_EQ(218, event->getOrientation(0));
ASSERT_EQ(228, event->getOrientation(1));
}
TEST_F(MotionEventTest, Properties) {
MotionEvent event;
// Initialize, add samples and check properties.
initializeEventWithHistory(&event);
ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event));
// Set source.
event.setSource(AINPUT_SOURCE_JOYSTICK);
ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
// Set action.
event.setAction(AMOTION_EVENT_ACTION_CANCEL);
ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction());
// Set meta state.
event.setMetaState(AMETA_CTRL_ON);
ASSERT_EQ(AMETA_CTRL_ON, event.getMetaState());
}
TEST_F(MotionEventTest, CopyFrom_KeepHistory) {
MotionEvent event;
initializeEventWithHistory(&event);
MotionEvent copy;
copy.copyFrom(&event, true /*keepHistory*/);
ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event));
}
TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) {
MotionEvent event;
initializeEventWithHistory(&event);
MotionEvent copy;
copy.copyFrom(&event, false /*keepHistory*/);
ASSERT_EQ(event.getPointerCount(), copy.getPointerCount());
ASSERT_EQ(0U, copy.getHistorySize());
ASSERT_EQ(event.getPointerId(0), copy.getPointerId(0));
ASSERT_EQ(event.getPointerId(1), copy.getPointerId(1));
ASSERT_EQ(event.getEventTime(), copy.getEventTime());
ASSERT_EQ(event.getX(0), copy.getX(0));
}
TEST_F(MotionEventTest, OffsetLocation) {
MotionEvent event;
initializeEventWithHistory(&event);
event.offsetLocation(5.0f, -2.0f);
ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset());
ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset());
}
TEST_F(MotionEventTest, Scale) {
MotionEvent event;
initializeEventWithHistory(&event);
event.scale(2.0f);
ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
ASSERT_EQ(210 * 2, event.getRawX(0));
ASSERT_EQ(211 * 2, event.getRawY(0));
ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0));
ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0));
ASSERT_EQ(212, event.getPressure(0));
ASSERT_EQ(222, event.getPressure(1));
ASSERT_EQ(13, event.getHistoricalSize(0, 0));
ASSERT_EQ(23, event.getHistoricalSize(1, 0));
ASSERT_EQ(113, event.getHistoricalSize(0, 1));
ASSERT_EQ(123, event.getHistoricalSize(1, 1));
ASSERT_EQ(213, event.getSize(0));
ASSERT_EQ(223, event.getSize(1));
ASSERT_EQ(14, event.getHistoricalTouchMajor(0, 0));
ASSERT_EQ(24, event.getHistoricalTouchMajor(1, 0));
ASSERT_EQ(114, event.getHistoricalTouchMajor(0, 1));
ASSERT_EQ(124, event.getHistoricalTouchMajor(1, 1));
ASSERT_EQ(214, event.getTouchMajor(0));
ASSERT_EQ(224, event.getTouchMajor(1));
ASSERT_EQ(15, event.getHistoricalTouchMinor(0, 0));
ASSERT_EQ(25, event.getHistoricalTouchMinor(1, 0));
ASSERT_EQ(115, event.getHistoricalTouchMinor(0, 1));
ASSERT_EQ(125, event.getHistoricalTouchMinor(1, 1));
ASSERT_EQ(215, event.getTouchMinor(0));
ASSERT_EQ(225, event.getTouchMinor(1));
ASSERT_EQ(16, event.getHistoricalToolMajor(0, 0));
ASSERT_EQ(26, event.getHistoricalToolMajor(1, 0));
ASSERT_EQ(116, event.getHistoricalToolMajor(0, 1));
ASSERT_EQ(126, event.getHistoricalToolMajor(1, 1));
ASSERT_EQ(216, event.getToolMajor(0));
ASSERT_EQ(226, event.getToolMajor(1));
ASSERT_EQ(17, event.getHistoricalToolMinor(0, 0));
ASSERT_EQ(27, event.getHistoricalToolMinor(1, 0));
ASSERT_EQ(117, event.getHistoricalToolMinor(0, 1));
ASSERT_EQ(127, event.getHistoricalToolMinor(1, 1));
ASSERT_EQ(217, event.getToolMinor(0));
ASSERT_EQ(227, event.getToolMinor(1));
ASSERT_EQ(18, event.getHistoricalOrientation(0, 0));
ASSERT_EQ(28, event.getHistoricalOrientation(1, 0));
ASSERT_EQ(118, event.getHistoricalOrientation(0, 1));
ASSERT_EQ(128, event.getHistoricalOrientation(1, 1));
ASSERT_EQ(214 * 2, event.getTouchMajor(0));
ASSERT_EQ(215 * 2, event.getTouchMinor(0));
ASSERT_EQ(216 * 2, event.getToolMajor(0));
ASSERT_EQ(217 * 2, event.getToolMinor(0));
ASSERT_EQ(218, event.getOrientation(0));
ASSERT_EQ(228, event.getOrientation(1));
}
TEST_F(MotionEventTest, Parcel) {
Parcel parcel;
MotionEvent inEvent;
initializeEventWithHistory(&inEvent);
MotionEvent outEvent;
// Round trip.
inEvent.writeToParcel(&parcel);
parcel.setDataPosition(0);
outEvent.readFromParcel(&parcel);
ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent));
}
TEST_F(MotionEventTest, Transform) {
// Generate some points on a circle.
// Each point 'i' is a point on a circle of radius ROTATION centered at (3,2) at an angle
// of ARC * i degrees clockwise relative to the Y axis.
// The geometrical representation is irrelevant to the test, it's just easy to generate
// and check rotation. We set the orientation to the same angle.
// Coordinate system: down is increasing Y, right is increasing X.
const float PI_180 = float(M_PI / 180);
const float RADIUS = 10;
const float ARC = 36;
const float ROTATION = ARC * 2;
const size_t pointerCount = 11;
int pointerIds[pointerCount];
PointerCoords pointerCoords[pointerCount];
for (size_t i = 0; i < pointerCount; i++) {
float angle = float(i * ARC * PI_180);
pointerIds[i] = i;
pointerCoords[i].clear();
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, sinf(angle) * RADIUS + 3);
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, -cosf(angle) * RADIUS + 2);
pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
}
MotionEvent event;
event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0,
0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
// Check original raw X and Y assumption.
ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
// Now translate the motion event so the circle's origin is at (0,0).
event.offsetLocation(-3, -2);
// Offsetting the location should preserve the raw X and Y of the first point.
ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
// Apply a rotation about the origin by ROTATION degrees clockwise.
SkMatrix matrix;
matrix.setRotate(ROTATION);
event.transform(&matrix);
// Check the points.
for (size_t i = 0; i < pointerCount; i++) {
float angle = float((i * ARC + ROTATION) * PI_180);
ASSERT_NEAR(sinf(angle) * RADIUS, event.getX(i), 0.001);
ASSERT_NEAR(-cosf(angle) * RADIUS, event.getY(i), 0.001);
ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1);
}
// Applying the transformation should preserve the raw X and Y of the first point.
ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
}
} // namespace android