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

Commit 717ec7c7 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update focus on focused task move to another display" into main

parents fbc55579 2a9b7695
Loading
Loading
Loading
Loading
+56 −20
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.wm.shell.transition;

import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
@@ -66,30 +68,69 @@ public class FocusTransitionObserver {
        if (!enableDisplayFocusInShellTransitions()) {
            return;
        }
        final SparseArray<RunningTaskInfo> lastTransitionFocusedTasks =
                mFocusedTaskOnDisplay.clone();

        final List<TransitionInfo.Change> changes = info.getChanges();
        for (int i = changes.size() - 1; i >= 0; i--) {
            final TransitionInfo.Change change = changes.get(i);

            final RunningTaskInfo task = change.getTaskInfo();
            if (task != null
                    && (change.hasFlags(FLAG_MOVED_TO_TOP) || change.getMode() == TRANSIT_OPEN)) {
            if (task != null) {
                if (change.hasFlags(FLAG_MOVED_TO_TOP) || change.getMode() == TRANSIT_OPEN) {
                    updateFocusedTaskPerDisplay(task, task.displayId);
                } else {
                    // Update focus assuming that any task moved to another display is focused in
                    // the new display.
                    // TODO(sahok): remove this logic when b/388665104 is fixed
                    final boolean isBeyondDisplay = change.getStartDisplayId() != INVALID_DISPLAY
                            && change.getEndDisplayId() != INVALID_DISPLAY
                            && change.getStartDisplayId() != change.getEndDisplayId();

                    RunningTaskInfo lastTransitionFocusedTaskOnStartDisplay =
                            lastTransitionFocusedTasks.get(change.getStartDisplayId());
                    final boolean isLastTransitionFocused =
                            lastTransitionFocusedTaskOnStartDisplay != null
                                    && task.taskId
                                            == lastTransitionFocusedTaskOnStartDisplay.taskId;
                    if (change.getMode() == TRANSIT_CHANGE && isBeyondDisplay
                            && isLastTransitionFocused) {
                        // The task have moved to another display and keeps its focus.
                        // MOVE_TO_TOP is not reported but we need to update the focused task in
                        // the end display.
                        updateFocusedTaskPerDisplay(task, change.getEndDisplayId());
                    }
                }
            }


            if (change.hasFlags(FLAG_IS_DISPLAY) && change.hasFlags(FLAG_MOVED_TO_TOP)) {
                if (mFocusedDisplayId != change.getEndDisplayId()) {
                    updateFocusedDisplay(change.getEndDisplayId());
                }
            }
        }
        mTmpTasksToBeNotified.forEach(this::notifyTaskFocusChanged);
        mTmpTasksToBeNotified.clear();
    }

    private void updateFocusedTaskPerDisplay(RunningTaskInfo task, int displayId) {
        final RunningTaskInfo lastFocusedTaskOnDisplay =
                        mFocusedTaskOnDisplay.get(task.displayId);
                mFocusedTaskOnDisplay.get(displayId);
        if (lastFocusedTaskOnDisplay != null) {
            mTmpTasksToBeNotified.add(lastFocusedTaskOnDisplay);
        }
        mTmpTasksToBeNotified.add(task);
                mFocusedTaskOnDisplay.put(task.displayId, task);
        mFocusedTaskOnDisplay.put(displayId, task);
    }

            if (change.hasFlags(FLAG_IS_DISPLAY) && change.hasFlags(FLAG_MOVED_TO_TOP)) {
                if (mFocusedDisplayId != change.getEndDisplayId()) {
    private void updateFocusedDisplay(int endDisplayId) {
        final RunningTaskInfo lastGloballyFocusedTask =
                mFocusedTaskOnDisplay.get(mFocusedDisplayId);
        if (lastGloballyFocusedTask != null) {
            mTmpTasksToBeNotified.add(lastGloballyFocusedTask);
        }
                    mFocusedDisplayId = change.getEndDisplayId();
        mFocusedDisplayId = endDisplayId;
        notifyFocusedDisplayChanged();
        final RunningTaskInfo currentGloballyFocusedTask =
                mFocusedTaskOnDisplay.get(mFocusedDisplayId);
@@ -97,11 +138,6 @@ public class FocusTransitionObserver {
            mTmpTasksToBeNotified.add(currentGloballyFocusedTask);
        }
    }
            }
        }
        mTmpTasksToBeNotified.forEach(this::notifyTaskFocusChanged);
        mTmpTasksToBeNotified.clear();
    }

    /**
     * Sets the focus transition listener that receives any transitions resulting in focus switch.
+120 −1
Original line number Diff line number Diff line
@@ -17,7 +17,9 @@
package com.android.wm.shell.transition;

import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;

@@ -127,19 +129,136 @@ public class FocusTransitionObserverTest extends ShellTestCase {
                true /* isFocusedOnDisplay */, false /* isFocusedGlobally */);
    }

    @Test
    public void testTaskFocusSwitch() throws RemoteException {
        final SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);

        // Open 2 tasks on the default display.
        TransitionInfo info = mock(TransitionInfo.class);
        final List<TransitionInfo.Change> changes = new ArrayList<>();
        setupTaskChange(changes, 1 /* taskId */, TRANSIT_OPEN,
                DEFAULT_DISPLAY, true /* focused */);
        when(info.getChanges()).thenReturn(changes);
        mFocusTransitionObserver.updateFocusState(info);
        mShellExecutor.flushAll();
        verify(mListener, never()).onFocusedDisplayChanged(anyInt());
        verify(mListener, times(1)).onFocusedTaskChanged(1 /* taskId */,
                true /* isFocusedOnDisplay */, true /* isFocusedGlobally */);
        clearInvocations(mListener);
        changes.clear();

        setupTaskChange(changes, 2 /* taskId */, TRANSIT_OPEN,
                DEFAULT_DISPLAY, true /* focused */);
        when(info.getChanges()).thenReturn(changes);
        mFocusTransitionObserver.updateFocusState(info);
        mShellExecutor.flushAll();
        verify(mListener, times(1)).onFocusedTaskChanged(1 /* taskId */,
                false /* isFocusedOnDisplay */, false /* isFocusedGlobally */);
        verify(mListener, times(1)).onFocusedTaskChanged(2 /* taskId */,
                true /* isFocusedOnDisplay */, true /* isFocusedGlobally */);
        clearInvocations(mListener);
        changes.clear();

        // Moving a task to front.
        changes.clear();
        setupTaskChange(changes, 1 /* taskId */, TRANSIT_TO_FRONT,
                DEFAULT_DISPLAY, true /* focused */);
        when(info.getChanges()).thenReturn(changes);
        mFocusTransitionObserver.updateFocusState(info);
        mShellExecutor.flushAll();
        verify(mListener, times(1)).onFocusedTaskChanged(1 /* taskId */,
                true /* isFocusedOnDisplay */, true /* isFocusedGlobally */);
        verify(mListener, times(1)).onFocusedTaskChanged(2 /* taskId */,
                false /* isFocusedOnDisplay */, false /* isFocusedGlobally */);
    }


    @Test
    public void testTaskMoveToAnotherDisplay() throws RemoteException {
        final SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);

        // First, open a task on the default display.
        TransitionInfo info = mock(TransitionInfo.class);
        final List<TransitionInfo.Change> changes = new ArrayList<>();
        setupTaskChange(changes, 1 /* taskId */, TRANSIT_OPEN,
                DEFAULT_DISPLAY, true /* focused */);
        when(info.getChanges()).thenReturn(changes);
        mFocusTransitionObserver.updateFocusState(info);
        mShellExecutor.flushAll();
        verify(mListener, never()).onFocusedDisplayChanged(anyInt());
        verify(mListener, times(1)).onFocusedTaskChanged(1 /* taskId */,
                true /* isFocusedOnDisplay */, true /* isFocusedGlobally */);
        clearInvocations(mListener);
        changes.clear();

        // Open 2 tasks on the secondary display.
        setupTaskChange(changes, 2 /* taskId */, TRANSIT_OPEN,
                SECONDARY_DISPLAY_ID, true /* focused */);
        setupDisplayToTopChange(changes, SECONDARY_DISPLAY_ID);
        when(info.getChanges()).thenReturn(changes);
        mFocusTransitionObserver.updateFocusState(info);
        mShellExecutor.flushAll();
        verify(mListener, times(1))
                .onFocusedDisplayChanged(SECONDARY_DISPLAY_ID);
        verify(mListener, times(1)).onFocusedTaskChanged(1 /* taskId */,
                true /* isFocusedOnDisplay */, false /* isFocusedGlobally */);
        verify(mListener, times(1)).onFocusedTaskChanged(2 /* taskId */,
                true /* isFocusedOnDisplay */, true /* isFocusedGlobally */);
        clearInvocations(mListener);
        changes.clear();

        setupTaskChange(changes, 3 /* taskId */, TRANSIT_OPEN,
                SECONDARY_DISPLAY_ID, true /* focused */);
        setupDisplayToTopChange(changes, SECONDARY_DISPLAY_ID);
        when(info.getChanges()).thenReturn(changes);
        mFocusTransitionObserver.updateFocusState(info);
        mShellExecutor.flushAll();
        verify(mListener, times(1)).onFocusedTaskChanged(2 /* taskId */,
                false /* isFocusedOnDisplay */, false /* isFocusedGlobally */);
        verify(mListener, times(1)).onFocusedTaskChanged(3 /* taskId */,
                true /* isFocusedOnDisplay */, true /* isFocusedGlobally */);
        clearInvocations(mListener);
        changes.clear();

        // Move focused task in the secondary display to the default display
        setupTaskChange(changes, 3 /* taskId */, TRANSIT_CHANGE,
                SECONDARY_DISPLAY_ID, DEFAULT_DISPLAY, true /* focused */);
        setupTaskChange(changes, 2 /* taskId */, TRANSIT_TO_FRONT,
                SECONDARY_DISPLAY_ID, true /* focused */);
        setupDisplayToTopChange(changes, DEFAULT_DISPLAY);
        when(info.getChanges()).thenReturn(changes);
        mFocusTransitionObserver.updateFocusState(info);
        mShellExecutor.flushAll();
        verify(mListener, times(1)).onFocusedTaskChanged(1 /* taskId */,
                false /* isFocusedOnDisplay */, false /* isFocusedGlobally */);
        verify(mListener, times(1)).onFocusedTaskChanged(2 /* taskId */,
                true /* isFocusedOnDisplay */, false /* isFocusedGlobally */);
        verify(mListener, times(1)).onFocusedTaskChanged(3 /* taskId */,
                true /* isFocusedOnDisplay */, true /* isFocusedGlobally */);
        clearInvocations(mListener);
    }

    private void setupTaskChange(List<TransitionInfo.Change> changes, int taskId,
            @TransitionMode int mode, int displayId, boolean focused) {
        setupTaskChange(changes, taskId, mode, displayId, displayId, focused);
    }

    private void setupTaskChange(List<TransitionInfo.Change> changes, int taskId,
            @TransitionMode int mode, int startDisplayId, int endDisplayId, boolean focused) {
        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
        RunningTaskInfo taskInfo = mock(RunningTaskInfo.class);
        taskInfo.taskId = taskId;
        taskInfo.isFocused = focused;
        when(change.hasFlags(FLAG_MOVED_TO_TOP)).thenReturn(focused);
        taskInfo.displayId = displayId;
        taskInfo.displayId = endDisplayId;
        when(change.getStartDisplayId()).thenReturn(startDisplayId);
        when(change.getEndDisplayId()).thenReturn(endDisplayId);
        when(change.getTaskInfo()).thenReturn(taskInfo);
        when(change.getMode()).thenReturn(mode);
        changes.add(change);
    }


    private void setupDisplayToTopChange(List<TransitionInfo.Change> changes, int displayId) {
        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
        when(change.hasFlags(FLAG_MOVED_TO_TOP)).thenReturn(true);