summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/NativeActivity.java6
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java7
-rw-r--r--core/java/android/view/InputHandler.java4
-rw-r--r--core/java/android/view/InputQueue.java16
-rw-r--r--core/java/android/view/ViewRoot.java433
-rw-r--r--core/java/android/view/WindowManagerPolicy.java29
-rw-r--r--core/java/com/android/internal/view/BaseInputHandler.java9
-rw-r--r--core/jni/android_app_NativeActivity.cpp22
-rw-r--r--core/jni/android_view_InputQueue.cpp21
9 files changed, 307 insertions, 240 deletions
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index de36f27..a5c49ec 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -344,12 +344,14 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
}
}
- void dispatchUnhandledKeyEvent(KeyEvent event) {
+ boolean dispatchUnhandledKeyEvent(KeyEvent event) {
try {
mDispatchingUnhandledKey = true;
View decor = getWindow().getDecorView();
if (decor != null) {
- decor.dispatchKeyEvent(event);
+ return decor.dispatchKeyEvent(event);
+ } else {
+ return false;
}
} finally {
mDispatchingUnhandledKey = false;
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 26346d2..755e39f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -219,14 +219,17 @@ public abstract class WallpaperService extends Service {
final InputHandler mInputHandler = new BaseInputHandler() {
@Override
- public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+ public void handleMotion(MotionEvent event,
+ InputQueue.FinishedCallback finishedCallback) {
+ boolean handled = false;
try {
int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
dispatchPointer(event);
+ handled = true;
}
} finally {
- finishedCallback.run();
+ finishedCallback.finished(handled);
}
}
};
diff --git a/core/java/android/view/InputHandler.java b/core/java/android/view/InputHandler.java
index 41a152d..14ce14c 100644
--- a/core/java/android/view/InputHandler.java
+++ b/core/java/android/view/InputHandler.java
@@ -29,7 +29,7 @@ public interface InputHandler {
* @param event The key event data.
* @param finishedCallback The callback to invoke when event processing is finished.
*/
- public void handleKey(KeyEvent event, Runnable finishedCallback);
+ public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback);
/**
* Handle a motion event.
@@ -39,5 +39,5 @@ public interface InputHandler {
* @param event The motion event data.
* @param finishedCallback The callback to invoke when event processing is finished.
*/
- public void handleMotion(MotionEvent event, Runnable finishedCallback);
+ public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback);
}
diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java
index 9e800df..5735b63 100644
--- a/core/java/android/view/InputQueue.java
+++ b/core/java/android/view/InputQueue.java
@@ -53,7 +53,7 @@ public final class InputQueue {
private static native void nativeRegisterInputChannel(InputChannel inputChannel,
InputHandler inputHandler, MessageQueue messageQueue);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
- private static native void nativeFinished(long finishedToken);
+ private static native void nativeFinished(long finishedToken, boolean handled);
/** @hide */
public InputQueue(InputChannel channel) {
@@ -116,18 +116,22 @@ public final class InputQueue {
@SuppressWarnings("unused")
private static void dispatchKeyEvent(InputHandler inputHandler,
KeyEvent event, long finishedToken) {
- Runnable finishedCallback = FinishedCallback.obtain(finishedToken);
+ FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken);
inputHandler.handleKey(event, finishedCallback);
}
@SuppressWarnings("unused")
private static void dispatchMotionEvent(InputHandler inputHandler,
MotionEvent event, long finishedToken) {
- Runnable finishedCallback = FinishedCallback.obtain(finishedToken);
+ FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken);
inputHandler.handleMotion(event, finishedCallback);
}
- private static class FinishedCallback implements Runnable {
+ /**
+ * A callback that must be invoked to when finished processing an event.
+ * @hide
+ */
+ public static final class FinishedCallback {
private static final boolean DEBUG_RECYCLING = false;
private static final int RECYCLE_MAX_COUNT = 4;
@@ -156,13 +160,13 @@ public final class InputQueue {
}
}
- public void run() {
+ public void finished(boolean handled) {
synchronized (sLock) {
if (mFinishedToken == -1) {
throw new IllegalStateException("Event finished callback already invoked.");
}
- nativeFinished(mFinishedToken);
+ nativeFinished(mFinishedToken, handled);
mFinishedToken = -1;
if (sRecycleCount < RECYCLE_MAX_COUNT) {
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);
}
}
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index e78d6a8..4deff5e 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -568,12 +568,8 @@ public interface WindowManagerPolicy {
* Called from the input dispatcher thread before a key is dispatched to a window.
*
* <p>Allows you to define
- * behavior for keys that can not be overridden by applications or redirect
- * key events to a different window. This method is called from the
- * input thread, with no locks held.
- *
- * <p>Note that if you change the window a key is dispatched to, the new
- * target window will receive the key event without having input focus.
+ * behavior for keys that can not be overridden by applications.
+ * This method is called from the input thread, with no locks held.
*
* @param win The window that currently has focus. This is where the key
* event will normally go.
@@ -591,6 +587,27 @@ public interface WindowManagerPolicy {
int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags);
/**
+ * Called from the input dispatcher thread when an application did not handle
+ * a key that was dispatched to it.
+ *
+ * <p>Allows you to define default global behavior for keys that were not handled
+ * by applications. This method is called from the input thread, with no locks held.
+ *
+ * @param win The window that currently has focus. This is where the key
+ * event will normally go.
+ * @param action The key event action.
+ * @param flags The key event flags.
+ * @param keyCode The key code.
+ * @param scanCode The key's scan code.
+ * @param metaState bit mask of meta keys that are held.
+ * @param repeatCount Number of times a key down has repeated.
+ * @param policyFlags The policy flags associated with the key.
+ * @return Returns true if the policy consumed the event.
+ */
+ public boolean dispatchUnhandledKey(WindowState win, int action, int flags,
+ int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags);
+
+ /**
* Called when layout of the windows is about to start.
*
* @param displayWidth The current full width of the screen.
diff --git a/core/java/com/android/internal/view/BaseInputHandler.java b/core/java/com/android/internal/view/BaseInputHandler.java
index e943a7d..74b4b06 100644
--- a/core/java/com/android/internal/view/BaseInputHandler.java
+++ b/core/java/com/android/internal/view/BaseInputHandler.java
@@ -17,6 +17,7 @@
package com.android.internal.view;
import android.view.InputHandler;
+import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -25,11 +26,11 @@ import android.view.MotionEvent;
* @hide
*/
public abstract class BaseInputHandler implements InputHandler {
- public void handleKey(KeyEvent event, Runnable finishedCallback) {
- finishedCallback.run();
+ public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
+ finishedCallback.finished(false);
}
- public void handleMotion(MotionEvent event, Runnable finishedCallback) {
- finishedCallback.run();
+ public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
+ finishedCallback.finished(false);
}
}
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index a586f49..695d50a4 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -195,7 +195,7 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
mLock.unlock();
if (finishNow) {
- finishEvent(*outEvent, true);
+ finishEvent(*outEvent, true, false);
*outEvent = NULL;
return -1;
} else if (*outEvent != NULL) {
@@ -215,7 +215,7 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
if (res != android::OK) {
LOGW("channel '%s' ~ Failed to consume input event. status=%d",
mConsumer.getChannel()->getName().string(), res);
- mConsumer.sendFinishedSignal();
+ mConsumer.sendFinishedSignal(false);
return -1;
}
@@ -245,10 +245,12 @@ bool AInputQueue::preDispatchEvent(AInputEvent* event) {
return preDispatchKey((KeyEvent*)event);
}
-void AInputQueue::finishEvent(AInputEvent* event, bool handled) {
- LOG_TRACE("finishEvent: %p handled=%d", event, handled ? 1 : 0);
+void AInputQueue::finishEvent(AInputEvent* event, bool handled, bool didDefaultHandling) {
+ LOG_TRACE("finishEvent: %p handled=%d, didDefaultHandling=%d", event,
+ handled ? 1 : 0, didDefaultHandling ? 1 : 0);
- if (!handled && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
+ if (!handled && !didDefaultHandling
+ && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
&& ((KeyEvent*)event)->hasDefaultAction()) {
// The app didn't handle this, but it may have a default action
// associated with it. We need to hand this back to Java to be
@@ -263,7 +265,7 @@ void AInputQueue::finishEvent(AInputEvent* event, bool handled) {
const in_flight_event& inflight(mInFlightEvents[i]);
if (inflight.event == event) {
if (inflight.doFinish) {
- int32_t res = mConsumer.sendFinishedSignal();
+ int32_t res = mConsumer.sendFinishedSignal(handled);
if (res != android::OK) {
LOGW("Failed to send finished signal on channel '%s'. status=%d",
mConsumer.getChannel()->getName().string(), res);
@@ -577,10 +579,11 @@ static int mainWorkCallback(int fd, int events, void* data) {
while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) {
jobject inputEventObj = android_view_KeyEvent_fromNative(
code->env, keyEvent);
- code->env->CallVoidMethod(code->clazz,
+ jboolean handled = code->env->CallBooleanMethod(code->clazz,
gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
- code->nativeInputQueue->finishEvent(keyEvent, true);
+ code->env->DeleteLocalRef(inputEventObj);
+ code->nativeInputQueue->finishEvent(keyEvent, handled, true);
}
int seq;
while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
@@ -589,6 +592,7 @@ static int mainWorkCallback(int fd, int events, void* data) {
code->env->CallVoidMethod(code->clazz,
gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
+ code->env->DeleteLocalRef(inputEventObj);
}
} break;
case CMD_FINISH: {
@@ -1044,7 +1048,7 @@ int register_android_app_NativeActivity(JNIEnv* env)
GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
gNativeActivityClassInfo.clazz,
- "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
+ "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)Z");
GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
gNativeActivityClassInfo.clazz,
"preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 282e9ed..b5a5d2e 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -61,7 +61,7 @@ public:
status_t unregisterInputChannel(JNIEnv* env, jobject inputChannelObj);
- status_t finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish);
+ status_t finished(JNIEnv* env, jlong finishedToken, bool handled, bool ignoreSpuriousFinish);
private:
class Connection : public RefBase {
@@ -211,7 +211,7 @@ status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChan
"while an input message is still in progress.",
connection->getInputChannelName());
connection->messageInProgress = false;
- connection->inputConsumer.sendFinishedSignal(); // ignoring result
+ connection->inputConsumer.sendFinishedSignal(false); // ignoring result
}
} // release lock
@@ -231,7 +231,8 @@ ssize_t NativeInputQueue::getConnectionIndex(const sp<InputChannel>& inputChanne
return -1;
}
-status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish) {
+status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken,
+ bool handled, bool ignoreSpuriousFinish) {
int32_t receiveFd;
uint16_t connectionId;
uint16_t messageSeqNum;
@@ -268,7 +269,7 @@ status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignor
connection->messageInProgress = false;
- status_t status = connection->inputConsumer.sendFinishedSignal();
+ status_t status = connection->inputConsumer.sendFinishedSignal(handled);
if (status) {
LOGW("Failed to send finished signal on channel '%s'. status=%d",
connection->getInputChannelName(), status);
@@ -341,7 +342,7 @@ int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* dat
if (status) {
LOGW("channel '%s' ~ Failed to consume input event. status=%d",
connection->getInputChannelName(), status);
- connection->inputConsumer.sendFinishedSignal();
+ connection->inputConsumer.sendFinishedSignal(false);
return 1;
}
@@ -393,7 +394,7 @@ int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* dat
LOGW("channel '%s' ~ Failed to obtain DVM event object.",
connection->getInputChannelName());
env->DeleteLocalRef(inputHandlerObjLocal);
- q->finished(env, finishedToken, false);
+ q->finished(env, finishedToken, false, false);
return 1;
}
@@ -412,7 +413,7 @@ int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* dat
LOGE_EX(env);
env->ExceptionClear();
- q->finished(env, finishedToken, true /*ignoreSpuriousFinish*/);
+ q->finished(env, finishedToken, false, true /*ignoreSpuriousFinish*/);
}
env->DeleteLocalRef(inputEventObj);
@@ -470,9 +471,9 @@ static void android_view_InputQueue_nativeUnregisterInputChannel(JNIEnv* env, jc
}
static void android_view_InputQueue_nativeFinished(JNIEnv* env, jclass clazz,
- jlong finishedToken) {
+ jlong finishedToken, bool handled) {
status_t status = gNativeInputQueue.finished(
- env, finishedToken, false /*ignoreSpuriousFinish*/);
+ env, finishedToken, handled, false /*ignoreSpuriousFinish*/);
// We ignore the case where an event could not be finished because the input channel
// was no longer registered (DEAD_OBJECT) since it is a common race that can occur
@@ -493,7 +494,7 @@ static JNINativeMethod gInputQueueMethods[] = {
{ "nativeUnregisterInputChannel",
"(Landroid/view/InputChannel;)V",
(void*)android_view_InputQueue_nativeUnregisterInputChannel },
- { "nativeFinished", "(J)V",
+ { "nativeFinished", "(JZ)V",
(void*)android_view_InputQueue_nativeFinished }
};