diff options
| -rw-r--r-- | api/current.txt | 3 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 108 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewClient.java | 34 |
3 files changed, 110 insertions, 35 deletions
diff --git a/api/current.txt b/api/current.txt index 6d577963f711..f6d976e9b74a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -33183,7 +33183,8 @@ package android.webkit { method public void onReceivedSslError(android.webkit.WebView, android.webkit.SslErrorHandler, android.net.http.SslError); method public void onScaleChanged(android.webkit.WebView, float, float); method public deprecated void onTooManyRedirects(android.webkit.WebView, android.os.Message, android.os.Message); - method public void onUnhandledKeyEvent(android.webkit.WebView, android.view.KeyEvent); + method public void onUnhandledInputEvent(android.webkit.WebView, android.view.InputEvent); + method public deprecated void onUnhandledKeyEvent(android.webkit.WebView, android.view.KeyEvent); method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebView, java.lang.String); method public boolean shouldOverrideKeyEvent(android.webkit.WebView, android.view.KeyEvent); method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index ea52924234cf..beebeb193d49 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -234,6 +234,7 @@ public final class ViewRootImpl implements ViewParent, InputStage mFirstInputStage; InputStage mFirstPostImeInputStage; + InputStage mSyntheticInputStage; boolean mWindowAttributesChanged = false; int mWindowAttributesChangesFlag = 0; @@ -599,8 +600,8 @@ public final class ViewRootImpl implements ViewParent, // Set up the input pipeline. CharSequence counterSuffix = attrs.getTitle(); - InputStage syntheticInputStage = new SyntheticInputStage(); - InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticInputStage); + mSyntheticInputStage = new SyntheticInputStage(); + InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage); InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage, "aq:native-post-ime:" + counterSuffix); InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage); @@ -3007,6 +3008,7 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_INVALIDATE_WORLD = 23; private final static int MSG_WINDOW_MOVED = 24; private final static int MSG_FLUSH_LAYER_UPDATES = 25; + private final static int MSG_SYNTHESIZE_INPUT_EVENT = 26; final class ViewRootHandler extends Handler { @Override @@ -3056,6 +3058,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_WINDOW_MOVED"; case MSG_FLUSH_LAYER_UPDATES: return "MSG_FLUSH_LAYER_UPDATES"; + case MSG_SYNTHESIZE_INPUT_EVENT: + return "MSG_SYNTHESIZE_INPUT_EVENT"; } return super.getMessageName(message); } @@ -3218,6 +3222,10 @@ public final class ViewRootImpl implements ViewParent, enqueueInputEvent(event, receiver, 0, true); args.recycle(); } break; + case MSG_SYNTHESIZE_INPUT_EVENT: { + InputEvent event = (InputEvent)msg.obj; + enqueueInputEvent(event, null, QueuedInputEvent.FLAG_UNHANDLED, true); + } break; case MSG_DISPATCH_KEY_FROM_IME: { if (LOCAL_LOGV) Log.v( TAG, "Dispatching key " @@ -3227,7 +3235,8 @@ public final class ViewRootImpl implements ViewParent, // The IME is trying to say this event is from the // system! Bad bad bad! //noinspection UnusedAssignment - event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); + event = KeyEvent.changeFlags(event, event.getFlags() & + ~KeyEvent.FLAG_FROM_SYSTEM); } enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true); } break; @@ -4023,6 +4032,7 @@ public final class ViewRootImpl implements ViewParent, private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler(); private final SyntheticTouchNavigationHandler mTouchNavigation = new SyntheticTouchNavigationHandler(); + private final SyntheticKeyboardHandler mKeyboard = new SyntheticKeyboardHandler(); public SyntheticInputStage() { super(null); @@ -4045,7 +4055,11 @@ public final class ViewRootImpl implements ViewParent, mTouchNavigation.process(event); return FINISH_HANDLED; } + } else if ((q.mFlags & QueuedInputEvent.FLAG_UNHANDLED) != 0) { + mKeyboard.process((KeyEvent)q.mEvent); + return FINISH_HANDLED; } + return FORWARD; } @@ -4882,6 +4896,33 @@ public final class ViewRootImpl implements ViewParent, }; } + final class SyntheticKeyboardHandler { + public void process(KeyEvent event) { + if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) { + return; + } + + final KeyCharacterMap kcm = event.getKeyCharacterMap(); + final int keyCode = event.getKeyCode(); + final int metaState = event.getMetaState(); + + // Check for fallback actions specified by the key character map. + KeyCharacterMap.FallbackAction fallbackAction = + kcm.getFallbackAction(keyCode, metaState); + if (fallbackAction != null) { + final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK; + KeyEvent fallbackEvent = KeyEvent.obtain( + event.getDownTime(), event.getEventTime(), + event.getAction(), fallbackAction.keyCode, + event.getRepeatCount(), fallbackAction.metaState, + event.getDeviceId(), event.getScanCode(), + flags, event.getSource(), null); + fallbackAction.recycle(); + enqueueInputEvent(fallbackEvent); + } + } + } + /** * Returns true if the key is used for keyboard navigation. * @param keyEvent The key event. @@ -5461,6 +5502,7 @@ public final class ViewRootImpl implements ViewParent, public static final int FLAG_FINISHED = 1 << 2; public static final int FLAG_FINISHED_HANDLED = 1 << 3; public static final int FLAG_RESYNTHESIZED = 1 << 4; + public static final int FLAG_UNHANDLED = 1 << 5; public QueuedInputEvent mNext; @@ -5475,6 +5517,14 @@ public final class ViewRootImpl implements ViewParent, return mEvent instanceof MotionEvent && mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER); } + + public boolean shouldSendToSynthesizer() { + if ((mFlags & FLAG_UNHANDLED) != 0) { + return true; + } + + return false; + } } private QueuedInputEvent obtainQueuedInputEvent(InputEvent event, @@ -5578,7 +5628,13 @@ public final class ViewRootImpl implements ViewParent, mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0); } - InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; + InputStage stage; + if (q.shouldSendToSynthesizer()) { + stage = mSyntheticInputStage; + } else { + stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; + } + if (stage != null) { stage.deliver(q); } else { @@ -5810,43 +5866,29 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } - public void dispatchKeyFromIme(KeyEvent event) { - Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event); + public void synthesizeInputEvent(InputEvent event) { + Message msg = mHandler.obtainMessage(MSG_SYNTHESIZE_INPUT_EVENT, event); msg.setAsynchronous(true); mHandler.sendMessage(msg); } - public void dispatchUnhandledKey(KeyEvent event) { - if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { - // Some fallback keys are decided by the ViewRoot as they might have special - // properties (e.g. are locale aware). These take precedence over fallbacks defined by - // the kcm. - final KeyCharacterMap kcm = event.getKeyCharacterMap(); - final int keyCode = event.getKeyCode(); - final int metaState = event.getMetaState(); - - // Check for fallback actions specified by the key character map. - KeyCharacterMap.FallbackAction fallbackAction = - kcm.getFallbackAction(keyCode, metaState); - if (fallbackAction != null) { - final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK; - KeyEvent fallbackEvent = KeyEvent.obtain( - event.getDownTime(), event.getEventTime(), - event.getAction(), fallbackAction.keyCode, - event.getRepeatCount(), fallbackAction.metaState, - event.getDeviceId(), event.getScanCode(), - flags, event.getSource(), null); - fallbackAction.recycle(); - dispatchInputEvent(fallbackEvent); - } - } + public void dispatchKeyFromIme(KeyEvent event) { + Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); } + /** + * Reinject unhandled {@link InputEvent}s in order to synthesize fallbacks events. + * + * Note that it is the responsibility of the caller of this API to recycle the InputEvent it + * passes in. + */ public void dispatchUnhandledInputEvent(InputEvent event) { - if (event instanceof KeyEvent) { - dispatchUnhandledKey((KeyEvent) event); - return; + if (event instanceof MotionEvent) { + event = MotionEvent.obtain((MotionEvent) event); } + synthesizeInputEvent(event); } public void dispatchAppVisibility(boolean visible) { diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 688c2518bf67..33a6df6a9131 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -19,6 +19,7 @@ package android.webkit; import android.graphics.Bitmap; import android.net.http.SslError; import android.os.Message; +import android.view.InputEvent; import android.view.KeyEvent; import android.view.ViewRootImpl; @@ -272,11 +273,42 @@ public class WebViewClient { * * @param view The WebView that is initiating the callback. * @param event The key event. + * @deprecated This method is subsumed by the more generic onUnhandledInputEvent. */ + @Deprecated public void onUnhandledKeyEvent(WebView view, KeyEvent event) { + onUnhandledInputEventInternal(view, event); + } + + /** + * Notify the host application that a input event was not handled by the WebView. + * Except system keys, WebView always consumes input events in the normal flow + * or if shouldOverrideKeyEvent returns true. This is called asynchronously + * from where the event is dispatched. It gives the host application a chance + * to handle the unhandled input events. + * + * Note that if the event is a {@link MotionEvent}, then it's lifetime is only that of the + * function call. If the WebViewClient wishes to use the event beyond that, then it <i>must</i> + * create a copy of the event. + * + * It is the responsibility of overriders of this method to call {@link onUnhandledKeyEvent} + * when appropriate if they wish to continue receiving events through it. + * + * @param view The WebView that is initiating the callback. + * @param event The input event. + */ + public void onUnhandledInputEvent(WebView view, InputEvent event) { + if (event instanceof KeyEvent) { + onUnhandledKeyEvent(view, (KeyEvent) event); + return; + } + onUnhandledInputEventInternal(view, event); + } + + private void onUnhandledInputEventInternal(WebView view, InputEvent event) { ViewRootImpl root = view.getViewRootImpl(); if (root != null) { - root.dispatchUnhandledKey(event); + root.dispatchUnhandledInputEvent(event); } } |