Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b37c4e91 authored by mattsziklay's avatar mattsziklay Committed by Matt Sziklay
Browse files

Add interactive view above status bar.

Adds an invisible view layered above status bar to forward input through
the existing handleCaptionThroughStatusBar logic. This reduces our
reliance on a global input monitor and also fixes an issue where desktop
mode could be invoked by dragging down in handle coordinates even while
notification shade is active.

Bug: 349135068
Bug: 341997116
Test: manual
Flag: com.android.window.flags.enable_additional_windows_above_status_bar
Change-Id: I94120c64c7f359661fca632df0251989cd93fa07
parent c1095d06
Loading
Loading
Loading
Loading
+12 −8
Original line number Diff line number Diff line
@@ -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
@@ -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 */
+75 −14
Original line number Diff line number Diff line
@@ -36,6 +36,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;

@@ -327,7 +329,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);
        }
@@ -391,7 +394,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
@@ -523,6 +527,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
@@ -659,13 +666,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()) {
@@ -674,7 +706,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.
@@ -694,7 +726,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                            decoration.mTaskSurface,
                            e.getRawX(dragPointerIdx),
                            newTaskBounds);
                    mIsDragging = true;
                    updateDragStatus(e.getActionMasked());
                    return true;
                }
                case MotionEvent.ACTION_UP:
@@ -724,7 +756,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                        // onClick call that results.
                        return false;
                    } else {
                        mIsDragging = false;
                        updateDragStatus(e.getActionMasked());
                        return true;
                    }
                }
@@ -732,6 +764,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.
@@ -856,6 +903,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) {
@@ -908,9 +959,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;
@@ -924,8 +976,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 {
@@ -937,7 +996,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                relevantDecor.checkTouchEvent(ev);
                break;
            }

            case ACTION_MOVE: {
                if (relevantDecor == null) {
                    return;
@@ -1097,6 +1155,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                mDesktopModeWindowDecorFactory.create(
                        mContext,
                        mDisplayController,
                        mSplitScreenController,
                        mTaskOrganizer,
                        taskInfo,
                        taskSurface,
@@ -1139,8 +1198,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
+77 −17
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -151,6 +153,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
    DesktopModeWindowDecoration(
            Context context,
            DisplayController displayController,
            SplitScreenController splitScreenController,
            ShellTaskOrganizer taskOrganizer,
            ActivityManager.RunningTaskInfo taskInfo,
            SurfaceControl taskSurface,
@@ -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,
@@ -187,6 +192,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
                surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
                windowContainerTransactionSupplier, surfaceControlSupplier,
                surfaceControlViewHostFactory);
        mSplitScreenController = splitScreenController;
        mHandler = handler;
        mBgExecutor = bgExecutor;
        mChoreographer = choreographer;
@@ -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)) {
@@ -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) {
@@ -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,
@@ -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);
    }
@@ -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()
@@ -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);
@@ -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;
@@ -1054,6 +1111,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        closeHandleMenu();
        mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
        disposeResizeVeil();
        disposeStatusBarInputLayer();
        clearCurrentViewHostRunnable();
        super.close();
    }
@@ -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,
@@ -1164,6 +1223,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
            return new DesktopModeWindowDecoration(
                    context,
                    displayController,
                    splitScreenController,
                    taskOrganizer,
                    taskInfo,
                    taskSurface,
+3 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+7 −3
Original line number Diff line number Diff line
@@ -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