diff options
| author | 2018-05-30 15:10:41 -0700 | |
|---|---|---|
| committer | 2018-05-30 15:10:41 -0700 | |
| commit | ee4376a2908818ff15c03c80748bedbd37e3da45 (patch) | |
| tree | f9a71080e64bdca24dd8b680db8b0fa4d06cf907 | |
| parent | f6a62cb64c505ed4cc608a65dfe763a5551f5f76 (diff) | |
| parent | 9bf11ede69a781811fd0ed0ffdd65ee0f3c97e0f (diff) | |
Merge "Fixed a bug where sometimes unhandled handler would consume all keys" into pi-dev am: 7ba4c2b254
am: 9bf11ede69
Change-Id: I3e1eb6884cedb86ec144e5ada09b4252ee266c96
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 6 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 107 | ||||
| -rw-r--r-- | core/java/com/android/internal/policy/DecorView.java | 2 |
3 files changed, 70 insertions, 45 deletions
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 8a7d00429691..794431966443 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -5060,9 +5060,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager child.assignParent(this); } else { child.mParent = this; - if (child.hasUnhandledKeyListener()) { - incrementChildUnhandledKeyListeners(); - } + } + if (child.hasUnhandledKeyListener()) { + incrementChildUnhandledKeyListeners(); } final boolean childHasFocus = child.hasFocus(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2b0d81c231a5..d34b92ba9a48 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -76,7 +76,6 @@ import android.util.LongArray; import android.util.MergedConfiguration; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.TypedValue; import android.view.Surface.OutOfResourcesException; @@ -4985,10 +4984,7 @@ public final class ViewRootImpl implements ViewParent, private int processKeyEvent(QueuedInputEvent q) { final KeyEvent event = (KeyEvent)q.mEvent; - mUnhandledKeyManager.mDispatched = false; - - if (mUnhandledKeyManager.hasFocus() - && mUnhandledKeyManager.dispatchUnique(mView, event)) { + if (mUnhandledKeyManager.preViewDispatch(event)) { return FINISH_HANDLED; } @@ -5001,7 +4997,10 @@ public final class ViewRootImpl implements ViewParent, return FINISH_NOT_HANDLED; } - if (mUnhandledKeyManager.dispatchUnique(mView, event)) { + // This dispatch is for windows that don't have a Window.Callback. Otherwise, + // the Window.Callback usually will have already called this (see + // DecorView.superDispatchKeyEvent) leaving this call a no-op. + if (mUnhandledKeyManager.dispatch(mView, event)) { return FINISH_HANDLED; } @@ -7039,6 +7038,10 @@ public final class ViewRootImpl implements ViewParent, stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } + if (q.mEvent instanceof KeyEvent) { + mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent); + } + if (stage != null) { handleWindowFocusChanged(); stage.deliver(q); @@ -7807,7 +7810,7 @@ public final class ViewRootImpl implements ViewParent, * @param event * @return {@code true} if the event was handled, {@code false} otherwise. */ - public boolean dispatchKeyFallbackEvent(KeyEvent event) { + public boolean dispatchUnhandledKeyEvent(KeyEvent event) { return mUnhandledKeyManager.dispatch(mView, event); } @@ -8399,35 +8402,74 @@ public final class ViewRootImpl implements ViewParent, } private static class UnhandledKeyManager { - // This is used to ensure that unhandled events are only dispatched once. We attempt // to dispatch more than once in order to achieve a certain order. Specifically, if we // are in an Activity or Dialog (and have a Window.Callback), the unhandled events should - // be dispatched after the view hierarchy, but before the Activity. However, if we aren't + // be dispatched after the view hierarchy, but before the Callback. However, if we aren't // in an activity, we still want unhandled keys to be dispatched. - boolean mDispatched = false; + private boolean mDispatched = true; - SparseBooleanArray mCapturedKeys = new SparseBooleanArray(); - WeakReference<View> mCurrentReceiver = null; + // Keeps track of which Views have unhandled key focus for which keys. This doesn't + // include modifiers. + private final SparseArray<WeakReference<View>> mCapturedKeys = new SparseArray<>(); - private void updateCaptureState(KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - mCapturedKeys.append(event.getKeyCode(), true); + // The current receiver. This value is transient and used between the pre-dispatch and + // pre-view phase to ensure that other input-stages don't interfere with tracking. + private WeakReference<View> mCurrentReceiver = null; + + boolean dispatch(View root, KeyEvent event) { + if (mDispatched) { + return false; } - if (event.getAction() == KeyEvent.ACTION_UP) { - mCapturedKeys.delete(event.getKeyCode()); + View consumer; + try { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "UnhandledKeyEvent dispatch"); + mDispatched = true; + + consumer = root.dispatchUnhandledKeyEvent(event); + + // If an unhandled listener handles one, then keep track of it so that the + // consuming view is first to receive its repeats and release as well. + if (event.getAction() == KeyEvent.ACTION_DOWN) { + int keycode = event.getKeyCode(); + if (consumer != null && !KeyEvent.isModifierKey(keycode)) { + mCapturedKeys.put(keycode, new WeakReference<>(consumer)); + } + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + return consumer != null; } - boolean dispatch(View root, KeyEvent event) { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "KeyFallback dispatch"); - mDispatched = true; - - updateCaptureState(event); + /** + * Called before the event gets dispatched to anything + */ + void preDispatch(KeyEvent event) { + // Always clean-up 'up' events since it's possible for earlier dispatch stages to + // consume them without consuming the corresponding 'down' event. + mCurrentReceiver = null; + if (event.getAction() == KeyEvent.ACTION_UP) { + int idx = mCapturedKeys.indexOfKey(event.getKeyCode()); + if (idx >= 0) { + mCurrentReceiver = mCapturedKeys.valueAt(idx); + mCapturedKeys.removeAt(idx); + } + } + } + /** + * Called before the event gets dispatched to the view hierarchy + * @return {@code true} if an unhandled handler has focus and consumed the event + */ + boolean preViewDispatch(KeyEvent event) { + mDispatched = false; + if (mCurrentReceiver == null) { + mCurrentReceiver = mCapturedKeys.get(event.getKeyCode()); + } if (mCurrentReceiver != null) { View target = mCurrentReceiver.get(); - if (mCapturedKeys.size() == 0) { + if (event.getAction() == KeyEvent.ACTION_UP) { mCurrentReceiver = null; } if (target != null && target.isAttachedToWindow()) { @@ -8436,24 +8478,7 @@ public final class ViewRootImpl implements ViewParent, // consume anyways so that we don't feed uncaptured key events to other views return true; } - - View consumer = root.dispatchUnhandledKeyEvent(event); - if (consumer != null) { - mCurrentReceiver = new WeakReference<>(consumer); - } - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - return consumer != null; - } - - boolean hasFocus() { - return mCurrentReceiver != null; - } - - boolean dispatchUnique(View root, KeyEvent event) { - if (mDispatched) { - return false; - } - return dispatch(root, event); + return false; } } } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 9ea17d641359..469726613513 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -436,7 +436,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return true; } - return (getViewRootImpl() != null) && getViewRootImpl().dispatchKeyFallbackEvent(event); + return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event); } public boolean superDispatchKeyShortcutEvent(KeyEvent event) { |