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

Commit 95c85034 authored by mattsziklay's avatar mattsziklay Committed by Matt Sziklay
Browse files

Allow split select transition from desktop mode.

Introduces a new controller to manage an app entering split select mode
from desktop mode.

Video: http://recall/-/gjymLwjdDT07aWqaK6101a/gU56zTDcWov6ukbKuH8tFx
Flag: ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE
Bug: 286550932
Bug: 279586624
Test: Manual

Change-Id: Ib94402553c88286894d94c95c38cac125be23a0d
parent 6682d58d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
import static com.android.launcher3.config.FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
@@ -261,6 +262,7 @@ public class QuickstepLauncher extends Launcher {
        mDesktopVisibilityController = new DesktopVisibilityController(this);
        if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
            mDesktopVisibilityController.registerSystemUiListener();
            mSplitSelectStateController.initSplitFromDesktopController(this);
        }
        mHotseatPredictionController = new HotseatPredictionController(this);

+36 −0
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ import com.android.wm.shell.recents.IRecentTasks;
import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.splitscreen.ISplitScreen;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
import com.android.wm.shell.splitscreen.ISplitSelectListener;
import com.android.wm.shell.startingsurface.IStartingWindow;
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import com.android.wm.shell.transition.IShellTransitions;
@@ -128,6 +129,7 @@ public class SystemUiProxy implements ISystemUiProxy {
    private IPipAnimationListener mPipAnimationListener;
    private IBubblesListener mBubblesListener;
    private ISplitScreenListener mSplitScreenListener;
    private ISplitSelectListener mSplitSelectListener;
    private IStartingWindowListener mStartingWindowListener;
    private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
    private IRecentTasksListener mRecentTasksListener;
@@ -239,6 +241,7 @@ public class SystemUiProxy implements ISystemUiProxy {
        setPipAnimationListener(mPipAnimationListener);
        setBubblesListener(mBubblesListener);
        registerSplitScreenListener(mSplitScreenListener);
        registerSplitSelectListener(mSplitSelectListener);
        setStartingWindowListener(mStartingWindowListener);
        setLauncherUnlockAnimationController(mLauncherUnlockAnimationController);
        new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition);
@@ -740,6 +743,28 @@ public class SystemUiProxy implements ISystemUiProxy {
        mSplitScreenListener = null;
    }

    public void registerSplitSelectListener(ISplitSelectListener listener) {
        if (mSplitScreen != null) {
            try {
                mSplitScreen.registerSplitSelectListener(listener);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed call registerSplitSelectListener");
            }
        }
        mSplitSelectListener = listener;
    }

    public void unregisterSplitSelectListener(ISplitSelectListener listener) {
        if (mSplitScreen != null) {
            try {
                mSplitScreen.unregisterSplitSelectListener(listener);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed call unregisterSplitSelectListener");
            }
        }
        mSplitSelectListener = null;
    }

    /** Start multiple tasks in split-screen simultaneously. */
    public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2,
            @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
@@ -1281,6 +1306,17 @@ public class SystemUiProxy implements ISystemUiProxy {
        }
    }

    /** Perform cleanup transactions after animation to split select is complete */
    public void onDesktopSplitSelectAnimComplete(ActivityManager.RunningTaskInfo taskInfo) {
        if (mDesktopMode != null) {
            try {
                mDesktopMode.onDesktopSplitSelectAnimComplete(taskInfo);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed call onDesktopSplitSelectAnimComplete", e);
            }
        }
    }

    //
    // Unfold transition
    //
+142 −0
Original line number Diff line number Diff line
@@ -17,10 +17,14 @@
package com.android.quickstep.util;

import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM;
import static com.android.launcher3.testing.shared.TestProtocol.LAUNCH_SPLIT_PAIR;
import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_PENDINGINTENT_PENDINGINTENT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_PENDINGINTENT_TASK;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_SHORTCUT_TASK;
@@ -31,6 +35,8 @@ import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_PENDIN
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_SHORTCUT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_TASK;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -38,11 +44,16 @@ import android.app.ActivityThread;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
@@ -57,7 +68,11 @@ import android.window.TransitionInfo;
import androidx.annotation.Nullable;

import com.android.internal.logging.InstanceId;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
@@ -66,6 +81,11 @@ import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SplitSelectionListener;
import com.android.quickstep.SystemUiProxy;
@@ -74,8 +94,11 @@ import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.SplitInstructionsView;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.wm.shell.splitscreen.ISplitSelectListener;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -99,6 +122,7 @@ public class SplitSelectStateController {
    private final StatsLogManager mStatsLogManager;
    private final SystemUiProxy mSystemUiProxy;
    private final StateManager mStateManager;
    private SplitFromDesktopController mSplitFromDesktopController;
    @Nullable
    private DepthController mDepthController;
    private boolean mRecentsAnimationRunning;
@@ -476,6 +500,14 @@ public class SplitSelectStateController {
        }
    }

    public void initSplitFromDesktopController(Launcher launcher) {
        mSplitFromDesktopController = new SplitFromDesktopController(launcher);
    }

    public void enterSplitFromDesktop(ActivityManager.RunningTaskInfo taskInfo) {
        mSplitFromDesktopController.enterSplitSelect(taskInfo);
    }

    private RemoteTransition getShellRemoteTransition(int firstTaskId, int secondTaskId,
            @Nullable Consumer<Boolean> callback, String transitionName) {
        final RemoteSplitLaunchTransitionRunner animationRunner =
@@ -686,4 +718,114 @@ public class SplitSelectStateController {
            mSplitSelectDataHolder.dump(prefix, writer);
        }
    }

    public class SplitFromDesktopController {
        private static final String TAG = "SplitFromDesktopController";

        private final Launcher mLauncher;
        private final OverviewComponentObserver mOverviewComponentObserver;
        private final int mSplitPlaceholderSize;
        private final int mSplitPlaceholderInset;
        private ActivityManager.RunningTaskInfo mTaskInfo;
        private ISplitSelectListener mSplitSelectListener;
        private Drawable mAppIcon;

        public SplitFromDesktopController(Launcher launcher) {
            mLauncher = launcher;
            RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(
                    launcher.getApplicationContext());
            mOverviewComponentObserver =
                    new OverviewComponentObserver(launcher.getApplicationContext(), deviceState);
            mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
                    R.dimen.split_placeholder_size);
            mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
                    R.dimen.split_placeholder_inset);
            mSplitSelectListener = new ISplitSelectListener.Stub() {
                @Override
                public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo) {
                    if (!ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE.get()) return false;
                    MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo));
                    return true;
                }
            };
            SystemUiProxy.INSTANCE.get(mLauncher).registerSplitSelectListener(mSplitSelectListener);
        }

        /**
         * Enter split select from desktop mode.
         * @param taskInfo the desktop task to move to split stage
         */
        public void enterSplitSelect(ActivityManager.RunningTaskInfo taskInfo) {
            mTaskInfo = taskInfo;
            String packageName = mTaskInfo.realActivity.getPackageName();
            PackageManager pm = mLauncher.getApplicationContext().getPackageManager();
            IconProvider provider = new IconProvider(mLauncher.getApplicationContext());
            try {
                mAppIcon = provider.getIcon(pm.getActivityInfo(mTaskInfo.baseActivity,
                     PackageManager.ComponentInfoFlags.of(0)));
            } catch (PackageManager.NameNotFoundException e) {
                Log.w(TAG, "Package not found: " + packageName, e);
            }
            RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
                    SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()),
                    false /* allowMinimizeSplitScreen */);

            DesktopSplitRecentsAnimationListener listener =
                    new DesktopSplitRecentsAnimationListener();

            MAIN_EXECUTOR.execute(() -> {
                callbacks.addListener(listener);
                UI_HELPER_EXECUTOR.execute(
                        // Transition from app to enter stage split in launcher with
                        // recents animation.
                        () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
                                mOverviewComponentObserver.getOverviewIntent(),
                                SystemClock.uptimeMillis(), callbacks, null, null));
            });
        }

        private class DesktopSplitRecentsAnimationListener implements
                RecentsAnimationCallbacks.RecentsAnimationListener {
            private final Rect mTempRect = new Rect();

            @Override
            public void onRecentsAnimationStart(RecentsAnimationController controller,
                    RecentsAnimationTargets targets) {
                setInitialTaskSelect(mTaskInfo, STAGE_POSITION_BOTTOM_OR_RIGHT,
                        null, LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM);

                RecentsView recentsView = mLauncher.getOverviewPanel();
                recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
                        mSplitPlaceholderSize, mSplitPlaceholderInset,
                        mLauncher.getDeviceProfile(), getActiveSplitStagePosition(), mTempRect);

                PendingAnimation anim = new PendingAnimation(
                        SplitAnimationTimings.TABLET_HOME_TO_SPLIT.getDuration());
                RectF startingTaskRect = new RectF(mTaskInfo.configuration.windowConfiguration
                        .getBounds());
                final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
                        mLauncher, mLauncher.getDragLayer(),
                        null /* thumbnail */,
                        mAppIcon, new RectF());
                floatingTaskView.setAlpha(1);
                floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
                        false /* fadeWithThumbnail */, true /* isStagedTask */);
                setFirstFloatingTaskView(floatingTaskView);

                anim.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                        controller.finish(true /* toRecents */, null /* onFinishComplete */,
                                false /* sendUserLeaveHint */);
                    }
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext())
                                .onDesktopSplitSelectAnimComplete(mTaskInfo);
                    }
                });
                anim.buildAnim().start();
            }
        }
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.quickstep.util;

import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;

@@ -178,7 +179,8 @@ public class SplitToWorkspaceController {

    private boolean shouldIgnoreSecondSplitLaunch() {
        return (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()
                && !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get())
                && !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()
                && !ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE.get())
                || !mController.isSplitSelectActive();
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -370,6 +370,10 @@ public final class FeatureFlags {
            270393453, "ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE", TEAMFOOD,
            "Enable initiating split screen from workspace to workspace.");

    public static final BooleanFlag ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE = getDebugFlag(
            279586624, "ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE", DISABLED,
            "Enable initiating split screen from desktop mode to workspace.");

    public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401,
            "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture.");

Loading