之前我们分析了无线鼠标右键无效,hal层的分析,我们可以把修改成返回键。
uint32_t CursorButtonAccumulator::getButtonState() const {
uint32_t result = 0;
if (mBtnLeft) {
result |= AMOTION_EVENT_BUTTON_PRIMARY;
}
if (mBtnRight) {
//result |= AMOTION_EVENT_BUTTON_SECONDARY;
result |= AMOTION_EVENT_BUTTON_BACK;//修改成返回键
}
但是具体AMOTION_EVENT_BUTTON_SECONDARY在哪里被过滤了,我们还没有分析到,现在我们来分析下鼠标按键机制的Framework层的分析,为什么之前的右键会被上层过滤?
我们先来看下ViewRootImpl.java这个文件,至于如何从hal层的按键进程,到Framework层的应用进程我们不分析了,之前的博客都分析过了。我们直接从鼠标处理的地方分析:
final class ViewPostImeInputStage extends InputStage {
public ViewPostImeInputStage(InputStage next) {
super(next);
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);//普通按键处理
} else {
// If delivering a new non-key event, make sure the window is
// now allowed to start updating.
handleDispatchWindowAnimationStopped();
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);//鼠标按键处理
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
return processTrackballEvent(q);
} else {
return processGenericMotionEvent(q);
}
}
}
ViewRootImpl文件处理按键消息会经过各个InputStage,而鼠标按键的处理是在ViewPostImeInputStage 和普通按键处理一样,会调用processPointerEvent来处理鼠标按键。
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
boolean handled = mView.dispatchPointerEvent(event);
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
processPointerEvent又调用了dispatchPointerEvent函数
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);//第一次鼠标按键进入这个函数
} else {
return dispatchGenericMotionEvent(event);
}
}
第一次鼠标按键进入dispatchTouchEvent这个函数
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {//关键看onTouchEvent函数
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
下面我们再来看下onTouchEvent函数,在对aciton进行分别处理,当是MotionEvent.ACTION_DOWN:会先调用performButtonActionOnTouchDown函数
public boolean onTouchEvent(MotionEvent event) {
......
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
......
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
Log.e(TAG, "kangchen postDelayed setPressed");
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
Log.e(TAG, "kangchen setPressed");
checkForLongClick(0);
}
break;
......
主要是下面这个函数,当获取ButtonState是MotionEvent.BUTTON_SECONDARY,就返回true,而在onTouchEvent函数中当performButtonActionOnTouchDown返回true,就会执行break,下面代码就不执行了。也就是因为这个导致右键无效,具体代码这边就不继续跟下去了,比较复杂。
protected boolean performButtonActionOnTouchDown(MotionEvent event) {
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE &&
(event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
showContextMenu(event.getX(), event.getY(), event.getMetaState());
mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT;
return true;//返回true
}
return false;
}
这样我们就大致分析了Framework层把鼠标右键过滤的分析。