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

Commit 907dd476 authored by Ats Jenk's avatar Ats Jenk
Browse files

Fix hotseat visibility after dragging task to bubble

Desktop drag handler sends a transition at the beginning to transiently
launch home task. This transition is ignored by the home transition
observer and we do not update home visibility in this case. As this
would otherwise cause flickers with launcher UI elements during drag.

When the desktop drag ends in a bubble drag zone, we send a new
transition to convert the task to a bubble. If the task was the only
task running on home, we now will have home showing and the bubbled task
showing. But since home was made visible by the drag start transition,
home task is not part of the second transition changes. It is already
considered visible.
This broke the existing logic with handling home visibility as we were
expecting the second transition to include information about the home
task.
As a fix, keep track of the desired home visibility state when desktop
drag starts. And if the subsequent transition to convert the task to
bubble does not contain information about home, apply the home
visibility that was calculated at the start of drag.
If the convert to bubble transition does include home task and it
triggers a change to home visibility, apply this instead and discard the
home visibility pending update from the start of the drag.
This second case is required in case there are two tasks running on top
of home. When desktop drag starts, home will be brought to front
transiently. And when drag ends with a transition to convert the dragged
task to bubble, the transition changes will include a change to move the
home task behind the second fullscreen task. We need to honor this case
and send an update that home is not visible.

Bug: 396475362
Flag: com.android.wm.shell.enable_bubble_to_fullscreen
Test: atest HomeTransitionObserver
Test: manual, open an app, drag it to a bubble, check that hotseat is
  shown in launcher
Change-Id: I197ac9b96c4312f3da12bdad21b5844b414847c2
parent 19ed7fa5
Loading
Loading
Loading
Loading
+66 −12
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;


import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
import static com.android.wm.shell.transition.Transitions.TransitionObserver;
import static com.android.wm.shell.transition.Transitions.TransitionObserver;


import android.annotation.NonNull;
import android.annotation.NonNull;
@@ -35,6 +36,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.shared.IHomeTransitionListener;
import com.android.wm.shell.shared.IHomeTransitionListener;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;


/**
/**
 * The {@link TransitionObserver} that observes for transitions involving the home
 * The {@link TransitionObserver} that observes for transitions involving the home
@@ -48,6 +50,8 @@ public class HomeTransitionObserver implements TransitionObserver,


    private @NonNull final Context mContext;
    private @NonNull final Context mContext;
    private @NonNull final ShellExecutor mMainExecutor;
    private @NonNull final ShellExecutor mMainExecutor;
    private Boolean mPendingHomeVisibilityUpdate;

    public HomeTransitionObserver(@NonNull Context context,
    public HomeTransitionObserver(@NonNull Context context,
            @NonNull ShellExecutor mainExecutor) {
            @NonNull ShellExecutor mainExecutor) {
        mContext = context;
        mContext = context;
@@ -59,16 +63,67 @@ public class HomeTransitionObserver implements TransitionObserver,
            @NonNull TransitionInfo info,
            @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction) {
            @NonNull SurfaceControl.Transaction finishTransaction) {
        if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
            handleTransitionReadyWithBubbleAnything(info);
        } else {
            handleTransitionReady(info);
        }
    }

    private void handleTransitionReady(@NonNull TransitionInfo info) {
        for (TransitionInfo.Change change : info.getChanges()) {
            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
            if (taskInfo == null
                    || info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
                    || taskInfo.displayId != DEFAULT_DISPLAY
                    || taskInfo.taskId == -1
                    || !taskInfo.isRunning) {
                continue;
            }
            Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info, change, taskInfo);
            if (homeVisibilityUpdate != null) {
                notifyHomeVisibilityChanged(homeVisibilityUpdate);
            }
        }
    }

    private void handleTransitionReadyWithBubbleAnything(@NonNull TransitionInfo info) {
        Boolean homeVisibilityUpdate = null;
        for (TransitionInfo.Change change : info.getChanges()) {
        for (TransitionInfo.Change change : info.getChanges()) {
            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
            if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
            if (taskInfo == null
                    || taskInfo == null
                    || taskInfo.displayId != DEFAULT_DISPLAY
                    || taskInfo.displayId != DEFAULT_DISPLAY
                    || taskInfo.taskId == -1
                    || taskInfo.taskId == -1
                    || !taskInfo.isRunning) {
                    || !taskInfo.isRunning) {
                continue;
                continue;
            }
            }


            Boolean update = getHomeVisibilityUpdate(info, change, taskInfo);
            if (update != null) {
                homeVisibilityUpdate = update;
            }
        }

        if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
            // Do not apply at the start of desktop drag as that updates launcher UI visibility.
            // Store the value and apply with a next transition if needed.
            mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
            return;
        }

        if (info.getType() == TRANSIT_CONVERT_TO_BUBBLE && homeVisibilityUpdate == null) {
            // We are converting to bubble and we did not get a change to home visibility in this
            // transition. Apply the value from start of drag.
            homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
        }
        if (homeVisibilityUpdate != null) {
            mPendingHomeVisibilityUpdate = null;
            notifyHomeVisibilityChanged(homeVisibilityUpdate);
        }
    }

    private Boolean getHomeVisibilityUpdate(TransitionInfo info,
            TransitionInfo.Change change, ActivityManager.RunningTaskInfo taskInfo) {
        final int mode = change.getMode();
        final int mode = change.getMode();
        final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
        final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
        if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
        if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
@@ -76,11 +131,10 @@ public class HomeTransitionObserver implements TransitionObserver,
                    && TransitionUtil.isClosingType(info.getType());
                    && TransitionUtil.isClosingType(info.getType());
            if (gestureToHomeTransition || TransitionUtil.isClosingMode(mode)
            if (gestureToHomeTransition || TransitionUtil.isClosingMode(mode)
                    || (!isBackGesture && TransitionUtil.isOpeningMode(mode))) {
                    || (!isBackGesture && TransitionUtil.isOpeningMode(mode))) {
                    notifyHomeVisibilityChanged(gestureToHomeTransition
                return gestureToHomeTransition || TransitionUtil.isOpeningType(mode);
                            || TransitionUtil.isOpeningType(mode));
                }
            }
            }
        }
        }
        return null;
    }
    }


    @Override
    @Override
+84 −0
Original line number Original line Diff line number Diff line
@@ -17,36 +17,44 @@
package com.android.wm.shell.transition;
package com.android.wm.shell.transition;


import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;


import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;


import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.app.ActivityManager;
import android.app.ActivityManager;
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.ActivityType;
import android.content.Context;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder;
import android.os.Looper;
import android.os.Looper;
import android.os.RemoteException;
import android.os.RemoteException;
import android.platform.test.annotations.EnableFlags;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo;
import android.window.TransitionInfo.TransitionMode;
import android.window.TransitionInfo.TransitionMode;
import android.window.WindowContainerToken;


import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.platform.app.InstrumentationRegistry;


import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.TestShellExecutor;
@@ -187,6 +195,72 @@ public class HomeTransitionObserverTest extends ShellTestCase {
        verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean());
        verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean());
    }
    }


    @Test
    @EnableFlags({Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE})
    public void testDragTaskToBubbleOverHome_notifiesHomeIsVisible() throws RemoteException {
        ActivityManager.RunningTaskInfo homeTask = createTaskInfo(1, ACTIVITY_TYPE_HOME);
        ActivityManager.RunningTaskInfo bubbleTask = createTaskInfo(2, ACTIVITY_TYPE_STANDARD);

        TransitionInfo startDragTransition =
                new TransitionInfoBuilder(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP)
                        .addChange(TRANSIT_TO_FRONT, homeTask)
                        .addChange(TRANSIT_TO_BACK, bubbleTask)
                        .build();

        // Start drag to desktop which brings home to front
        mHomeTransitionObserver.onTransitionReady(new Binder(), startDragTransition,
                MockTransactionPool.create(), MockTransactionPool.create());
        // Does not notify home visibility yet
        verify(mListener, never()).onHomeVisibilityChanged(anyBoolean());

        TransitionInfo convertToBubbleTransition =
                new TransitionInfoBuilder(TRANSIT_CONVERT_TO_BUBBLE)
                        .addChange(TRANSIT_TO_FRONT, bubbleTask)
                        .build();

        // Convert to bubble. Transition does not include changes for home task
        mHomeTransitionObserver.onTransitionReady(new Binder(), convertToBubbleTransition,
                MockTransactionPool.create(), MockTransactionPool.create());

        // Notifies home visibility change that was pending from the start of drag
        verify(mListener).onHomeVisibilityChanged(true);
    }

    @Test
    @EnableFlags({Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE})
    public void testDragTaskToBubbleOverOtherTask_notifiesHomeIsNotVisible()
            throws RemoteException {
        ActivityManager.RunningTaskInfo homeTask = createTaskInfo(1, ACTIVITY_TYPE_HOME);
        ActivityManager.RunningTaskInfo bubbleTask = createTaskInfo(2, ACTIVITY_TYPE_STANDARD);
        ActivityManager.RunningTaskInfo otherTask = createTaskInfo(3, ACTIVITY_TYPE_STANDARD);

        TransitionInfo startDragTransition =
                new TransitionInfoBuilder(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP)
                        .addChange(TRANSIT_TO_FRONT, homeTask)
                        .addChange(TRANSIT_TO_BACK, bubbleTask)
                        .build();

        // Start drag to desktop which brings home to front
        mHomeTransitionObserver.onTransitionReady(new Binder(), startDragTransition,
                MockTransactionPool.create(), MockTransactionPool.create());
        // Does not notify home visibility yet
        verify(mListener, never()).onHomeVisibilityChanged(anyBoolean());

        TransitionInfo convertToBubbleTransition =
                new TransitionInfoBuilder(TRANSIT_CONVERT_TO_BUBBLE)
                        .addChange(TRANSIT_TO_FRONT, bubbleTask)
                        .addChange(TRANSIT_TO_FRONT, otherTask)
                        .addChange(TRANSIT_TO_BACK, homeTask)
                        .build();

        // Convert to bubble. Transition includes home task to back which updates home visibility
        mHomeTransitionObserver.onTransitionReady(new Binder(), convertToBubbleTransition,
                MockTransactionPool.create(), MockTransactionPool.create());

        // Notifies home visibility change due to home moving to back in the second transition
        verify(mListener).onHomeVisibilityChanged(false);
    }

    @Test
    @Test
    public void testHomeActivityWithBackGestureNotifiesHomeIsVisibleAfterClose()
    public void testHomeActivityWithBackGestureNotifiesHomeIsVisibleAfterClose()
            throws RemoteException {
            throws RemoteException {
@@ -227,4 +301,14 @@ public class HomeTransitionObserverTest extends ShellTestCase {
        when(change.getMode()).thenReturn(mode);
        when(change.getMode()).thenReturn(mode);
        taskInfo.isRunning = isRunning;
        taskInfo.isRunning = isRunning;
    }
    }

    private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int activityType) {
        ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
        taskInfo.taskId = taskId;
        taskInfo.topActivityType = activityType;
        taskInfo.configuration.windowConfiguration.setActivityType(activityType);
        taskInfo.token = mock(WindowContainerToken.class);
        taskInfo.isRunning = true;
        return taskInfo;
    }
}
}