Loading libs/WindowManager/Shell/res/values/dimen.xml +4 −0 Original line number Diff line number Diff line Loading @@ -432,6 +432,10 @@ <dimen name="freeform_resize_corner">44dp</dimen> <!-- The width of the area at the sides of the screen where a freeform task will transition to split select if dragged until the touch input is within the range. --> <dimen name="desktop_mode_transition_area_width">32dp</dimen> <!-- The height of the area at the top of the screen where a freeform task will transition to fullscreen if dragged until the top bound of the task is within the area. --> <dimen name="desktop_mode_transition_area_height">16dp</dimen> Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +144 −50 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.app.ActivityManager; import android.content.Context; import android.content.res.Resources; import android.graphics.PixelFormat; import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; import android.view.SurfaceControl; Loading @@ -47,6 +48,15 @@ import com.android.wm.shell.common.SyncTransactionQueue; * Animated visual indicator for Desktop Mode windowing transitions. */ public class DesktopModeVisualIndicator { public static final int INVALID_INDICATOR = -1; /** Indicates impending transition into desktop mode */ public static final int TO_DESKTOP_INDICATOR = 1; /** Indicates impending transition into fullscreen */ public static final int TO_FULLSCREEN_INDICATOR = 2; /** Indicates impending transition into split select on the left side */ public static final int TO_SPLIT_LEFT_INDICATOR = 3; /** Indicates impending transition into split select on the right side */ public static final int TO_SPLIT_RIGHT_INDICATOR = 4; private final Context mContext; private final DisplayController mDisplayController; Loading @@ -54,6 +64,7 @@ public class DesktopModeVisualIndicator { private final RootTaskDisplayAreaOrganizer mRootTdaOrganizer; private final ActivityManager.RunningTaskInfo mTaskInfo; private final SurfaceControl mTaskSurface; private final Rect mIndicatorRange = new Rect(); private SurfaceControl mLeash; private final SyncTransactionQueue mSyncQueue; Loading @@ -61,11 +72,12 @@ public class DesktopModeVisualIndicator { private View mView; private boolean mIsFullscreen; private int mType; public DesktopModeVisualIndicator(SyncTransactionQueue syncQueue, ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController, Context context, SurfaceControl taskSurface, ShellTaskOrganizer taskOrganizer, RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer) { RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer, int type) { mSyncQueue = syncQueue; mTaskInfo = taskInfo; mDisplayController = displayController; Loading @@ -73,9 +85,63 @@ public class DesktopModeVisualIndicator { mTaskSurface = taskSurface; mTaskOrganizer = taskOrganizer; mRootTdaOrganizer = taskDisplayAreaOrganizer; mType = type; defineIndicatorRange(); createView(); } /** * If an indicator is warranted based on the input and task bounds, return the type of * indicator that should be created. */ public static int determineIndicatorType(PointF inputCoordinates, Rect taskBounds, DisplayLayout layout, Context context) { int transitionAreaHeight = context.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_height); int transitionAreaWidth = context.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_width); if (taskBounds.top <= transitionAreaHeight) return TO_FULLSCREEN_INDICATOR; if (inputCoordinates.x <= transitionAreaWidth) return TO_SPLIT_LEFT_INDICATOR; if (inputCoordinates.x >= layout.width() - transitionAreaWidth) { return TO_SPLIT_RIGHT_INDICATOR; } return INVALID_INDICATOR; } /** * Determine range of inputs that will keep this indicator displaying. */ private void defineIndicatorRange() { DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId); int captionHeight = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.freeform_decor_caption_height); int transitionAreaHeight = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_height); int transitionAreaWidth = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_width); switch (mType) { case TO_DESKTOP_INDICATOR: // TO_DESKTOP indicator is only dismissed on release; entire display is valid. mIndicatorRange.set(0, 0, layout.width(), layout.height()); break; case TO_FULLSCREEN_INDICATOR: // If drag results in caption going above the top edge of the display, we still // want to transition to fullscreen. mIndicatorRange.set(0, -captionHeight, layout.width(), transitionAreaHeight); break; case TO_SPLIT_LEFT_INDICATOR: mIndicatorRange.set(0, transitionAreaHeight, transitionAreaWidth, layout.height()); break; case TO_SPLIT_RIGHT_INDICATOR: mIndicatorRange.set(layout.width() - transitionAreaWidth, transitionAreaHeight, layout.width(), layout.height()); break; default: break; } } /** * Create a fullscreen indicator with no animation */ Loading @@ -85,11 +151,30 @@ public class DesktopModeVisualIndicator { final DisplayMetrics metrics = resources.getDisplayMetrics(); final int screenWidth = metrics.widthPixels; final int screenHeight = metrics.heightPixels; mView = new View(mContext); final SurfaceControl.Builder builder = new SurfaceControl.Builder(); mRootTdaOrganizer.attachToDisplayArea(mTaskInfo.displayId, builder); String description; switch (mType) { case TO_DESKTOP_INDICATOR: description = "Desktop indicator"; break; case TO_FULLSCREEN_INDICATOR: description = "Fullscreen indicator"; break; case TO_SPLIT_LEFT_INDICATOR: description = "Split Left indicator"; break; case TO_SPLIT_RIGHT_INDICATOR: description = "Split Right indicator"; break; default: description = "Invalid indicator"; break; } mLeash = builder .setName("Fullscreen Indicator") .setName(description) .setContainerLayer() .build(); t.show(mLeash); Loading @@ -97,14 +182,14 @@ public class DesktopModeVisualIndicator { new WindowManager.LayoutParams(screenWidth, screenHeight, WindowManager.LayoutParams.TYPE_APPLICATION, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); lp.setTitle("Fullscreen indicator for Task=" + mTaskInfo.taskId); lp.setTitle(description + " for Task=" + mTaskInfo.taskId); lp.setTrustedOverlay(); final WindowlessWindowManager windowManager = new WindowlessWindowManager( mTaskInfo.configuration, mLeash, null /* hostInputToken */); mViewHost = new SurfaceControlViewHost(mContext, mDisplayController.getDisplay(mTaskInfo.displayId), windowManager, "FullscreenVisualIndicator"); "DesktopModeVisualIndicator"); mViewHost.setView(mView, lp); // We want this indicator to be behind the dragged task, but in front of all others. t.setRelativeLayer(mLeash, mTaskSurface, -1); Loading @@ -116,24 +201,13 @@ public class DesktopModeVisualIndicator { } /** * Create fullscreen indicator and fades it in. */ public void createFullscreenIndicator() { mIsFullscreen = true; mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background); final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFullscreenAnimator( mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId)); animator.start(); } /** * Create a fullscreen indicator. Animator fades it in while expanding the bounds outwards. * Create an indicator. Animator fades it in while expanding the bounds outwards. */ public void createFullscreenIndicatorWithAnimatedBounds() { mIsFullscreen = true; public void createIndicatorWithAnimatedBounds() { mIsFullscreen = mType == TO_FULLSCREEN_INDICATOR; mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background); final VisualIndicatorAnimator animator = VisualIndicatorAnimator .toFullscreenAnimatorWithAnimatedBounds(mView, .animateBounds(mView, mType, mDisplayController.getDisplayLayout(mTaskInfo.displayId)); animator.start(); } Loading @@ -143,6 +217,7 @@ public class DesktopModeVisualIndicator { */ public void transitionFullscreenIndicatorToFreeform() { mIsFullscreen = false; mType = TO_DESKTOP_INDICATOR; final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFreeformAnimator( mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId)); animator.start(); Loading @@ -153,12 +228,21 @@ public class DesktopModeVisualIndicator { */ public void transitionFreeformIndicatorToFullscreen() { mIsFullscreen = true; mType = TO_FULLSCREEN_INDICATOR; final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFullscreenAnimatorWithAnimatedBounds( mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId)); animator.start(); } /** * Determine if a MotionEvent is in the same range that enabled the indicator. * Used to dismiss the indicator when a transition will no longer result from releasing. */ public boolean eventOutsideRange(float x, float y) { return !mIndicatorRange.contains((int) x, (int) y); } /** * Release the indicator and its components when it is no longer needed. */ Loading Loading @@ -210,32 +294,45 @@ public class DesktopModeVisualIndicator { * @param view the view for this indicator * @param displayLayout information about the display the transitioning task is currently on */ public static VisualIndicatorAnimator toFullscreenAnimator(@NonNull View view, @NonNull DisplayLayout displayLayout) { final Rect bounds = getMaxBounds(displayLayout); public static VisualIndicatorAnimator toFullscreenAnimatorWithAnimatedBounds( @NonNull View view, @NonNull DisplayLayout displayLayout) { final int padding = displayLayout.stableInsets().top; Rect startBounds = new Rect(padding, padding, displayLayout.width() - padding, displayLayout.height() - padding); view.getBackground().setBounds(startBounds); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( view, bounds, bounds); view, startBounds, getMaxBounds(startBounds)); animator.setInterpolator(new DecelerateInterpolator()); setupIndicatorAnimation(animator); return animator; } /** * Create animator for visual indicator of fullscreen transition * * @param view the view for this indicator * @param displayLayout information about the display the transitioning task is currently on */ public static VisualIndicatorAnimator toFullscreenAnimatorWithAnimatedBounds( @NonNull View view, @NonNull DisplayLayout displayLayout) { public static VisualIndicatorAnimator animateBounds( @NonNull View view, int type, @NonNull DisplayLayout displayLayout) { final int padding = displayLayout.stableInsets().top; Rect startBounds = new Rect(padding, padding, displayLayout.width() - padding, displayLayout.height() - padding); Rect startBounds = new Rect(); switch (type) { case TO_FULLSCREEN_INDICATOR: startBounds.set(padding, padding, displayLayout.width() - padding, displayLayout.height() - padding); break; case TO_SPLIT_LEFT_INDICATOR: startBounds.set(padding, padding, displayLayout.width() / 2 - padding, displayLayout.height() - padding); break; case TO_SPLIT_RIGHT_INDICATOR: startBounds.set(displayLayout.width() / 2 + padding, padding, displayLayout.width() - padding, displayLayout.height() - padding); break; } view.getBackground().setBounds(startBounds); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( view, startBounds, getMaxBounds(displayLayout)); view, startBounds, getMaxBounds(startBounds)); animator.setInterpolator(new DecelerateInterpolator()); setupIndicatorAnimation(animator); return animator; Loading @@ -252,12 +349,13 @@ public class DesktopModeVisualIndicator { final float adjustmentPercentage = 1f - FINAL_FREEFORM_SCALE; final int width = displayLayout.width(); final int height = displayLayout.height(); Rect startBounds = new Rect(0, 0, width, height); Rect endBounds = new Rect((int) (adjustmentPercentage * width / 2), (int) (adjustmentPercentage * height / 2), (int) (displayLayout.width() - (adjustmentPercentage * width / 2)), (int) (displayLayout.height() - (adjustmentPercentage * height / 2))); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( view, getMaxBounds(displayLayout), endBounds); view, startBounds, endBounds); animator.setInterpolator(new DecelerateInterpolator()); setupIndicatorAnimation(animator); return animator; Loading Loading @@ -310,21 +408,17 @@ public class DesktopModeVisualIndicator { } /** * Return the max bounds of a fullscreen indicator * Return the max bounds of a visual indicator */ private static Rect getMaxBounds(@NonNull DisplayLayout displayLayout) { final int padding = displayLayout.stableInsets().top; final int width = displayLayout.width() - 2 * padding; final int height = displayLayout.height() - 2 * padding; Rect endBounds = new Rect((int) (padding - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)), (int) (padding - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height)), (int) (displayLayout.width() - padding + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)), (int) (displayLayout.height() - padding + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height))); return endBounds; private static Rect getMaxBounds(Rect startBounds) { return new Rect((int) (startBounds.left - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())), (int) (startBounds.top - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())), (int) (startBounds.right + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())), (int) (startBounds.bottom + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height()))); } } } libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +63 −23 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.app.WindowConfiguration.WindowingMode import android.content.Context import android.content.res.TypedArray import android.graphics.Point import android.graphics.PointF import android.graphics.Rect import android.graphics.Region import android.os.IBinder Loading @@ -55,7 +56,10 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.annotations.ExternalThread import com.android.wm.shell.common.annotations.ShellMainThread import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler Loading Loading @@ -107,6 +111,10 @@ class DesktopTasksController( get() = context.resources.getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_height) private val transitionAreaWidth get() = context.resources.getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_width) // This is public to avoid cyclic dependency; it is set by SplitScreenController lateinit var splitScreenController: SplitScreenController Loading Loading @@ -755,7 +763,8 @@ class DesktopTasksController( ) { val wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect(taskInfo, wct) splitScreenController.requestEnterSplitSelect(taskInfo, wct, SPLIT_POSITION_BOTTOM_OR_RIGHT, taskInfo.configuration.windowConfiguration.bounds) } } Loading @@ -779,25 +788,36 @@ class DesktopTasksController( /** * Perform checks required on drag move. Create/release fullscreen indicator as needed. * Different sources for x and y coordinates are used due to different needs for each: * We want split transitions to be based on input coordinates but fullscreen transition * to be based on task edge coordinate. * * @param taskInfo the task being dragged. * @param taskSurface SurfaceControl of dragged task. * @param y coordinate of dragged task. Used for checks against status bar height. * @param inputCoordinate coordinates of input. Used for checks against left/right edge of screen. * @param taskBounds bounds of dragged task. Used for checks against status bar height. */ fun onDragPositioningMove( taskInfo: RunningTaskInfo, taskSurface: SurfaceControl, y: Float inputCoordinate: PointF, taskBounds: Rect ) { if (taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { if (y <= transitionAreaHeight && visualIndicator == null) { visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return if (taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) return var type = DesktopModeVisualIndicator.determineIndicatorType(inputCoordinate, taskBounds, displayLayout, context) if (type != DesktopModeVisualIndicator.INVALID_INDICATOR && visualIndicator == null) { visualIndicator = DesktopModeVisualIndicator( syncQueue, taskInfo, displayController, context, taskSurface, shellTaskOrganizer, rootTaskDisplayAreaOrganizer) visualIndicator?.createFullscreenIndicatorWithAnimatedBounds() } else if (y > transitionAreaHeight && visualIndicator != null) { releaseVisualIndicator() rootTaskDisplayAreaOrganizer, type) visualIndicator?.createIndicatorWithAnimatedBounds() return } if (visualIndicator?.eventOutsideRange(inputCoordinate.x, taskBounds.top.toFloat()) == true) { releaseVisualIndicator() } } Loading @@ -806,19 +826,39 @@ class DesktopTasksController( * * @param taskInfo the task being dragged. * @param position position of surface when drag ends. * @param y the Y position of the top edge of the task * @param inputCoordinate the coordinates of the motion event * @param taskBounds the updated bounds of the task being dragged. * @param windowDecor the window decoration for the task being dragged */ fun onDragPositioningEnd( taskInfo: RunningTaskInfo, position: Point, y: Float, inputCoordinate: PointF, taskBounds: Rect, windowDecor: DesktopModeWindowDecoration ) { if (y <= transitionAreaHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) { return } if (taskBounds.top <= transitionAreaHeight) { windowDecor.incrementRelayoutBlock() moveToFullscreenWithAnimation(taskInfo, position) } if (inputCoordinate.x <= transitionAreaWidth) { releaseVisualIndicator() var wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect(taskInfo, wct, SPLIT_POSITION_TOP_OR_LEFT, taskBounds) } if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width() ?.minus(transitionAreaWidth) ?: return)) { releaseVisualIndicator() var wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect(taskInfo, wct, SPLIT_POSITION_BOTTOM_OR_RIGHT, taskBounds) } } /** Loading @@ -842,8 +882,8 @@ class DesktopTasksController( if (visualIndicator == null) { visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, displayController, context, taskSurface, shellTaskOrganizer, rootTaskDisplayAreaOrganizer) visualIndicator?.createFullscreenIndicator() rootTaskDisplayAreaOrganizer, TO_DESKTOP_INDICATOR) visualIndicator?.createIndicatorWithAnimatedBounds() } val indicator = visualIndicator ?: return if (y >= getFreeformTransitionStatusBarDragThreshold(taskInfo)) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl +2 −2 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ package com.android.wm.shell.splitscreen; import android.app.ActivityManager.RunningTaskInfo; import android.graphics.Rect; /** * Listener interface that Launcher attaches to SystemUI to get split-select callbacks. */ Loading @@ -25,5 +25,5 @@ interface ISplitSelectListener { /** * Called when a task requests to enter split select */ boolean onRequestSplitSelect(in RunningTaskInfo taskInfo); boolean onRequestSplitSelect(in RunningTaskInfo taskInfo, int splitPosition, in Rect taskBounds); } No newline at end of file libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +2 −1 Original line number Diff line number Diff line Loading @@ -66,7 +66,8 @@ public interface SplitScreen { /** Callback interface for listening to requests to enter split select */ interface SplitSelectListener { default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo) { default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo, int splitPosition, Rect taskBounds) { return false; } } Loading Loading
libs/WindowManager/Shell/res/values/dimen.xml +4 −0 Original line number Diff line number Diff line Loading @@ -432,6 +432,10 @@ <dimen name="freeform_resize_corner">44dp</dimen> <!-- The width of the area at the sides of the screen where a freeform task will transition to split select if dragged until the touch input is within the range. --> <dimen name="desktop_mode_transition_area_width">32dp</dimen> <!-- The height of the area at the top of the screen where a freeform task will transition to fullscreen if dragged until the top bound of the task is within the area. --> <dimen name="desktop_mode_transition_area_height">16dp</dimen> Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +144 −50 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.app.ActivityManager; import android.content.Context; import android.content.res.Resources; import android.graphics.PixelFormat; import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; import android.view.SurfaceControl; Loading @@ -47,6 +48,15 @@ import com.android.wm.shell.common.SyncTransactionQueue; * Animated visual indicator for Desktop Mode windowing transitions. */ public class DesktopModeVisualIndicator { public static final int INVALID_INDICATOR = -1; /** Indicates impending transition into desktop mode */ public static final int TO_DESKTOP_INDICATOR = 1; /** Indicates impending transition into fullscreen */ public static final int TO_FULLSCREEN_INDICATOR = 2; /** Indicates impending transition into split select on the left side */ public static final int TO_SPLIT_LEFT_INDICATOR = 3; /** Indicates impending transition into split select on the right side */ public static final int TO_SPLIT_RIGHT_INDICATOR = 4; private final Context mContext; private final DisplayController mDisplayController; Loading @@ -54,6 +64,7 @@ public class DesktopModeVisualIndicator { private final RootTaskDisplayAreaOrganizer mRootTdaOrganizer; private final ActivityManager.RunningTaskInfo mTaskInfo; private final SurfaceControl mTaskSurface; private final Rect mIndicatorRange = new Rect(); private SurfaceControl mLeash; private final SyncTransactionQueue mSyncQueue; Loading @@ -61,11 +72,12 @@ public class DesktopModeVisualIndicator { private View mView; private boolean mIsFullscreen; private int mType; public DesktopModeVisualIndicator(SyncTransactionQueue syncQueue, ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController, Context context, SurfaceControl taskSurface, ShellTaskOrganizer taskOrganizer, RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer) { RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer, int type) { mSyncQueue = syncQueue; mTaskInfo = taskInfo; mDisplayController = displayController; Loading @@ -73,9 +85,63 @@ public class DesktopModeVisualIndicator { mTaskSurface = taskSurface; mTaskOrganizer = taskOrganizer; mRootTdaOrganizer = taskDisplayAreaOrganizer; mType = type; defineIndicatorRange(); createView(); } /** * If an indicator is warranted based on the input and task bounds, return the type of * indicator that should be created. */ public static int determineIndicatorType(PointF inputCoordinates, Rect taskBounds, DisplayLayout layout, Context context) { int transitionAreaHeight = context.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_height); int transitionAreaWidth = context.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_width); if (taskBounds.top <= transitionAreaHeight) return TO_FULLSCREEN_INDICATOR; if (inputCoordinates.x <= transitionAreaWidth) return TO_SPLIT_LEFT_INDICATOR; if (inputCoordinates.x >= layout.width() - transitionAreaWidth) { return TO_SPLIT_RIGHT_INDICATOR; } return INVALID_INDICATOR; } /** * Determine range of inputs that will keep this indicator displaying. */ private void defineIndicatorRange() { DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId); int captionHeight = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.freeform_decor_caption_height); int transitionAreaHeight = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_height); int transitionAreaWidth = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_width); switch (mType) { case TO_DESKTOP_INDICATOR: // TO_DESKTOP indicator is only dismissed on release; entire display is valid. mIndicatorRange.set(0, 0, layout.width(), layout.height()); break; case TO_FULLSCREEN_INDICATOR: // If drag results in caption going above the top edge of the display, we still // want to transition to fullscreen. mIndicatorRange.set(0, -captionHeight, layout.width(), transitionAreaHeight); break; case TO_SPLIT_LEFT_INDICATOR: mIndicatorRange.set(0, transitionAreaHeight, transitionAreaWidth, layout.height()); break; case TO_SPLIT_RIGHT_INDICATOR: mIndicatorRange.set(layout.width() - transitionAreaWidth, transitionAreaHeight, layout.width(), layout.height()); break; default: break; } } /** * Create a fullscreen indicator with no animation */ Loading @@ -85,11 +151,30 @@ public class DesktopModeVisualIndicator { final DisplayMetrics metrics = resources.getDisplayMetrics(); final int screenWidth = metrics.widthPixels; final int screenHeight = metrics.heightPixels; mView = new View(mContext); final SurfaceControl.Builder builder = new SurfaceControl.Builder(); mRootTdaOrganizer.attachToDisplayArea(mTaskInfo.displayId, builder); String description; switch (mType) { case TO_DESKTOP_INDICATOR: description = "Desktop indicator"; break; case TO_FULLSCREEN_INDICATOR: description = "Fullscreen indicator"; break; case TO_SPLIT_LEFT_INDICATOR: description = "Split Left indicator"; break; case TO_SPLIT_RIGHT_INDICATOR: description = "Split Right indicator"; break; default: description = "Invalid indicator"; break; } mLeash = builder .setName("Fullscreen Indicator") .setName(description) .setContainerLayer() .build(); t.show(mLeash); Loading @@ -97,14 +182,14 @@ public class DesktopModeVisualIndicator { new WindowManager.LayoutParams(screenWidth, screenHeight, WindowManager.LayoutParams.TYPE_APPLICATION, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); lp.setTitle("Fullscreen indicator for Task=" + mTaskInfo.taskId); lp.setTitle(description + " for Task=" + mTaskInfo.taskId); lp.setTrustedOverlay(); final WindowlessWindowManager windowManager = new WindowlessWindowManager( mTaskInfo.configuration, mLeash, null /* hostInputToken */); mViewHost = new SurfaceControlViewHost(mContext, mDisplayController.getDisplay(mTaskInfo.displayId), windowManager, "FullscreenVisualIndicator"); "DesktopModeVisualIndicator"); mViewHost.setView(mView, lp); // We want this indicator to be behind the dragged task, but in front of all others. t.setRelativeLayer(mLeash, mTaskSurface, -1); Loading @@ -116,24 +201,13 @@ public class DesktopModeVisualIndicator { } /** * Create fullscreen indicator and fades it in. */ public void createFullscreenIndicator() { mIsFullscreen = true; mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background); final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFullscreenAnimator( mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId)); animator.start(); } /** * Create a fullscreen indicator. Animator fades it in while expanding the bounds outwards. * Create an indicator. Animator fades it in while expanding the bounds outwards. */ public void createFullscreenIndicatorWithAnimatedBounds() { mIsFullscreen = true; public void createIndicatorWithAnimatedBounds() { mIsFullscreen = mType == TO_FULLSCREEN_INDICATOR; mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background); final VisualIndicatorAnimator animator = VisualIndicatorAnimator .toFullscreenAnimatorWithAnimatedBounds(mView, .animateBounds(mView, mType, mDisplayController.getDisplayLayout(mTaskInfo.displayId)); animator.start(); } Loading @@ -143,6 +217,7 @@ public class DesktopModeVisualIndicator { */ public void transitionFullscreenIndicatorToFreeform() { mIsFullscreen = false; mType = TO_DESKTOP_INDICATOR; final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFreeformAnimator( mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId)); animator.start(); Loading @@ -153,12 +228,21 @@ public class DesktopModeVisualIndicator { */ public void transitionFreeformIndicatorToFullscreen() { mIsFullscreen = true; mType = TO_FULLSCREEN_INDICATOR; final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFullscreenAnimatorWithAnimatedBounds( mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId)); animator.start(); } /** * Determine if a MotionEvent is in the same range that enabled the indicator. * Used to dismiss the indicator when a transition will no longer result from releasing. */ public boolean eventOutsideRange(float x, float y) { return !mIndicatorRange.contains((int) x, (int) y); } /** * Release the indicator and its components when it is no longer needed. */ Loading Loading @@ -210,32 +294,45 @@ public class DesktopModeVisualIndicator { * @param view the view for this indicator * @param displayLayout information about the display the transitioning task is currently on */ public static VisualIndicatorAnimator toFullscreenAnimator(@NonNull View view, @NonNull DisplayLayout displayLayout) { final Rect bounds = getMaxBounds(displayLayout); public static VisualIndicatorAnimator toFullscreenAnimatorWithAnimatedBounds( @NonNull View view, @NonNull DisplayLayout displayLayout) { final int padding = displayLayout.stableInsets().top; Rect startBounds = new Rect(padding, padding, displayLayout.width() - padding, displayLayout.height() - padding); view.getBackground().setBounds(startBounds); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( view, bounds, bounds); view, startBounds, getMaxBounds(startBounds)); animator.setInterpolator(new DecelerateInterpolator()); setupIndicatorAnimation(animator); return animator; } /** * Create animator for visual indicator of fullscreen transition * * @param view the view for this indicator * @param displayLayout information about the display the transitioning task is currently on */ public static VisualIndicatorAnimator toFullscreenAnimatorWithAnimatedBounds( @NonNull View view, @NonNull DisplayLayout displayLayout) { public static VisualIndicatorAnimator animateBounds( @NonNull View view, int type, @NonNull DisplayLayout displayLayout) { final int padding = displayLayout.stableInsets().top; Rect startBounds = new Rect(padding, padding, displayLayout.width() - padding, displayLayout.height() - padding); Rect startBounds = new Rect(); switch (type) { case TO_FULLSCREEN_INDICATOR: startBounds.set(padding, padding, displayLayout.width() - padding, displayLayout.height() - padding); break; case TO_SPLIT_LEFT_INDICATOR: startBounds.set(padding, padding, displayLayout.width() / 2 - padding, displayLayout.height() - padding); break; case TO_SPLIT_RIGHT_INDICATOR: startBounds.set(displayLayout.width() / 2 + padding, padding, displayLayout.width() - padding, displayLayout.height() - padding); break; } view.getBackground().setBounds(startBounds); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( view, startBounds, getMaxBounds(displayLayout)); view, startBounds, getMaxBounds(startBounds)); animator.setInterpolator(new DecelerateInterpolator()); setupIndicatorAnimation(animator); return animator; Loading @@ -252,12 +349,13 @@ public class DesktopModeVisualIndicator { final float adjustmentPercentage = 1f - FINAL_FREEFORM_SCALE; final int width = displayLayout.width(); final int height = displayLayout.height(); Rect startBounds = new Rect(0, 0, width, height); Rect endBounds = new Rect((int) (adjustmentPercentage * width / 2), (int) (adjustmentPercentage * height / 2), (int) (displayLayout.width() - (adjustmentPercentage * width / 2)), (int) (displayLayout.height() - (adjustmentPercentage * height / 2))); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( view, getMaxBounds(displayLayout), endBounds); view, startBounds, endBounds); animator.setInterpolator(new DecelerateInterpolator()); setupIndicatorAnimation(animator); return animator; Loading Loading @@ -310,21 +408,17 @@ public class DesktopModeVisualIndicator { } /** * Return the max bounds of a fullscreen indicator * Return the max bounds of a visual indicator */ private static Rect getMaxBounds(@NonNull DisplayLayout displayLayout) { final int padding = displayLayout.stableInsets().top; final int width = displayLayout.width() - 2 * padding; final int height = displayLayout.height() - 2 * padding; Rect endBounds = new Rect((int) (padding - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)), (int) (padding - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height)), (int) (displayLayout.width() - padding + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)), (int) (displayLayout.height() - padding + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height))); return endBounds; private static Rect getMaxBounds(Rect startBounds) { return new Rect((int) (startBounds.left - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())), (int) (startBounds.top - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())), (int) (startBounds.right + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())), (int) (startBounds.bottom + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height()))); } } }
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +63 −23 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.app.WindowConfiguration.WindowingMode import android.content.Context import android.content.res.TypedArray import android.graphics.Point import android.graphics.PointF import android.graphics.Rect import android.graphics.Region import android.os.IBinder Loading @@ -55,7 +56,10 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.annotations.ExternalThread import com.android.wm.shell.common.annotations.ShellMainThread import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler Loading Loading @@ -107,6 +111,10 @@ class DesktopTasksController( get() = context.resources.getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_height) private val transitionAreaWidth get() = context.resources.getDimensionPixelSize( com.android.wm.shell.R.dimen.desktop_mode_transition_area_width) // This is public to avoid cyclic dependency; it is set by SplitScreenController lateinit var splitScreenController: SplitScreenController Loading Loading @@ -755,7 +763,8 @@ class DesktopTasksController( ) { val wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect(taskInfo, wct) splitScreenController.requestEnterSplitSelect(taskInfo, wct, SPLIT_POSITION_BOTTOM_OR_RIGHT, taskInfo.configuration.windowConfiguration.bounds) } } Loading @@ -779,25 +788,36 @@ class DesktopTasksController( /** * Perform checks required on drag move. Create/release fullscreen indicator as needed. * Different sources for x and y coordinates are used due to different needs for each: * We want split transitions to be based on input coordinates but fullscreen transition * to be based on task edge coordinate. * * @param taskInfo the task being dragged. * @param taskSurface SurfaceControl of dragged task. * @param y coordinate of dragged task. Used for checks against status bar height. * @param inputCoordinate coordinates of input. Used for checks against left/right edge of screen. * @param taskBounds bounds of dragged task. Used for checks against status bar height. */ fun onDragPositioningMove( taskInfo: RunningTaskInfo, taskSurface: SurfaceControl, y: Float inputCoordinate: PointF, taskBounds: Rect ) { if (taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { if (y <= transitionAreaHeight && visualIndicator == null) { visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return if (taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) return var type = DesktopModeVisualIndicator.determineIndicatorType(inputCoordinate, taskBounds, displayLayout, context) if (type != DesktopModeVisualIndicator.INVALID_INDICATOR && visualIndicator == null) { visualIndicator = DesktopModeVisualIndicator( syncQueue, taskInfo, displayController, context, taskSurface, shellTaskOrganizer, rootTaskDisplayAreaOrganizer) visualIndicator?.createFullscreenIndicatorWithAnimatedBounds() } else if (y > transitionAreaHeight && visualIndicator != null) { releaseVisualIndicator() rootTaskDisplayAreaOrganizer, type) visualIndicator?.createIndicatorWithAnimatedBounds() return } if (visualIndicator?.eventOutsideRange(inputCoordinate.x, taskBounds.top.toFloat()) == true) { releaseVisualIndicator() } } Loading @@ -806,19 +826,39 @@ class DesktopTasksController( * * @param taskInfo the task being dragged. * @param position position of surface when drag ends. * @param y the Y position of the top edge of the task * @param inputCoordinate the coordinates of the motion event * @param taskBounds the updated bounds of the task being dragged. * @param windowDecor the window decoration for the task being dragged */ fun onDragPositioningEnd( taskInfo: RunningTaskInfo, position: Point, y: Float, inputCoordinate: PointF, taskBounds: Rect, windowDecor: DesktopModeWindowDecoration ) { if (y <= transitionAreaHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) { return } if (taskBounds.top <= transitionAreaHeight) { windowDecor.incrementRelayoutBlock() moveToFullscreenWithAnimation(taskInfo, position) } if (inputCoordinate.x <= transitionAreaWidth) { releaseVisualIndicator() var wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect(taskInfo, wct, SPLIT_POSITION_TOP_OR_LEFT, taskBounds) } if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width() ?.minus(transitionAreaWidth) ?: return)) { releaseVisualIndicator() var wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect(taskInfo, wct, SPLIT_POSITION_BOTTOM_OR_RIGHT, taskBounds) } } /** Loading @@ -842,8 +882,8 @@ class DesktopTasksController( if (visualIndicator == null) { visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, displayController, context, taskSurface, shellTaskOrganizer, rootTaskDisplayAreaOrganizer) visualIndicator?.createFullscreenIndicator() rootTaskDisplayAreaOrganizer, TO_DESKTOP_INDICATOR) visualIndicator?.createIndicatorWithAnimatedBounds() } val indicator = visualIndicator ?: return if (y >= getFreeformTransitionStatusBarDragThreshold(taskInfo)) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl +2 −2 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ package com.android.wm.shell.splitscreen; import android.app.ActivityManager.RunningTaskInfo; import android.graphics.Rect; /** * Listener interface that Launcher attaches to SystemUI to get split-select callbacks. */ Loading @@ -25,5 +25,5 @@ interface ISplitSelectListener { /** * Called when a task requests to enter split select */ boolean onRequestSplitSelect(in RunningTaskInfo taskInfo); boolean onRequestSplitSelect(in RunningTaskInfo taskInfo, int splitPosition, in Rect taskBounds); } No newline at end of file
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +2 −1 Original line number Diff line number Diff line Loading @@ -66,7 +66,8 @@ public interface SplitScreen { /** Callback interface for listening to requests to enter split select */ interface SplitSelectListener { default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo) { default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo, int splitPosition, Rect taskBounds) { return false; } } Loading