• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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.launcher3.util;
18 
19 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP;
20 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
21 
22 import static java.lang.annotation.RetentionPolicy.SOURCE;
23 
24 import android.content.Intent;
25 import android.graphics.Rect;
26 import android.graphics.drawable.Drawable;
27 import android.view.View;
28 
29 import androidx.annotation.IntDef;
30 
31 import com.android.launcher3.logging.StatsLogManager;
32 import com.android.launcher3.model.data.ItemInfo;
33 
34 import java.lang.annotation.Retention;
35 
36 public final class SplitConfigurationOptions {
37 
38     ///////////////////////////////////
39     // Taken from
40     // frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
41     /**
42      * Stage position isn't specified normally meaning to use what ever it is currently set to.
43      */
44     public static final int STAGE_POSITION_UNDEFINED = -1;
45     /**
46      * Specifies that a stage is positioned at the top half of the screen if
47      * in portrait mode or at the left half of the screen if in landscape mode.
48      */
49     public static final int STAGE_POSITION_TOP_OR_LEFT = 0;
50 
51     /**
52      * Specifies that a stage is positioned at the bottom half of the screen if
53      * in portrait mode or at the right half of the screen if in landscape mode.
54      */
55     public static final int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
56 
57     @Retention(SOURCE)
58     @IntDef({STAGE_POSITION_UNDEFINED, STAGE_POSITION_TOP_OR_LEFT, STAGE_POSITION_BOTTOM_OR_RIGHT})
59     public @interface StagePosition {}
60 
61     /**
62      * Stage type isn't specified normally meaning to use what ever the default is.
63      * E.g. exit split-screen and launch the app in fullscreen.
64      */
65     public static final int STAGE_TYPE_UNDEFINED = -1;
66     /**
67      * The main stage type.
68      */
69     public static final int STAGE_TYPE_MAIN = 0;
70 
71     /**
72      * The side stage type.
73      */
74     public static final int STAGE_TYPE_SIDE = 1;
75 
76     @IntDef({STAGE_TYPE_UNDEFINED, STAGE_TYPE_MAIN, STAGE_TYPE_SIDE})
77     public @interface StageType {}
78     ///////////////////////////////////
79 
80     /**
81      * Default split ratio for launching app pair from overview.
82      */
83     public static final float DEFAULT_SPLIT_RATIO = 0.5f;
84 
85     public static class SplitPositionOption {
86         public final int iconResId;
87         public final int textResId;
88         @StagePosition
89         public final int stagePosition;
90 
91         @StageType
92         public final int mStageType;
93 
SplitPositionOption(int iconResId, int textResId, int stagePosition, int stageType)94         public SplitPositionOption(int iconResId, int textResId, int stagePosition, int stageType) {
95             this.iconResId = iconResId;
96             this.textResId = textResId;
97             this.stagePosition = stagePosition;
98             mStageType = stageType;
99         }
100     }
101 
102     /**
103      * NOTE: Engineers complained about too little ambiguity in the last survey, so there is a class
104      * with the same name/functionality in wm.shell.util (which launcher3 cannot be built against)
105      *
106      * If you make changes here, consider making the same changes there
107      * TODO(b/254378592): We really need to consolidate this
108      */
109     public static class SplitBounds {
110         public final Rect leftTopBounds;
111         public final Rect rightBottomBounds;
112         /** This rect represents the actual gap between the two apps */
113         public final Rect visualDividerBounds;
114         // This class is orientation-agnostic, so we compute both for later use
115         public final float topTaskPercent;
116         public final float leftTaskPercent;
117         public final float dividerWidthPercent;
118         public final float dividerHeightPercent;
119         /**
120          * If {@code true}, that means at the time of creation of this object, the
121          * split-screened apps were vertically stacked. This is useful in scenarios like
122          * rotation where the bounds won't change, but this variable can indicate what orientation
123          * the bounds were originally in
124          */
125         public final boolean appsStackedVertically;
126         /**
127          * If {@code true}, that means at the time of creation of this object, the phone was in
128          * seascape orientation. This is important on devices with insets, because they do not split
129          * evenly -- one of the insets must be slightly larger to account for the inset.
130          * From landscape, it is the leftTop task that expands slightly.
131          * From seascape, it is the rightBottom task that expands slightly.
132          */
133         public final boolean initiatedFromSeascape;
134         public final int leftTopTaskId;
135         public final int rightBottomTaskId;
136 
SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId, int rightBottomTaskId)137         public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
138                 int rightBottomTaskId) {
139             this.leftTopBounds = leftTopBounds;
140             this.rightBottomBounds = rightBottomBounds;
141             this.leftTopTaskId = leftTopTaskId;
142             this.rightBottomTaskId = rightBottomTaskId;
143 
144             if (rightBottomBounds.top > leftTopBounds.top) {
145                 // vertical apps, horizontal divider
146                 this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
147                         leftTopBounds.right, rightBottomBounds.top);
148                 appsStackedVertically = true;
149                 initiatedFromSeascape = false;
150             } else {
151                 // horizontal apps, vertical divider
152                 this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
153                         rightBottomBounds.left, leftTopBounds.bottom);
154                 appsStackedVertically = false;
155                 // The following check is unreliable on devices without insets
156                 // (initiatedFromSeascape will always be set to false.) This happens to be OK for
157                 // all our current uses, but should be refactored.
158                 // TODO: Create a more reliable check, or refactor how splitting works on devices
159                 //  with insets.
160                 if (rightBottomBounds.width() > leftTopBounds.width()) {
161                     initiatedFromSeascape = true;
162                 } else {
163                     initiatedFromSeascape = false;
164                 }
165             }
166 
167             float totalWidth = rightBottomBounds.right - leftTopBounds.left;
168             float totalHeight = rightBottomBounds.bottom - leftTopBounds.top;
169             leftTaskPercent = leftTopBounds.width() / totalWidth;
170             topTaskPercent = leftTopBounds.height() / totalHeight;
171             dividerWidthPercent = visualDividerBounds.width() / totalWidth;
172             dividerHeightPercent = visualDividerBounds.height() / totalHeight;
173         }
174     }
175 
176     public static class SplitStageInfo {
177         public int taskId = -1;
178         @StagePosition
179         public int stagePosition = STAGE_POSITION_UNDEFINED;
180         @StageType
181         public int stageType = STAGE_TYPE_UNDEFINED;
182     }
183 
getLogEventForPosition(@tagePosition int position)184     public static StatsLogManager.EventEnum getLogEventForPosition(@StagePosition int position) {
185         return position == STAGE_POSITION_TOP_OR_LEFT
186                 ? LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP
187                 : LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
188     }
189 
getOppositeStagePosition(@tagePosition int position)190     public static @StagePosition int getOppositeStagePosition(@StagePosition int position) {
191         if (position == STAGE_POSITION_UNDEFINED) {
192             return position;
193         }
194         return position == STAGE_POSITION_TOP_OR_LEFT ? STAGE_POSITION_BOTTOM_OR_RIGHT
195                 : STAGE_POSITION_TOP_OR_LEFT;
196     }
197 
198     public static class SplitSelectSource {
199 
200         /** Keep in sync w/ ActivityTaskManager#INVALID_TASK_ID (unreference-able) */
201         private static final int INVALID_TASK_ID = -1;
202 
203         private View view;
204         private Drawable drawable;
205         public final Intent intent;
206         public final SplitPositionOption position;
207         public final ItemInfo itemInfo;
208         public final StatsLogManager.EventEnum splitEvent;
209         /** Represents the taskId of the first app to start in split screen */
210         public int alreadyRunningTaskId = INVALID_TASK_ID;
211         /**
212          * If {@code true}, animates the view represented by {@link #alreadyRunningTaskId} into the
213          * split placeholder view
214          */
215         public boolean animateCurrentTaskDismissal;
216 
SplitSelectSource(View view, Drawable drawable, Intent intent, SplitPositionOption position, ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent)217         public SplitSelectSource(View view, Drawable drawable, Intent intent,
218                 SplitPositionOption position, ItemInfo itemInfo,
219                 StatsLogManager.EventEnum splitEvent) {
220             this.view = view;
221             this.drawable = drawable;
222             this.intent = intent;
223             this.position = position;
224             this.itemInfo = itemInfo;
225             this.splitEvent = splitEvent;
226         }
227 
getDrawable()228         public Drawable getDrawable() {
229             return drawable;
230         }
231 
getView()232         public View getView() {
233             return view;
234         }
235     }
236 }
237