• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.quickstep.util;
18 
19 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP;
20 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM;
21 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
22 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
23 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
24 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
25 
26 import android.animation.Animator;
27 import android.animation.AnimatorListenerAdapter;
28 import android.app.ActivityManager;
29 import android.app.ActivityOptions;
30 import android.graphics.Rect;
31 import android.graphics.RectF;
32 import android.window.TransitionInfo;
33 
34 import androidx.annotation.BinderThread;
35 
36 import com.android.launcher3.R;
37 import com.android.launcher3.anim.PendingAnimation;
38 import com.android.launcher3.taskbar.LauncherTaskbarUIController;
39 import com.android.launcher3.uioverrides.QuickstepLauncher;
40 import com.android.quickstep.OverviewComponentObserver;
41 import com.android.quickstep.RecentsAnimationCallbacks;
42 import com.android.quickstep.RecentsAnimationController;
43 import com.android.quickstep.RecentsAnimationTargets;
44 import com.android.quickstep.RecentsModel;
45 import com.android.quickstep.SystemUiProxy;
46 import com.android.quickstep.TopTaskTracker;
47 import com.android.quickstep.views.FloatingTaskView;
48 import com.android.quickstep.views.RecentsView;
49 import com.android.systemui.shared.recents.model.Task;
50 import com.android.systemui.shared.system.ActivityManagerWrapper;
51 
52 /** Transitions app from fullscreen to stage split when triggered from keyboard shortcuts. */
53 public class SplitWithKeyboardShortcutController {
54 
55     private final QuickstepLauncher mLauncher;
56     private final SplitSelectStateController mController;
57     private final OverviewComponentObserver mOverviewComponentObserver;
58 
59     private final int mSplitPlaceholderSize;
60     private final int mSplitPlaceholderInset;
61 
SplitWithKeyboardShortcutController( QuickstepLauncher launcher, SplitSelectStateController controller)62     public SplitWithKeyboardShortcutController(
63             QuickstepLauncher launcher, SplitSelectStateController controller) {
64         mLauncher = launcher;
65         mController = controller;
66         mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(launcher);
67 
68         mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
69                 R.dimen.split_placeholder_size);
70         mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
71                 R.dimen.split_placeholder_inset);
72     }
73 
74     @BinderThread
enterStageSplit(boolean leftOrTop)75     public void enterStageSplit(boolean leftOrTop) {
76         if (TopTaskTracker.INSTANCE.get(mLauncher).getRunningSplitTaskIds().length == 2) {
77             // Do not enter stage split from keyboard shortcuts if the user is already in split
78             return;
79         }
80         RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
81                 SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()));
82         SplitWithKeyboardShortcutRecentsAnimationListener listener =
83                 new SplitWithKeyboardShortcutRecentsAnimationListener(leftOrTop);
84 
85         MAIN_EXECUTOR.execute(() -> {
86             callbacks.addListener(listener);
87             UI_HELPER_EXECUTOR.execute(() -> {
88                 // Transition from fullscreen app to enter stage split in launcher with
89                 // recents animation
90                 final ActivityOptions options = ActivityOptions.makeBasic();
91                 options.setPendingIntentBackgroundActivityStartMode(
92                         ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
93                 options.setTransientLaunch();
94                 SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext())
95                         .startRecentsActivity(mOverviewComponentObserver.getOverviewIntent(),
96                                 ActivityOptions.makeBasic(), callbacks,
97                                 false /* useSyntheticRecentsTransition */);
98             });
99         });
100     }
101 
102     private class SplitWithKeyboardShortcutRecentsAnimationListener implements
103             RecentsAnimationCallbacks.RecentsAnimationListener {
104 
105         private final boolean mLeftOrTop;
106         private final Rect mTempRect = new Rect();
107         private final ActivityManager.RunningTaskInfo mRunningTaskInfo;
108 
SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop)109         private SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop) {
110             mLeftOrTop = leftOrTop;
111             mRunningTaskInfo = ActivityManagerWrapper.getInstance().getRunningTask();
112         }
113 
114         @Override
onRecentsAnimationStart(RecentsAnimationController controller, RecentsAnimationTargets targets, TransitionInfo transitionInfo)115         public void onRecentsAnimationStart(RecentsAnimationController controller,
116                 RecentsAnimationTargets targets, TransitionInfo transitionInfo) {
117             mController.setInitialTaskSelect(mRunningTaskInfo,
118                     mLeftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT,
119                     null /* itemInfo */,
120                     mLeftOrTop ? LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP
121                             : LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM);
122 
123             RecentsView recentsView = mLauncher.getOverviewPanel();
124             recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
125                     mSplitPlaceholderSize, mSplitPlaceholderInset, mLauncher.getDeviceProfile(),
126                     mController.getActiveSplitStagePosition(), mTempRect);
127 
128             PendingAnimation anim = new PendingAnimation(
129                     SplitAnimationTimings.TABLET_HOME_TO_SPLIT.getDuration());
130             RectF startingTaskRect = new RectF();
131             final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
132                     mLauncher, mLauncher.getDragLayer(),
133                     controller.screenshotTask(mRunningTaskInfo.taskId).getThumbnail(),
134                     null /* icon */, startingTaskRect);
135             Task task = Task.from(new Task.TaskKey(mRunningTaskInfo), mRunningTaskInfo,
136                     false /* isLocked */);
137             RecentsModel.INSTANCE.get(mLauncher.getApplicationContext())
138                     .getIconCache()
139                     .getIconInBackground(
140                             task,
141                             (icon, contentDescription, title) -> floatingTaskView.setIcon(icon));
142             floatingTaskView.setAlpha(1);
143             floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
144                     false /* fadeWithThumbnail */, true /* isStagedTask */);
145             mController.setFirstFloatingTaskView(floatingTaskView);
146 
147             anim.addListener(new AnimatorListenerAdapter() {
148                 @Override
149                 public void onAnimationStart(Animator animation) {
150                     controller.finish(
151                             true /* toRecents */,
152                             () -> {
153                                 LauncherTaskbarUIController controller =
154                                         mLauncher.getTaskbarUIController();
155                                 if (controller != null) {
156                                     controller.updateTaskbarLauncherStateGoingHome();
157                                 }
158 
159                             },
160                             false /* sendUserLeaveHint */);
161                 }
162 
163                 @Override
164                 public void onAnimationCancel(Animator animation) {
165                     mLauncher.getDragLayer().removeView(floatingTaskView);
166                     mController.getSplitAnimationController()
167                             .removeSplitInstructionsView(mLauncher);
168                     mController.resetState();
169                 }
170             });
171             anim.add(mController.getSplitAnimationController()
172                     .getShowSplitInstructionsAnim(mLauncher).buildAnim());
173             anim.buildAnim().start();
174         }
175     };
176 }
177