diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/app/NativeActivity.java | 6 | ||||
-rw-r--r-- | core/java/android/service/wallpaper/WallpaperService.java | 7 | ||||
-rw-r--r-- | core/java/android/view/InputHandler.java | 4 | ||||
-rw-r--r-- | core/java/android/view/InputQueue.java | 16 | ||||
-rw-r--r-- | core/java/android/view/ViewRoot.java | 433 | ||||
-rw-r--r-- | core/java/android/view/WindowManagerPolicy.java | 29 | ||||
-rw-r--r-- | core/java/com/android/internal/view/BaseInputHandler.java | 9 | ||||
-rw-r--r-- | core/jni/android_app_NativeActivity.cpp | 22 | ||||
-rw-r--r-- | core/jni/android_view_InputQueue.cpp | 21 |
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 } }; |