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

Commit 682eb472 authored by Wei Sheng Shih's avatar Wei Sheng Shih Committed by Android (Google) Code Review
Browse files

Merge "Forward back key event to host app for SCVH." into main

parents 5b26763b 39a2a6d9
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.view;

import android.view.KeyEvent;
import android.view.WindowManager;

/**
@@ -24,4 +25,6 @@ import android.view.WindowManager;
 */
oneway interface ISurfaceControlViewHostParent {
    void updateParams(in WindowManager.LayoutParams[] childAttrs);
    // To forward the back key event from embedded to host app.
    void forwardBackKeyToParent(in KeyEvent keyEvent);
}
+1 −0
Original line number Diff line number Diff line
@@ -447,6 +447,7 @@ public class SurfaceControlViewHost {
        addWindowToken(attrs);
        view.setLayoutParams(attrs);
        mViewRoot.setView(view, attrs, null);
        mViewRoot.setBackKeyCallbackForWindowlessWindow(mWm::forwardBackKeyToParent);
    }

    /**
+38 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.RenderNode;
import android.hardware.input.InputManager;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -159,6 +160,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
    private static final boolean DEBUG = false;
    private static final boolean DEBUG_POSITION = false;

    private static final long FORWARD_BACK_KEY_TOLERANCE_MS = 100;

    @UnsupportedAppUsage(
            maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
            publicAlternatives = "Track {@link SurfaceHolder#addCallback} instead")
@@ -326,6 +329,41 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
                });
            }
        }

        @Override
        public void forwardBackKeyToParent(@NonNull KeyEvent keyEvent) {
                runOnUiThread(() -> {
                    if (!isAttachedToWindow() || keyEvent.getKeyCode() != KeyEvent.KEYCODE_BACK) {
                        return;
                    }
                    final ViewRootImpl vri = getViewRootImpl();
                    if (vri == null) {
                        return;
                    }
                    final InputManager inputManager = mContext.getSystemService(InputManager.class);
                    if (inputManager == null) {
                        return;
                    }
                    // Check that the event was created recently.
                    final long timeDiff = SystemClock.uptimeMillis() - keyEvent.getEventTime();
                    if (timeDiff > FORWARD_BACK_KEY_TOLERANCE_MS) {
                        Log.e(TAG, "Ignore the input event that exceed the tolerance time, "
                                + "exceed " + timeDiff + "ms");
                        return;
                    }
                    if (inputManager.verifyInputEvent(keyEvent) == null) {
                        Log.e(TAG, "Received invalid input event");
                        return;
                    }
                    try {
                        vri.processingBackKey(true);
                        vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
                                true /* processImmediately */);
                    } finally {
                        vri.processingBackKey(false);
                    }
                });
        }
    };

    private final boolean mRtDrivenClipping = Flags.clipSurfaceviews();
+51 −10
Original line number Diff line number Diff line
@@ -250,7 +250,7 @@ import java.util.OptionalInt;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * The top of a view hierarchy, implementing the needed protocol between View
@@ -615,6 +615,13 @@ public final class ViewRootImpl implements ViewParent,
    boolean mUpcomingWindowFocus;
    @GuardedBy("this")
    boolean mUpcomingInTouchMode;
    // While set, allow this VRI to handle back key without drop it.
    private boolean mProcessingBackKey;
    /**
     * Compatibility {@link OnBackInvokedCallback} for windowless window, to forward the back
     * key event host app.
     */
    private Predicate<KeyEvent> mWindowlessBackKeyCallback;

    public boolean mTraversalScheduled;
    int mTraversalBarrier;
@@ -3189,7 +3196,11 @@ public final class ViewRootImpl implements ViewParent,
            host.dispatchAttachedToWindow(mAttachInfo, 0);
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
            dispatchApplyInsets(host);
            if (!mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) {
            if (!mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()
                    // Don't register compat OnBackInvokedCallback for windowless window.
                    // The onBackInvoked event by default should forward to host app, so the
                    // host app can decide the behavior.
                    && mWindowlessBackKeyCallback == null) {
                // For apps requesting legacy back behavior, we add a compat callback that
                // dispatches {@link KeyEvent#KEYCODE_BACK} to their root views.
                // This way from system point of view, these apps are providing custom
@@ -6655,7 +6666,8 @@ public final class ViewRootImpl implements ViewParent,

            // Find a reason for dropping or canceling the event.
            final String reason;
            if (!mAttachInfo.mHasWindowFocus
            // The embedded window is focused, allow this VRI to handle back key.
            if (!mAttachInfo.mHasWindowFocus && !(mProcessingBackKey && isBack(q.mEvent))
                    && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
                    && !isAutofillUiShowing()) {
                // This is a non-pointer event and the window doesn't currently have input focus
@@ -6878,11 +6890,21 @@ public final class ViewRootImpl implements ViewParent,

                // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
                // view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}.
                if (isBack(keyEvent)
                        && mContext != null
                if (isBack(keyEvent)) {
                    if (mWindowlessBackKeyCallback != null) {
                        if (mWindowlessBackKeyCallback.test(keyEvent)) {
                            return keyEvent.getAction() == KeyEvent.ACTION_UP
                                    && !keyEvent.isCanceled()
                                    ? FINISH_HANDLED : FINISH_NOT_HANDLED;
                        } else {
                            // Unable to forward the back key to host, forward to next stage.
                            return FORWARD;
                        }
                    } else if (mContext != null
                            && mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) {
                        return doOnBackKeyEvent(keyEvent);
                    }
                }

                if (mInputQueue != null) {
                    mInputQueue.sendInputEvent(q.mEvent, q, true, this);
@@ -10524,6 +10546,11 @@ public final class ViewRootImpl implements ViewParent,
        mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget();
    }

    // Make this VRI able to process back key without drop it.
    void processingBackKey(boolean processing) {
        mProcessingBackKey = processing;
    }

    /**
     * Collect and include any ScrollCaptureCallback instances registered with the window.
     *
@@ -11748,13 +11775,18 @@ public final class ViewRootImpl implements ViewParent,
                KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                InputDevice.SOURCE_KEYBOARD);
        enqueueInputEvent(ev);
        enqueueInputEvent(ev, null /* receiver */, 0 /* flags */, true /* processImmediately */);
    }

    private void registerCompatOnBackInvokedCallback() {
        mCompatOnBackInvokedCallback = () -> {
            try {
                processingBackKey(true);
                sendBackKeyEvent(KeyEvent.ACTION_DOWN);
                sendBackKeyEvent(KeyEvent.ACTION_UP);
            } finally {
                processingBackKey(false);
            }
        };
        if (mOnBackInvokedDispatcher.hasImeOnBackInvokedDispatcher()) {
            Log.d(TAG, "Skip registering CompatOnBackInvokedCallback on IME dispatcher");
@@ -12189,4 +12221,13 @@ public final class ViewRootImpl implements ViewParent,
        }
        return false;
    }

    /**
     * Set the default back key callback for windowless window, to forward the back key event
     * to host app.
     * MUST NOT call this method for normal window.
     */
    void setBackKeyCallbackForWindowlessWindow(@NonNull Predicate<KeyEvent> callback) {
        mWindowlessBackKeyCallback = callback;
    }
}
+14 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.view;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
@@ -703,4 +704,17 @@ public class WindowlessWindowManager implements IWindowSession {
            }
        }
    }

    boolean forwardBackKeyToParent(@NonNull KeyEvent keyEvent) {
        if (mParentInterface == null) {
            return false;
        }
        try {
            mParentInterface.forwardBackKeyToParent(keyEvent);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to forward back key To Parent: ", e);
            return false;
        }
        return true;
    }
}