summaryrefslogtreecommitdiff
path: root/com/android/server/am/RecentsAnimation.java
blob: fe576fdaacbed6940e6b86f4b25fc34cbe8391f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
 * Copyright (C) 2018 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.server.am;

import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;

import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Handler;
import android.view.IRecentsAnimationRunner;
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
import com.android.server.wm.WindowManagerService;

/**
 * Manages the recents animation, including the reordering of the stacks for the transition and
 * cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
 */
class RecentsAnimation implements RecentsAnimationCallbacks {
    private static final String TAG = RecentsAnimation.class.getSimpleName();

    private static final int RECENTS_ANIMATION_TIMEOUT = 10 * 1000;

    private final ActivityManagerService mService;
    private final ActivityStackSupervisor mStackSupervisor;
    private final ActivityStartController mActivityStartController;
    private final WindowManagerService mWindowManager;
    private final UserController mUserController;
    private final Handler mHandler;

    private final Runnable mCancelAnimationRunnable;

    // The stack to restore the home stack behind when the animation is finished
    private ActivityStack mRestoreHomeBehindStack;

    RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor,
            ActivityStartController activityStartController, WindowManagerService wm,
            UserController userController) {
        mService = am;
        mStackSupervisor = stackSupervisor;
        mActivityStartController = activityStartController;
        mHandler = new Handler(mStackSupervisor.mLooper);
        mWindowManager = wm;
        mUserController = userController;
        mCancelAnimationRunnable = () -> {
            // The caller has not finished the animation in a predefined amount of time, so
            // force-cancel the animation
            mWindowManager.cancelRecentsAnimation();
        };
    }

    void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
            ComponentName recentsComponent, int recentsUid) {

        // Cancel the previous recents animation if necessary
        mWindowManager.cancelRecentsAnimation();

        final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null;
        if (!hasExistingHomeActivity) {
            // No home activity
            final ActivityOptions opts = ActivityOptions.makeBasic();
            opts.setLaunchActivityType(ACTIVITY_TYPE_HOME);
            opts.setAvoidMoveToFront();
            intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);

            mActivityStartController.obtainStarter(intent, "startRecentsActivity_noHomeActivity")
                    .setCallingUid(recentsUid)
                    .setCallingPackage(recentsComponent.getPackageName())
                    .setActivityOptions(SafeActivityOptions.fromBundle(opts.toBundle()))
                    .setMayWait(mUserController.getCurrentUserId())
                    .execute();
            mWindowManager.prepareAppTransition(TRANSIT_NONE, false);

            // TODO: Maybe wait for app to draw in this particular case?
        }

        final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
        final ActivityDisplay display = homeActivity.getDisplay();

        // Save the initial position of the home activity stack to be restored to after the
        // animation completes
        mRestoreHomeBehindStack = hasExistingHomeActivity
                ? display.getStackAboveHome()
                : null;

        // Move the home activity into place for the animation
        display.moveHomeStackBehindBottomMostVisibleStack();

        // Mark the home activity as launch-behind to bump its visibility for the
        // duration of the gesture that is driven by the recents component
        homeActivity.mLaunchTaskBehind = true;

        // Fetch all the surface controls and pass them to the client to get the animation
        // started
        mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, display.mDisplayId);

        // If we updated the launch-behind state, update the visibility of the activities after we
        // fetch the visible tasks to be controlled by the animation
        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);

        // Post a timeout for the animation
        mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
    }

    @Override
    public void onAnimationFinished(boolean moveHomeToTop) {
        mHandler.removeCallbacks(mCancelAnimationRunnable);
        synchronized (mService) {
            if (mWindowManager.getRecentsAnimationController() == null) return;

            mWindowManager.inSurfaceTransaction(() -> {
                mWindowManager.cleanupRecentsAnimation();

                // Move the home stack to the front
                final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
                if (homeActivity == null) {
                    return;
                }

                // Restore the launched-behind state
                homeActivity.mLaunchTaskBehind = false;

                if (moveHomeToTop) {
                    // Bring the home stack to the front
                    final ActivityStack homeStack = homeActivity.getStack();
                    homeStack.mNoAnimActivities.add(homeActivity);
                    homeStack.moveToFront("RecentsAnimation.onAnimationFinished()");
                } else {
                    // Restore the home stack to its previous position
                    final ActivityDisplay display = homeActivity.getDisplay();
                    display.moveHomeStackBehindStack(mRestoreHomeBehindStack);
                }

                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
                mStackSupervisor.resumeFocusedStackTopActivityLocked();
            });
        }
    }
}