mirror of
https://gitee.com/houstudio/Cdroid.git
synced 2024-12-04 05:10:06 +08:00
add inputeventconsistencyverifier
This commit is contained in:
parent
09b5e14fdf
commit
c186116720
@ -737,9 +737,9 @@ nsecs_t MotionEvent::getHistoricalEventTime(size_t historyPos) const{
|
||||
}
|
||||
}
|
||||
|
||||
nsecs_t MotionEvent::getHistoricalEventTimeNano(size_t historyPos) const{
|
||||
nsecs_t MotionEvent::getHistoricalEventTimeNanos(size_t historyPos) const{
|
||||
if(historyPos==HISTORY_CURRENT){
|
||||
return getEventTimeNano();
|
||||
return getEventTimeNanos();
|
||||
}else{
|
||||
const size_t historySize = getHistorySize();
|
||||
if(historyPos<0||historyPos>=historySize)return 0;
|
||||
|
@ -88,14 +88,17 @@ public:
|
||||
InputEvent();
|
||||
virtual ~InputEvent();
|
||||
virtual int getType()const=0;
|
||||
virtual InputEvent*copy()const=0;
|
||||
void initialize(int32_t deviceId, int32_t source);
|
||||
void initialize(const InputEvent& from);
|
||||
void setSource(int source){mSource=source;}
|
||||
int getSource()const{return mSource;}
|
||||
bool isFromSource(int s)const;
|
||||
int getDeviceId(){return mDeviceId;}
|
||||
int getDeviceId()const{return mDeviceId;}
|
||||
long getSequenceNumber()const{return mSeq;}
|
||||
virtual nsecs_t getEventTimeNano() const { return mEventTime*NS_PER_MS; }
|
||||
virtual bool isTainted()const=0;
|
||||
virtual void setTainted(bool)=0;
|
||||
virtual nsecs_t getEventTimeNanos() const { return mEventTime*NS_PER_MS; }
|
||||
virtual nsecs_t getEventTime()const{ return mEventTime;}
|
||||
virtual void recycle();/*only obtained event can call recycle*/
|
||||
};
|
||||
@ -219,9 +222,15 @@ public:
|
||||
int deviceId, int scancode, int flags, int source/*,std::string characters*/);
|
||||
static KeyEvent* obtain(const KeyEvent& other);
|
||||
virtual int getType()const {return EV_KEY;}
|
||||
KeyEvent*copy()const override{return obtain(*this);}
|
||||
int getKeyCode()const {return mKeyCode;}
|
||||
void setKeyCode(int k){mKeyCode=k;}
|
||||
int getFlags()const{return mFlags;}
|
||||
inline bool isTainted()const{return (mFlags&FLAG_TAINTED)!=0;}
|
||||
inline void setTainted(bool tainted){
|
||||
if(tainted)mFlags|=FLAG_TAINTED;
|
||||
else mFlags&=~FLAG_TAINTED;
|
||||
}
|
||||
inline int32_t getScanCode() const { return mScanCode; }
|
||||
inline int32_t getMetaState() const { return mMetaState; }
|
||||
int getAction()const{return mAction;}//key up-->0 down-->1
|
||||
@ -346,6 +355,12 @@ public:
|
||||
FLAG_IS_GENERATED_GESTURE = 0x8,
|
||||
FLAG_TAINTED = 0x80000000,
|
||||
FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000,
|
||||
CLASSIFICATION_NONE = 0,
|
||||
CLASSIFICATION_AMBIGUOUS_GESTURE = 1,
|
||||
CLASSIFICATION_DEEP_PRESS = 2,
|
||||
CLASSIFICATION_TWO_FINGER_SWIPE = 3,
|
||||
CLASSIFICATION_MULTI_FINGER_SWIPE = 4,
|
||||
CLASSIFICATION_PINCH = 5,
|
||||
};
|
||||
private:
|
||||
static const int HISTORY_CURRENT = -0x80000000;
|
||||
@ -368,7 +383,7 @@ protected:
|
||||
public:
|
||||
MotionEvent();
|
||||
MotionEvent(const MotionEvent&m);
|
||||
|
||||
MotionEvent*copy()const override{return obtain(*this);}
|
||||
void initialize(int deviceId,int source,int action,int actionButton,
|
||||
int flags, int edgeFlags,int metaState, int buttonState,
|
||||
float xOffset, float yOffset, float xPrecision, float yPrecision,
|
||||
@ -401,6 +416,11 @@ public:
|
||||
inline nsecs_t getDownTime()const{return mDownTime;}
|
||||
inline int32_t getFlags() const { return mFlags; }
|
||||
inline void setFlags(int32_t flags) { mFlags = flags; }
|
||||
inline bool isTainted()const{return (mFlags&FLAG_TAINTED)!=0;}
|
||||
inline void setTainted(bool tainted){
|
||||
if(tainted)mFlags|=FLAG_TAINTED;
|
||||
else mFlags&=~FLAG_TAINTED;
|
||||
}
|
||||
inline int32_t getEdgeFlags() const { return mEdgeFlags; }
|
||||
inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; }
|
||||
inline bool isHoverExitPending()const{return getFlags()&FLAG_HOVER_EXIT_PENDING!=0;}
|
||||
@ -421,7 +441,7 @@ public:
|
||||
inline float getYPrecision() const { return mYPrecision; }
|
||||
inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; }
|
||||
nsecs_t getHistoricalEventTime(size_t historicalIndex) const;
|
||||
nsecs_t getHistoricalEventTimeNano(size_t historicalIndex) const;
|
||||
nsecs_t getHistoricalEventTimeNanos(size_t historicalIndex) const;
|
||||
void getPointerCoords(int pointerIndex, PointerCoords& outPointerCoords){
|
||||
getHistoricalRawPointerCoords(pointerIndex,HISTORY_CURRENT,outPointerCoords);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ bool UIEventSource::postDelayed(Runnable& run,uint32_t delayedtime){
|
||||
RUNNER runner;
|
||||
runner.time = SystemClock::uptimeMillis() + delayedtime;
|
||||
runner.run = run;
|
||||
|
||||
|
||||
for(auto itr = mRunnables.begin();itr != mRunnables.end();itr++){
|
||||
if(runner.time < itr->time){
|
||||
mRunnables.insert(itr,runner);
|
||||
|
676
src/gui/view/inputeventconsistencyverifier.cc
Executable file
676
src/gui/view/inputeventconsistencyverifier.cc
Executable file
@ -0,0 +1,676 @@
|
||||
#include <view/inputeventconsistencyverifier.h>
|
||||
#include <core/inputdevice.h>
|
||||
#include <core/uievents.h>
|
||||
|
||||
namespace cdroid{
|
||||
static const char* EVENT_TYPE_KEY = "KeyEvent";
|
||||
static const char* EVENT_TYPE_TRACKBALL = "TrackballEvent";
|
||||
static const char* EVENT_TYPE_TOUCH = "TouchEvent";
|
||||
static const char* EVENT_TYPE_GENERIC_MOTION = "GenericMotionEvent";
|
||||
|
||||
InputEventConsistencyVerifier::InputEventConsistencyVerifier(Object* caller, int flags) {
|
||||
mCaller = caller;
|
||||
mFlags = flags;
|
||||
mMostRecentEventIndex = 0;
|
||||
mCurrentEvent = nullptr;
|
||||
mKeyStateList = nullptr;
|
||||
//mLogTag = (logTag != null) ? logTag : "InputEventConsistencyVerifier";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the instrumentation should be enabled.
|
||||
* @return True if it should be enabled.
|
||||
*/
|
||||
bool InputEventConsistencyVerifier::isInstrumentationEnabled() {
|
||||
#ifndef _DEBUG
|
||||
return true;//IS_ENG_BUILD;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputEventConsistencyVerifier::reset() {
|
||||
mLastEventSeq = -1;
|
||||
mLastNestingLevel = 0;
|
||||
mTrackballDown = false;
|
||||
mTrackballUnhandled = false;
|
||||
mTouchEventStreamPointers = 0;
|
||||
mTouchEventStreamIsTainted = false;
|
||||
mTouchEventStreamUnhandled = false;
|
||||
mHoverEntered = false;
|
||||
mButtonsPressed = 0;
|
||||
|
||||
while (mKeyStateList != nullptr) {
|
||||
KeyState* state = mKeyStateList;
|
||||
mKeyStateList = state->next;
|
||||
state->recycle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks an arbitrary input event.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void InputEventConsistencyVerifier::onInputEvent(InputEvent& event, int nestingLevel) {
|
||||
if (dynamic_cast<KeyEvent*>(&event)) {
|
||||
KeyEvent& keyEvent = (KeyEvent&)event;
|
||||
onKeyEvent(keyEvent, nestingLevel);
|
||||
} else {
|
||||
MotionEvent& motionEvent = (MotionEvent&)event;
|
||||
if (motionEvent.isTouchEvent()) {
|
||||
onTouchEvent(motionEvent, nestingLevel);
|
||||
} else if ((motionEvent.getSource() & InputDevice::SOURCE_CLASS_TRACKBALL) != 0) {
|
||||
onTrackballEvent(motionEvent, nestingLevel);
|
||||
} else {
|
||||
onGenericMotionEvent(motionEvent, nestingLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a key event.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void InputEventConsistencyVerifier::onKeyEvent(KeyEvent& event, int nestingLevel) {
|
||||
if (!startEvent(event, nestingLevel, EVENT_TYPE_KEY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ensureMetaStateIsNormalized(event.getMetaState());
|
||||
|
||||
const int action = event.getAction();
|
||||
const int deviceId = event.getDeviceId();
|
||||
const int source = event.getSource();
|
||||
const int keyCode = event.getKeyCode();
|
||||
switch (action) {
|
||||
case KeyEvent::ACTION_DOWN: {
|
||||
KeyState* state = findKeyState(deviceId, source, keyCode, /*remove*/ false);
|
||||
if (state != nullptr) {
|
||||
// If the key is already down, ensure it is a repeat.
|
||||
// We don't perform this check when processing raw device input
|
||||
// because the input dispatcher itself is responsible for setting
|
||||
// the key repeat count before it delivers input events.
|
||||
if (state->unhandled) {
|
||||
state->unhandled = false;
|
||||
} else if ((mFlags & FLAG_RAW_DEVICE_INPUT) == 0
|
||||
&& event.getRepeatCount() == 0) {
|
||||
problem("ACTION_DOWN but key is already down and this event is not a key repeat.");
|
||||
}
|
||||
} else {
|
||||
addKeyState(deviceId, source, keyCode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyEvent::ACTION_UP: {
|
||||
KeyState* state = findKeyState(deviceId, source, keyCode, /*remove*/ true);
|
||||
if (state == nullptr) {
|
||||
problem("ACTION_UP but key was not down.");
|
||||
} else {
|
||||
state->recycle();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyEvent::ACTION_MULTIPLE:
|
||||
break;
|
||||
default:
|
||||
problem("Invalid action " + KeyEvent::actionToString(action)
|
||||
+ " for key event.");
|
||||
break;
|
||||
}
|
||||
}catch(std::exception&e){
|
||||
}
|
||||
finishEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a trackball event.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void InputEventConsistencyVerifier::onTrackballEvent(MotionEvent& event, int nestingLevel) {
|
||||
if (!startEvent(event, nestingLevel, EVENT_TYPE_TRACKBALL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ensureMetaStateIsNormalized(event.getMetaState());
|
||||
|
||||
const int action = event.getAction();
|
||||
const int source = event.getSource();
|
||||
if ((source & InputDevice::SOURCE_CLASS_TRACKBALL) != 0) {
|
||||
switch (action) {
|
||||
case MotionEvent::ACTION_DOWN:
|
||||
if (mTrackballDown && !mTrackballUnhandled) {
|
||||
problem("ACTION_DOWN but trackball is already down.");
|
||||
} else {
|
||||
mTrackballDown = true;
|
||||
mTrackballUnhandled = false;
|
||||
}
|
||||
ensureHistorySizeIsZeroForThisAction(event);
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
break;
|
||||
case MotionEvent::ACTION_UP:
|
||||
if (!mTrackballDown) {
|
||||
problem("ACTION_UP but trackball is not down.");
|
||||
} else {
|
||||
mTrackballDown = false;
|
||||
mTrackballUnhandled = false;
|
||||
}
|
||||
ensureHistorySizeIsZeroForThisAction(event);
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
break;
|
||||
case MotionEvent::ACTION_MOVE:
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
break;
|
||||
default:
|
||||
problem("Invalid action " + MotionEvent::actionToString(action)
|
||||
+ " for trackball event.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (mTrackballDown && event.getPressure(0) <= 0.f) {
|
||||
problem("Trackball is down but pressure is not greater than 0.");
|
||||
} else if (!mTrackballDown && event.getPressure(0) != 0) {
|
||||
problem("Trackball is up but pressure is not equal to 0.");
|
||||
}
|
||||
} else {
|
||||
problem("Source was not SOURCE_CLASS_TRACKBALL.");
|
||||
}
|
||||
}catch(std::exception&e){
|
||||
}
|
||||
finishEvent();
|
||||
}
|
||||
|
||||
static int bitCount(int number) {
|
||||
int count = 0;
|
||||
while (number) {
|
||||
count += number & 1;
|
||||
number >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
/**
|
||||
* Checks a touch event.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void InputEventConsistencyVerifier::onTouchEvent(MotionEvent& event, int nestingLevel) {
|
||||
if (!startEvent(event, nestingLevel, EVENT_TYPE_TOUCH)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int action = event.getAction();
|
||||
const bool newStream = action == MotionEvent::ACTION_DOWN
|
||||
|| action == MotionEvent::ACTION_CANCEL || action == MotionEvent::ACTION_OUTSIDE;
|
||||
if (newStream && (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled)) {
|
||||
mTouchEventStreamIsTainted = false;
|
||||
mTouchEventStreamUnhandled = false;
|
||||
mTouchEventStreamPointers = 0;
|
||||
}
|
||||
if (mTouchEventStreamIsTainted) {
|
||||
event.setTainted(true);
|
||||
}
|
||||
|
||||
try {
|
||||
ensureMetaStateIsNormalized(event.getMetaState());
|
||||
|
||||
const int deviceId = event.getDeviceId();
|
||||
const int source = event.getSource();
|
||||
|
||||
if (!newStream && mTouchEventStreamDeviceId != -1
|
||||
&& (mTouchEventStreamDeviceId != deviceId
|
||||
|| mTouchEventStreamSource != source)) {
|
||||
problem("Touch event stream contains events from multiple sources: previous device id %d"
|
||||
", previous source %x, new device id %d, new source %x",
|
||||
mTouchEventStreamDeviceId,mTouchEventStreamSource,deviceId,source);
|
||||
}
|
||||
mTouchEventStreamDeviceId = deviceId;
|
||||
mTouchEventStreamSource = source;
|
||||
|
||||
const int pointerCount = event.getPointerCount();
|
||||
if ((source & InputDevice::SOURCE_CLASS_POINTER) != 0) {
|
||||
switch (action) {
|
||||
case MotionEvent::ACTION_DOWN:
|
||||
if (mTouchEventStreamPointers != 0) {
|
||||
problem("ACTION_DOWN but pointers are already down. "
|
||||
"Probably missing ACTION_UP from previous gesture.");
|
||||
}
|
||||
ensureHistorySizeIsZeroForThisAction(event);
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
mTouchEventStreamPointers = 1 << event.getPointerId(0);
|
||||
break;
|
||||
case MotionEvent::ACTION_UP:
|
||||
ensureHistorySizeIsZeroForThisAction(event);
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
mTouchEventStreamPointers = 0;
|
||||
mTouchEventStreamIsTainted = false;
|
||||
break;
|
||||
case MotionEvent::ACTION_MOVE: {
|
||||
const int expectedPointerCount = bitCount(mTouchEventStreamPointers);
|
||||
if (pointerCount != expectedPointerCount) {
|
||||
problem("ACTION_MOVE contained %d pointers but there are currently %d pointers down.",pointerCount,expectedPointerCount);
|
||||
mTouchEventStreamIsTainted = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent::ACTION_CANCEL:
|
||||
mTouchEventStreamPointers = 0;
|
||||
mTouchEventStreamIsTainted = false;
|
||||
break;
|
||||
case MotionEvent::ACTION_OUTSIDE:
|
||||
if (mTouchEventStreamPointers != 0) {
|
||||
problem("ACTION_OUTSIDE but pointers are still down.");
|
||||
}
|
||||
ensureHistorySizeIsZeroForThisAction(event);
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
mTouchEventStreamIsTainted = false;
|
||||
break;
|
||||
default: {
|
||||
const int actionMasked = event.getActionMasked();
|
||||
const int actionIndex = event.getActionIndex();
|
||||
if (actionMasked == MotionEvent::ACTION_POINTER_DOWN) {
|
||||
if (mTouchEventStreamPointers == 0) {
|
||||
problem("ACTION_POINTER_DOWN but no other pointers were down.");
|
||||
mTouchEventStreamIsTainted = true;
|
||||
}
|
||||
if (actionIndex < 0 || actionIndex >= pointerCount) {
|
||||
problem("ACTION_POINTER_DOWN index is %d but the pointer count is %d.",
|
||||
actionIndex,pointerCount);
|
||||
mTouchEventStreamIsTainted = true;
|
||||
} else {
|
||||
const int id = event.getPointerId(actionIndex);
|
||||
const int idBit = 1 << id;
|
||||
if ((mTouchEventStreamPointers & idBit) != 0) {
|
||||
problem("ACTION_POINTER_DOWN specified pointer id %d which is already down.",id);
|
||||
mTouchEventStreamIsTainted = true;
|
||||
} else {
|
||||
mTouchEventStreamPointers |= idBit;
|
||||
}
|
||||
}
|
||||
ensureHistorySizeIsZeroForThisAction(event);
|
||||
} else if (actionMasked == MotionEvent::ACTION_POINTER_UP) {
|
||||
if (actionIndex < 0 || actionIndex >= pointerCount) {
|
||||
problem("ACTION_POINTER_UP index is % but the pointer count is %d.",actionIndex,pointerCount);
|
||||
mTouchEventStreamIsTainted = true;
|
||||
} else {
|
||||
const int id = event.getPointerId(actionIndex);
|
||||
const int idBit = 1 << id;
|
||||
if ((mTouchEventStreamPointers & idBit) == 0) {
|
||||
problem("ACTION_POINTER_UP specified pointer id %d which is not currently down.",id);
|
||||
mTouchEventStreamIsTainted = true;
|
||||
} else {
|
||||
mTouchEventStreamPointers &= ~idBit;
|
||||
}
|
||||
}
|
||||
ensureHistorySizeIsZeroForThisAction(event);
|
||||
} else {
|
||||
problem("Invalid action %s for touch event.",MotionEvent::actionToString(action));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
problem("Source was not SOURCE_CLASS_POINTER.");
|
||||
}
|
||||
}catch(std::exception&e){
|
||||
}
|
||||
finishEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a generic motion event.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void InputEventConsistencyVerifier::onGenericMotionEvent(MotionEvent& event, int nestingLevel) {
|
||||
if (!startEvent(event, nestingLevel, EVENT_TYPE_GENERIC_MOTION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ensureMetaStateIsNormalized(event.getMetaState());
|
||||
|
||||
const int action = event.getAction();
|
||||
const int source = event.getSource();
|
||||
const int buttonState = event.getButtonState();
|
||||
const int actionButton = event.getActionButton();
|
||||
if ((source & InputDevice::SOURCE_CLASS_POINTER) != 0) {
|
||||
switch (action) {
|
||||
case MotionEvent::ACTION_HOVER_ENTER:
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
mHoverEntered = true;
|
||||
break;
|
||||
case MotionEvent::ACTION_HOVER_MOVE:
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
break;
|
||||
case MotionEvent::ACTION_HOVER_EXIT:
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
if (!mHoverEntered) {
|
||||
problem("ACTION_HOVER_EXIT without prior ACTION_HOVER_ENTER");
|
||||
}
|
||||
mHoverEntered = false;
|
||||
break;
|
||||
case MotionEvent::ACTION_SCROLL:
|
||||
ensureHistorySizeIsZeroForThisAction(event);
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
break;
|
||||
case MotionEvent::ACTION_BUTTON_PRESS:
|
||||
ensureActionButtonIsNonZeroForThisAction(event);
|
||||
if ((mButtonsPressed & actionButton) != 0) {
|
||||
problem("Action button for ACTION_BUTTON_PRESS event is %d"
|
||||
", but it has already been pressed and "
|
||||
"has yet to be released.",actionButton);
|
||||
}
|
||||
|
||||
mButtonsPressed |= actionButton;
|
||||
// The system will automatically mirror the stylus buttons onto the button
|
||||
// state as the old set of generic buttons for apps targeting pre-M. If
|
||||
// it looks this has happened, go ahead and set the generic buttons as
|
||||
// pressed to prevent spurious errors.
|
||||
if (actionButton == MotionEvent::BUTTON_STYLUS_PRIMARY &&
|
||||
(buttonState & MotionEvent::BUTTON_SECONDARY) != 0) {
|
||||
mButtonsPressed |= MotionEvent::BUTTON_SECONDARY;
|
||||
} else if (actionButton == MotionEvent::BUTTON_STYLUS_SECONDARY &&
|
||||
(buttonState & MotionEvent::BUTTON_TERTIARY) != 0) {
|
||||
mButtonsPressed |= MotionEvent::BUTTON_TERTIARY;
|
||||
}
|
||||
|
||||
if (mButtonsPressed != buttonState) {
|
||||
problem("Reported button state differs from expect button state "
|
||||
"based on press and release events. Is 0x%08x but expected 0x%08x.",
|
||||
buttonState, mButtonsPressed);
|
||||
}
|
||||
break;
|
||||
case MotionEvent::ACTION_BUTTON_RELEASE:
|
||||
ensureActionButtonIsNonZeroForThisAction(event);
|
||||
if ((mButtonsPressed & actionButton) != actionButton) {
|
||||
problem("Action button for ACTION_BUTTON_RELEASE event is %d"
|
||||
", but it was either never pressed or has "
|
||||
"already been released.",actionButton);
|
||||
}
|
||||
|
||||
mButtonsPressed &= ~actionButton;
|
||||
// The system will automatically mirror the stylus buttons onto the button
|
||||
// state as the old set of generic buttons for apps targeting pre-M. If
|
||||
// it looks this has happened, go ahead and set the generic buttons as
|
||||
// released to prevent spurious errors.
|
||||
if (actionButton == MotionEvent::BUTTON_STYLUS_PRIMARY &&
|
||||
(buttonState & MotionEvent::BUTTON_SECONDARY) == 0) {
|
||||
mButtonsPressed &= ~MotionEvent::BUTTON_SECONDARY;
|
||||
} else if (actionButton == MotionEvent::BUTTON_STYLUS_SECONDARY &&
|
||||
(buttonState & MotionEvent::BUTTON_TERTIARY) == 0) {
|
||||
mButtonsPressed &= ~MotionEvent::BUTTON_TERTIARY;
|
||||
}
|
||||
|
||||
if (mButtonsPressed != buttonState) {
|
||||
problem("Reported button state differs from "
|
||||
"expected button state based on press and release events. "
|
||||
"Is 0x%08x but expected 0x%08x.",
|
||||
buttonState, mButtonsPressed);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
problem("Invalid action for generic pointer event.");
|
||||
break;
|
||||
}
|
||||
} else if ((source & InputDevice::SOURCE_CLASS_JOYSTICK) != 0) {
|
||||
switch (action) {
|
||||
case MotionEvent::ACTION_MOVE:
|
||||
ensurePointerCountIsOneForThisAction(event);
|
||||
break;
|
||||
default:
|
||||
problem("Invalid action for generic joystick event.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}catch(std::exception&){
|
||||
}
|
||||
finishEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the verifier that a given event was unhandled and the rest of the
|
||||
* trace for the event should be ignored.
|
||||
* This method should only be called if the event was previously checked by
|
||||
* the consistency verifier using {@link #onInputEvent} and other methods.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void InputEventConsistencyVerifier::onUnhandledEvent(InputEvent& event, int nestingLevel) {
|
||||
if (nestingLevel != mLastNestingLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRecentEventsUnhandled.size()) {
|
||||
mRecentEventsUnhandled[mMostRecentEventIndex] = true;
|
||||
}
|
||||
|
||||
if (dynamic_cast<KeyEvent*>(&event)) {
|
||||
const KeyEvent& keyEvent = (KeyEvent&)event;
|
||||
const int deviceId = keyEvent.getDeviceId();
|
||||
const int source = keyEvent.getSource();
|
||||
const int keyCode = keyEvent.getKeyCode();
|
||||
KeyState* state = findKeyState(deviceId, source, keyCode, /*remove*/ false);
|
||||
if (state != nullptr) {
|
||||
state->unhandled = true;
|
||||
}
|
||||
} else {
|
||||
MotionEvent& motionEvent = (MotionEvent&)event;
|
||||
if (motionEvent.isTouchEvent()) {
|
||||
mTouchEventStreamUnhandled = true;
|
||||
} else if ((motionEvent.getSource() & InputDevice::SOURCE_CLASS_TRACKBALL) != 0) {
|
||||
if (mTrackballDown) {
|
||||
mTrackballUnhandled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputEventConsistencyVerifier::ensureMetaStateIsNormalized(int metaState) {
|
||||
const int normalizedMetaState = KeyEvent::normalizeMetaState(metaState);
|
||||
if (normalizedMetaState != metaState) {
|
||||
problem("Metastate not normalized. Was 0x%08x but expected 0x%08x.",
|
||||
metaState, normalizedMetaState);
|
||||
}
|
||||
}
|
||||
|
||||
void InputEventConsistencyVerifier::ensurePointerCountIsOneForThisAction(MotionEvent& event) {
|
||||
const int pointerCount = event.getPointerCount();
|
||||
if (pointerCount != 1) {
|
||||
problem("Pointer count is %d but it should always be 1 for %s",
|
||||
pointerCount, MotionEvent::actionToString(event.getAction()));
|
||||
}
|
||||
}
|
||||
|
||||
void InputEventConsistencyVerifier::ensureActionButtonIsNonZeroForThisAction(MotionEvent& event) {
|
||||
const int actionButton = event.getActionButton();
|
||||
if (actionButton == 0) {
|
||||
problem("No action button set. Action button should always be non-zero for %s",
|
||||
MotionEvent::actionToString(event.getAction()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void InputEventConsistencyVerifier::ensureHistorySizeIsZeroForThisAction(MotionEvent& event) {
|
||||
const int historySize = event.getHistorySize();
|
||||
if (historySize != 0) {
|
||||
problem("History size is %d but it should always be 0 for %s",
|
||||
historySize,MotionEvent::actionToString(event.getAction()));
|
||||
}
|
||||
}
|
||||
|
||||
bool InputEventConsistencyVerifier::startEvent(InputEvent& event, int nestingLevel,const std::string& eventType) {
|
||||
// Ignore the event if we already checked it at a higher nesting level.
|
||||
const int seq = event.getSequenceNumber();
|
||||
if (seq == mLastEventSeq && nestingLevel < mLastNestingLevel
|
||||
&& eventType == mLastEventType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nestingLevel > 0) {
|
||||
mLastEventSeq = seq;
|
||||
mLastEventType = eventType;
|
||||
mLastNestingLevel = nestingLevel;
|
||||
} else {
|
||||
mLastEventSeq = -1;
|
||||
mLastEventType.clear();// = nullptr;
|
||||
mLastNestingLevel = 0;
|
||||
}
|
||||
|
||||
mCurrentEvent = &event;
|
||||
mCurrentEventType = eventType;
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputEventConsistencyVerifier::finishEvent() {
|
||||
if (mViolationMessage.str().length() != 0) {
|
||||
if (!mCurrentEvent->isTainted()) {
|
||||
// Write a log message only if the event was not already tainted.
|
||||
//mViolationMessage.append("\n in ").append(mCaller);
|
||||
mViolationMessage<<("\n ");
|
||||
appendEvent(mViolationMessage, 0, *mCurrentEvent, false);
|
||||
|
||||
if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents.size()) {
|
||||
mViolationMessage<<("\n -- recent events --");
|
||||
for (int i = 0; i < RECENT_EVENTS_TO_LOG; i++) {
|
||||
const int index = (mMostRecentEventIndex + RECENT_EVENTS_TO_LOG - i)
|
||||
% RECENT_EVENTS_TO_LOG;
|
||||
InputEvent* event = mRecentEvents[index];
|
||||
if (event == nullptr) {
|
||||
break;
|
||||
}
|
||||
mViolationMessage<<("\n ");
|
||||
appendEvent(mViolationMessage, i + 1, *event, mRecentEventsUnhandled[index]);
|
||||
}
|
||||
}
|
||||
|
||||
LOGD("%s",mViolationMessage.str().c_str());
|
||||
|
||||
// Taint the event so that we do not generate additional violations from it
|
||||
// further downstream.
|
||||
mCurrentEvent->setTainted(true);
|
||||
}
|
||||
mViolationMessage.clear();//setLength(0);
|
||||
}
|
||||
|
||||
if (RECENT_EVENTS_TO_LOG != 0) {
|
||||
if (mRecentEvents.empty()) {
|
||||
mRecentEvents.resize(RECENT_EVENTS_TO_LOG);
|
||||
mRecentEventsUnhandled.resize(RECENT_EVENTS_TO_LOG);
|
||||
}
|
||||
const int index = (mMostRecentEventIndex + 1) % RECENT_EVENTS_TO_LOG;
|
||||
mMostRecentEventIndex = index;
|
||||
if (mRecentEvents[index] != nullptr) {
|
||||
mRecentEvents[index]->recycle();
|
||||
}
|
||||
mRecentEvents[index] = mCurrentEvent->copy();
|
||||
mRecentEventsUnhandled[index] = false;
|
||||
}
|
||||
|
||||
mCurrentEvent = nullptr;
|
||||
mCurrentEventType.clear();
|
||||
}
|
||||
|
||||
void InputEventConsistencyVerifier::appendEvent(std::ostringstream& message, int index,
|
||||
const InputEvent& event, bool unhandled) {
|
||||
message<<index<<": sent at "<<(event.getEventTimeNanos());
|
||||
message<<", ";
|
||||
if (unhandled) {
|
||||
message<<"(unhandled) ";
|
||||
}
|
||||
//message<<event;
|
||||
}
|
||||
|
||||
void InputEventConsistencyVerifier::problem(const std::string& message,...) {
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
LogPrintf(0,"tag","fun",0,message.c_str(),args);
|
||||
va_end(args);
|
||||
if (mViolationMessage.str().length() == 0) {
|
||||
mViolationMessage<<mCurrentEventType<<": ";
|
||||
} else {
|
||||
mViolationMessage<<"\n ";
|
||||
}
|
||||
mViolationMessage<<message;
|
||||
}
|
||||
|
||||
InputEventConsistencyVerifier::KeyState* InputEventConsistencyVerifier::findKeyState(int deviceId, int source, int keyCode, bool remove) {
|
||||
KeyState* last = nullptr;
|
||||
KeyState* state = mKeyStateList;
|
||||
while (state != nullptr) {
|
||||
if (state->deviceId == deviceId && state->source == source
|
||||
&& state->keyCode == keyCode) {
|
||||
if (remove) {
|
||||
if (last != nullptr) {
|
||||
last->next = state->next;
|
||||
} else {
|
||||
mKeyStateList = state->next;
|
||||
}
|
||||
state->next = nullptr;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
last = state;
|
||||
state = state->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void InputEventConsistencyVerifier::addKeyState(int deviceId, int source, int keyCode) {
|
||||
KeyState* state = KeyState::obtain(deviceId, source, keyCode);
|
||||
state->next = mKeyStateList;
|
||||
mKeyStateList = state;
|
||||
}
|
||||
|
||||
InputEventConsistencyVerifier::KeyState* InputEventConsistencyVerifier::KeyState::mRecycledList = nullptr;
|
||||
InputEventConsistencyVerifier::KeyState::KeyState() {
|
||||
}
|
||||
|
||||
InputEventConsistencyVerifier::KeyState* InputEventConsistencyVerifier::KeyState::obtain(int deviceId, int source, int keyCode) {
|
||||
KeyState* state;
|
||||
state = mRecycledList;
|
||||
if (state != nullptr) {
|
||||
mRecycledList = state->next;
|
||||
} else {
|
||||
state = new KeyState();
|
||||
}
|
||||
state->deviceId = deviceId;
|
||||
state->source = source;
|
||||
state->keyCode = keyCode;
|
||||
state->unhandled = false;
|
||||
return state;
|
||||
}
|
||||
|
||||
void InputEventConsistencyVerifier::KeyState::recycle() {
|
||||
next = mRecycledList;
|
||||
mRecycledList = next;
|
||||
}
|
||||
|
||||
}/*endof namespace*/
|
196
src/gui/view/inputeventconsistencyverifier.h
Executable file
196
src/gui/view/inputeventconsistencyverifier.h
Executable file
@ -0,0 +1,196 @@
|
||||
#ifndef __INPUTEVENT_CONSISTENCYVERIFIER_H__
|
||||
#define __INPUTEVENT_CONSISTENCYVERIFIER_H__
|
||||
#include <core/uievents.h>
|
||||
#include <sstream>
|
||||
#include <stdarg.h>
|
||||
namespace cdroid{
|
||||
class Object;
|
||||
class InputEventConsistencyVerifier {
|
||||
private:
|
||||
class KeyState;
|
||||
// The number of recent events to log when a problem is detected.
|
||||
// Can be set to 0 to disable logging recent events but the runtime overhead of
|
||||
// this feature is negligible on current hardware.
|
||||
static constexpr int RECENT_EVENTS_TO_LOG = 5;
|
||||
|
||||
// The object to which the verifier is attached.
|
||||
Object* mCaller;
|
||||
|
||||
// Consistency verifier flags.
|
||||
int mFlags;
|
||||
|
||||
// Tag for logging which a client can set to help distinguish the output
|
||||
// from different verifiers since several can be active at the same time.
|
||||
// If not provided defaults to the simple class name.
|
||||
std::string mLogTag;
|
||||
|
||||
// The most recently checked event and the nesting level at which it was checked.
|
||||
// This is only set when the verifier is called from a nesting level greater than 0
|
||||
// so that the verifier can detect when it has been asked to verify the same event twice.
|
||||
// It does not make sense to examine the contents of the last event since it may have
|
||||
// been recycled.
|
||||
int mLastEventSeq;
|
||||
std::string mLastEventType;
|
||||
int mLastNestingLevel;
|
||||
|
||||
// Copy of the most recent events.
|
||||
std::vector<InputEvent*> mRecentEvents;
|
||||
std::vector<bool> mRecentEventsUnhandled;
|
||||
int mMostRecentEventIndex;
|
||||
|
||||
// Current event and its type.
|
||||
InputEvent* mCurrentEvent;
|
||||
std::string mCurrentEventType;
|
||||
|
||||
// Linked list of key state objects.
|
||||
KeyState* mKeyStateList;
|
||||
|
||||
// Current state of the trackball.
|
||||
bool mTrackballDown;
|
||||
bool mTrackballUnhandled;
|
||||
|
||||
// Bitfield of pointer ids that are currently down.
|
||||
// Assumes that the largest possible pointer id is 31, which is potentially subject to change.
|
||||
// (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
|
||||
int mTouchEventStreamPointers;
|
||||
|
||||
// The device id and source of the current stream of touch events.
|
||||
int mTouchEventStreamDeviceId = -1;
|
||||
int mTouchEventStreamSource;
|
||||
|
||||
// Set to true when we discover that the touch event stream is inconsistent.
|
||||
// Reset on down or cancel.
|
||||
bool mTouchEventStreamIsTainted;
|
||||
|
||||
// Set to true if the touch event stream is partially unhandled.
|
||||
bool mTouchEventStreamUnhandled;
|
||||
|
||||
// Set to true if we received hover enter.
|
||||
bool mHoverEntered;
|
||||
|
||||
// The bitset of buttons which we've received ACTION_BUTTON_PRESS for.
|
||||
int mButtonsPressed;
|
||||
|
||||
// The current violation message.
|
||||
std::ostringstream mViolationMessage;
|
||||
|
||||
/**
|
||||
* Indicates that the verifier is intended to act on raw device input event streams.
|
||||
* Disables certain checks for invariants that are established by the input dispatcher
|
||||
* itself as it delivers input events, such as key repeating behavior.
|
||||
*/
|
||||
private:
|
||||
void ensureMetaStateIsNormalized(int metaState);
|
||||
void ensurePointerCountIsOneForThisAction(MotionEvent& event);
|
||||
void ensureActionButtonIsNonZeroForThisAction(MotionEvent& event);
|
||||
void ensureHistorySizeIsZeroForThisAction(MotionEvent& event);
|
||||
bool startEvent(InputEvent& event, int nestingLevel, const std::string& eventType);
|
||||
void finishEvent();
|
||||
static void appendEvent(std::ostringstream& message, int index,const InputEvent& event, bool unhandled);
|
||||
void problem(const std::string& message,...);
|
||||
KeyState* findKeyState(int deviceId, int source, int keyCode, bool remove);
|
||||
void addKeyState(int deviceId, int source, int keyCode);
|
||||
public:
|
||||
static const int FLAG_RAW_DEVICE_INPUT = 1 << 0;
|
||||
|
||||
|
||||
/**
|
||||
* Creates an input consistency verifier.
|
||||
* @param caller The object to which the verifier is attached.
|
||||
* @param flags Flags to the verifier, or 0 if none.
|
||||
* @param logTag Tag for logging. If null defaults to the short class name.
|
||||
*/
|
||||
InputEventConsistencyVerifier(Object* caller, int flags);
|
||||
|
||||
/**
|
||||
* Determines whether the instrumentation should be enabled.
|
||||
* @return True if it should be enabled.
|
||||
*/
|
||||
static bool isInstrumentationEnabled();
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Checks an arbitrary input event.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void onInputEvent(InputEvent& event, int nestingLevel);
|
||||
/**
|
||||
* Checks a key event.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void onKeyEvent(KeyEvent& event, int nestingLevel);
|
||||
|
||||
/**
|
||||
* Checks a trackball event.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void onTrackballEvent(MotionEvent& event, int nestingLevel);
|
||||
|
||||
/**
|
||||
* Checks a touch event.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void onTouchEvent(MotionEvent& event, int nestingLevel);
|
||||
/**
|
||||
* Checks a generic motion event.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void onGenericMotionEvent(MotionEvent& event, int nestingLevel);
|
||||
|
||||
/**
|
||||
* Notifies the verifier that a given event was unhandled and the rest of the
|
||||
* trace for the event should be ignored.
|
||||
* This method should only be called if the event was previously checked by
|
||||
* the consistency verifier using {@link #onInputEvent} and other methods.
|
||||
* @param event The event.
|
||||
* @param nestingLevel The nesting level: 0 if called from the base class,
|
||||
* or 1 from a subclass. If the event was already checked by this consistency verifier
|
||||
* at a higher nesting level, it will not be checked again. Used to handle the situation
|
||||
* where a subclass dispatching method delegates to its superclass's dispatching method
|
||||
* and both dispatching methods call into the consistency verifier.
|
||||
*/
|
||||
void onUnhandledEvent(InputEvent& event, int nestingLevel);
|
||||
};
|
||||
|
||||
class InputEventConsistencyVerifier::KeyState {
|
||||
private:
|
||||
static KeyState* mRecycledList;
|
||||
private:
|
||||
friend InputEventConsistencyVerifier;
|
||||
KeyState();
|
||||
public:
|
||||
KeyState* next;
|
||||
int deviceId;
|
||||
int source;
|
||||
int keyCode;
|
||||
bool unhandled;
|
||||
static KeyState* obtain(int deviceId, int source, int keyCode);
|
||||
void recycle();
|
||||
};
|
||||
}/*endof namespace*/
|
||||
#endif/*__INPUTEVENT_CONSISTENCYVERIFIER_H__*/
|
@ -316,7 +316,7 @@ void VelocityTrackerImpl::addMovement(const MotionEvent& event) {
|
||||
size_t historySize = event.getHistorySize();
|
||||
memset(positions,0,sizeof(positions));
|
||||
for (size_t h = 0; h < historySize; h++) {
|
||||
eventTime = event.getHistoricalEventTimeNano(h);
|
||||
eventTime = event.getHistoricalEventTimeNanos(h);
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
uint32_t index = pointerIndex[i];
|
||||
positions[index].x = event.getHistoricalRawX(i, h);
|
||||
@ -325,7 +325,7 @@ void VelocityTrackerImpl::addMovement(const MotionEvent& event) {
|
||||
addMovement(eventTime, idBits, positions);
|
||||
}
|
||||
|
||||
eventTime = event.getEventTimeNano();
|
||||
eventTime = event.getEventTimeNanos();
|
||||
for (size_t i = 0; i < pointerCount; i++) {
|
||||
uint32_t index = pointerIndex[i];
|
||||
positions[index].x = event.getRawX(i);
|
||||
|
@ -318,6 +318,9 @@ void View::initView(){
|
||||
mPendingCheckForTap = nullptr;
|
||||
mUnsetPressedState = nullptr;;
|
||||
mPendingCheckForLongPress = nullptr;
|
||||
mInputEventConsistencyVerifier = nullptr;
|
||||
if(InputEventConsistencyVerifier::isInstrumentationEnabled())
|
||||
mInputEventConsistencyVerifier = new InputEventConsistencyVerifier(nullptr,0);
|
||||
|
||||
mRenderNode = new RenderNode();
|
||||
mScrollX = mScrollY = 0;
|
||||
@ -396,6 +399,7 @@ View::~View(){
|
||||
delete mTooltipInfo;
|
||||
delete mScrollIndicatorDrawable;
|
||||
delete mDefaultFocusHighlight;
|
||||
delete mInputEventConsistencyVerifier;
|
||||
|
||||
delete mBackground;
|
||||
delete mBackgroundTint;
|
||||
@ -5713,12 +5717,16 @@ KeyEvent::DispatcherState* View::getKeyDispatcherState()const{
|
||||
}
|
||||
|
||||
bool View::dispatchKeyEvent(KeyEvent&event){
|
||||
if(mInputEventConsistencyVerifier)
|
||||
mInputEventConsistencyVerifier->onKeyEvent(event,0);
|
||||
if (mListenerInfo && mListenerInfo->mOnKeyListener && (mViewFlags & ENABLED_MASK) == ENABLED
|
||||
&& mListenerInfo->mOnKeyListener(*this, event.getKeyCode(), event)) {
|
||||
return true;
|
||||
}
|
||||
const bool result = event.dispatch(this,(mAttachInfo? &mAttachInfo->mKeyDispatchState : nullptr),this);
|
||||
LOGV("%s.%s=%d",event.getLabel(event.getKeyCode()),KeyEvent::actionToString(event.getAction()).c_str(),result);
|
||||
if(mInputEventConsistencyVerifier && (result==false))
|
||||
mInputEventConsistencyVerifier->onUnhandledEvent(event, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -5899,6 +5907,8 @@ bool View::dispatchGenericMotionEventInternal(MotionEvent& event){
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(mInputEventConsistencyVerifier)
|
||||
mInputEventConsistencyVerifier->onUnhandledEvent(event, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5936,7 +5946,9 @@ bool View::pointInHoveredChild(MotionEvent& event) {
|
||||
}
|
||||
|
||||
bool View::dispatchGenericMotionEvent(MotionEvent&event){
|
||||
int source = event.getSource();
|
||||
const int source = event.getSource();
|
||||
if (mInputEventConsistencyVerifier)
|
||||
mInputEventConsistencyVerifier->onGenericMotionEvent(event, 0);
|
||||
if ((source & InputDevice::SOURCE_CLASS_POINTER) != 0) {
|
||||
int action = event.getAction();
|
||||
if (action == MotionEvent::ACTION_HOVER_ENTER
|
||||
@ -5954,12 +5966,17 @@ bool View::dispatchGenericMotionEvent(MotionEvent&event){
|
||||
if (dispatchGenericMotionEventInternal(event)) {
|
||||
return true;
|
||||
}
|
||||
if (mInputEventConsistencyVerifier)
|
||||
mInputEventConsistencyVerifier->onUnhandledEvent(event, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool View::dispatchTouchEvent(MotionEvent&event){
|
||||
bool result = false;
|
||||
const int actionMasked = event.getActionMasked();
|
||||
if (mInputEventConsistencyVerifier)
|
||||
mInputEventConsistencyVerifier->onTouchEvent(event, 0);
|
||||
|
||||
if (actionMasked == MotionEvent::ACTION_UP ||
|
||||
actionMasked == MotionEvent::ACTION_CANCEL ||
|
||||
(actionMasked == MotionEvent::ACTION_DOWN && !result)) {
|
||||
@ -5979,7 +5996,8 @@ bool View::dispatchTouchEvent(MotionEvent&event){
|
||||
if(!result&& onTouchEvent(event)){
|
||||
result=true;
|
||||
}
|
||||
|
||||
if (!result && mInputEventConsistencyVerifier)
|
||||
mInputEventConsistencyVerifier->onUnhandledEvent(event, 0);
|
||||
if (actionMasked == MotionEvent::ACTION_UP ||
|
||||
actionMasked == MotionEvent::ACTION_CANCEL ||
|
||||
(actionMasked == MotionEvent::ACTION_DOWN && !result)) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <view/soundeffectconstants.h>
|
||||
#include <view/hapticfeedbackconstants.h>
|
||||
#include <view/accessibility/accessibilityevent.h>
|
||||
#include <view/inputeventconsistencyverifier.h>
|
||||
#include <animation/animation.h>
|
||||
#include <animation/statelistanimator.h>
|
||||
#include <set>
|
||||
@ -357,6 +358,7 @@ private:
|
||||
bool mBoundsChangedmDefaultFocusHighlightSizeChanged;
|
||||
|
||||
ViewOverlay* mOverlay;
|
||||
InputEventConsistencyVerifier* mInputEventConsistencyVerifier;
|
||||
ViewTreeObserver* mFloatingTreeObserver;
|
||||
StateListAnimator* mStateListAnimator;
|
||||
ViewPropertyAnimator* mAnimator;
|
||||
|
@ -2843,6 +2843,8 @@ void ViewGroup::onDescendantInvalidated(View* child,View* target){
|
||||
}
|
||||
|
||||
bool ViewGroup::dispatchKeyEvent(KeyEvent&event){
|
||||
if (mInputEventConsistencyVerifier)
|
||||
mInputEventConsistencyVerifier->onKeyEvent(event, 1);
|
||||
if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
|
||||
== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
|
||||
if (View::dispatchKeyEvent(event)) {
|
||||
@ -2854,6 +2856,8 @@ bool ViewGroup::dispatchKeyEvent(KeyEvent&event){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (mInputEventConsistencyVerifier)
|
||||
mInputEventConsistencyVerifier->onUnhandledEvent(event, 1);
|
||||
return View::dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
@ -2876,6 +2880,9 @@ bool ViewGroup::dispatchTouchEvent(MotionEvent&ev){
|
||||
const float scrolledXFloat = xf + mScrollX;
|
||||
const float scrolledYFloat = yf + mScrollY;
|
||||
|
||||
if (mInputEventConsistencyVerifier)
|
||||
mInputEventConsistencyVerifier->onTouchEvent(ev, 1);
|
||||
|
||||
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
|
||||
ev.setTargetAccessibilityFocus(false);
|
||||
}
|
||||
@ -3010,7 +3017,8 @@ bool ViewGroup::dispatchTouchEvent(MotionEvent&ev){
|
||||
int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
|
||||
removePointersFromTouchTargets(idBitsToRemove);
|
||||
}
|
||||
|
||||
if (!handled && mInputEventConsistencyVerifier)
|
||||
mInputEventConsistencyVerifier->onUnhandledEvent(ev, 1);
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user