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.desktopmode; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 21 22 import static com.android.internal.policy.SystemBarUtils.getDesktopViewAppHeaderHeightPx; 23 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR; 24 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_LEFT_INDICATOR; 25 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR; 26 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR; 27 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; 28 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; 29 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; 30 import static com.android.wm.shell.shared.ShellSharedConstants.SMALL_TABLET_MAX_EDGE_DP; 31 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 import android.app.ActivityManager; 35 import android.content.Context; 36 import android.graphics.PointF; 37 import android.graphics.Rect; 38 import android.graphics.Region; 39 import android.util.Pair; 40 import android.view.Display; 41 import android.view.SurfaceControl; 42 import android.window.DesktopModeFlags; 43 44 import androidx.annotation.VisibleForTesting; 45 46 import com.android.internal.policy.SystemBarUtils; 47 import com.android.wm.shell.R; 48 import com.android.wm.shell.RootTaskDisplayAreaOrganizer; 49 import com.android.wm.shell.common.DisplayController; 50 import com.android.wm.shell.common.DisplayLayout; 51 import com.android.wm.shell.common.ShellExecutor; 52 import com.android.wm.shell.common.SyncTransactionQueue; 53 import com.android.wm.shell.common.split.SplitScreenUtils; 54 import com.android.wm.shell.shared.annotations.ShellDesktopThread; 55 import com.android.wm.shell.shared.annotations.ShellMainThread; 56 import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper; 57 import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider; 58 import com.android.wm.shell.windowdecor.tiling.SnapEventHandler; 59 60 import java.util.ArrayList; 61 import java.util.Collections; 62 import java.util.List; 63 64 /** 65 * Animated visual indicator for Desktop Mode windowing transitions. 66 */ 67 public class DesktopModeVisualIndicator { 68 public enum IndicatorType { 69 /** To be used when we don't want to indicate any transition */ 70 NO_INDICATOR, 71 /** Indicates impending transition into desktop mode */ 72 TO_DESKTOP_INDICATOR, 73 /** Indicates impending transition into fullscreen */ 74 TO_FULLSCREEN_INDICATOR, 75 /** Indicates impending transition into split select on the left side */ 76 TO_SPLIT_LEFT_INDICATOR, 77 /** Indicates impending transition into split select on the right side */ 78 TO_SPLIT_RIGHT_INDICATOR, 79 /** Indicates impending transition into bubble on the left side */ 80 TO_BUBBLE_LEFT_INDICATOR, 81 /** Indicates impending transition into bubble on the right side */ 82 TO_BUBBLE_RIGHT_INDICATOR 83 } 84 85 /** 86 * The conditions surrounding the drag event that led to the indicator's creation. 87 */ 88 public enum DragStartState { 89 /** The indicator is resulting from a freeform task drag. */ 90 FROM_FREEFORM, 91 /** The indicator is resulting from a split screen task drag */ 92 FROM_SPLIT, 93 /** The indicator is resulting from a fullscreen task drag */ 94 FROM_FULLSCREEN, 95 /** The indicator is resulting from an Intent generated during a drag-and-drop event */ 96 DRAGGED_INTENT; 97 98 /** 99 * Get the {@link DragStartState} of a drag event based on the windowing mode of the task. 100 * Note that DRAGGED_INTENT will be specified by the caller if needed and not returned 101 * here. 102 */ getDragStartState( ActivityManager.RunningTaskInfo taskInfo )103 public static DesktopModeVisualIndicator.DragStartState getDragStartState( 104 ActivityManager.RunningTaskInfo taskInfo 105 ) { 106 if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { 107 return FROM_FULLSCREEN; 108 } else if (taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { 109 return FROM_SPLIT; 110 } else if (taskInfo.isFreeform()) { 111 return FROM_FREEFORM; 112 } else { 113 return null; 114 } 115 } 116 isDragToDesktopStartState(DragStartState startState)117 private static boolean isDragToDesktopStartState(DragStartState startState) { 118 return startState == FROM_FULLSCREEN || startState == FROM_SPLIT; 119 } 120 } 121 122 private final VisualIndicatorViewContainer mVisualIndicatorViewContainer; 123 124 private final Context mContext; 125 private final DisplayController mDisplayController; 126 private final ActivityManager.RunningTaskInfo mTaskInfo; 127 128 private IndicatorType mCurrentType; 129 private final DragStartState mDragStartState; 130 private final SnapEventHandler mSnapEventHandler; 131 132 private final boolean mUseSmallTabletRegions; 133 /** 134 * Ordered list of {@link Rect} zones that we will match an input coordinate against. 135 * List is traversed from first to last element. The first rect that contains the input event 136 * will be used and the matching {@link IndicatorType} is returned. 137 * Empty rect matches all. 138 */ 139 private final List<Pair<Rect, IndicatorType>> mSortedRegions; 140 DesktopModeVisualIndicator(@hellDesktopThread ShellExecutor desktopExecutor, @ShellMainThread ShellExecutor mainExecutor, SyncTransactionQueue syncQueue, ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController, Context context, SurfaceControl taskSurface, RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer, DragStartState dragStartState, @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider, SnapEventHandler snapEventHandler)141 public DesktopModeVisualIndicator(@ShellDesktopThread ShellExecutor desktopExecutor, 142 @ShellMainThread ShellExecutor mainExecutor, 143 SyncTransactionQueue syncQueue, 144 ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController, 145 Context context, SurfaceControl taskSurface, 146 RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer, 147 DragStartState dragStartState, 148 @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider, 149 SnapEventHandler snapEventHandler) { 150 this(desktopExecutor, mainExecutor, syncQueue, taskInfo, displayController, context, 151 taskSurface, taskDisplayAreaOrganizer, dragStartState, bubbleBoundsProvider, 152 snapEventHandler, useSmallTabletRegions(displayController, taskInfo), 153 isLeftRightSplit(context, displayController, taskInfo)); 154 } 155 156 @VisibleForTesting DesktopModeVisualIndicator(@hellDesktopThread ShellExecutor desktopExecutor, @ShellMainThread ShellExecutor mainExecutor, SyncTransactionQueue syncQueue, ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController, Context context, SurfaceControl taskSurface, RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer, DragStartState dragStartState, @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider, SnapEventHandler snapEventHandler, boolean useSmallTabletRegions, boolean isLeftRightSplit)157 DesktopModeVisualIndicator(@ShellDesktopThread ShellExecutor desktopExecutor, 158 @ShellMainThread ShellExecutor mainExecutor, 159 SyncTransactionQueue syncQueue, 160 ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController, 161 Context context, SurfaceControl taskSurface, 162 RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer, 163 DragStartState dragStartState, 164 @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider, 165 SnapEventHandler snapEventHandler, 166 boolean useSmallTabletRegions, 167 boolean isLeftRightSplit) { 168 SurfaceControl.Builder builder = new SurfaceControl.Builder(); 169 if (!DragStartState.isDragToDesktopStartState(dragStartState) 170 || !DesktopModeFlags.ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX.isTrue()) { 171 // In the DragToDesktop transition we attach the indicator to the transition root once 172 // that is available - for all other cases attach the indicator here. 173 taskDisplayAreaOrganizer.attachToDisplayArea(taskInfo.displayId, builder); 174 } 175 mVisualIndicatorViewContainer = new VisualIndicatorViewContainer( 176 DesktopModeFlags.ENABLE_DESKTOP_INDICATOR_IN_SEPARATE_THREAD_BUGFIX.isTrue() 177 ? desktopExecutor : mainExecutor, 178 mainExecutor, builder, syncQueue, bubbleBoundsProvider, snapEventHandler); 179 mTaskInfo = taskInfo; 180 mDisplayController = displayController; 181 mContext = context; 182 mCurrentType = NO_INDICATOR; 183 mDragStartState = dragStartState; 184 mSnapEventHandler = snapEventHandler; 185 Display display = mDisplayController.getDisplay(mTaskInfo.displayId); 186 DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mTaskInfo.displayId); 187 mVisualIndicatorViewContainer.createView( 188 mContext, 189 display, 190 displayLayout, 191 mTaskInfo, 192 taskSurface 193 ); 194 195 mUseSmallTabletRegions = useSmallTabletRegions; 196 197 if (useSmallTabletRegions) { 198 mSortedRegions = initSmallTabletRegions(displayLayout, isLeftRightSplit); 199 } else { 200 // TODO(b/401596837): add support for initializing regions for large tablets 201 mSortedRegions = Collections.emptyList(); 202 } 203 } 204 useSmallTabletRegions(DisplayController displayController, ActivityManager.RunningTaskInfo taskInfo)205 private static boolean useSmallTabletRegions(DisplayController displayController, 206 ActivityManager.RunningTaskInfo taskInfo) { 207 if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) { 208 // Small tablet regions get enabled with bubbles feature 209 return false; 210 } 211 Display display = displayController.getDisplay(taskInfo.displayId); 212 DisplayLayout displayLayout = displayController.getDisplayLayout(taskInfo.displayId); 213 if (displayLayout == null) return false; 214 return displayLayout.pxToDp(display.getMaximumSizeDimension()) < SMALL_TABLET_MAX_EDGE_DP; 215 } 216 isLeftRightSplit(Context context, DisplayController displayController, ActivityManager.RunningTaskInfo taskInfo)217 private static boolean isLeftRightSplit(Context context, DisplayController displayController, 218 ActivityManager.RunningTaskInfo taskInfo) { 219 DisplayLayout layout = displayController.getDisplayLayout(taskInfo.displayId); 220 boolean landscape = layout != null && layout.isLandscape(); 221 boolean leftRightSplitInPortrait = SplitScreenUtils.allowLeftRightSplitInPortrait( 222 context.getResources()); 223 return SplitScreenUtils.isLeftRightSplit(leftRightSplitInPortrait, 224 /* isLargeScreen= */ true, landscape); 225 } 226 227 /** Start the fade out animation, running the callback on the main thread once it is done. */ fadeOutIndicator( @onNull Runnable callback)228 public void fadeOutIndicator( 229 @NonNull Runnable callback) { 230 mVisualIndicatorViewContainer.fadeOutIndicator( 231 mDisplayController.getDisplayLayout(mTaskInfo.displayId), mCurrentType, callback, 232 mTaskInfo.displayId, mSnapEventHandler 233 ); 234 } 235 236 /** Release the visual indicator view and its viewhost. */ releaseVisualIndicator()237 public void releaseVisualIndicator() { 238 mVisualIndicatorViewContainer.releaseVisualIndicator(); 239 } 240 241 /** Reparent the visual indicator to {@code newParent}. */ reparentLeash(SurfaceControl.Transaction t, SurfaceControl newParent)242 void reparentLeash(SurfaceControl.Transaction t, SurfaceControl newParent) { 243 mVisualIndicatorViewContainer.reparentLeash(t, newParent); 244 } 245 246 /** Start the fade-in animation. */ fadeInIndicator()247 void fadeInIndicator() { 248 if (mCurrentType == NO_INDICATOR) return; 249 mVisualIndicatorViewContainer.fadeInIndicator( 250 mDisplayController.getDisplayLayout(mTaskInfo.displayId), mCurrentType, 251 mTaskInfo.displayId); 252 } 253 254 /** 255 * Based on the coordinates of the current drag event, determine which indicator type we should 256 * display, including no visible indicator. 257 */ 258 @NonNull updateIndicatorType(PointF inputCoordinates)259 IndicatorType updateIndicatorType(PointF inputCoordinates) { 260 final IndicatorType result; 261 if (mUseSmallTabletRegions) { 262 result = getIndicatorSmallTablet(inputCoordinates); 263 } else { 264 result = getIndicatorLargeTablet(inputCoordinates); 265 } 266 if (mDragStartState != DragStartState.DRAGGED_INTENT) { 267 mVisualIndicatorViewContainer.transitionIndicator( 268 mTaskInfo, mDisplayController, mCurrentType, result 269 ); 270 mCurrentType = result; 271 } 272 return result; 273 } 274 275 @NonNull getIndicatorLargeTablet(PointF inputCoordinates)276 private IndicatorType getIndicatorLargeTablet(PointF inputCoordinates) { 277 // TODO(b/401596837): cache the regions to avoid recalculating on each motion event 278 final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId); 279 // Perform a quick check first: any input off the left edge of the display should be split 280 // left, and split right for the right edge. This is universal across all drag event types. 281 if (inputCoordinates.x < 0) return TO_SPLIT_LEFT_INDICATOR; 282 if (inputCoordinates.x > layout.width()) return TO_SPLIT_RIGHT_INDICATOR; 283 // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone. 284 // In drags not originating on a freeform caption, we should default to a TO_DESKTOP 285 // indicator. 286 IndicatorType result = mDragStartState == DragStartState.FROM_FREEFORM 287 ? NO_INDICATOR 288 : TO_DESKTOP_INDICATOR; 289 final int transitionAreaWidth = mContext.getResources().getDimensionPixelSize( 290 com.android.wm.shell.R.dimen.desktop_mode_transition_region_thickness); 291 // Because drags in freeform use task position for indicator calculation, we need to 292 // account for the possibility of the task going off the top of the screen by captionHeight 293 final int captionHeight = getDesktopViewAppHeaderHeightPx(mContext); 294 final Region fullscreenRegion = calculateFullscreenRegion(layout, captionHeight); 295 final Rect splitLeftRegion = calculateSplitLeftRegion(layout, transitionAreaWidth, 296 captionHeight); 297 final Rect splitRightRegion = calculateSplitRightRegion(layout, transitionAreaWidth, 298 captionHeight); 299 final int x = (int) inputCoordinates.x; 300 final int y = (int) inputCoordinates.y; 301 if (fullscreenRegion.contains(x, y)) { 302 result = TO_FULLSCREEN_INDICATOR; 303 } 304 if (splitLeftRegion.contains(x, y)) { 305 result = IndicatorType.TO_SPLIT_LEFT_INDICATOR; 306 } 307 if (splitRightRegion.contains(x, y)) { 308 result = IndicatorType.TO_SPLIT_RIGHT_INDICATOR; 309 } 310 if (BubbleAnythingFlagHelper.enableBubbleToFullscreen() 311 && mDragStartState == DragStartState.FROM_FULLSCREEN) { 312 if (calculateBubbleLeftRegion(layout).contains(x, y)) { 313 result = IndicatorType.TO_BUBBLE_LEFT_INDICATOR; 314 } else if (calculateBubbleRightRegion(layout).contains(x, y)) { 315 result = TO_BUBBLE_RIGHT_INDICATOR; 316 } 317 } 318 return result; 319 } 320 321 @NonNull getIndicatorSmallTablet(PointF inputCoordinates)322 private IndicatorType getIndicatorSmallTablet(PointF inputCoordinates) { 323 for (Pair<Rect, IndicatorType> region : mSortedRegions) { 324 if (region.first.isEmpty()) return region.second; // empty rect matches all 325 if (region.first.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) { 326 return region.second; 327 } 328 } 329 return NO_INDICATOR; 330 } 331 332 /** 333 * Returns the [DragStartState] of the visual indicator. 334 */ getDragStartState()335 DragStartState getDragStartState() { 336 return mDragStartState; 337 } 338 339 @VisibleForTesting calculateFullscreenRegion(DisplayLayout layout, int captionHeight)340 Region calculateFullscreenRegion(DisplayLayout layout, int captionHeight) { 341 final Region region = new Region(); 342 int transitionHeight = mDragStartState == DragStartState.FROM_FREEFORM 343 || mDragStartState == DragStartState.DRAGGED_INTENT 344 ? SystemBarUtils.getStatusBarHeight(mContext) 345 : 2 * layout.stableInsets().top; 346 // A Rect at the top of the screen that takes up the center 40%. 347 if (mDragStartState == DragStartState.FROM_FREEFORM) { 348 final float toFullscreenScale = mContext.getResources().getFloat( 349 R.dimen.desktop_mode_fullscreen_region_scale); 350 final float toFullscreenWidth = (layout.width() * toFullscreenScale); 351 region.union(new Rect((int) ((layout.width() / 2f) - (toFullscreenWidth / 2f)), 352 Short.MIN_VALUE, 353 (int) ((layout.width() / 2f) + (toFullscreenWidth / 2f)), 354 transitionHeight)); 355 } 356 // A screen-wide Rect if the task is in fullscreen, split, or a dragged intent. 357 if (mDragStartState == DragStartState.FROM_FULLSCREEN 358 || mDragStartState == DragStartState.FROM_SPLIT 359 || mDragStartState == DragStartState.DRAGGED_INTENT 360 ) { 361 region.union(new Rect(0, 362 Short.MIN_VALUE, 363 layout.width(), 364 transitionHeight)); 365 } 366 return region; 367 } 368 369 @VisibleForTesting calculateSplitLeftRegion(DisplayLayout layout, int transitionEdgeWidth, int captionHeight)370 Rect calculateSplitLeftRegion(DisplayLayout layout, 371 int transitionEdgeWidth, int captionHeight) { 372 // In freeform, keep the top corners clear. 373 int transitionHeight = mDragStartState == DragStartState.FROM_FREEFORM 374 ? mContext.getResources().getDimensionPixelSize( 375 com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) : 376 -captionHeight; 377 return new Rect(0, transitionHeight, transitionEdgeWidth, layout.height()); 378 } 379 380 @VisibleForTesting calculateSplitRightRegion(DisplayLayout layout, int transitionEdgeWidth, int captionHeight)381 Rect calculateSplitRightRegion(DisplayLayout layout, 382 int transitionEdgeWidth, int captionHeight) { 383 // In freeform, keep the top corners clear. 384 int transitionHeight = mDragStartState == DragStartState.FROM_FREEFORM 385 ? mContext.getResources().getDimensionPixelSize( 386 com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) : 387 -captionHeight; 388 return new Rect(layout.width() - transitionEdgeWidth, transitionHeight, 389 layout.width(), layout.height()); 390 } 391 392 @VisibleForTesting calculateBubbleLeftRegion(DisplayLayout layout)393 Rect calculateBubbleLeftRegion(DisplayLayout layout) { 394 int regionSize = getBubbleRegionSize(); 395 return new Rect(0, layout.height() - regionSize, regionSize, layout.height()); 396 } 397 398 @VisibleForTesting calculateBubbleRightRegion(DisplayLayout layout)399 Rect calculateBubbleRightRegion(DisplayLayout layout) { 400 int regionSize = getBubbleRegionSize(); 401 return new Rect(layout.width() - regionSize, layout.height() - regionSize, 402 layout.width(), layout.height()); 403 } 404 getBubbleRegionSize()405 private int getBubbleRegionSize() { 406 int resId = mUseSmallTabletRegions 407 ? com.android.wm.shell.shared.R.dimen.drag_zone_bubble_fold 408 : com.android.wm.shell.shared.R.dimen.drag_zone_bubble_tablet; 409 return mContext.getResources().getDimensionPixelSize(resId); 410 } 411 412 @VisibleForTesting getIndicatorBounds()413 Rect getIndicatorBounds() { 414 return mVisualIndicatorViewContainer.getIndicatorBounds(); 415 } 416 initSmallTabletRegions(DisplayLayout layout, boolean isLeftRightSplit)417 private List<Pair<Rect, IndicatorType>> initSmallTabletRegions(DisplayLayout layout, 418 boolean isLeftRightSplit) { 419 return switch (mDragStartState) { 420 case FROM_FULLSCREEN -> initSmallTabletRegionsFromFullscreen(layout, isLeftRightSplit); 421 case FROM_SPLIT -> initSmallTabletRegionsFromSplit(layout, isLeftRightSplit); 422 default -> Collections.emptyList(); 423 }; 424 } 425 initSmallTabletRegionsFromFullscreen( DisplayLayout layout, boolean isLeftRightSplit)426 private List<Pair<Rect, IndicatorType>> initSmallTabletRegionsFromFullscreen( 427 DisplayLayout layout, boolean isLeftRightSplit) { 428 429 List<Pair<Rect, IndicatorType>> result = new ArrayList<>(); 430 if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) { 431 result.add(new Pair<>(calculateBubbleLeftRegion(layout), TO_BUBBLE_LEFT_INDICATOR)); 432 result.add(new Pair<>(calculateBubbleRightRegion(layout), TO_BUBBLE_RIGHT_INDICATOR)); 433 } 434 435 if (isLeftRightSplit) { 436 int splitRegionWidth = mContext.getResources().getDimensionPixelSize( 437 com.android.wm.shell.shared.R.dimen.drag_zone_h_split_from_app_width_fold); 438 result.add(new Pair<>(calculateSplitLeftRegion(layout, splitRegionWidth, 439 /* captionHeight= */ 0), TO_SPLIT_LEFT_INDICATOR)); 440 result.add(new Pair<>(calculateSplitRightRegion(layout, splitRegionWidth, 441 /* captionHeight= */ 0), TO_SPLIT_RIGHT_INDICATOR)); 442 } 443 // TODO(b/401352409): add support for top/bottom split zones 444 // default to fullscreen 445 result.add(new Pair<>(new Rect(), TO_FULLSCREEN_INDICATOR)); 446 return result; 447 } 448 initSmallTabletRegionsFromSplit(DisplayLayout layout, boolean isLeftRightSplit)449 private List<Pair<Rect, IndicatorType>> initSmallTabletRegionsFromSplit(DisplayLayout layout, 450 boolean isLeftRightSplit) { 451 if (!isLeftRightSplit) { 452 // Dragging a top/bottom split is not supported on small tablets 453 return Collections.emptyList(); 454 } 455 456 List<Pair<Rect, IndicatorType>> result = new ArrayList<>(); 457 if (BubbleAnythingFlagHelper.enableBubbleAnything()) { 458 result.add(new Pair<>(calculateBubbleLeftRegion(layout), TO_BUBBLE_LEFT_INDICATOR)); 459 result.add(new Pair<>(calculateBubbleRightRegion(layout), TO_BUBBLE_RIGHT_INDICATOR)); 460 } 461 462 int splitRegionWidth = mContext.getResources().getDimensionPixelSize( 463 com.android.wm.shell.shared.R.dimen.drag_zone_h_split_from_app_width_fold); 464 result.add(new Pair<>(calculateSplitLeftRegion(layout, splitRegionWidth, 465 /* captionHeight= */ 0), TO_SPLIT_LEFT_INDICATOR)); 466 result.add(new Pair<>(calculateSplitRightRegion(layout, splitRegionWidth, 467 /* captionHeight= */ 0), TO_SPLIT_RIGHT_INDICATOR)); 468 // default to fullscreen 469 result.add(new Pair<>(new Rect(), TO_FULLSCREEN_INDICATOR)); 470 return result; 471 } 472 } 473