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

Commit a24b63d6 authored by Ats Jenk's avatar Ats Jenk
Browse files

Animate launching desktop from overview

Create a new controller that handles launching desktop from recents. It
creates a remote transition runner. Then notifies wmshell to show
desktop apps and use this remote runner to run the animation.

TODO:
- there are flickers in the animation

Bug: 297590571
Test: open an app on desktop, swipe up to overview, click on desktop
      tile
      open multiple apps on desktop, swipe up to overview, click on
      desktop tile

Change-Id: Ie8c9189bace77ba80e6be90222b3f786dfe5bb15
parent e745dda4
Loading
Loading
Loading
Loading
+106 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.launcher3.desktop

import android.app.IApplicationThread
import android.os.IBinder
import android.os.RemoteException
import android.util.Log
import android.view.SurfaceControl
import android.window.IRemoteTransition
import android.window.IRemoteTransitionFinishedCallback
import android.window.RemoteTransition
import android.window.TransitionInfo
import com.android.launcher3.statehandlers.DepthController
import com.android.launcher3.statemanager.StateManager
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.TaskViewUtils
import com.android.quickstep.views.DesktopTaskView
import java.util.function.Consumer

/** Manage recents related operations with desktop tasks */
class DesktopRecentsTransitionController(
    private val stateManager: StateManager<*>,
    private val systemUiProxy: SystemUiProxy,
    private val appThread: IApplicationThread,
    private val depthController: DepthController?
) {

    /** Launch desktop tasks from recents view */
    fun launchDesktopFromRecents(
        desktopTaskView: DesktopTaskView,
        callback: Consumer<Boolean>? = null
    ) {
        val animRunner =
            RemoteDesktopLaunchTransitionRunner(
                desktopTaskView,
                stateManager,
                depthController,
                callback
            )
        val transition = RemoteTransition(animRunner, appThread, "RecentsToDesktop")
        systemUiProxy.showDesktopApps(desktopTaskView.display.displayId, transition)
    }

    private class RemoteDesktopLaunchTransitionRunner(
        private val desktopTaskView: DesktopTaskView,
        private val stateManager: StateManager<*>,
        private val depthController: DepthController?,
        private val successCallback: Consumer<Boolean>?
    ) : IRemoteTransition.Stub() {

        override fun startAnimation(
            token: IBinder,
            info: TransitionInfo,
            t: SurfaceControl.Transaction,
            finishCallback: IRemoteTransitionFinishedCallback
        ) {
            val errorHandlingFinishCallback = Runnable {
                try {
                    finishCallback.onTransitionFinished(null /* wct */, null /* sct */)
                } catch (e: RemoteException) {
                    Log.e(TAG, "Failed to call finish callback for desktop recents animation", e)
                }
            }

            MAIN_EXECUTOR.execute {
                TaskViewUtils.composeRecentsDesktopLaunchAnimator(
                    desktopTaskView,
                    stateManager,
                    depthController,
                    info,
                    t
                ) {
                    errorHandlingFinishCallback.run()
                    successCallback?.accept(true)
                }
            }
        }

        override fun mergeAnimation(
            transition: IBinder,
            info: TransitionInfo,
            t: SurfaceControl.Transaction,
            mergeTarget: IBinder,
            finishCallback: IRemoteTransitionFinishedCallback
        ) {}
    }

    companion object {
        const val TAG = "DesktopRecentsTransitionController"
    }
}
+13 −2
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
@@ -225,6 +226,9 @@ public class QuickstepLauncher extends Launcher {
     */
    private PendingSplitSelectInfo mPendingSplitSelectInfo = null;

    @Nullable
    private DesktopRecentsTransitionController mDesktopRecentsTransitionController;

    private SafeCloseable mViewCapture;

    private boolean mEnableWidgetDepth;
@@ -235,12 +239,19 @@ public class QuickstepLauncher extends Launcher {

        mActionsView = findViewById(R.id.overview_actions_view);
        RecentsView overviewPanel = getOverviewPanel();
        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
        mSplitSelectStateController =
                new SplitSelectStateController(this, mHandler, getStateManager(),
                        getDepthController(), getStatsLogManager(),
                        SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this),
                        systemUiProxy, RecentsModel.INSTANCE.get(this),
                        () -> onStateBack());
        overviewPanel.init(mActionsView, mSplitSelectStateController);
        if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
            mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
                    getStateManager(), systemUiProxy, getIApplicationThread(),
                    getDepthController());
        }
        overviewPanel.init(mActionsView, mSplitSelectStateController,
                mDesktopRecentsTransitionController);
        mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
                mSplitSelectStateController);
        mSplitToWorkspaceController = new SplitToWorkspaceController(this,
+14 −2
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
@@ -82,6 +83,7 @@ import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.RecentsAtomicAnimationFactory;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -121,6 +123,8 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
    private final Handler mHandler = new Handler();
    private final Runnable mAnimationStartTimeoutRunnable = this::onAnimationStartTimeout;
    private SplitSelectStateController mSplitSelectStateController;
    @Nullable
    private DesktopRecentsTransitionController mDesktopRecentsTransitionController;

    /**
     * Init drag layer and overview panel views.
@@ -133,13 +137,21 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
        mFallbackRecentsView = findViewById(R.id.overview_panel);
        mActionsView = findViewById(R.id.overview_actions_view);
        getRootView().getSysUiScrim().getSysUIProgress().updateValue(0);
        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
        mSplitSelectStateController =
                new SplitSelectStateController(this, mHandler, getStateManager(),
                        null /* depthController */, getStatsLogManager(),
                        SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this),
                        systemUiProxy, RecentsModel.INSTANCE.get(this),
                        null /*activityBackCallback*/);
        mDragLayer.recreateControllers();
        mFallbackRecentsView.init(mActionsView, mSplitSelectStateController);
        if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
            mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
                    getStateManager(), systemUiProxy, getIApplicationThread(),
                    null /* depthController */
            );
        }
        mFallbackRecentsView.init(mActionsView, mSplitSelectStateController,
                mDesktopRecentsTransitionController);

        mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
    }
+2 −2
Original line number Diff line number Diff line
@@ -1264,10 +1264,10 @@ public class SystemUiProxy implements ISystemUiProxy {
    //

    /** Call shell to show all apps active on the desktop */
    public void showDesktopApps(int displayId) {
    public void showDesktopApps(int displayId, @Nullable RemoteTransition transition) {
        if (mDesktopMode != null) {
            try {
                mDesktopMode.showDesktopApps(displayId);
                mDesktopMode.showDesktopApps(displayId, transition);
            } catch (RemoteException e) {
                Log.w(TAG, "Failed call showDesktopApps", e);
            }
+36 −0
Original line number Diff line number Diff line
@@ -612,6 +612,42 @@ public final class TaskViewUtils {
        animator.start();
    }

    /**
     * Start recents to desktop animation
     */
    public static void composeRecentsDesktopLaunchAnimator(
            @NonNull DesktopTaskView launchingTaskView,
            @NonNull StateManager stateManager, @Nullable DepthController depthController,
            @NonNull TransitionInfo transitionInfo,
            SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                t.apply();
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                finishCallback.run();
            }
        });

        final RemoteAnimationTarget[] apps = RemoteAnimationTargetCompat.wrapApps(
                transitionInfo, t, null /* leashMap */);
        final RemoteAnimationTarget[] wallpaper = RemoteAnimationTargetCompat.wrapNonApps(
                transitionInfo, true /* wallpapers */, t, null /* leashMap */);
        final RemoteAnimationTarget[] nonApps = RemoteAnimationTargetCompat.wrapNonApps(
                transitionInfo, false /* wallpapers */, t, null /* leashMap */);

        composeRecentsLaunchAnimator(animatorSet, launchingTaskView, apps, wallpaper, nonApps,
                true /* launcherClosing */, stateManager, launchingTaskView.getRecentsView(),
                depthController);

        animatorSet.start();
    }

    public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
            @NonNull RemoteAnimationTarget[] appTargets,
            @NonNull RemoteAnimationTarget[] wallpaperTargets,
Loading