2021-02-22 14:49:27 -08:00
|
|
|
/*
|
|
|
|
|
* Copyright 2021 The Android Open Source Project
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package com.android.launcher3.util;
|
|
|
|
|
|
2022-09-06 18:15:35 -07:00
|
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP;
|
|
|
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
|
|
|
|
|
|
2021-02-22 14:49:27 -08:00
|
|
|
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
|
|
|
|
|
2023-01-12 18:50:36 -08:00
|
|
|
import android.content.Intent;
|
2021-08-05 12:54:58 -07:00
|
|
|
import android.graphics.Rect;
|
2023-01-12 18:50:36 -08:00
|
|
|
import android.graphics.drawable.Drawable;
|
|
|
|
|
import android.view.View;
|
2021-08-05 12:54:58 -07:00
|
|
|
|
2021-02-22 14:49:27 -08:00
|
|
|
import androidx.annotation.IntDef;
|
|
|
|
|
|
2022-09-06 18:15:35 -07:00
|
|
|
import com.android.launcher3.logging.StatsLogManager;
|
2023-01-12 18:50:36 -08:00
|
|
|
import com.android.launcher3.model.data.ItemInfo;
|
2022-09-06 18:15:35 -07:00
|
|
|
|
2021-02-22 14:49:27 -08:00
|
|
|
import java.lang.annotation.Retention;
|
|
|
|
|
|
|
|
|
|
public final class SplitConfigurationOptions {
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////
|
|
|
|
|
// Taken from
|
|
|
|
|
// frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
|
|
|
|
|
/**
|
|
|
|
|
* Stage position isn't specified normally meaning to use what ever it is currently set to.
|
|
|
|
|
*/
|
|
|
|
|
public static final int STAGE_POSITION_UNDEFINED = -1;
|
|
|
|
|
/**
|
|
|
|
|
* Specifies that a stage is positioned at the top half of the screen if
|
|
|
|
|
* in portrait mode or at the left half of the screen if in landscape mode.
|
|
|
|
|
*/
|
|
|
|
|
public static final int STAGE_POSITION_TOP_OR_LEFT = 0;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Specifies that a stage is positioned at the bottom half of the screen if
|
|
|
|
|
* in portrait mode or at the right half of the screen if in landscape mode.
|
|
|
|
|
*/
|
|
|
|
|
public static final int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
|
|
|
|
|
|
|
|
|
|
@Retention(SOURCE)
|
|
|
|
|
@IntDef({STAGE_POSITION_UNDEFINED, STAGE_POSITION_TOP_OR_LEFT, STAGE_POSITION_BOTTOM_OR_RIGHT})
|
|
|
|
|
public @interface StagePosition {}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stage type isn't specified normally meaning to use what ever the default is.
|
|
|
|
|
* E.g. exit split-screen and launch the app in fullscreen.
|
|
|
|
|
*/
|
|
|
|
|
public static final int STAGE_TYPE_UNDEFINED = -1;
|
|
|
|
|
/**
|
|
|
|
|
* The main stage type.
|
|
|
|
|
*/
|
|
|
|
|
public static final int STAGE_TYPE_MAIN = 0;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The side stage type.
|
|
|
|
|
*/
|
|
|
|
|
public static final int STAGE_TYPE_SIDE = 1;
|
|
|
|
|
|
|
|
|
|
@IntDef({STAGE_TYPE_UNDEFINED, STAGE_TYPE_MAIN, STAGE_TYPE_SIDE})
|
|
|
|
|
public @interface StageType {}
|
|
|
|
|
///////////////////////////////////
|
|
|
|
|
|
2021-11-24 12:18:34 +08:00
|
|
|
/**
|
|
|
|
|
* Default split ratio for launching app pair from overview.
|
|
|
|
|
*/
|
|
|
|
|
public static final float DEFAULT_SPLIT_RATIO = 0.5f;
|
|
|
|
|
|
2021-02-22 14:49:27 -08:00
|
|
|
public static class SplitPositionOption {
|
2021-08-25 11:59:43 -07:00
|
|
|
public final int iconResId;
|
|
|
|
|
public final int textResId;
|
2021-02-22 14:49:27 -08:00
|
|
|
@StagePosition
|
2021-08-25 11:59:43 -07:00
|
|
|
public final int stagePosition;
|
2021-02-22 14:49:27 -08:00
|
|
|
|
|
|
|
|
@StageType
|
|
|
|
|
public final int mStageType;
|
|
|
|
|
|
|
|
|
|
public SplitPositionOption(int iconResId, int textResId, int stagePosition, int stageType) {
|
2021-08-25 11:59:43 -07:00
|
|
|
this.iconResId = iconResId;
|
|
|
|
|
this.textResId = textResId;
|
|
|
|
|
this.stagePosition = stagePosition;
|
2021-02-22 14:49:27 -08:00
|
|
|
mStageType = stageType;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-05 12:54:58 -07:00
|
|
|
|
2021-10-27 12:39:21 -07:00
|
|
|
/**
|
|
|
|
|
* NOTE: Engineers complained about too little ambiguity in the last survey, so there is a class
|
|
|
|
|
* with the same name/functionality in wm.shell.util (which launcher3 cannot be built against)
|
|
|
|
|
*
|
|
|
|
|
* If you make changes here, consider making the same changes there
|
2022-10-18 21:11:51 -07:00
|
|
|
* TODO(b/254378592): We really need to consolidate this
|
2021-10-27 12:39:21 -07:00
|
|
|
*/
|
2022-05-27 15:59:40 -07:00
|
|
|
public static class SplitBounds {
|
2021-08-25 11:59:43 -07:00
|
|
|
public final Rect leftTopBounds;
|
|
|
|
|
public final Rect rightBottomBounds;
|
2021-09-07 16:50:21 -07:00
|
|
|
/** This rect represents the actual gap between the two apps */
|
|
|
|
|
public final Rect visualDividerBounds;
|
2021-08-24 17:24:49 -07:00
|
|
|
// This class is orientation-agnostic, so we compute both for later use
|
2021-08-25 11:59:43 -07:00
|
|
|
public final float topTaskPercent;
|
|
|
|
|
public final float leftTaskPercent;
|
2021-11-17 08:09:49 +00:00
|
|
|
public final float dividerWidthPercent;
|
|
|
|
|
public final float dividerHeightPercent;
|
2021-09-13 18:00:52 -07:00
|
|
|
/**
|
|
|
|
|
* If {@code true}, that means at the time of creation of this object, the
|
|
|
|
|
* split-screened apps were vertically stacked. This is useful in scenarios like
|
|
|
|
|
* rotation where the bounds won't change, but this variable can indicate what orientation
|
|
|
|
|
* the bounds were originally in
|
|
|
|
|
*/
|
|
|
|
|
public final boolean appsStackedVertically;
|
2022-02-20 17:39:51 -08:00
|
|
|
/**
|
|
|
|
|
* If {@code true}, that means at the time of creation of this object, the phone was in
|
|
|
|
|
* seascape orientation. This is important on devices with insets, because they do not split
|
|
|
|
|
* evenly -- one of the insets must be slightly larger to account for the inset.
|
|
|
|
|
* From landscape, it is the leftTop task that expands slightly.
|
|
|
|
|
* From seascape, it is the rightBottom task that expands slightly.
|
|
|
|
|
*/
|
|
|
|
|
public final boolean initiatedFromSeascape;
|
2021-10-27 12:39:21 -07:00
|
|
|
public final int leftTopTaskId;
|
|
|
|
|
public final int rightBottomTaskId;
|
2021-08-05 12:54:58 -07:00
|
|
|
|
2022-05-27 15:59:40 -07:00
|
|
|
public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
|
2021-10-27 12:39:21 -07:00
|
|
|
int rightBottomTaskId) {
|
2021-08-25 11:59:43 -07:00
|
|
|
this.leftTopBounds = leftTopBounds;
|
|
|
|
|
this.rightBottomBounds = rightBottomBounds;
|
2021-10-27 12:39:21 -07:00
|
|
|
this.leftTopTaskId = leftTopTaskId;
|
|
|
|
|
this.rightBottomTaskId = rightBottomTaskId;
|
2021-09-07 16:50:21 -07:00
|
|
|
|
|
|
|
|
if (rightBottomBounds.top > leftTopBounds.top) {
|
|
|
|
|
// vertical apps, horizontal divider
|
|
|
|
|
this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
|
|
|
|
|
leftTopBounds.right, rightBottomBounds.top);
|
2021-09-13 18:00:52 -07:00
|
|
|
appsStackedVertically = true;
|
2022-02-20 17:39:51 -08:00
|
|
|
initiatedFromSeascape = false;
|
2021-09-07 16:50:21 -07:00
|
|
|
} else {
|
|
|
|
|
// horizontal apps, vertical divider
|
|
|
|
|
this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
|
|
|
|
|
rightBottomBounds.left, leftTopBounds.bottom);
|
2021-09-13 18:00:52 -07:00
|
|
|
appsStackedVertically = false;
|
2022-02-20 17:39:51 -08:00
|
|
|
// The following check is unreliable on devices without insets
|
|
|
|
|
// (initiatedFromSeascape will always be set to false.) This happens to be OK for
|
|
|
|
|
// all our current uses, but should be refactored.
|
|
|
|
|
// TODO: Create a more reliable check, or refactor how splitting works on devices
|
|
|
|
|
// with insets.
|
|
|
|
|
if (rightBottomBounds.width() > leftTopBounds.width()) {
|
|
|
|
|
initiatedFromSeascape = true;
|
|
|
|
|
} else {
|
|
|
|
|
initiatedFromSeascape = false;
|
|
|
|
|
}
|
2021-09-07 16:50:21 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-30 11:20:28 +00:00
|
|
|
float totalWidth = rightBottomBounds.right - leftTopBounds.left;
|
|
|
|
|
float totalHeight = rightBottomBounds.bottom - leftTopBounds.top;
|
|
|
|
|
leftTaskPercent = leftTopBounds.width() / totalWidth;
|
|
|
|
|
topTaskPercent = leftTopBounds.height() / totalHeight;
|
|
|
|
|
dividerWidthPercent = visualDividerBounds.width() / totalWidth;
|
|
|
|
|
dividerHeightPercent = visualDividerBounds.height() / totalHeight;
|
2021-08-05 12:54:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 15:59:40 -07:00
|
|
|
public static class SplitStageInfo {
|
2021-08-05 12:54:58 -07:00
|
|
|
public int taskId = -1;
|
|
|
|
|
@StagePosition
|
|
|
|
|
public int stagePosition = STAGE_POSITION_UNDEFINED;
|
|
|
|
|
@StageType
|
|
|
|
|
public int stageType = STAGE_TYPE_UNDEFINED;
|
|
|
|
|
}
|
2022-09-06 18:15:35 -07:00
|
|
|
|
|
|
|
|
public static StatsLogManager.EventEnum getLogEventForPosition(@StagePosition int position) {
|
|
|
|
|
return position == STAGE_POSITION_TOP_OR_LEFT
|
|
|
|
|
? LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP
|
|
|
|
|
: LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
|
|
|
|
|
}
|
2022-10-14 01:28:07 -07:00
|
|
|
|
|
|
|
|
public static @StagePosition int getOppositeStagePosition(@StagePosition int position) {
|
|
|
|
|
if (position == STAGE_POSITION_UNDEFINED) {
|
|
|
|
|
return position;
|
|
|
|
|
}
|
|
|
|
|
return position == STAGE_POSITION_TOP_OR_LEFT ? STAGE_POSITION_BOTTOM_OR_RIGHT
|
|
|
|
|
: STAGE_POSITION_TOP_OR_LEFT;
|
|
|
|
|
}
|
2023-01-12 18:50:36 -08:00
|
|
|
|
|
|
|
|
public static class SplitSelectSource {
|
|
|
|
|
|
|
|
|
|
/** Keep in sync w/ ActivityTaskManager#INVALID_TASK_ID (unreference-able) */
|
|
|
|
|
private static final int INVALID_TASK_ID = -1;
|
|
|
|
|
|
2023-03-29 22:03:34 -07:00
|
|
|
private View view;
|
|
|
|
|
private Drawable drawable;
|
2023-01-12 18:50:36 -08:00
|
|
|
public final Intent intent;
|
|
|
|
|
public final SplitPositionOption position;
|
|
|
|
|
public final ItemInfo itemInfo;
|
|
|
|
|
public final StatsLogManager.EventEnum splitEvent;
|
|
|
|
|
/** Represents the taskId of the first app to start in split screen */
|
|
|
|
|
public int alreadyRunningTaskId = INVALID_TASK_ID;
|
|
|
|
|
/**
|
|
|
|
|
* If {@code true}, animates the view represented by {@link #alreadyRunningTaskId} into the
|
|
|
|
|
* split placeholder view
|
|
|
|
|
*/
|
|
|
|
|
public boolean animateCurrentTaskDismissal;
|
|
|
|
|
|
|
|
|
|
public SplitSelectSource(View view, Drawable drawable, Intent intent,
|
|
|
|
|
SplitPositionOption position, ItemInfo itemInfo,
|
|
|
|
|
StatsLogManager.EventEnum splitEvent) {
|
|
|
|
|
this.view = view;
|
|
|
|
|
this.drawable = drawable;
|
|
|
|
|
this.intent = intent;
|
|
|
|
|
this.position = position;
|
|
|
|
|
this.itemInfo = itemInfo;
|
|
|
|
|
this.splitEvent = splitEvent;
|
|
|
|
|
}
|
2023-03-29 22:03:34 -07:00
|
|
|
|
|
|
|
|
public Drawable getDrawable() {
|
|
|
|
|
return drawable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public View getView() {
|
|
|
|
|
return view;
|
|
|
|
|
}
|
2023-01-12 18:50:36 -08:00
|
|
|
}
|
2021-02-22 14:49:27 -08:00
|
|
|
}
|