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

Commit 75f3b1b8 authored by Tiger's avatar Tiger
Browse files

Let view focus move across adjacent task fragments

This CL moves the focus to the adjacent task/activity/window
(if there is any) while the next focused view cannot be found with the
given direction in the local window.

Fix: 283618824
Test: atest TaskFragmentTest#testMoveFocusToAdjacentWindow
Test: Run commands: `adb shell input keyevent KEYCODE_DPAD_XXX` while
      running 2-pane Settings.
Change-Id: I45681150e9edd25d59136c5762586c504cb301f9
parent 3d460a1e
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -370,4 +370,14 @@ interface IWindowSession {
    boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow);

    boolean transferHostTouchGestureToEmbedded(IWindow hostWindow, IBinder transferTouchToken);

    /**
     * Moves the focus to the adjacent window if there is one in the given direction. This can only
     * move the focus to the window in the same leaf task.
     *
     * @param fromWindow The calling window that the focus is moved from.
     * @param direction The {@link android.view.View.FocusDirection} that the new focus should go.
     * @return {@code true} if the focus changes. Otherwise, {@code false}.
     */
    boolean moveFocusToAdjacentWindow(IWindow fromWindow, int direction);
}
+18 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
package android.view;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS;
import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
@@ -7232,7 +7233,7 @@ public final class ViewRootImpl implements ViewParent,
        }
        private boolean performFocusNavigation(KeyEvent event) {
            int direction = 0;
            @FocusDirection int direction = 0;
            switch (event.getKeyCode()) {
                case KeyEvent.KEYCODE_DPAD_LEFT:
                    if (event.hasNoModifiers()) {
@@ -7284,6 +7285,8 @@ public final class ViewRootImpl implements ViewParent,
                                            isFastScrolling));
                            return true;
                        }
                    } else if (moveFocusToAdjacentWindow(direction)) {
                        return true;
                    }
                    // Give the focused view a last chance to handle the dpad key.
@@ -7293,11 +7296,25 @@ public final class ViewRootImpl implements ViewParent,
                } else {
                    if (mView.restoreDefaultFocus()) {
                        return true;
                    } else if (moveFocusToAdjacentWindow(direction)) {
                        return true;
                    }
                }
            }
            return false;
        }
        private boolean moveFocusToAdjacentWindow(@FocusDirection int direction) {
            if (getConfiguration().windowConfiguration.getWindowingMode()
                    != WINDOWING_MODE_MULTI_WINDOW) {
                return false;
            }
            try {
                return mWindowSession.moveFocusToAdjacentWindow(mWindow, direction);
            } catch (RemoteException e) {
                return false;
            }
        }
        private boolean performKeyboardGroupNavigation(int direction) {
            final View focused = mView.findFocus();
+8 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
import android.util.MergedConfiguration;
import android.view.View.FocusDirection;
import android.view.WindowInsets.Type.InsetsType;
import android.window.ClientWindowFrames;
import android.window.OnBackInvokedCallbackInfo;
@@ -665,6 +666,13 @@ public class WindowlessWindowManager implements IWindowSession {
        return false;
    }

    @Override
    public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
        Log.e(TAG, "Received request to moveFocusToAdjacentWindow on"
                + " WindowlessWindowManager. We shouldn't get here!");
        return false;
    }

    void setParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) {
        IBinder oldInterface = mParentInterface == null ? null : mParentInterface.asBinder();
        IBinder newInterface = parentInterface == null ? null : parentInterface.asBinder();
+12 −0
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
import android.view.View.FocusDirection;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager;
@@ -1000,6 +1001,17 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
        }
        return didTransfer;
    }

    @Override
    public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
        final long identity = Binder.clearCallingIdentity();
        try {
            return mService.moveFocusToAdjacentWindow(this, fromWindow, direction);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    @Override
    public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
            RemoteCallback callback) {
+61 −0
Original line number Diff line number Diff line
@@ -287,6 +287,7 @@ import android.view.SurfaceControlViewHost;
import android.view.SurfaceSession;
import android.view.TaskTransitionSpec;
import android.view.View;
import android.view.View.FocusDirection;
import android.view.ViewDebug;
import android.view.WindowContentFrameStats;
import android.view.WindowInsets;
@@ -9104,6 +9105,66 @@ public class WindowManagerService extends IWindowManager.Stub
                win.mClient);
    }

    boolean moveFocusToAdjacentWindow(Session session, IWindow fromWindow,
            @FocusDirection int direction) {
        synchronized (mGlobalLock) {
            final WindowState fromWin = windowForClientLocked(session, fromWindow, false);
            if (fromWin == null || !fromWin.isFocused()) {
                return false;
            }
            final TaskFragment fromFragment = fromWin.getTaskFragment();
            if (fromFragment == null) {
                return false;
            }
            final TaskFragment adjacentFragment = fromFragment.getAdjacentTaskFragment();
            if (adjacentFragment == null || adjacentFragment.asTask() != null) {
                // Don't move the focus to another task.
                return false;
            }
            final Rect fromBounds = fromFragment.getBounds();
            final Rect adjacentBounds = adjacentFragment.getBounds();
            switch (direction) {
                case View.FOCUS_LEFT:
                    if (adjacentBounds.left >= fromBounds.left) {
                        return false;
                    }
                    break;
                case View.FOCUS_UP:
                    if (adjacentBounds.top >= fromBounds.top) {
                        return false;
                    }
                    break;
                case View.FOCUS_RIGHT:
                    if (adjacentBounds.right <= fromBounds.right) {
                        return false;
                    }
                    break;
                case View.FOCUS_DOWN:
                    if (adjacentBounds.bottom <= fromBounds.bottom) {
                        return false;
                    }
                    break;
                case View.FOCUS_BACKWARD:
                case View.FOCUS_FORWARD:
                    // These are not absolute directions. Skip checking the bounds.
                    break;
                default:
                    return false;
            }
            final ActivityRecord topRunningActivity = adjacentFragment.topRunningActivity(
                    true /* focusableOnly */);
            if (topRunningActivity == null) {
                return false;
            }
            moveDisplayToTopInternal(topRunningActivity.getDisplayId());
            handleTaskFocusChange(topRunningActivity.getTask(), topRunningActivity);
            if (fromWin.isFocused()) {
                return false;
            }
        }
        return true;
    }

    /** Return whether layer tracing is enabled */
    public boolean isLayerTracing() {
        if (!checkCallingPermission(
Loading