• 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.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
20 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP;
21 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM;
22 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
23 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
24 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
25 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
26 
27 import android.animation.Animator;
28 import android.animation.AnimatorListenerAdapter;
29 import android.app.ActivityManager;
30 import android.graphics.Rect;
31 import android.graphics.RectF;
32 import android.os.SystemClock;
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.uioverrides.QuickstepLauncher;
39 import com.android.quickstep.OverviewComponentObserver;
40 import com.android.quickstep.RecentsAnimationCallbacks;
41 import com.android.quickstep.RecentsAnimationController;
42 import com.android.quickstep.RecentsAnimationDeviceState;
43 import com.android.quickstep.RecentsAnimationTargets;
44 import com.android.quickstep.RecentsModel;
45 import com.android.quickstep.SystemUiProxy;
46 import com.android.quickstep.views.FloatingTaskView;
47 import com.android.quickstep.views.RecentsView;
48 import com.android.systemui.shared.recents.model.Task;
49 import com.android.systemui.shared.system.ActivityManagerWrapper;
50 
51 /** Transitions app from fullscreen to stage split when triggered from keyboard shortcuts. */
52 public class SplitWithKeyboardShortcutController {
53 
54     private final QuickstepLauncher mLauncher;
55     private final SplitSelectStateController mController;
56     private final OverviewComponentObserver mOverviewComponentObserver;
57 
58     private final int mSplitPlaceholderSize;
59     private final int mSplitPlaceholderInset;
60 
SplitWithKeyboardShortcutController(QuickstepLauncher launcher, SplitSelectStateController controller)61     public SplitWithKeyboardShortcutController(QuickstepLauncher launcher,
62             SplitSelectStateController controller) {
63         mLauncher = launcher;
64         mController = controller;
65         RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(
66                 launcher.getApplicationContext());
67         mOverviewComponentObserver = new OverviewComponentObserver(launcher.getApplicationContext(),
68                 deviceState);
69 
70         mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
71                 R.dimen.split_placeholder_size);
72         mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
73                 R.dimen.split_placeholder_inset);
74     }
75 
76     @BinderThread
enterStageSplit(boolean leftOrTop)77     public void enterStageSplit(boolean leftOrTop) {
78         if (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()) {
79             return;
80         }
81         RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
82                 SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()),
83                 false /* allowMinimizeSplitScreen */);
84         SplitWithKeyboardShortcutRecentsAnimationListener listener =
85                 new SplitWithKeyboardShortcutRecentsAnimationListener(leftOrTop);
86 
87         MAIN_EXECUTOR.execute(() -> {
88             callbacks.addListener(listener);
89             UI_HELPER_EXECUTOR.execute(
90                     // Transition from fullscreen app to enter stage split in launcher with
91                     // recents animation.
92                     () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
93                             mOverviewComponentObserver.getOverviewIntent(),
94                             SystemClock.uptimeMillis(), callbacks, null, null));
95         });
96     }
97 
onDestroy()98     public void onDestroy() {
99         mOverviewComponentObserver.onDestroy();
100     }
101 
102     private class SplitWithKeyboardShortcutRecentsAnimationListener implements
103             RecentsAnimationCallbacks.RecentsAnimationListener {
104 
105         private final boolean mLeftOrTop;
106         private final Rect mTempRect = new Rect();
107 
SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop)108         private SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop) {
109             mLeftOrTop = leftOrTop;
110         }
111 
112         @Override
onRecentsAnimationStart(RecentsAnimationController controller, RecentsAnimationTargets targets)113         public void onRecentsAnimationStart(RecentsAnimationController controller,
114                 RecentsAnimationTargets targets) {
115             ActivityManager.RunningTaskInfo runningTaskInfo =
116                     ActivityManagerWrapper.getInstance().getRunningTask();
117             mController.setInitialTaskSelect(runningTaskInfo,
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(runningTaskInfo.taskId).thumbnail,
134                     null /* icon */, startingTaskRect);
135             RecentsModel.INSTANCE.get(mLauncher.getApplicationContext())
136                     .getIconCache()
137                     .updateIconInBackground(
138                             Task.from(new Task.TaskKey(runningTaskInfo), runningTaskInfo,
139                                     false /* isLocked */),
140                             (task) -> floatingTaskView.setIcon(task.icon));
141             floatingTaskView.setAlpha(1);
142             floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
143                     false /* fadeWithThumbnail */, true /* isStagedTask */);
144             mController.setFirstFloatingTaskView(floatingTaskView);
145 
146             anim.addListener(new AnimatorListenerAdapter() {
147                 @Override
148                 public void onAnimationStart(Animator animation) {
149                     controller.finish(true /* toRecents */, null /* onFinishComplete */,
150                             false /* sendUserLeaveHint */);
151                 }
152             });
153             anim.buildAnim().start();
154         }
155     };
156 }
157