Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +12 −8 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener import com.android.wm.shell.draganddrop.DragAndDropController import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE Loading Loading @@ -1334,33 +1335,36 @@ class DesktopTasksController( * * @param taskInfo the task being dragged. * @param y height of drag, to be checked against status bar height. * @return the [IndicatorType] used for the resulting transition */ fun onDragPositioningEndThroughStatusBar( inputCoordinates: PointF, taskInfo: RunningTaskInfo, ) { val indicator = getVisualIndicator() ?: return ): IndicatorType { val indicator = getVisualIndicator() ?: return IndicatorType.NO_INDICATOR val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode) when (indicatorType) { DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return IndicatorType.TO_DESKTOP_INDICATOR -> { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return IndicatorType.NO_INDICATOR if (Flags.enableWindowingDynamicInitialBounds()) { finalizeDragToDesktop(taskInfo, calculateInitialBounds(displayLayout, taskInfo)) } else { finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout)) } } DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR, DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { IndicatorType.NO_INDICATOR, IndicatorType.TO_FULLSCREEN_INDICATOR -> { cancelDragToDesktop(taskInfo) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { requestSplit(taskInfo, leftOrTop = true) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { requestSplit(taskInfo, leftOrTop = false) } } return indicatorType } /** Update the exclusion region for a specified task */ Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +75 −14 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; Loading Loading @@ -326,7 +328,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (decoration == null) return; final RunningTaskInfo oldTaskInfo = decoration.mTaskInfo; if (taskInfo.displayId != oldTaskInfo.displayId) { if (taskInfo.displayId != oldTaskInfo.displayId && !Flags.enableAdditionalWindowsAboveStatusBar()) { removeTaskFromEventReceiver(oldTaskInfo.displayId); incrementEventReceiverTasks(taskInfo.displayId); } Loading Loading @@ -390,7 +393,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.close(); final int displayId = taskInfo.displayId; if (mEventReceiversByDisplay.contains(displayId)) { if (mEventReceiversByDisplay.contains(displayId) && !Flags.enableAdditionalWindowsAboveStatusBar()) { removeTaskFromEventReceiver(displayId); } // Remove the decoration from the cache last because WindowDecoration#close could still Loading Loading @@ -515,6 +519,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } } else if (id == R.id.split_screen_button) { decoration.closeHandleMenu(); // When the app enters split-select, the handle will no longer be visible, meaning // we shouldn't receive input for it any longer. decoration.disposeStatusBarInputLayer(); mDesktopTasksController.requestSplit(decoration.mTaskInfo); } else if (id == R.id.open_in_browser_button) { // TODO(b/346441962): let the decoration handle the click gesture and only call back Loading Loading @@ -651,13 +658,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); final RunningTaskInfo taskInfo = decoration.mTaskInfo; if (DesktopModeStatus.canEnterDesktopMode(mContext) && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { && !taskInfo.isFreeform()) { return handleNonFreeformMotionEvent(decoration, v, e); } else { return handleFreeformMotionEvent(decoration, taskInfo, v, e); } } private boolean handleNonFreeformMotionEvent(DesktopModeWindowDecoration decoration, View v, MotionEvent e) { final int id = v.getId(); if (id == R.id.caption_handle) { if (e.getActionMasked() == MotionEvent.ACTION_DOWN) { // Caption handle is located within the status bar region, meaning the // DisplayPolicy will attempt to transfer this input to status bar if it's // a swipe down. Pilfer here to keep the gesture in handle alone. mInputManager.pilferPointers(v.getViewRootImpl().getInputToken()); } handleCaptionThroughStatusBar(e, decoration); final boolean wasDragging = mIsDragging; updateDragStatus(e.getActionMasked()); // Only prevent onClick from receiving this event if it's a drag. return wasDragging; } return false; } private boolean handleFreeformMotionEvent(DesktopModeWindowDecoration decoration, RunningTaskInfo taskInfo, View v, MotionEvent e) { final int id = v.getId(); if (mGestureDetector.onTouchEvent(e)) { return true; } final int id = v.getId(); final boolean touchingButton = (id == R.id.close_window || id == R.id.maximize_window || id == R.id.open_menu_button); switch (e.getActionMasked()) { Loading @@ -666,7 +698,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDragPositioningCallback.onDragPositioningStart( 0 /* ctrlType */, e.getRawX(0), e.getRawY(0)); mIsDragging = false; updateDragStatus(e.getActionMasked()); mHasLongClicked = false; // Do not consume input event if a button is touched, otherwise it would // prevent the button's ripple effect from showing. Loading @@ -686,7 +718,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.mTaskSurface, e.getRawX(dragPointerIdx), newTaskBounds); mIsDragging = true; updateDragStatus(e.getActionMasked()); return true; } case MotionEvent.ACTION_UP: Loading Loading @@ -716,7 +748,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { // onClick call that results. return false; } else { mIsDragging = false; updateDragStatus(e.getActionMasked()); return true; } } Loading @@ -724,6 +756,21 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return true; } private void updateDragStatus(int eventAction) { switch (eventAction) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { mIsDragging = false; break; } case MotionEvent.ACTION_MOVE: { mIsDragging = true; break; } } } /** * Perform a task size toggle on release of the double-tap, assuming no drag event * was handled during the double-tap. Loading Loading @@ -848,6 +895,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * * @param relevantDecor the window decoration of the focused task's caption. This method only * handles motion events outside this caption's bounds. * TODO(b/349135068): Outside-touch detection no longer works with the * enableAdditionalWindowsAboveStatusBar flag enabled. This * will be fixed once we can add FLAG_WATCH_OUTSIDE_TOUCH to relevant menus, * at which point, all EventReceivers and external touch logic should be removed. */ private void handleEventOutsideCaption(MotionEvent ev, DesktopModeWindowDecoration relevantDecor) { Loading Loading @@ -900,9 +951,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { dragFromStatusBarAllowed = windowingMode == WINDOWING_MODE_FULLSCREEN || windowingMode == WINDOWING_MODE_MULTI_WINDOW; } if (dragFromStatusBarAllowed && relevantDecor.checkTouchEventInFocusedCaptionHandle(ev)) { final boolean shouldStartTransitionDrag = relevantDecor.checkTouchEventInFocusedCaptionHandle(ev) || Flags.enableAdditionalWindowsAboveStatusBar(); if (dragFromStatusBarAllowed && shouldStartTransitionDrag) { mTransitionDragActive = true; } break; Loading @@ -916,8 +968,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { // Though this isn't a hover event, we need to update handle's hover state // as it likely will change. relevantDecor.updateHoverAndPressStatus(ev); DesktopModeVisualIndicator.IndicatorType resultType = mDesktopTasksController.onDragPositioningEndThroughStatusBar( new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo); // If we are entering split select, handle will no longer be visible and // should not be receiving any input. if (resultType == TO_SPLIT_LEFT_INDICATOR || resultType != TO_SPLIT_RIGHT_INDICATOR) { relevantDecor.disposeStatusBarInputLayer(); } mMoveToDesktopAnimator = null; return; } else { Loading @@ -929,7 +988,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { relevantDecor.checkTouchEvent(ev); break; } case ACTION_MOVE: { if (relevantDecor == null) { return; Loading Loading @@ -1089,6 +1147,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDesktopModeWindowDecorFactory.create( mContext, mDisplayController, mSplitScreenController, mTaskOrganizer, taskInfo, taskSurface, Loading Loading @@ -1131,8 +1190,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { windowDecoration.setDragDetector(touchEventListener.mDragDetector); windowDecoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */); if (!Flags.enableAdditionalWindowsAboveStatusBar()) { incrementEventReceiverTasks(taskInfo.displayId); } } private RunningTaskInfo getOtherSplitTask(int taskId) { @SplitPosition int remainingTaskPosition = mSplitScreenController Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +77 −17 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize; Loading Loading @@ -101,6 +102,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private final @ShellBackgroundThread ShellExecutor mBgExecutor; private final Choreographer mChoreographer; private final SyncTransactionQueue mSyncQueue; private final SplitScreenController mSplitScreenController; private WindowDecorationViewHolder mWindowDecorViewHolder; private View.OnClickListener mOnCaptionButtonClickListener; Loading Loading @@ -151,6 +153,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin DesktopModeWindowDecoration( Context context, DisplayController displayController, SplitScreenController splitScreenController, ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, Loading @@ -159,16 +162,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin Choreographer choreographer, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { this (context, displayController, taskOrganizer, taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue, rootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE); this (context, displayController, splitScreenController, taskOrganizer, taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue, rootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE); } DesktopModeWindowDecoration( Context context, DisplayController displayController, SplitScreenController splitScreenController, ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, Loading @@ -187,6 +192,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, surfaceControlViewHostFactory); mSplitScreenController = splitScreenController; mHandler = handler; mBgExecutor = bgExecutor; mChoreographer = choreographer; Loading Loading @@ -367,27 +373,41 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin if (mResult.mRootView == null) { // This means something blocks the window decor from showing, e.g. the task is hidden. // Nothing is set up in this case including the decoration surface. disposeStatusBarInputLayer(); Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces return; } if (oldRootView != mResult.mRootView) { disposeStatusBarInputLayer(); mWindowDecorViewHolder = createViewHolder(); } Trace.beginSection("DesktopModeWindowDecoration#relayout-binding"); mWindowDecorViewHolder.bindData(mTaskInfo); final Point position = new Point(); if (isAppHandle(mWindowDecorViewHolder)) { position.set(determineHandlePosition()); } mWindowDecorViewHolder.bindData(mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight, isCaptionVisible()); Trace.endSection(); if (!mTaskInfo.isFocused) { closeHandleMenu(); closeMaximizeMenu(); } updateDragResizeListener(oldDecorationSurface); updateMaximizeMenu(startT); Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces } private boolean isCaptionVisible() { return mTaskInfo.isVisible && mIsCaptionVisible; } private void setCapturedLink(Uri capturedLink, long timeStamp) { if (capturedLink == null || (mCapturedLink != null && mCapturedLink.mTimeStamp == timeStamp)) { Loading Loading @@ -469,12 +489,42 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } } private Point determineHandlePosition() { final Point position = new Point(mResult.mCaptionX, 0); if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT && mDisplayController.getDisplayLayout(mTaskInfo.displayId).isLandscape() ) { // If this is the right split task, add left stage's width. final Rect leftStageBounds = new Rect(); mSplitScreenController.getStageBounds(leftStageBounds, new Rect()); position.x += leftStageBounds.width(); } return position; } /** * Dispose of the view used to forward inputs in status bar region. Intended to be * used any time handle is no longer visible. */ void disposeStatusBarInputLayer() { if (!isAppHandle(mWindowDecorViewHolder) || !Flags.enableAdditionalWindowsAboveStatusBar()) { return; } ((AppHandleViewHolder) mWindowDecorViewHolder).disposeStatusBarInputLayer(); } private WindowDecorationViewHolder createViewHolder() { if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) { return new AppHandleViewHolder( mResult.mRootView, mOnCaptionTouchListener, mOnCaptionButtonClickListener mOnCaptionButtonClickListener, (v, event) -> { updateHoverAndPressStatus(event); return true; } ); } else if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_header) { Loading @@ -497,6 +547,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin throw new IllegalArgumentException("Unexpected layout resource id"); } private boolean isAppHandle(WindowDecorationViewHolder viewHolder) { return viewHolder instanceof AppHandleViewHolder; } @VisibleForTesting static void updateRelayoutParams( RelayoutParams relayoutParams, Loading Loading @@ -965,10 +1019,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin * @return {@code true} if event is inside caption handle view, {@code false} if not */ boolean checkTouchEventInFocusedCaptionHandle(MotionEvent ev) { if (isHandleMenuActive() || !(mWindowDecorViewHolder instanceof AppHandleViewHolder)) { if (isHandleMenuActive() || !isAppHandle(mWindowDecorViewHolder) || Flags.enableAdditionalWindowsAboveStatusBar()) { return false; } // The status bar input layer can only receive input in handle coordinates to begin with, // so checking coordinates is unnecessary as input is always within handle bounds. if (isAppHandle(mWindowDecorViewHolder) && Flags.enableAdditionalWindowsAboveStatusBar() && isCaptionVisible()) { return true; } return checkTouchEventInCaption(ev); } Loading Loading @@ -1002,7 +1063,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin * @param ev the MotionEvent to compare */ void checkTouchEvent(MotionEvent ev) { if (mResult.mRootView == null) return; if (mResult.mRootView == null || Flags.enableAdditionalWindowsAboveStatusBar()) return; final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption); final View handle = caption.findViewById(R.id.caption_handle); final boolean inHandle = !isHandleMenuActive() Loading @@ -1024,7 +1085,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin * @param ev the MotionEvent to compare against. */ void updateHoverAndPressStatus(MotionEvent ev) { if (mResult.mRootView == null) return; if (mResult.mRootView == null || Flags.enableAdditionalWindowsAboveStatusBar()) return; final View handle = mResult.mRootView.findViewById(R.id.caption_handle); final boolean inHandle = !isHandleMenuActive() && checkTouchEventInFocusedCaptionHandle(ev); Loading @@ -1034,15 +1095,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // We want handle to remain pressed if the pointer moves outside of it during a drag. handle.setPressed((inHandle && action == ACTION_DOWN) || (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL)); if (isHandleMenuActive() && !isHandleMenuAboveStatusBar()) { if (isHandleMenuActive()) { mHandleMenu.checkMotionEvent(ev); } } private boolean isHandleMenuAboveStatusBar() { return Flags.enableAdditionalWindowsAboveStatusBar() && !mTaskInfo.isFreeform(); } private boolean pointInView(View v, float x, float y) { return v != null && v.getLeft() <= x && v.getRight() >= x && v.getTop() <= y && v.getBottom() >= y; Loading @@ -1054,6 +1111,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin closeHandleMenu(); mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId); disposeResizeVeil(); disposeStatusBarInputLayer(); clearCurrentViewHostRunnable(); super.close(); } Loading Loading @@ -1153,6 +1211,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin DesktopModeWindowDecoration create( Context context, DisplayController displayController, SplitScreenController splitScreenController, ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, Loading @@ -1164,6 +1223,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin return new DesktopModeWindowDecoration( context, displayController, splitScreenController, taskOrganizer, taskInfo, taskSurface, Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +3 −1 Original line number Diff line number Diff line Loading @@ -139,7 +139,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> private SurfaceControlViewHost mViewHost; private Configuration mWindowDecorConfig; TaskDragResizer mTaskDragResizer; private boolean mIsCaptionVisible; boolean mIsCaptionVisible; /** The most recent set of insets applied to this window decoration. */ private WindowDecorationInsets mWindowDecorationInsets; Loading Loading @@ -508,6 +508,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mTaskDragResizer = taskDragResizer; } // TODO(b/346441962): Move these three methods closer to implementing or View-level classes to // keep implementation details more encapsulated. private void setCaptionVisibility(View rootView, boolean visible) { if (rootView == null) { return; Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt +7 −3 Original line number Diff line number Diff line Loading @@ -30,18 +30,22 @@ import android.view.WindowManager */ class AdditionalSystemViewContainer( private val context: Context, layoutId: Int, taskId: Int, x: Int, y: Int, width: Int, height: Int height: Int, layoutId: Int? = null ) : AdditionalViewContainer() { override val view: View val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java) init { if (layoutId != null) { view = LayoutInflater.from(context).inflate(layoutId, null) } else { view = View(context) } val lp = WindowManager.LayoutParams( width, height, x, y, WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL, Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +12 −8 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOT import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener import com.android.wm.shell.draganddrop.DragAndDropController import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE Loading Loading @@ -1334,33 +1335,36 @@ class DesktopTasksController( * * @param taskInfo the task being dragged. * @param y height of drag, to be checked against status bar height. * @return the [IndicatorType] used for the resulting transition */ fun onDragPositioningEndThroughStatusBar( inputCoordinates: PointF, taskInfo: RunningTaskInfo, ) { val indicator = getVisualIndicator() ?: return ): IndicatorType { val indicator = getVisualIndicator() ?: return IndicatorType.NO_INDICATOR val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode) when (indicatorType) { DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return IndicatorType.TO_DESKTOP_INDICATOR -> { val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return IndicatorType.NO_INDICATOR if (Flags.enableWindowingDynamicInitialBounds()) { finalizeDragToDesktop(taskInfo, calculateInitialBounds(displayLayout, taskInfo)) } else { finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout)) } } DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR, DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> { IndicatorType.NO_INDICATOR, IndicatorType.TO_FULLSCREEN_INDICATOR -> { cancelDragToDesktop(taskInfo) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { requestSplit(taskInfo, leftOrTop = true) } DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { requestSplit(taskInfo, leftOrTop = false) } } return indicatorType } /** Update the exclusion region for a specified task */ Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +75 −14 Original line number Diff line number Diff line Loading @@ -38,6 +38,8 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; Loading Loading @@ -326,7 +328,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (decoration == null) return; final RunningTaskInfo oldTaskInfo = decoration.mTaskInfo; if (taskInfo.displayId != oldTaskInfo.displayId) { if (taskInfo.displayId != oldTaskInfo.displayId && !Flags.enableAdditionalWindowsAboveStatusBar()) { removeTaskFromEventReceiver(oldTaskInfo.displayId); incrementEventReceiverTasks(taskInfo.displayId); } Loading Loading @@ -390,7 +393,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.close(); final int displayId = taskInfo.displayId; if (mEventReceiversByDisplay.contains(displayId)) { if (mEventReceiversByDisplay.contains(displayId) && !Flags.enableAdditionalWindowsAboveStatusBar()) { removeTaskFromEventReceiver(displayId); } // Remove the decoration from the cache last because WindowDecoration#close could still Loading Loading @@ -515,6 +519,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } } else if (id == R.id.split_screen_button) { decoration.closeHandleMenu(); // When the app enters split-select, the handle will no longer be visible, meaning // we shouldn't receive input for it any longer. decoration.disposeStatusBarInputLayer(); mDesktopTasksController.requestSplit(decoration.mTaskInfo); } else if (id == R.id.open_in_browser_button) { // TODO(b/346441962): let the decoration handle the click gesture and only call back Loading Loading @@ -651,13 +658,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); final RunningTaskInfo taskInfo = decoration.mTaskInfo; if (DesktopModeStatus.canEnterDesktopMode(mContext) && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { && !taskInfo.isFreeform()) { return handleNonFreeformMotionEvent(decoration, v, e); } else { return handleFreeformMotionEvent(decoration, taskInfo, v, e); } } private boolean handleNonFreeformMotionEvent(DesktopModeWindowDecoration decoration, View v, MotionEvent e) { final int id = v.getId(); if (id == R.id.caption_handle) { if (e.getActionMasked() == MotionEvent.ACTION_DOWN) { // Caption handle is located within the status bar region, meaning the // DisplayPolicy will attempt to transfer this input to status bar if it's // a swipe down. Pilfer here to keep the gesture in handle alone. mInputManager.pilferPointers(v.getViewRootImpl().getInputToken()); } handleCaptionThroughStatusBar(e, decoration); final boolean wasDragging = mIsDragging; updateDragStatus(e.getActionMasked()); // Only prevent onClick from receiving this event if it's a drag. return wasDragging; } return false; } private boolean handleFreeformMotionEvent(DesktopModeWindowDecoration decoration, RunningTaskInfo taskInfo, View v, MotionEvent e) { final int id = v.getId(); if (mGestureDetector.onTouchEvent(e)) { return true; } final int id = v.getId(); final boolean touchingButton = (id == R.id.close_window || id == R.id.maximize_window || id == R.id.open_menu_button); switch (e.getActionMasked()) { Loading @@ -666,7 +698,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDragPositioningCallback.onDragPositioningStart( 0 /* ctrlType */, e.getRawX(0), e.getRawY(0)); mIsDragging = false; updateDragStatus(e.getActionMasked()); mHasLongClicked = false; // Do not consume input event if a button is touched, otherwise it would // prevent the button's ripple effect from showing. Loading @@ -686,7 +718,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { decoration.mTaskSurface, e.getRawX(dragPointerIdx), newTaskBounds); mIsDragging = true; updateDragStatus(e.getActionMasked()); return true; } case MotionEvent.ACTION_UP: Loading Loading @@ -716,7 +748,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { // onClick call that results. return false; } else { mIsDragging = false; updateDragStatus(e.getActionMasked()); return true; } } Loading @@ -724,6 +756,21 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return true; } private void updateDragStatus(int eventAction) { switch (eventAction) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { mIsDragging = false; break; } case MotionEvent.ACTION_MOVE: { mIsDragging = true; break; } } } /** * Perform a task size toggle on release of the double-tap, assuming no drag event * was handled during the double-tap. Loading Loading @@ -848,6 +895,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * * @param relevantDecor the window decoration of the focused task's caption. This method only * handles motion events outside this caption's bounds. * TODO(b/349135068): Outside-touch detection no longer works with the * enableAdditionalWindowsAboveStatusBar flag enabled. This * will be fixed once we can add FLAG_WATCH_OUTSIDE_TOUCH to relevant menus, * at which point, all EventReceivers and external touch logic should be removed. */ private void handleEventOutsideCaption(MotionEvent ev, DesktopModeWindowDecoration relevantDecor) { Loading Loading @@ -900,9 +951,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { dragFromStatusBarAllowed = windowingMode == WINDOWING_MODE_FULLSCREEN || windowingMode == WINDOWING_MODE_MULTI_WINDOW; } if (dragFromStatusBarAllowed && relevantDecor.checkTouchEventInFocusedCaptionHandle(ev)) { final boolean shouldStartTransitionDrag = relevantDecor.checkTouchEventInFocusedCaptionHandle(ev) || Flags.enableAdditionalWindowsAboveStatusBar(); if (dragFromStatusBarAllowed && shouldStartTransitionDrag) { mTransitionDragActive = true; } break; Loading @@ -916,8 +968,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { // Though this isn't a hover event, we need to update handle's hover state // as it likely will change. relevantDecor.updateHoverAndPressStatus(ev); DesktopModeVisualIndicator.IndicatorType resultType = mDesktopTasksController.onDragPositioningEndThroughStatusBar( new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo); // If we are entering split select, handle will no longer be visible and // should not be receiving any input. if (resultType == TO_SPLIT_LEFT_INDICATOR || resultType != TO_SPLIT_RIGHT_INDICATOR) { relevantDecor.disposeStatusBarInputLayer(); } mMoveToDesktopAnimator = null; return; } else { Loading @@ -929,7 +988,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { relevantDecor.checkTouchEvent(ev); break; } case ACTION_MOVE: { if (relevantDecor == null) { return; Loading Loading @@ -1089,6 +1147,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDesktopModeWindowDecorFactory.create( mContext, mDisplayController, mSplitScreenController, mTaskOrganizer, taskInfo, taskSurface, Loading Loading @@ -1131,8 +1190,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { windowDecoration.setDragDetector(touchEventListener.mDragDetector); windowDecoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */); if (!Flags.enableAdditionalWindowsAboveStatusBar()) { incrementEventReceiverTasks(taskInfo.displayId); } } private RunningTaskInfo getOtherSplitTask(int taskId) { @SplitPosition int remainingTaskPosition = mSplitScreenController Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +77 −17 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize; Loading Loading @@ -101,6 +102,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private final @ShellBackgroundThread ShellExecutor mBgExecutor; private final Choreographer mChoreographer; private final SyncTransactionQueue mSyncQueue; private final SplitScreenController mSplitScreenController; private WindowDecorationViewHolder mWindowDecorViewHolder; private View.OnClickListener mOnCaptionButtonClickListener; Loading Loading @@ -151,6 +153,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin DesktopModeWindowDecoration( Context context, DisplayController displayController, SplitScreenController splitScreenController, ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, Loading @@ -159,16 +162,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin Choreographer choreographer, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { this (context, displayController, taskOrganizer, taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue, rootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE); this (context, displayController, splitScreenController, taskOrganizer, taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue, rootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE); } DesktopModeWindowDecoration( Context context, DisplayController displayController, SplitScreenController splitScreenController, ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, Loading @@ -187,6 +192,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, surfaceControlViewHostFactory); mSplitScreenController = splitScreenController; mHandler = handler; mBgExecutor = bgExecutor; mChoreographer = choreographer; Loading Loading @@ -367,27 +373,41 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin if (mResult.mRootView == null) { // This means something blocks the window decor from showing, e.g. the task is hidden. // Nothing is set up in this case including the decoration surface. disposeStatusBarInputLayer(); Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces return; } if (oldRootView != mResult.mRootView) { disposeStatusBarInputLayer(); mWindowDecorViewHolder = createViewHolder(); } Trace.beginSection("DesktopModeWindowDecoration#relayout-binding"); mWindowDecorViewHolder.bindData(mTaskInfo); final Point position = new Point(); if (isAppHandle(mWindowDecorViewHolder)) { position.set(determineHandlePosition()); } mWindowDecorViewHolder.bindData(mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight, isCaptionVisible()); Trace.endSection(); if (!mTaskInfo.isFocused) { closeHandleMenu(); closeMaximizeMenu(); } updateDragResizeListener(oldDecorationSurface); updateMaximizeMenu(startT); Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces } private boolean isCaptionVisible() { return mTaskInfo.isVisible && mIsCaptionVisible; } private void setCapturedLink(Uri capturedLink, long timeStamp) { if (capturedLink == null || (mCapturedLink != null && mCapturedLink.mTimeStamp == timeStamp)) { Loading Loading @@ -469,12 +489,42 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } } private Point determineHandlePosition() { final Point position = new Point(mResult.mCaptionX, 0); if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT && mDisplayController.getDisplayLayout(mTaskInfo.displayId).isLandscape() ) { // If this is the right split task, add left stage's width. final Rect leftStageBounds = new Rect(); mSplitScreenController.getStageBounds(leftStageBounds, new Rect()); position.x += leftStageBounds.width(); } return position; } /** * Dispose of the view used to forward inputs in status bar region. Intended to be * used any time handle is no longer visible. */ void disposeStatusBarInputLayer() { if (!isAppHandle(mWindowDecorViewHolder) || !Flags.enableAdditionalWindowsAboveStatusBar()) { return; } ((AppHandleViewHolder) mWindowDecorViewHolder).disposeStatusBarInputLayer(); } private WindowDecorationViewHolder createViewHolder() { if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) { return new AppHandleViewHolder( mResult.mRootView, mOnCaptionTouchListener, mOnCaptionButtonClickListener mOnCaptionButtonClickListener, (v, event) -> { updateHoverAndPressStatus(event); return true; } ); } else if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_header) { Loading @@ -497,6 +547,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin throw new IllegalArgumentException("Unexpected layout resource id"); } private boolean isAppHandle(WindowDecorationViewHolder viewHolder) { return viewHolder instanceof AppHandleViewHolder; } @VisibleForTesting static void updateRelayoutParams( RelayoutParams relayoutParams, Loading Loading @@ -965,10 +1019,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin * @return {@code true} if event is inside caption handle view, {@code false} if not */ boolean checkTouchEventInFocusedCaptionHandle(MotionEvent ev) { if (isHandleMenuActive() || !(mWindowDecorViewHolder instanceof AppHandleViewHolder)) { if (isHandleMenuActive() || !isAppHandle(mWindowDecorViewHolder) || Flags.enableAdditionalWindowsAboveStatusBar()) { return false; } // The status bar input layer can only receive input in handle coordinates to begin with, // so checking coordinates is unnecessary as input is always within handle bounds. if (isAppHandle(mWindowDecorViewHolder) && Flags.enableAdditionalWindowsAboveStatusBar() && isCaptionVisible()) { return true; } return checkTouchEventInCaption(ev); } Loading Loading @@ -1002,7 +1063,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin * @param ev the MotionEvent to compare */ void checkTouchEvent(MotionEvent ev) { if (mResult.mRootView == null) return; if (mResult.mRootView == null || Flags.enableAdditionalWindowsAboveStatusBar()) return; final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption); final View handle = caption.findViewById(R.id.caption_handle); final boolean inHandle = !isHandleMenuActive() Loading @@ -1024,7 +1085,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin * @param ev the MotionEvent to compare against. */ void updateHoverAndPressStatus(MotionEvent ev) { if (mResult.mRootView == null) return; if (mResult.mRootView == null || Flags.enableAdditionalWindowsAboveStatusBar()) return; final View handle = mResult.mRootView.findViewById(R.id.caption_handle); final boolean inHandle = !isHandleMenuActive() && checkTouchEventInFocusedCaptionHandle(ev); Loading @@ -1034,15 +1095,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // We want handle to remain pressed if the pointer moves outside of it during a drag. handle.setPressed((inHandle && action == ACTION_DOWN) || (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL)); if (isHandleMenuActive() && !isHandleMenuAboveStatusBar()) { if (isHandleMenuActive()) { mHandleMenu.checkMotionEvent(ev); } } private boolean isHandleMenuAboveStatusBar() { return Flags.enableAdditionalWindowsAboveStatusBar() && !mTaskInfo.isFreeform(); } private boolean pointInView(View v, float x, float y) { return v != null && v.getLeft() <= x && v.getRight() >= x && v.getTop() <= y && v.getBottom() >= y; Loading @@ -1054,6 +1111,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin closeHandleMenu(); mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId); disposeResizeVeil(); disposeStatusBarInputLayer(); clearCurrentViewHostRunnable(); super.close(); } Loading Loading @@ -1153,6 +1211,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin DesktopModeWindowDecoration create( Context context, DisplayController displayController, SplitScreenController splitScreenController, ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, Loading @@ -1164,6 +1223,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin return new DesktopModeWindowDecoration( context, displayController, splitScreenController, taskOrganizer, taskInfo, taskSurface, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +3 −1 Original line number Diff line number Diff line Loading @@ -139,7 +139,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> private SurfaceControlViewHost mViewHost; private Configuration mWindowDecorConfig; TaskDragResizer mTaskDragResizer; private boolean mIsCaptionVisible; boolean mIsCaptionVisible; /** The most recent set of insets applied to this window decoration. */ private WindowDecorationInsets mWindowDecorationInsets; Loading Loading @@ -508,6 +508,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mTaskDragResizer = taskDragResizer; } // TODO(b/346441962): Move these three methods closer to implementing or View-level classes to // keep implementation details more encapsulated. private void setCaptionVisibility(View rootView, boolean visible) { if (rootView == null) { return; Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt +7 −3 Original line number Diff line number Diff line Loading @@ -30,18 +30,22 @@ import android.view.WindowManager */ class AdditionalSystemViewContainer( private val context: Context, layoutId: Int, taskId: Int, x: Int, y: Int, width: Int, height: Int height: Int, layoutId: Int? = null ) : AdditionalViewContainer() { override val view: View val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java) init { if (layoutId != null) { view = LayoutInflater.from(context).inflate(layoutId, null) } else { view = View(context) } val lp = WindowManager.LayoutParams( width, height, x, y, WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL, Loading