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.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE; 21 22 import android.animation.Animator; 23 import android.animation.AnimatorListenerAdapter; 24 import android.content.Intent; 25 import android.graphics.Rect; 26 import android.graphics.RectF; 27 import android.os.UserHandle; 28 import android.view.View; 29 30 import com.android.launcher3.DeviceProfile; 31 import com.android.launcher3.Launcher; 32 import com.android.launcher3.R; 33 import com.android.launcher3.anim.PendingAnimation; 34 import com.android.launcher3.icons.BitmapInfo; 35 import com.android.launcher3.model.data.WorkspaceItemInfo; 36 import com.android.quickstep.views.FloatingTaskView; 37 import com.android.quickstep.views.RecentsView; 38 import com.android.systemui.shared.system.InteractionJankMonitorWrapper; 39 40 /** Handles when the stage split lands on the home screen. */ 41 public class SplitToWorkspaceController { 42 43 private final Launcher mLauncher; 44 private final DeviceProfile mDP; 45 private final SplitSelectStateController mController; 46 47 private final int mHalfDividerSize; 48 SplitToWorkspaceController(Launcher launcher, SplitSelectStateController controller)49 public SplitToWorkspaceController(Launcher launcher, SplitSelectStateController controller) { 50 mLauncher = launcher; 51 mDP = mLauncher.getDeviceProfile(); 52 mController = controller; 53 54 mHalfDividerSize = mLauncher.getResources().getDimensionPixelSize( 55 R.dimen.multi_window_task_divider_size) / 2; 56 } 57 58 /** 59 * Handles second app selection from stage split. If the item can't be opened in split or 60 * it's not in stage split state, we pass it onto Launcher's default item click handler. 61 */ handleSecondAppSelectionForSplit(View view)62 public boolean handleSecondAppSelectionForSplit(View view) { 63 if ((!ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS.get() 64 && !ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) 65 || !mController.isSplitSelectActive()) { 66 return false; 67 } 68 Object tag = view.getTag(); 69 Intent intent; 70 UserHandle user; 71 BitmapInfo bitmapInfo; 72 if (tag instanceof WorkspaceItemInfo) { 73 final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) tag; 74 intent = workspaceItemInfo.intent; 75 user = workspaceItemInfo.user; 76 bitmapInfo = workspaceItemInfo.bitmap; 77 } else if (tag instanceof com.android.launcher3.model.data.AppInfo) { 78 final com.android.launcher3.model.data.AppInfo appInfo = 79 (com.android.launcher3.model.data.AppInfo) tag; 80 intent = appInfo.intent; 81 user = appInfo.user; 82 bitmapInfo = appInfo.bitmap; 83 } else { 84 return false; 85 } 86 87 mController.setSecondTask(intent, user); 88 89 boolean isTablet = mLauncher.getDeviceProfile().isTablet; 90 SplitAnimationTimings timings = AnimUtils.getDeviceSplitToConfirmTimings(isTablet); 91 PendingAnimation pendingAnimation = new PendingAnimation(timings.getDuration()); 92 93 Rect firstTaskStartingBounds = new Rect(); 94 Rect firstTaskEndingBounds = new Rect(); 95 RectF secondTaskStartingBounds = new RectF(); 96 Rect secondTaskEndingBounds = new Rect(); 97 98 RecentsView recentsView = mLauncher.getOverviewPanel(); 99 recentsView.getPagedOrientationHandler().getFinalSplitPlaceholderBounds(mHalfDividerSize, 100 mDP, mController.getActiveSplitStagePosition(), firstTaskEndingBounds, 101 secondTaskEndingBounds); 102 103 FloatingTaskView firstFloatingTaskView = mController.getFirstFloatingTaskView(); 104 firstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds); 105 firstFloatingTaskView.addConfirmAnimation(pendingAnimation, 106 new RectF(firstTaskStartingBounds), firstTaskEndingBounds, 107 false /* fadeWithThumbnail */, true /* isStagedTask */); 108 109 FloatingTaskView secondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mLauncher, 110 view, null /* thumbnail */, bitmapInfo.newIcon(mLauncher), 111 secondTaskStartingBounds); 112 secondFloatingTaskView.setAlpha(1); 113 secondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds, 114 secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */); 115 116 pendingAnimation.addListener(new AnimatorListenerAdapter() { 117 private boolean mIsCancelled = false; 118 119 @Override 120 public void onAnimationCancel(Animator animation) { 121 mIsCancelled = true; 122 cleanUp(); 123 } 124 125 @Override 126 public void onAnimationEnd(Animator animation) { 127 if (!mIsCancelled) { 128 mController.launchSplitTasks(aBoolean -> cleanUp()); 129 InteractionJankMonitorWrapper.end( 130 InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER); 131 } 132 } 133 134 private void cleanUp() { 135 mLauncher.getDragLayer().removeView(firstFloatingTaskView); 136 mLauncher.getDragLayer().removeView(secondFloatingTaskView); 137 mController.resetState(); 138 } 139 }); 140 pendingAnimation.buildAnim().start(); 141 return true; 142 } 143 } 144