Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9352e47d authored by Evan Rosky's avatar Evan Rosky Committed by Android (Google) Code Review
Browse files

Merge "Add "KeyFallback" handling ability to Views"

parents 5b968dbe 5e29c076
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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);
@@ -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);
@@ -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();
@@ -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);
  }
+7 −0
Original line number Diff line number Diff line
@@ -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);
@@ -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);
@@ -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();
@@ -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);
  }
+7 −0
Original line number Diff line number Diff line
@@ -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);
@@ -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);
@@ -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();
@@ -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);
  }
+77 −0
Original line number Diff line number Diff line
@@ -4245,6 +4245,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
        OnCapturedPointerListener mOnCapturedPointerListener;
        private ArrayList<OnKeyFallbackListener> mKeyFallbackListeners;
    }
    ListenerInfo mListenerInfo;
@@ -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
@@ -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;
                }
            }
        }
    }
}
+112 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;

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