1 /* 2 * Copyright (C) 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 package com.android.wm.shell.common.split; 18 19 import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES; 20 import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES; 21 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90; 22 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10; 23 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_10_45_45; 24 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_45_45_10; 25 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; 26 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; 27 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; 28 29 import android.app.ActivityManager; 30 import android.content.res.Configuration; 31 import android.content.res.Resources; 32 import android.graphics.Rect; 33 34 import com.android.internal.util.ArrayUtils; 35 import com.android.wm.shell.Flags; 36 import com.android.wm.shell.ShellTaskOrganizer; 37 import com.android.wm.shell.shared.split.SplitScreenConstants; 38 39 /** Helper utility class for split screen components to use. */ 40 public class SplitScreenUtils { 41 private static final int LARGE_SCREEN_MIN_EDGE_DP = 600; 42 43 /** Reverse the split position. */ 44 @SplitScreenConstants.SplitPosition reverseSplitPosition(@plitScreenConstants.SplitPosition int position)45 public static int reverseSplitPosition(@SplitScreenConstants.SplitPosition int position) { 46 switch (position) { 47 case SPLIT_POSITION_TOP_OR_LEFT: 48 return SPLIT_POSITION_BOTTOM_OR_RIGHT; 49 case SPLIT_POSITION_BOTTOM_OR_RIGHT: 50 return SPLIT_POSITION_TOP_OR_LEFT; 51 case SPLIT_POSITION_UNDEFINED: 52 default: 53 return SPLIT_POSITION_UNDEFINED; 54 } 55 } 56 57 /** Returns true if the task is valid for split screen. */ isValidToSplit(ActivityManager.RunningTaskInfo taskInfo)58 public static boolean isValidToSplit(ActivityManager.RunningTaskInfo taskInfo) { 59 return taskInfo != null && taskInfo.supportsMultiWindow 60 && ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType()) 61 && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode()); 62 } 63 64 /** Retrieve user id from a taskId */ getUserId(int taskId, ShellTaskOrganizer taskOrganizer)65 public static int getUserId(int taskId, ShellTaskOrganizer taskOrganizer) { 66 final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId); 67 return taskInfo != null ? taskInfo.userId : -1; 68 } 69 70 /** Generates a common log message for split screen failures */ splitFailureMessage(String caller, String reason)71 public static String splitFailureMessage(String caller, String reason) { 72 return "(" + caller + ") Splitscreen aborted: " + reason; 73 } 74 75 /** 76 * Returns whether left/right split is allowed in portrait. 77 */ allowLeftRightSplitInPortrait(Resources res)78 public static boolean allowLeftRightSplitInPortrait(Resources res) { 79 return res.getBoolean(com.android.internal.R.bool.config_leftRightSplitInPortrait); 80 } 81 82 /** 83 * Returns whether left/right split is supported in the given configuration. 84 */ isLeftRightSplit(boolean allowLeftRightSplitInPortrait, Configuration config)85 public static boolean isLeftRightSplit(boolean allowLeftRightSplitInPortrait, 86 Configuration config) { 87 // Compare the max bounds sizes as on near-square devices, the insets may result in a 88 // configuration in the other orientation 89 final Rect maxBounds = config.windowConfiguration.getMaxBounds(); 90 final boolean isLandscape = maxBounds.width() >= maxBounds.height(); 91 return isLeftRightSplit(allowLeftRightSplitInPortrait, isLargeScreen(config), isLandscape); 92 } 93 94 /** 95 * Returns whether left/right split is supported in the given configuration state. This method 96 * is useful for cases where we need to calculate this given last saved state. 97 */ isLeftRightSplit(boolean allowLeftRightSplitInPortrait, boolean isLargeScreen, boolean isLandscape)98 public static boolean isLeftRightSplit(boolean allowLeftRightSplitInPortrait, 99 boolean isLargeScreen, boolean isLandscape) { 100 if (allowLeftRightSplitInPortrait && isLargeScreen) { 101 return !isLandscape; 102 } else { 103 return isLandscape; 104 } 105 } 106 107 /** 108 * Returns whether the current config is a large screen (tablet or unfolded foldable) 109 */ isLargeScreen(Configuration config)110 public static boolean isLargeScreen(Configuration config) { 111 return config.smallestScreenWidthDp >= LARGE_SCREEN_MIN_EDGE_DP; 112 } 113 114 /** 115 * Convenience function for {@link #isLargeScreen(Configuration)}. 116 */ isLargeScreen(Resources res)117 public static boolean isLargeScreen(Resources res) { 118 return isLargeScreen(res.getConfiguration()); 119 } 120 121 /** 122 * Returns whether the current device is a foldable 123 */ isFoldable(Resources res)124 public static boolean isFoldable(Resources res) { 125 return res.getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0; 126 } 127 128 /** 129 * Returns whether we should allow split ratios to go offscreen or not. If the device is a phone 130 * or a foldable (either screen), we allow it. 131 */ allowOffscreenRatios(Resources res)132 public static boolean allowOffscreenRatios(Resources res) { 133 return Flags.enableFlexibleTwoAppSplit() && (!isLargeScreen(res) || isFoldable(res)); 134 } 135 136 /** 137 * Within a particular split layout, we label the stages numerically: 0, 1, 2... from left to 138 * right (or top to bottom). This function takes in a stage index (0th, 1st, 2nd...) and a 139 * PersistentSnapPosition and returns if that particular stage is offscreen in that layout. 140 */ isPartiallyOffscreen(int stageIndex, @SplitScreenConstants.PersistentSnapPosition int snapPosition)141 public static boolean isPartiallyOffscreen(int stageIndex, 142 @SplitScreenConstants.PersistentSnapPosition int snapPosition) { 143 switch(snapPosition) { 144 case SNAP_TO_2_10_90: 145 case SNAP_TO_3_10_45_45: 146 return stageIndex == 0; 147 case SNAP_TO_2_90_10: 148 return stageIndex == 1; 149 case SNAP_TO_3_45_45_10: 150 return stageIndex == 2; 151 default: 152 return false; 153 } 154 } 155 } 156