Merge "Fixed a bug where sometimes unhandled handler would consume all keys" into pi-dev

This commit is contained in:
Evan Rosky
2018-05-30 21:47:25 +00:00
committed by Android (Google) Code Review
3 changed files with 70 additions and 45 deletions

View File

@@ -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();

View File

@@ -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;
@@ -5008,10 +5007,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;
}
@@ -5024,7 +5020,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;
}
@@ -7062,6 +7061,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);
@@ -7830,7 +7833,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);
}
@@ -8422,35 +8425,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;
}
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;
}
/**
* 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) {
mCapturedKeys.delete(event.getKeyCode());
int idx = mCapturedKeys.indexOfKey(event.getKeyCode());
if (idx >= 0) {
mCurrentReceiver = mCapturedKeys.valueAt(idx);
mCapturedKeys.removeAt(idx);
}
}
}
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 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()) {
@@ -8459,24 +8501,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;
}
}
}

View File

@@ -429,7 +429,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) {