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

Commit 63a3d4d7 authored by omarmt's avatar omarmt
Browse files

Support for OnBackAnimationCallback: added for 3-button navigation / hardware keys

The callback can be called as follows if it supports the `onBackStarted()` and `onBackCancelled()` methods:
- `onBackStarted()` is called when the back button is pressed (`ACTION_DOWN`) and `keyEvent.getRepeatCount()` is `0`.
- `onBackCancelled()` is called when the back button is released (`ACTION_UP`) and the `keyEvent.isCanceled()` is `true`.
- `onBackInvoked()` is called when the back button is released (`ACTION_UP`) and the `keyEvent.isCanceled()` is `false`.

Test: atest OnBackInvokedCallbackGestureTest
Bug: 278634019
Change-Id: Iba5a27e68607ce33c38dbbc818d66e8753688761
parent 3e1928ca
Loading
Loading
Loading
Loading
+46 −13
Original line number Original line Diff line number Diff line
@@ -200,8 +200,10 @@ import android.view.contentcapture.MainContentCaptureSession;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
import android.widget.Scroller;
import android.window.BackEvent;
import android.window.ClientWindowFrames;
import android.window.ClientWindowFrames;
import android.window.CompatOnBackInvokedCallback;
import android.window.CompatOnBackInvokedCallback;
import android.window.OnBackAnimationCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.OnBackInvokedDispatcher;
import android.window.ScreenCapture;
import android.window.ScreenCapture;
@@ -6508,6 +6510,7 @@ public final class ViewRootImpl implements ViewParent,
     */
     */
    final class NativePreImeInputStage extends AsyncInputStage
    final class NativePreImeInputStage extends AsyncInputStage
            implements InputQueue.FinishedInputEventCallback {
            implements InputQueue.FinishedInputEventCallback {

        public NativePreImeInputStage(InputStage next, String traceCounter) {
        public NativePreImeInputStage(InputStage next, String traceCounter) {
            super(next, traceCounter);
            super(next, traceCounter);
        }
        }
@@ -6515,32 +6518,62 @@ public final class ViewRootImpl implements ViewParent,
        @Override
        @Override
        protected int onProcess(QueuedInputEvent q) {
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
            if (q.mEvent instanceof KeyEvent) {
                final KeyEvent event = (KeyEvent) q.mEvent;
                final KeyEvent keyEvent = (KeyEvent) q.mEvent;


                // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
                // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
                // view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}.
                // view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}.
                if (isBack(event)
                if (isBack(keyEvent)
                        && mContext != null
                        && mContext != null
                        && mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) {
                        && mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) {
                    OnBackInvokedCallback topCallback =
                    return doOnBackKeyEvent(keyEvent);
                            getOnBackInvokedDispatcher().getTopCallback();
                }
                    if (event.getAction() == KeyEvent.ACTION_UP) {

                        if (topCallback != null) {
                if (mInputQueue != null) {
                    mInputQueue.sendInputEvent(q.mEvent, q, true, this);
                    return DEFER;
                }
            }
            return FORWARD;
        }

        private int doOnBackKeyEvent(KeyEvent keyEvent) {
            OnBackInvokedCallback topCallback = getOnBackInvokedDispatcher().getTopCallback();
            if (topCallback instanceof OnBackAnimationCallback) {
                final OnBackAnimationCallback animationCallback =
                        (OnBackAnimationCallback) topCallback;
                switch (keyEvent.getAction()) {
                    case KeyEvent.ACTION_DOWN:
                        // ACTION_DOWN is emitted twice: once when the user presses the button,
                        // and again a few milliseconds later.
                        // Based on the result of `keyEvent.getRepeatCount()` we have:
                        // - 0 means the button was pressed.
                        // - 1 means the button continues to be pressed (long press).
                        if (keyEvent.getRepeatCount() == 0) {
                            animationCallback.onBackStarted(
                                    new BackEvent(0, 0, 0f, BackEvent.EDGE_LEFT));
                        }
                        break;
                    case KeyEvent.ACTION_UP:
                        if (keyEvent.isCanceled()) {
                            animationCallback.onBackCancelled();
                        } else {
                            topCallback.onBackInvoked();
                            topCallback.onBackInvoked();
                            return FINISH_HANDLED;
                            return FINISH_HANDLED;
                        }
                        }
                        break;
                }
            } else if (topCallback != null) {
                if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
                    if (!keyEvent.isCanceled()) {
                        topCallback.onBackInvoked();
                        return FINISH_HANDLED;
                    } else {
                    } else {
                        // Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
                        Log.d(mTag, "Skip onBackInvoked(), reason: keyEvent.isCanceled=true");
                        return FINISH_NOT_HANDLED;
                    }
                    }
                }
                }
            }
            }


            if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
            return FINISH_NOT_HANDLED;
                mInputQueue.sendInputEvent(q.mEvent, q, true, this);
                return DEFER;
            }
            return FORWARD;
        }
        }


        @Override
        @Override