• 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_DESKTOP_TO_WORKSPACE;
20 import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS;
21 import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
22 
23 import android.animation.Animator;
24 import android.animation.AnimatorListenerAdapter;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.app.PendingIntent;
28 import android.content.Intent;
29 import android.graphics.Bitmap;
30 import android.graphics.Canvas;
31 import android.graphics.Rect;
32 import android.graphics.RectF;
33 import android.graphics.drawable.Drawable;
34 import android.os.UserHandle;
35 import android.view.View;
36 
37 import com.android.launcher3.DeviceProfile;
38 import com.android.launcher3.Launcher;
39 import com.android.launcher3.R;
40 import com.android.launcher3.anim.PendingAnimation;
41 import com.android.launcher3.icons.BitmapInfo;
42 import com.android.launcher3.model.data.WorkspaceItemInfo;
43 import com.android.quickstep.views.FloatingTaskView;
44 import com.android.quickstep.views.RecentsView;
45 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
46 
47 /** Handles when the stage split lands on the home screen. */
48 public class SplitToWorkspaceController {
49 
50     private final Launcher mLauncher;
51     private final DeviceProfile mDP;
52     private final SplitSelectStateController mController;
53 
54     private final int mHalfDividerSize;
55 
SplitToWorkspaceController(Launcher launcher, SplitSelectStateController controller)56     public SplitToWorkspaceController(Launcher launcher, SplitSelectStateController controller) {
57         mLauncher = launcher;
58         mDP = mLauncher.getDeviceProfile();
59         mController = controller;
60 
61         mHalfDividerSize = mLauncher.getResources().getDimensionPixelSize(
62                 R.dimen.multi_window_task_divider_size) / 2;
63     }
64 
65     /**
66      * Handles widget selection from staged split.
67      * @param view Original widget view
68      * @param pendingIntent Provided by widget via InteractionHandler
69      * @return {@code true} if we can attempt launch the widget into split, {@code false} otherwise
70      *         to allow launcher to handle the click
71      */
handleSecondWidgetSelectionForSplit(View view, PendingIntent pendingIntent)72     public boolean handleSecondWidgetSelectionForSplit(View view, PendingIntent pendingIntent) {
73         if (shouldIgnoreSecondSplitLaunch()) {
74             return false;
75         }
76 
77         // Convert original widgetView into bitmap to use for animation
78         // TODO(b/276361926) get the icon for this widget via PackageManager?
79         int width = view.getWidth();
80         int height = view.getHeight();
81         Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
82         Canvas canvas = new Canvas(bitmap);
83         view.draw(canvas);
84 
85         mController.setSecondTask(pendingIntent);
86 
87         startWorkspaceAnimation(view, bitmap, null /*icon*/);
88         return true;
89     }
90 
91     /**
92      * Handles second app selection from stage split. If the item can't be opened in split or
93      * it's not in stage split state, we pass it onto Launcher's default item click handler.
94      */
handleSecondAppSelectionForSplit(View view)95     public boolean handleSecondAppSelectionForSplit(View view) {
96         if (shouldIgnoreSecondSplitLaunch()) {
97             return false;
98         }
99         Object tag = view.getTag();
100         Intent intent;
101         UserHandle user;
102         BitmapInfo bitmapInfo;
103         if (tag instanceof WorkspaceItemInfo) {
104             final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag;
105             intent = workspaceItemInfo.intent;
106             user = workspaceItemInfo.user;
107             bitmapInfo = workspaceItemInfo.bitmap;
108         } else if (tag instanceof com.android.launcher3.model.data.AppInfo) {
109             final com.android.launcher3.model.data.AppInfo appInfo =
110                     (com.android.launcher3.model.data.AppInfo) tag;
111             intent = appInfo.intent;
112             user = appInfo.user;
113             bitmapInfo = appInfo.bitmap;
114         } else {
115             return false;
116         }
117 
118         mController.setSecondTask(intent, user);
119 
120         startWorkspaceAnimation(view, null /*bitmap*/, bitmapInfo.newIcon(mLauncher));
121         return true;
122     }
123 
startWorkspaceAnimation(@onNull View view, @Nullable Bitmap bitmap, @Nullable Drawable icon)124     private void startWorkspaceAnimation(@NonNull View view, @Nullable Bitmap bitmap,
125             @Nullable Drawable icon) {
126         boolean isTablet = mLauncher.getDeviceProfile().isTablet;
127         SplitAnimationTimings timings = AnimUtils.getDeviceSplitToConfirmTimings(isTablet);
128         PendingAnimation pendingAnimation = new PendingAnimation(timings.getDuration());
129 
130         Rect firstTaskStartingBounds = new Rect();
131         Rect firstTaskEndingBounds = new Rect();
132         RectF secondTaskStartingBounds = new RectF();
133         Rect secondTaskEndingBounds = new Rect();
134 
135         RecentsView recentsView = mLauncher.getOverviewPanel();
136         recentsView.getPagedOrientationHandler().getFinalSplitPlaceholderBounds(mHalfDividerSize,
137                 mDP, mController.getActiveSplitStagePosition(), firstTaskEndingBounds,
138                 secondTaskEndingBounds);
139 
140         FloatingTaskView firstFloatingTaskView = mController.getFirstFloatingTaskView();
141         firstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
142         firstFloatingTaskView.addConfirmAnimation(pendingAnimation,
143                 new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
144                 false /* fadeWithThumbnail */, true /* isStagedTask */);
145 
146         FloatingTaskView secondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mLauncher,
147                 view, bitmap, icon, secondTaskStartingBounds);
148         secondFloatingTaskView.setAlpha(1);
149         secondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds,
150                 secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
151 
152         pendingAnimation.addListener(new AnimatorListenerAdapter() {
153             private boolean mIsCancelled = false;
154 
155             @Override
156             public void onAnimationCancel(Animator animation) {
157                 mIsCancelled = true;
158                 cleanUp();
159             }
160 
161             @Override
162             public void onAnimationEnd(Animator animation) {
163                 if (!mIsCancelled) {
164                     mController.launchSplitTasks(aBoolean -> cleanUp());
165                     InteractionJankMonitorWrapper.end(
166                             InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
167                 }
168             }
169 
170             private void cleanUp() {
171                 mLauncher.getDragLayer().removeView(firstFloatingTaskView);
172                 mLauncher.getDragLayer().removeView(secondFloatingTaskView);
173                 mController.getSplitAnimationController().removeSplitInstructionsView(mLauncher);
174                 mController.resetState();
175             }
176         });
177         pendingAnimation.buildAnim().start();
178     }
179 
shouldIgnoreSecondSplitLaunch()180     private boolean shouldIgnoreSecondSplitLaunch() {
181         return (!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get()
182                 && !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()
183                 && !ENABLE_SPLIT_FROM_DESKTOP_TO_WORKSPACE.get())
184                 || !mController.isSplitSelectActive();
185     }
186 }
187