1 /* 2 * Copyright 2023 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 18 package com.android.quickstep.util; 19 20 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_LAUNCH; 21 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 22 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; 23 24 import android.app.ActivityTaskManager; 25 import android.content.Context; 26 import android.content.Intent; 27 28 import androidx.annotation.Nullable; 29 30 import com.android.launcher3.Launcher; 31 import com.android.launcher3.LauncherAppState; 32 import com.android.launcher3.LauncherSettings; 33 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; 34 import com.android.launcher3.icons.IconCache; 35 import com.android.launcher3.logging.StatsLogManager; 36 import com.android.launcher3.model.data.FolderInfo; 37 import com.android.launcher3.model.data.WorkspaceItemInfo; 38 import com.android.launcher3.util.ComponentKey; 39 import com.android.launcher3.util.SplitConfigurationOptions; 40 import com.android.quickstep.views.TaskView; 41 import com.android.systemui.shared.recents.model.Task; 42 43 import java.util.Arrays; 44 45 /** 46 * Mini controller class that handles app pair interactions: saving, modifying, deleting, etc. 47 */ 48 public class AppPairsController { 49 50 private static final int POINT_THREE_RATIO = 0; 51 private static final int POINT_FIVE_RATIO = 1; 52 private static final int POINT_SEVEN_RATIO = 2; 53 /** 54 * Used to calculate {@link #complement(int)} 55 */ 56 private static final int FULL_RATIO = 2; 57 58 private static final int LEFT_TOP = 0; 59 private static final int RIGHT_BOTTOM = 1 << 2; 60 61 // TODO (jeremysim b/274189428): Support saving different ratios in future. 62 public int DEFAULT_RATIO = POINT_FIVE_RATIO; 63 64 private final Context mContext; 65 private final SplitSelectStateController mSplitSelectStateController; 66 private final StatsLogManager mStatsLogManager; AppPairsController(Context context, SplitSelectStateController splitSelectStateController, StatsLogManager statsLogManager)67 public AppPairsController(Context context, 68 SplitSelectStateController splitSelectStateController, 69 StatsLogManager statsLogManager) { 70 mContext = context; 71 mSplitSelectStateController = splitSelectStateController; 72 mStatsLogManager = statsLogManager; 73 } 74 75 /** 76 * Creates a new app pair ItemInfo and adds it to the workspace 77 */ saveAppPair(TaskView taskView)78 public void saveAppPair(TaskView taskView) { 79 TaskView.TaskIdAttributeContainer[] attributes = taskView.getTaskIdAttributeContainers(); 80 WorkspaceItemInfo app1 = attributes[0].getItemInfo().clone(); 81 WorkspaceItemInfo app2 = attributes[1].getItemInfo().clone(); 82 app1.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 83 app2.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 84 app1.rank = DEFAULT_RATIO + LEFT_TOP; 85 app2.rank = complement(DEFAULT_RATIO) + RIGHT_BOTTOM; 86 FolderInfo newAppPair = FolderInfo.createAppPair(app1, app2); 87 // TODO (jeremysim b/274189428): Generate default title here. 88 newAppPair.title = "App pair 1"; 89 90 IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache(); 91 MODEL_EXECUTOR.execute(() -> { 92 newAppPair.contents.forEach(member -> { 93 member.title = ""; 94 member.bitmap = iconCache.getDefaultIcon(newAppPair.user); 95 iconCache.getTitleAndIcon(member, member.usingLowResIcon()); 96 }); 97 MAIN_EXECUTOR.execute(() -> { 98 LauncherAccessibilityDelegate delegate = 99 Launcher.getLauncher(mContext).getAccessibilityDelegate(); 100 if (delegate != null) { 101 delegate.addToWorkspace(newAppPair, true); 102 mStatsLogManager.logger().withItemInfo(newAppPair) 103 .log(StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_SAVE); 104 } 105 }); 106 }); 107 } 108 109 /** 110 * Launches an app pair by searching the RecentsModel for running instances of each app, and 111 * staging either those running instances or launching the apps as new Intents. 112 */ launchAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2)113 public void launchAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) { 114 ComponentKey app1Key = new ComponentKey(app1.getTargetComponent(), app1.user); 115 ComponentKey app2Key = new ComponentKey(app2.getTargetComponent(), app2.user); 116 mSplitSelectStateController.findLastActiveTasksAndRunCallback( 117 Arrays.asList(app1Key, app2Key), 118 foundTasks -> { 119 @Nullable Task foundTask1 = foundTasks.get(0); 120 Intent task1Intent; 121 int task1Id; 122 if (foundTask1 != null) { 123 task1Id = foundTask1.key.id; 124 task1Intent = null; 125 } else { 126 task1Id = ActivityTaskManager.INVALID_TASK_ID; 127 task1Intent = app1.intent; 128 } 129 130 mSplitSelectStateController.setInitialTaskSelect(task1Intent, 131 SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, 132 app1, 133 LAUNCHER_APP_PAIR_LAUNCH, 134 task1Id); 135 136 @Nullable Task foundTask2 = foundTasks.get(1); 137 if (foundTask2 != null) { 138 mSplitSelectStateController.setSecondTask(foundTask2); 139 } else { 140 mSplitSelectStateController.setSecondTask( 141 app2.intent, app2.user); 142 } 143 144 mSplitSelectStateController.launchSplitTasks(); 145 }); 146 } 147 148 /** 149 * Used to calculate the "opposite" side of the split ratio, so we can know how big the split 150 * apps are supposed to be. This math works because POINT_THREE_RATIO is internally represented 151 * by 0, POINT_FIVE_RATIO is represented by 1, and POINT_SEVEN_RATIO is represented by 2. There 152 * are no other supported ratios for now. 153 */ complement(int ratio1)154 private int complement(int ratio1) { 155 int ratio2 = FULL_RATIO - ratio1; 156 return ratio2; 157 } 158 } 159