• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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