summaryrefslogtreecommitdiffstats
path: root/core/java/android/view/ViewRoot.java
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2010-11-05 15:02:16 -0700
committerJeff Brown <jeffbrown@google.com>2010-11-08 12:49:43 -0800
commit3915bb845b032dc184dba5e60970b803390ca3ed (patch)
tree198a47c1d4ada990ef04d563b5e0caaec35abc18 /core/java/android/view/ViewRoot.java
parent60029771d26ca3c51288c3d92cab1d3537147acd (diff)
downloadframeworks_base-3915bb845b032dc184dba5e60970b803390ca3ed.zip
frameworks_base-3915bb845b032dc184dba5e60970b803390ca3ed.tar.gz
frameworks_base-3915bb845b032dc184dba5e60970b803390ca3ed.tar.bz2
Tell system server whether the app handled input events.
Refactored ViewRoot, NativeActivity and related classes to tell the dispatcher whether an input event was actually handled by the application. This will be used to move more of the global default key processing into the system server instead of the application. Change-Id: If06b98b6f45c543e5ac5b1eae2b3baf9371fba28
Diffstat (limited to 'core/java/android/view/ViewRoot.java')
-rw-r--r--core/java/android/view/ViewRoot.java433
1 files changed, 234 insertions, 199 deletions
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 5b18715..fa7fe80 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -53,6 +53,7 @@ import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.InputQueue.FinishedCallback;
import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -1742,34 +1743,14 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
handleFinishedEvent(msg.arg1, msg.arg2 != 0);
break;
case DISPATCH_KEY:
- if (LOCAL_LOGV) Log.v(
- TAG, "Dispatching key "
- + msg.obj + " to " + mView);
deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0);
break;
- case DISPATCH_POINTER: {
- MotionEvent event = (MotionEvent) msg.obj;
- try {
- deliverPointerEvent(event);
- } finally {
- event.recycle();
- if (msg.arg1 != 0) {
- finishInputEvent();
- }
- if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
- }
- } break;
- case DISPATCH_TRACKBALL: {
- MotionEvent event = (MotionEvent) msg.obj;
- try {
- deliverTrackballEvent(event);
- } finally {
- event.recycle();
- if (msg.arg1 != 0) {
- finishInputEvent();
- }
- }
- } break;
+ case DISPATCH_POINTER:
+ deliverPointerEvent((MotionEvent) msg.obj, msg.arg1 != 0);
+ break;
+ case DISPATCH_TRACKBALL:
+ deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0);
+ break;
case DISPATCH_APP_VISIBILITY:
handleAppVisibility(msg.arg1 != 0);
break;
@@ -1871,7 +1852,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
// system! Bad bad bad!
event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
}
- deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false);
+ deliverKeyEventPostIme((KeyEvent)msg.obj, false);
} break;
case FINISH_INPUT_CONNECTION: {
InputMethodManager imm = InputMethodManager.peekInstance();
@@ -1897,7 +1878,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
}
- private void startInputEvent(Runnable finishedCallback) {
+ private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
if (mFinishedCallback != null) {
Slog.w(TAG, "Received a new input event from the input queue but there is "
+ "already an unfinished input event in progress.");
@@ -1906,11 +1887,11 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
mFinishedCallback = finishedCallback;
}
- private void finishInputEvent() {
+ private void finishInputEvent(boolean handled) {
if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished");
if (mFinishedCallback != null) {
- mFinishedCallback.run();
+ mFinishedCallback.finished(handled);
mFinishedCallback = null;
} else {
Slog.w(TAG, "Attempted to tell the input queue that the current input event "
@@ -2039,105 +2020,134 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
return false;
}
- private void deliverPointerEvent(MotionEvent event) {
+ private void deliverPointerEvent(MotionEvent event, boolean sendDone) {
+ // If there is no view, then the event will not be handled.
+ if (mView == null || !mAdded) {
+ finishPointerEvent(event, sendDone, false);
+ return;
+ }
+
+ // Translate the pointer event for compatibility, if needed.
if (mTranslator != null) {
mTranslator.translateEventInScreenToAppWindow(event);
}
-
- boolean handled;
- if (mView != null && mAdded) {
- // enter touch mode on the down
- boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
- if (isDown) {
- ensureTouchMode(true);
- }
- if(Config.LOGV) {
- captureMotionLog("captureDispatchPointer", event);
- }
- if (mCurScrollY != 0) {
- event.offsetLocation(0, mCurScrollY);
- }
- if (MEASURE_LATENCY) {
- lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
- }
- // cache for possible drag-initiation
- mLastTouchPoint.x = event.getRawX();
- mLastTouchPoint.y = event.getRawY();
- handled = mView.dispatchTouchEvent(event);
- if (MEASURE_LATENCY) {
- lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
- }
- if (!handled && isDown) {
- int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
-
- final int edgeFlags = event.getEdgeFlags();
- int direction = View.FOCUS_UP;
- int x = (int)event.getX();
- int y = (int)event.getY();
- final int[] deltas = new int[2];
-
- if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
- direction = View.FOCUS_DOWN;
- if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
- deltas[0] = edgeSlop;
- x += edgeSlop;
- } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
- deltas[0] = -edgeSlop;
- x -= edgeSlop;
- }
- } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
- direction = View.FOCUS_UP;
- if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
- deltas[0] = edgeSlop;
- x += edgeSlop;
- } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
- deltas[0] = -edgeSlop;
- x -= edgeSlop;
- }
- } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
- direction = View.FOCUS_RIGHT;
- } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
- direction = View.FOCUS_LEFT;
- }
+ // Enter touch mode on the down.
+ boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
+ if (isDown) {
+ ensureTouchMode(true);
+ }
+ if(Config.LOGV) {
+ captureMotionLog("captureDispatchPointer", event);
+ }
- if (edgeFlags != 0 && mView instanceof ViewGroup) {
- View nearest = FocusFinder.getInstance().findNearestTouchable(
- ((ViewGroup) mView), x, y, direction, deltas);
- if (nearest != null) {
- event.offsetLocation(deltas[0], deltas[1]);
- event.setEdgeFlags(0);
- mView.dispatchTouchEvent(event);
- }
+ // Offset the scroll position.
+ if (mCurScrollY != 0) {
+ event.offsetLocation(0, mCurScrollY);
+ }
+ if (MEASURE_LATENCY) {
+ lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
+ }
+
+ // Remember the touch position for possible drag-initiation.
+ mLastTouchPoint.x = event.getRawX();
+ mLastTouchPoint.y = event.getRawY();
+
+ // Dispatch touch to view hierarchy.
+ boolean handled = mView.dispatchTouchEvent(event);
+ if (MEASURE_LATENCY) {
+ lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
+ }
+ if (handled) {
+ finishPointerEvent(event, sendDone, true);
+ return;
+ }
+
+ // Apply edge slop and try again, if appropriate.
+ final int edgeFlags = event.getEdgeFlags();
+ if (edgeFlags != 0 && mView instanceof ViewGroup) {
+ final int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
+ int direction = View.FOCUS_UP;
+ int x = (int)event.getX();
+ int y = (int)event.getY();
+ final int[] deltas = new int[2];
+
+ if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
+ direction = View.FOCUS_DOWN;
+ if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+ deltas[0] = edgeSlop;
+ x += edgeSlop;
+ } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+ deltas[0] = -edgeSlop;
+ x -= edgeSlop;
+ }
+ } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
+ direction = View.FOCUS_UP;
+ if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+ deltas[0] = edgeSlop;
+ x += edgeSlop;
+ } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+ deltas[0] = -edgeSlop;
+ x -= edgeSlop;
+ }
+ } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+ direction = View.FOCUS_RIGHT;
+ } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+ direction = View.FOCUS_LEFT;
+ }
+
+ View nearest = FocusFinder.getInstance().findNearestTouchable(
+ ((ViewGroup) mView), x, y, direction, deltas);
+ if (nearest != null) {
+ event.offsetLocation(deltas[0], deltas[1]);
+ event.setEdgeFlags(0);
+ if (mView.dispatchTouchEvent(event)) {
+ finishPointerEvent(event, sendDone, true);
+ return;
}
}
}
+
+ // Pointer event was unhandled.
+ finishPointerEvent(event, sendDone, false);
}
- private void deliverTrackballEvent(MotionEvent event) {
+ private void finishPointerEvent(MotionEvent event, boolean sendDone, boolean handled) {
+ event.recycle();
+ if (sendDone) {
+ finishInputEvent(handled);
+ }
+ if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
+ }
+
+ private void deliverTrackballEvent(MotionEvent event, boolean sendDone) {
if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
- boolean handled = false;
- if (mView != null && mAdded) {
- handled = mView.dispatchTrackballEvent(event);
- if (handled) {
- // If we reach this, we delivered a trackball event to mView and
- // mView consumed it. Because we will not translate the trackball
- // event into a key event, touch mode will not exit, so we exit
- // touch mode here.
- ensureTouchMode(false);
- return;
- }
-
- // Otherwise we could do something here, like changing the focus
- // or something?
+ // If there is no view, then the event will not be handled.
+ if (mView == null || !mAdded) {
+ finishTrackballEvent(event, sendDone, false);
+ return;
+ }
+
+ // Deliver the trackball event to the view.
+ if (mView.dispatchTrackballEvent(event)) {
+ // If we reach this, we delivered a trackball event to mView and
+ // mView consumed it. Because we will not translate the trackball
+ // event into a key event, touch mode will not exit, so we exit
+ // touch mode here.
+ ensureTouchMode(false);
+
+ finishTrackballEvent(event, sendDone, true);
+ mLastTrackballTime = Integer.MIN_VALUE;
+ return;
}
+ // Translate the trackball event into DPAD keys and try to deliver those.
final TrackballAxis x = mTrackballAxisX;
final TrackballAxis y = mTrackballAxisY;
long curTime = SystemClock.uptimeMillis();
- if ((mLastTrackballTime+MAX_TRACKBALL_DELAY) < curTime) {
+ if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) {
// It has been too long since the last movement,
// so restart at the beginning.
x.reset(0);
@@ -2226,6 +2236,17 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
mLastTrackballTime = curTime;
}
+
+ // Unfortunately we can't tell whether the application consumed the keys, so
+ // we always consider the trackball event handled.
+ finishTrackballEvent(event, sendDone, true);
+ }
+
+ private void finishTrackballEvent(MotionEvent event, boolean sendDone, boolean handled) {
+ event.recycle();
+ if (sendDone) {
+ finishInputEvent(handled);
+ }
}
/**
@@ -2371,123 +2392,137 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
- // If mView is null, we just consume the key event because it doesn't
- // make sense to do anything else with it.
- boolean handled = mView == null || mView.dispatchKeyEventPreIme(event);
- if (handled) {
- if (sendDone) {
- finishInputEvent();
- }
+ // If there is no view, then the event will not be handled.
+ if (mView == null || !mAdded) {
+ finishKeyEvent(event, sendDone, false);
+ return;
+ }
+
+ if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
+
+ // Perform predispatching before the IME.
+ if (mView.dispatchKeyEventPreIme(event)) {
+ finishKeyEvent(event, sendDone, true);
return;
}
- // If it is possible for this window to interact with the input
- // method window, then we want to first dispatch our key events
- // to the input method.
+
+ // Dispatch to the IME before propagating down the view hierarchy.
+ // The IME will eventually call back into handleFinishedEvent.
if (mLastWasImTarget) {
InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null && mView != null) {
+ if (imm != null) {
int seq = enqueuePendingEvent(event, sendDone);
if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
+ seq + " event=" + event);
- imm.dispatchKeyEvent(mView.getContext(), seq, event,
- mInputMethodCallback);
+ imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
return;
}
}
- deliverKeyEventToViewHierarchy(event, sendDone);
+
+ // Not dispatching to IME, continue with post IME actions.
+ deliverKeyEventPostIme(event, sendDone);
}
- void handleFinishedEvent(int seq, boolean handled) {
+ private void handleFinishedEvent(int seq, boolean handled) {
final KeyEvent event = (KeyEvent)retrievePendingEvent(seq);
if (DEBUG_IMF) Log.v(TAG, "IME finished event: seq=" + seq
+ " handled=" + handled + " event=" + event);
if (event != null) {
final boolean sendDone = seq >= 0;
- if (!handled) {
- deliverKeyEventToViewHierarchy(event, sendDone);
- } else if (sendDone) {
- finishInputEvent();
+ if (handled) {
+ finishKeyEvent(event, sendDone, true);
} else {
- Log.w(TAG, "handleFinishedEvent(seq=" + seq
- + " handled=" + handled + " ev=" + event
- + ") neither delivering nor finishing key");
+ deliverKeyEventPostIme(event, sendDone);
}
}
}
- private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) {
- try {
- if (mView != null && mAdded) {
- final int action = event.getAction();
- boolean isDown = (action == KeyEvent.ACTION_DOWN);
+ private void deliverKeyEventPostIme(KeyEvent event, boolean sendDone) {
+ // If the view went away, then the event will not be handled.
+ if (mView == null || !mAdded) {
+ finishKeyEvent(event, sendDone, false);
+ return;
+ }
- if (checkForLeavingTouchModeAndConsume(event)) {
- return;
- }
+ // If the key's purpose is to exit touch mode then we consume it and consider it handled.
+ if (checkForLeavingTouchModeAndConsume(event)) {
+ finishKeyEvent(event, sendDone, true);
+ return;
+ }
- if (Config.LOGV) {
- captureKeyLog("captureDispatchKeyEvent", event);
- }
- mFallbackEventHandler.preDispatchKeyEvent(event);
- boolean keyHandled = mView.dispatchKeyEvent(event);
+ if (Config.LOGV) {
+ captureKeyLog("captureDispatchKeyEvent", event);
+ }
- if (!keyHandled) {
- mFallbackEventHandler.dispatchKeyEvent(event);
- }
+ // Deliver the key to the view hierarchy.
+ if (mView.dispatchKeyEvent(event)) {
+ finishKeyEvent(event, sendDone, true);
+ return;
+ }
- if (!keyHandled && isDown) {
- int direction = 0;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- direction = View.FOCUS_LEFT;
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- direction = View.FOCUS_RIGHT;
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- direction = View.FOCUS_UP;
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- direction = View.FOCUS_DOWN;
- break;
- }
+ // Apply the fallback event policy.
+ if (mFallbackEventHandler.dispatchKeyEvent(event)) {
+ finishKeyEvent(event, sendDone, true);
+ return;
+ }
- if (direction != 0) {
-
- View focused = mView != null ? mView.findFocus() : null;
- if (focused != null) {
- View v = focused.focusSearch(direction);
- boolean focusPassed = false;
- if (v != null && v != focused) {
- // do the math the get the interesting rect
- // of previous focused into the coord system of
- // newly focused view
- focused.getFocusedRect(mTempRect);
- if (mView instanceof ViewGroup) {
- ((ViewGroup) mView).offsetDescendantRectToMyCoords(
- focused, mTempRect);
- ((ViewGroup) mView).offsetRectIntoDescendantCoords(
- v, mTempRect);
- }
- focusPassed = v.requestFocus(direction, mTempRect);
- }
+ // Handle automatic focus changes.
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ int direction = 0;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ direction = View.FOCUS_LEFT;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ direction = View.FOCUS_RIGHT;
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ direction = View.FOCUS_UP;
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ direction = View.FOCUS_DOWN;
+ break;
+ }
- if (!focusPassed) {
- mView.dispatchUnhandledMove(focused, direction);
- } else {
- playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
- }
+ if (direction != 0) {
+ View focused = mView != null ? mView.findFocus() : null;
+ if (focused != null) {
+ View v = focused.focusSearch(direction);
+ if (v != null && v != focused) {
+ // do the math the get the interesting rect
+ // of previous focused into the coord system of
+ // newly focused view
+ focused.getFocusedRect(mTempRect);
+ if (mView instanceof ViewGroup) {
+ ((ViewGroup) mView).offsetDescendantRectToMyCoords(
+ focused, mTempRect);
+ ((ViewGroup) mView).offsetRectIntoDescendantCoords(
+ v, mTempRect);
+ }
+ if (v.requestFocus(direction, mTempRect)) {
+ playSoundEffect(
+ SoundEffectConstants.getContantForFocusDirection(direction));
+ finishKeyEvent(event, sendDone, true);
+ return;
}
}
+
+ // Give the focused view a last chance to handle the dpad key.
+ if (mView.dispatchUnhandledMove(focused, direction)) {
+ finishKeyEvent(event, sendDone, true);
+ return;
+ }
}
}
+ }
- } finally {
- if (sendDone) {
- finishInputEvent();
- }
- // Let the exception fall through -- the looper will catch
- // it and take care of the bad app for us.
+ // Key was unhandled.
+ finishKeyEvent(event, sendDone, false);
+ }
+
+ private void finishKeyEvent(KeyEvent event, boolean sendDone, boolean handled) {
+ if (sendDone) {
+ finishInputEvent(handled);
}
}
@@ -2759,15 +2794,15 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
sendMessage(msg);
}
- private Runnable mFinishedCallback;
+ private InputQueue.FinishedCallback mFinishedCallback;
private final InputHandler mInputHandler = new InputHandler() {
- public void handleKey(KeyEvent event, Runnable finishedCallback) {
+ public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
startInputEvent(finishedCallback);
dispatchKey(event, true);
}
- public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+ public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
startInputEvent(finishedCallback);
dispatchMotion(event, true);
}
@@ -2814,7 +2849,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
// TODO
Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event);
if (sendDone) {
- finishInputEvent();
+ finishInputEvent(false);
}
}
}