• 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.content.Intent;
31 import android.graphics.Rect;
32 import android.graphics.RectF;
33 import android.os.SystemClock;
34 import android.os.UserHandle;
35 import android.view.View;
36 
37 import androidx.annotation.BinderThread;
38 
39 import com.android.launcher3.LauncherSettings;
40 import com.android.launcher3.R;
41 import com.android.launcher3.anim.PendingAnimation;
42 import com.android.launcher3.model.data.WorkspaceItemInfo;
43 import com.android.launcher3.uioverrides.QuickstepLauncher;
44 import com.android.quickstep.OverviewCommandHelper;
45 import com.android.quickstep.OverviewComponentObserver;
46 import com.android.quickstep.RecentsAnimationCallbacks;
47 import com.android.quickstep.RecentsAnimationController;
48 import com.android.quickstep.RecentsAnimationDeviceState;
49 import com.android.quickstep.RecentsAnimationTargets;
50 import com.android.quickstep.RecentsModel;
51 import com.android.quickstep.SystemUiProxy;
52 import com.android.quickstep.views.FloatingTaskView;
53 import com.android.quickstep.views.RecentsView;
54 import com.android.systemui.shared.recents.model.Task;
55 import com.android.systemui.shared.system.ActivityManagerWrapper;
56 
57 /** Transitions app from fullscreen to stage split when triggered from keyboard shortcuts. */
58 public class SplitWithKeyboardShortcutController {
59 
60     private final QuickstepLauncher mLauncher;
61     private final SplitSelectStateController mController;
62     private final OverviewComponentObserver mOverviewComponentObserver;
63 
64     private final int mSplitPlaceholderSize;
65     private final int mSplitPlaceholderInset;
66 
SplitWithKeyboardShortcutController(QuickstepLauncher launcher, SplitSelectStateController controller)67     public SplitWithKeyboardShortcutController(QuickstepLauncher launcher,
68             SplitSelectStateController controller) {
69         mLauncher = launcher;
70         mController = controller;
71         RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(
72                 launcher.getApplicationContext());
73         mOverviewComponentObserver = new OverviewComponentObserver(launcher.getApplicationContext(),
74                 deviceState);
75 
76         mSplitPlaceholderSize = mLauncher.getResources().getDimensionPixelSize(
77                 R.dimen.split_placeholder_size);
78         mSplitPlaceholderInset = mLauncher.getResources().getDimensionPixelSize(
79                 R.dimen.split_placeholder_inset);
80     }
81 
82     @BinderThread
enterStageSplit(boolean leftOrTop)83     public void enterStageSplit(boolean leftOrTop) {
84         if (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()) {
85             return;
86         }
87         RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
88                 SystemUiProxy.INSTANCE.get(mLauncher.getApplicationContext()),
89                 false /* allowMinimizeSplitScreen */);
90         SplitWithKeyboardShortcutRecentsAnimationListener listener =
91                 new SplitWithKeyboardShortcutRecentsAnimationListener(leftOrTop);
92 
93         MAIN_EXECUTOR.execute(() -> {
94             callbacks.addListener(listener);
95             UI_HELPER_EXECUTOR.execute(
96                     // Transition from fullscreen app to enter stage split in launcher with
97                     // recents animation.
98                     () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
99                             mOverviewComponentObserver.getOverviewIntent(),
100                             SystemClock.uptimeMillis(), callbacks, null, null));
101         });
102     }
103 
onDestroy()104     public void onDestroy() {
105         mOverviewComponentObserver.onDestroy();
106     }
107 
108     private class SplitWithKeyboardShortcutRecentsAnimationListener implements
109             RecentsAnimationCallbacks.RecentsAnimationListener {
110 
111         private final boolean mLeftOrTop;
112         private final Rect mTempRect = new Rect();
113 
SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop)114         private SplitWithKeyboardShortcutRecentsAnimationListener(boolean leftOrTop) {
115             mLeftOrTop = leftOrTop;
116         }
117 
118         @Override
onRecentsAnimationStart(RecentsAnimationController controller, RecentsAnimationTargets targets)119         public void onRecentsAnimationStart(RecentsAnimationController controller,
120                 RecentsAnimationTargets targets) {
121             ActivityManager.RunningTaskInfo runningTaskInfo =
122                     ActivityManagerWrapper.getInstance().getRunningTask();
123             mController.setInitialTaskSelect(runningTaskInfo,
124                     mLeftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT,
125                     null /* itemInfo */,
126                     mLeftOrTop ? LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP
127                             : LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_RIGHT_BOTTOM);
128 
129             RecentsView recentsView = mLauncher.getOverviewPanel();
130             recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
131                     mSplitPlaceholderSize, mSplitPlaceholderInset, mLauncher.getDeviceProfile(),
132                     mController.getActiveSplitStagePosition(), mTempRect);
133 
134             PendingAnimation anim = new PendingAnimation(
135                     SplitAnimationTimings.TABLET_HOME_TO_SPLIT.getDuration());
136             RectF startingTaskRect = new RectF();
137             final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(
138                     mLauncher, mLauncher.getDragLayer(),
139                     controller.screenshotTask(runningTaskInfo.taskId).thumbnail,
140                     null /* icon */, startingTaskRect);
141             RecentsModel.INSTANCE.get(mLauncher.getApplicationContext())
142                     .getIconCache()
143                     .updateIconInBackground(
144                             Task.from(new Task.TaskKey(runningTaskInfo), runningTaskInfo,
145                                     false /* isLocked */),
146                             (task) -> {
147                                 if (task.thumbnail != null) {
148                                     floatingTaskView.setIcon(task.thumbnail.thumbnail);
149                                 }
150                             });
151             floatingTaskView.setAlpha(1);
152             floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
153                     false /* fadeWithThumbnail */, true /* isStagedTask */);
154             mController.setFirstFloatingTaskView(floatingTaskView);
155 
156             anim.addListener(new AnimatorListenerAdapter() {
157                 @Override
158                 public void onAnimationStart(Animator animation) {
159                     controller.finish(true /* toRecents */, null /* onFinishComplete */,
160                             false /* sendUserLeaveHint */);
161                 }
162             });
163             anim.buildAnim().start();
164         }
165     };
166 }
167