Loading api/current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -46087,6 +46087,7 @@ package android.view { method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); Loading Loading @@ -46421,6 +46422,7 @@ package android.view { method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo); method public boolean onKeyDown(int, android.view.KeyEvent); method public boolean onKeyFallback(android.view.KeyEvent); method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); method public boolean onKeyPreIme(int, android.view.KeyEvent); Loading Loading @@ -46474,6 +46476,7 @@ package android.view { method public void refreshDrawableState(); method public void releasePointerCapture(); method public boolean removeCallbacks(java.lang.Runnable); method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void requestApplyInsets(); Loading Loading @@ -46892,6 +46895,10 @@ package android.view { method public abstract boolean onHover(android.view.View, android.view.MotionEvent); } public static abstract interface View.OnKeyFallbackListener { method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent); } public static abstract interface View.OnKeyListener { method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent); } api/system-current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -49832,6 +49832,7 @@ package android.view { method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); Loading Loading @@ -50166,6 +50167,7 @@ package android.view { method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo); method public boolean onKeyDown(int, android.view.KeyEvent); method public boolean onKeyFallback(android.view.KeyEvent); method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); method public boolean onKeyPreIme(int, android.view.KeyEvent); Loading Loading @@ -50219,6 +50221,7 @@ package android.view { method public void refreshDrawableState(); method public void releasePointerCapture(); method public boolean removeCallbacks(java.lang.Runnable); method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void requestApplyInsets(); Loading Loading @@ -50637,6 +50640,10 @@ package android.view { method public abstract boolean onHover(android.view.View, android.view.MotionEvent); } public static abstract interface View.OnKeyFallbackListener { method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent); } public static abstract interface View.OnKeyListener { method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent); } api/test-current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -46744,6 +46744,7 @@ package android.view { method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); Loading Loading @@ -47080,6 +47081,7 @@ package android.view { method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo); method public boolean onKeyDown(int, android.view.KeyEvent); method public boolean onKeyFallback(android.view.KeyEvent); method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); method public boolean onKeyPreIme(int, android.view.KeyEvent); Loading Loading @@ -47133,6 +47135,7 @@ package android.view { method public void refreshDrawableState(); method public void releasePointerCapture(); method public boolean removeCallbacks(java.lang.Runnable); method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void requestApplyInsets(); Loading Loading @@ -47555,6 +47558,10 @@ package android.view { method public abstract boolean onHover(android.view.View, android.view.MotionEvent); } public static abstract interface View.OnKeyFallbackListener { method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent); } public static abstract interface View.OnKeyListener { method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent); } core/java/android/view/View.java +77 −0 Original line number Diff line number Diff line Loading @@ -4245,6 +4245,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, OnApplyWindowInsetsListener mOnApplyWindowInsetsListener; OnCapturedPointerListener mOnCapturedPointerListener; private ArrayList<OnKeyFallbackListener> mKeyFallbackListeners; } ListenerInfo mListenerInfo; Loading Loading @@ -25193,6 +25195,29 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean onKey(View v, int keyCode, KeyEvent event); } /** * Interface definition for a callback to be invoked when a hardware key event is * dispatched to this view during the fallback phase. This means no view in the hierarchy * has handled this event. */ public interface OnKeyFallbackListener { /** * Called when a hardware key is dispatched to a view in the fallback phase. This allows * listeners to respond to events after the view hierarchy has had a chance to respond. * <p>Key presses in software keyboards will generally NOT trigger this method, * although some may elect to do so in some situations. Do not assume a * software input method has to be key-based; even if it is, it may use key presses * in a different way than you expect, so there is no way to reliably catch soft * input key presses. * * @param v The view the key has been dispatched to. * @param event The KeyEvent object containing full information about * the event. * @return True if the listener has consumed the event, false otherwise. */ boolean onKeyFallback(View v, KeyEvent event); } /** * Interface definition for a callback to be invoked when a touch event is * dispatched to this view. The callback will be invoked before the touch Loading Loading @@ -26866,4 +26891,56 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } return mTooltipInfo.mTooltipPopup.getContentView(); } /** * Allows this view to handle {@link KeyEvent}s which weren't handled by normal dispatch. This * occurs after the normal view hierarchy dispatch, but before the window callback. By default, * this will dispatch into all the listeners registered via * {@link #addKeyFallbackListener(OnKeyFallbackListener)} in last-in-first-out order (most * recently added will receive events first). * * @param event A not-previously-handled event. * @return {@code true} if the event was handled, {@code false} otherwise. * @see #addKeyFallbackListener */ public boolean onKeyFallback(@NonNull KeyEvent event) { if (mListenerInfo != null && mListenerInfo.mKeyFallbackListeners != null) { for (int i = mListenerInfo.mKeyFallbackListeners.size() - 1; i >= 0; --i) { if (mListenerInfo.mKeyFallbackListeners.get(i).onKeyFallback(this, event)) { return true; } } } return false; } /** * Adds a listener which will receive unhandled {@link KeyEvent}s. * @param listener the receiver of fallback {@link KeyEvent}s. * @see #onKeyFallback(KeyEvent) */ public void addKeyFallbackListener(OnKeyFallbackListener listener) { ArrayList<OnKeyFallbackListener> fallbacks = getListenerInfo().mKeyFallbackListeners; if (fallbacks == null) { fallbacks = new ArrayList<>(); getListenerInfo().mKeyFallbackListeners = fallbacks; } fallbacks.add(listener); } /** * Removes a listener which will receive unhandled {@link KeyEvent}s. * @param listener the receiver of fallback {@link KeyEvent}s. * @see #onKeyFallback(KeyEvent) */ public void removeKeyFallbackListener(OnKeyFallbackListener listener) { if (mListenerInfo != null) { if (mListenerInfo.mKeyFallbackListeners != null) { mListenerInfo.mKeyFallbackListeners.remove(listener); if (mListenerInfo.mKeyFallbackListeners.isEmpty()) { mListenerInfo.mKeyFallbackListeners = null; } } } } } core/java/android/view/ViewRootImpl.java +112 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ import android.util.Log; 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; Loading Loading @@ -362,6 +363,8 @@ public final class ViewRootImpl implements ViewParent, InputStage mFirstPostImeInputStage; InputStage mSyntheticInputStage; private final KeyFallbackManager mKeyFallbackManager = new KeyFallbackManager(); boolean mWindowAttributesChanged = false; int mWindowAttributesChangesFlag = 0; Loading Loading @@ -4764,6 +4767,13 @@ public final class ViewRootImpl implements ViewParent, private int processKeyEvent(QueuedInputEvent q) { final KeyEvent event = (KeyEvent)q.mEvent; mKeyFallbackManager.mDispatched = false; if (mKeyFallbackManager.hasFocus() && mKeyFallbackManager.dispatchUnique(mView, event)) { return FINISH_HANDLED; } // Deliver the key to the view hierarchy. if (mView.dispatchKeyEvent(event)) { return FINISH_HANDLED; Loading @@ -4773,6 +4783,10 @@ public final class ViewRootImpl implements ViewParent, return FINISH_NOT_HANDLED; } if (mKeyFallbackManager.dispatchUnique(mView, event)) { return FINISH_HANDLED; } int groupNavigationDirection = 0; if (event.getAction() == KeyEvent.ACTION_DOWN Loading Loading @@ -7529,6 +7543,16 @@ public final class ViewRootImpl implements ViewParent, } } /** * Dispatches a KeyEvent to all registered key fallback handlers. * * @param event * @return {@code true} if the event was handled, {@code false} otherwise. */ public boolean dispatchKeyFallbackEvent(KeyEvent event) { return mKeyFallbackManager.dispatch(mView, event); } class TakenSurfaceHolder extends BaseSurfaceHolder { @Override public boolean onAllowLockCanvas() { Loading Loading @@ -8093,4 +8117,92 @@ public final class ViewRootImpl implements ViewParent, run(); } } private static class KeyFallbackManager { // This is used to ensure that key-fallback 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 keyfallback events should // be dispatched after the view hierarchy, but before the Activity. However, if we aren't // in an activity, we still want key fallbacks to be dispatched. boolean mDispatched = false; SparseBooleanArray mCapturedKeys = new SparseBooleanArray(); WeakReference<View> mFallbackReceiver = null; int mVisitCount = 0; private void updateCaptureState(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { mCapturedKeys.append(event.getKeyCode(), true); } if (event.getAction() == KeyEvent.ACTION_UP) { mCapturedKeys.delete(event.getKeyCode()); } } boolean dispatch(View root, KeyEvent event) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "KeyFallback dispatch"); mDispatched = true; updateCaptureState(event); if (mFallbackReceiver != null) { View target = mFallbackReceiver.get(); if (mCapturedKeys.size() == 0) { mFallbackReceiver = null; } if (target != null && target.isAttachedToWindow()) { return target.onKeyFallback(event); } // consume anyways so that we don't feed uncaptured key events to other views return true; } boolean result = dispatchInZOrder(root, event); Trace.traceEnd(Trace.TRACE_TAG_VIEW); return result; } private boolean dispatchInZOrder(View view, KeyEvent evt) { if (view instanceof ViewGroup) { ViewGroup vg = (ViewGroup) view; ArrayList<View> orderedViews = vg.buildOrderedChildList(); if (orderedViews != null) { try { for (int i = orderedViews.size() - 1; i >= 0; --i) { View v = orderedViews.get(i); if (dispatchInZOrder(v, evt)) { return true; } } } finally { orderedViews.clear(); } } else { for (int i = vg.getChildCount() - 1; i >= 0; --i) { View v = vg.getChildAt(i); if (dispatchInZOrder(v, evt)) { return true; } } } } if (view.onKeyFallback(evt)) { mFallbackReceiver = new WeakReference<>(view); return true; } return false; } boolean hasFocus() { return mFallbackReceiver != null; } boolean dispatchUnique(View root, KeyEvent event) { if (mDispatched) { return false; } return dispatch(root, event); } } } Loading
api/current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -46087,6 +46087,7 @@ package android.view { method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); Loading Loading @@ -46421,6 +46422,7 @@ package android.view { method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo); method public boolean onKeyDown(int, android.view.KeyEvent); method public boolean onKeyFallback(android.view.KeyEvent); method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); method public boolean onKeyPreIme(int, android.view.KeyEvent); Loading Loading @@ -46474,6 +46476,7 @@ package android.view { method public void refreshDrawableState(); method public void releasePointerCapture(); method public boolean removeCallbacks(java.lang.Runnable); method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void requestApplyInsets(); Loading Loading @@ -46892,6 +46895,10 @@ package android.view { method public abstract boolean onHover(android.view.View, android.view.MotionEvent); } public static abstract interface View.OnKeyFallbackListener { method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent); } public static abstract interface View.OnKeyListener { method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent); }
api/system-current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -49832,6 +49832,7 @@ package android.view { method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); Loading Loading @@ -50166,6 +50167,7 @@ package android.view { method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo); method public boolean onKeyDown(int, android.view.KeyEvent); method public boolean onKeyFallback(android.view.KeyEvent); method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); method public boolean onKeyPreIme(int, android.view.KeyEvent); Loading Loading @@ -50219,6 +50221,7 @@ package android.view { method public void refreshDrawableState(); method public void releasePointerCapture(); method public boolean removeCallbacks(java.lang.Runnable); method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void requestApplyInsets(); Loading Loading @@ -50637,6 +50640,10 @@ package android.view { method public abstract boolean onHover(android.view.View, android.view.MotionEvent); } public static abstract interface View.OnKeyFallbackListener { method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent); } public static abstract interface View.OnKeyListener { method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent); }
api/test-current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -46744,6 +46744,7 @@ package android.view { method public void addExtraDataToAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo, java.lang.String, android.os.Bundle); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); method public void addKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); Loading Loading @@ -47080,6 +47081,7 @@ package android.view { method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo); method public boolean onKeyDown(int, android.view.KeyEvent); method public boolean onKeyFallback(android.view.KeyEvent); method public boolean onKeyLongPress(int, android.view.KeyEvent); method public boolean onKeyMultiple(int, int, android.view.KeyEvent); method public boolean onKeyPreIme(int, android.view.KeyEvent); Loading Loading @@ -47133,6 +47135,7 @@ package android.view { method public void refreshDrawableState(); method public void releasePointerCapture(); method public boolean removeCallbacks(java.lang.Runnable); method public void removeKeyFallbackListener(android.view.View.OnKeyFallbackListener); method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void requestApplyInsets(); Loading Loading @@ -47555,6 +47558,10 @@ package android.view { method public abstract boolean onHover(android.view.View, android.view.MotionEvent); } public static abstract interface View.OnKeyFallbackListener { method public abstract boolean onKeyFallback(android.view.View, android.view.KeyEvent); } public static abstract interface View.OnKeyListener { method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent); }
core/java/android/view/View.java +77 −0 Original line number Diff line number Diff line Loading @@ -4245,6 +4245,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, OnApplyWindowInsetsListener mOnApplyWindowInsetsListener; OnCapturedPointerListener mOnCapturedPointerListener; private ArrayList<OnKeyFallbackListener> mKeyFallbackListeners; } ListenerInfo mListenerInfo; Loading Loading @@ -25193,6 +25195,29 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean onKey(View v, int keyCode, KeyEvent event); } /** * Interface definition for a callback to be invoked when a hardware key event is * dispatched to this view during the fallback phase. This means no view in the hierarchy * has handled this event. */ public interface OnKeyFallbackListener { /** * Called when a hardware key is dispatched to a view in the fallback phase. This allows * listeners to respond to events after the view hierarchy has had a chance to respond. * <p>Key presses in software keyboards will generally NOT trigger this method, * although some may elect to do so in some situations. Do not assume a * software input method has to be key-based; even if it is, it may use key presses * in a different way than you expect, so there is no way to reliably catch soft * input key presses. * * @param v The view the key has been dispatched to. * @param event The KeyEvent object containing full information about * the event. * @return True if the listener has consumed the event, false otherwise. */ boolean onKeyFallback(View v, KeyEvent event); } /** * Interface definition for a callback to be invoked when a touch event is * dispatched to this view. The callback will be invoked before the touch Loading Loading @@ -26866,4 +26891,56 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } return mTooltipInfo.mTooltipPopup.getContentView(); } /** * Allows this view to handle {@link KeyEvent}s which weren't handled by normal dispatch. This * occurs after the normal view hierarchy dispatch, but before the window callback. By default, * this will dispatch into all the listeners registered via * {@link #addKeyFallbackListener(OnKeyFallbackListener)} in last-in-first-out order (most * recently added will receive events first). * * @param event A not-previously-handled event. * @return {@code true} if the event was handled, {@code false} otherwise. * @see #addKeyFallbackListener */ public boolean onKeyFallback(@NonNull KeyEvent event) { if (mListenerInfo != null && mListenerInfo.mKeyFallbackListeners != null) { for (int i = mListenerInfo.mKeyFallbackListeners.size() - 1; i >= 0; --i) { if (mListenerInfo.mKeyFallbackListeners.get(i).onKeyFallback(this, event)) { return true; } } } return false; } /** * Adds a listener which will receive unhandled {@link KeyEvent}s. * @param listener the receiver of fallback {@link KeyEvent}s. * @see #onKeyFallback(KeyEvent) */ public void addKeyFallbackListener(OnKeyFallbackListener listener) { ArrayList<OnKeyFallbackListener> fallbacks = getListenerInfo().mKeyFallbackListeners; if (fallbacks == null) { fallbacks = new ArrayList<>(); getListenerInfo().mKeyFallbackListeners = fallbacks; } fallbacks.add(listener); } /** * Removes a listener which will receive unhandled {@link KeyEvent}s. * @param listener the receiver of fallback {@link KeyEvent}s. * @see #onKeyFallback(KeyEvent) */ public void removeKeyFallbackListener(OnKeyFallbackListener listener) { if (mListenerInfo != null) { if (mListenerInfo.mKeyFallbackListeners != null) { mListenerInfo.mKeyFallbackListeners.remove(listener); if (mListenerInfo.mKeyFallbackListeners.isEmpty()) { mListenerInfo.mKeyFallbackListeners = null; } } } } }
core/java/android/view/ViewRootImpl.java +112 −0 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ import android.util.Log; 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; Loading Loading @@ -362,6 +363,8 @@ public final class ViewRootImpl implements ViewParent, InputStage mFirstPostImeInputStage; InputStage mSyntheticInputStage; private final KeyFallbackManager mKeyFallbackManager = new KeyFallbackManager(); boolean mWindowAttributesChanged = false; int mWindowAttributesChangesFlag = 0; Loading Loading @@ -4764,6 +4767,13 @@ public final class ViewRootImpl implements ViewParent, private int processKeyEvent(QueuedInputEvent q) { final KeyEvent event = (KeyEvent)q.mEvent; mKeyFallbackManager.mDispatched = false; if (mKeyFallbackManager.hasFocus() && mKeyFallbackManager.dispatchUnique(mView, event)) { return FINISH_HANDLED; } // Deliver the key to the view hierarchy. if (mView.dispatchKeyEvent(event)) { return FINISH_HANDLED; Loading @@ -4773,6 +4783,10 @@ public final class ViewRootImpl implements ViewParent, return FINISH_NOT_HANDLED; } if (mKeyFallbackManager.dispatchUnique(mView, event)) { return FINISH_HANDLED; } int groupNavigationDirection = 0; if (event.getAction() == KeyEvent.ACTION_DOWN Loading Loading @@ -7529,6 +7543,16 @@ public final class ViewRootImpl implements ViewParent, } } /** * Dispatches a KeyEvent to all registered key fallback handlers. * * @param event * @return {@code true} if the event was handled, {@code false} otherwise. */ public boolean dispatchKeyFallbackEvent(KeyEvent event) { return mKeyFallbackManager.dispatch(mView, event); } class TakenSurfaceHolder extends BaseSurfaceHolder { @Override public boolean onAllowLockCanvas() { Loading Loading @@ -8093,4 +8117,92 @@ public final class ViewRootImpl implements ViewParent, run(); } } private static class KeyFallbackManager { // This is used to ensure that key-fallback 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 keyfallback events should // be dispatched after the view hierarchy, but before the Activity. However, if we aren't // in an activity, we still want key fallbacks to be dispatched. boolean mDispatched = false; SparseBooleanArray mCapturedKeys = new SparseBooleanArray(); WeakReference<View> mFallbackReceiver = null; int mVisitCount = 0; private void updateCaptureState(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { mCapturedKeys.append(event.getKeyCode(), true); } if (event.getAction() == KeyEvent.ACTION_UP) { mCapturedKeys.delete(event.getKeyCode()); } } boolean dispatch(View root, KeyEvent event) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "KeyFallback dispatch"); mDispatched = true; updateCaptureState(event); if (mFallbackReceiver != null) { View target = mFallbackReceiver.get(); if (mCapturedKeys.size() == 0) { mFallbackReceiver = null; } if (target != null && target.isAttachedToWindow()) { return target.onKeyFallback(event); } // consume anyways so that we don't feed uncaptured key events to other views return true; } boolean result = dispatchInZOrder(root, event); Trace.traceEnd(Trace.TRACE_TAG_VIEW); return result; } private boolean dispatchInZOrder(View view, KeyEvent evt) { if (view instanceof ViewGroup) { ViewGroup vg = (ViewGroup) view; ArrayList<View> orderedViews = vg.buildOrderedChildList(); if (orderedViews != null) { try { for (int i = orderedViews.size() - 1; i >= 0; --i) { View v = orderedViews.get(i); if (dispatchInZOrder(v, evt)) { return true; } } } finally { orderedViews.clear(); } } else { for (int i = vg.getChildCount() - 1; i >= 0; --i) { View v = vg.getChildAt(i); if (dispatchInZOrder(v, evt)) { return true; } } } } if (view.onKeyFallback(evt)) { mFallbackReceiver = new WeakReference<>(view); return true; } return false; } boolean hasFocus() { return mFallbackReceiver != null; } boolean dispatchUnique(View root, KeyEvent event) { if (mDispatched) { return false; } return dispatch(root, event); } } }