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

Commit 60695853 authored by Eghosa Ewansiha-Vlachavas's avatar Eghosa Ewansiha-Vlachavas Committed by Android (Google) Code Review
Browse files

Merge "Respect bounds and size constraints when modifying launch params for dw" into main

parents 7c86fdfa 2abc7d40
Loading
Loading
Loading
Loading
+45 −10
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.server.wm;

import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.LaunchParamsModifierUtils.applyLayoutGravity;
import static com.android.server.wm.LaunchParamsModifierUtils.calculateLayoutBounds;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,7 +28,9 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.SystemProperties;
import android.util.Size;
import android.util.Slog;
import android.view.Gravity;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -119,10 +123,37 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
            return RESULT_SKIP;
        }

        // Use stable frame instead of raw frame to avoid launching freeform windows on top of
        // stable insets, which usually are system widgets such as sysbar & navbar.
        final Rect stableBounds = new Rect();
        task.getDisplayArea().getStableRect(stableBounds);
        final int desiredWidth = (int) (stableBounds.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
        final int desiredHeight = (int) (stableBounds.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);

        if (options != null && options.getLaunchBounds() != null) {
            outParams.mBounds.set(options.getLaunchBounds());
            appendLog("inherit-from-options=" + outParams.mBounds);
        } else if (layout != null) {
            final int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
            final int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
            if (layout.hasSpecifiedSize()) {
                calculateLayoutBounds(stableBounds, layout, outParams.mBounds,
                        new Size(desiredWidth, desiredHeight));
                applyLayoutGravity(verticalGravity, horizontalGravity, outParams.mBounds,
                        stableBounds);
                appendLog("layout specifies sizes, inheriting size and applying gravity");
            } else if (verticalGravity > 0 || horizontalGravity > 0) {
                calculateAndCentreInitialBounds(task, outParams);
                applyLayoutGravity(verticalGravity, horizontalGravity, outParams.mBounds,
                        stableBounds);
                appendLog("layout specifies gravity, applying desired bounds and gravity");
            }
        } else {
            calculateAndCentreInitialBounds(task, outParams);
            appendLog("layout not specified, applying desired bounds");
        }

        appendLog("setting desktop mode task bounds to %s", outParams.mBounds);

        appendLog("final desktop mode task bounds set to %s", outParams.mBounds);
        return RESULT_CONTINUE;
    }

@@ -133,13 +164,17 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
    private void calculateAndCentreInitialBounds(Task task,
            LaunchParamsController.LaunchParams outParams) {
        // TODO(b/319819547): Account for app constraints so apps do not become letterboxed
        final Rect windowBounds = task.getDisplayArea().getBounds();
        final int width = (int) (windowBounds.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
        final int height = (int) (windowBounds.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
        outParams.mBounds.right = width;
        outParams.mBounds.bottom = height;
        outParams.mBounds.offset(windowBounds.centerX() - outParams.mBounds.centerX(),
                windowBounds.centerY() - outParams.mBounds.centerY());
        final Rect stableBounds = new Rect();
        task.getDisplayArea().getStableRect(stableBounds);
        // The desired dimensions that a fully resizable window should take when initially entering
        // desktop mode. Calculated as a percentage of the available display area as defined by the
        // DESKTOP_MODE_INITIAL_BOUNDS_SCALE.
        final int desiredWidth = (int) (stableBounds.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
        final int desiredHeight = (int) (stableBounds.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
        outParams.mBounds.right = desiredWidth;
        outParams.mBounds.bottom = desiredHeight;
        outParams.mBounds.offset(stableBounds.centerX() - outParams.mBounds.centerX(),
                stableBounds.centerY() - outParams.mBounds.centerY());
    }

    private void initLogBuilder(Task task, ActivityRecord activity) {
+102 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.util.Size;
import android.view.Gravity;

class LaunchParamsModifierUtils {

    /**
     * Calculates bounds based on window layout size manifest values. These can include width,
     * height, width fraction and height fraction. In the event only one dimension of values are
     * specified in the manifest (e.g. width but no height value), the corresponding display area
     * dimension will be used as the default value unless some desired sizes have been specified.
     */
    static void calculateLayoutBounds(@NonNull Rect stableBounds,
            @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect inOutBounds,
            @Nullable Size desiredSize) {
        final int defaultWidth = stableBounds.width();
        final int defaultHeight = stableBounds.height();
        int width;
        int height;

        if (desiredSize == null) {
            // If desired bounds have not been specified, use the exiting default bounds as the
            // desired.
            desiredSize = new Size(stableBounds.width(), stableBounds.height());
        }

        width = desiredSize.getWidth();
        if (windowLayout.width > 0 && windowLayout.width < defaultWidth) {
            width = windowLayout.width;
        } else if (windowLayout.widthFraction > 0 && windowLayout.widthFraction < 1.0f) {
            width = (int) (defaultWidth * windowLayout.widthFraction);
        }

        height = desiredSize.getHeight();
        if (windowLayout.height > 0 && windowLayout.height < defaultHeight) {
            height = windowLayout.height;
        } else if (windowLayout.heightFraction > 0 && windowLayout.heightFraction < 1.0f) {
            height = (int) (defaultHeight * windowLayout.heightFraction);
        }

        inOutBounds.set(0, 0, width, height);
    }

    /**
     * Applies a vertical and horizontal gravity on the inOutBounds in relation to the stableBounds.
     */
    static void applyLayoutGravity(int verticalGravity, int horizontalGravity,
            @NonNull Rect inOutBounds, @NonNull Rect stableBounds) {
        final int width = inOutBounds.width();
        final int height = inOutBounds.height();

        final float fractionOfHorizontalOffset;
        switch (horizontalGravity) {
            case Gravity.LEFT:
                fractionOfHorizontalOffset = 0f;
                break;
            case Gravity.RIGHT:
                fractionOfHorizontalOffset = 1f;
                break;
            default:
                fractionOfHorizontalOffset = 0.5f;
        }

        final float fractionOfVerticalOffset;
        switch (verticalGravity) {
            case Gravity.TOP:
                fractionOfVerticalOffset = 0f;
                break;
            case Gravity.BOTTOM:
                fractionOfVerticalOffset = 1f;
                break;
            default:
                fractionOfVerticalOffset = 0.5f;
        }

        inOutBounds.offsetTo(stableBounds.left, stableBounds.top);
        final int xOffset = (int) (fractionOfHorizontalOffset * (stableBounds.width() - width));
        final int yOffset = (int) (fractionOfVerticalOffset * (stableBounds.height() - height));
        inOutBounds.offset(xOffset, yOffset);
    }
}
+10 −60
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static com.android.server.wm.ActivityStarter.Request;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.LaunchParamsModifierUtils.applyLayoutGravity;
import static com.android.server.wm.LaunchParamsModifierUtils.calculateLayoutBounds;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -640,68 +642,16 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
        // stable insets, which usually are system widgets such as sysbar & navbar.
        final Rect stableBounds = mTmpStableBounds;
        displayArea.getStableRect(stableBounds);
        final int defaultWidth = stableBounds.width();
        final int defaultHeight = stableBounds.height();

        int width;
        int height;
        if (!windowLayout.hasSpecifiedSize()) {
            if (!inOutBounds.isEmpty()) {
                // If the bounds is resolved already and WindowLayout doesn't have any opinion on
                // its size, use the already resolved size and apply the gravity to it.
                width = inOutBounds.width();
                height = inOutBounds.height();
            } else {

        if (windowLayout.hasSpecifiedSize()) {
            calculateLayoutBounds(stableBounds, windowLayout, inOutBounds,
                    /* desiredBounds */ null);
        } else if (inOutBounds.isEmpty()) {
            getTaskBounds(root, displayArea, windowLayout, WINDOWING_MODE_FREEFORM,
                    /* hasInitialBounds */ false, inOutBounds);
                width = inOutBounds.width();
                height = inOutBounds.height();
            }
        } else {
            width = defaultWidth;
            if (windowLayout.width > 0 && windowLayout.width < defaultWidth) {
                width = windowLayout.width;
            } else if (windowLayout.widthFraction > 0 && windowLayout.widthFraction < 1.0f) {
                width = (int) (width * windowLayout.widthFraction);
            }

            height = defaultHeight;
            if (windowLayout.height > 0 && windowLayout.height < defaultHeight) {
                height = windowLayout.height;
            } else if (windowLayout.heightFraction > 0 && windowLayout.heightFraction < 1.0f) {
                height = (int) (height * windowLayout.heightFraction);
            }
        }

        final float fractionOfHorizontalOffset;
        switch (horizontalGravity) {
            case Gravity.LEFT:
                fractionOfHorizontalOffset = 0f;
                break;
            case Gravity.RIGHT:
                fractionOfHorizontalOffset = 1f;
                break;
            default:
                fractionOfHorizontalOffset = 0.5f;
        }

        final float fractionOfVerticalOffset;
        switch (verticalGravity) {
            case Gravity.TOP:
                fractionOfVerticalOffset = 0f;
                break;
            case Gravity.BOTTOM:
                fractionOfVerticalOffset = 1f;
                break;
            default:
                fractionOfVerticalOffset = 0.5f;
        }

        inOutBounds.set(0, 0, width, height);
        inOutBounds.offset(stableBounds.left, stableBounds.top);
        final int xOffset = (int) (fractionOfHorizontalOffset * (defaultWidth - width));
        final int yOffset = (int) (fractionOfVerticalOffset * (defaultHeight - height));
        inOutBounds.offset(xOffset, yOffset);
        applyLayoutGravity(verticalGravity, horizontalGravity, inOutBounds,
                stableBounds);
    }

    private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
+245 −46

File changed.

Preview size limit exceeded, changes collapsed.

+206 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm;

import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;

import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;

import android.app.ActivityOptions;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.view.Gravity;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.WindowInsets;

import com.android.server.wm.LaunchParamsController.LaunchParams;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;

/** Common base class for launch param modifier unit test classes. */
public class LaunchParamsModifierTestsBase extends WindowTestsBase{

    static final Rect DISPLAY_BOUNDS = new Rect(/* left */ 0, /* top */ 0,
            /* right */ 1920, /* bottom */ 1080);
    static final Rect DISPLAY_STABLE_BOUNDS = new Rect(/* left */ 100,
            /* top */ 200, /* right */ 1620, /* bottom */ 680);

    ActivityRecord mActivity;

    LaunchParamsModifier mTarget;

    LaunchParams mCurrent;
    LaunchParams mResult;


    TestDisplayContent createNewDisplayContent(int windowingMode) {
        return createNewDisplayContent(windowingMode, DISPLAY_BOUNDS, DISPLAY_STABLE_BOUNDS);
    }

    TestDisplayContent createNewDisplayContent(int windowingMode, Rect displayBounds,
            Rect displayStableBounds) {
        final TestDisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
        display.getDefaultTaskDisplayArea().setWindowingMode(windowingMode);
        display.setBounds(displayBounds);
        display.getConfiguration().densityDpi = DENSITY_DEFAULT;
        display.getConfiguration().orientation = ORIENTATION_LANDSCAPE;
        configInsetsState(display.getInsetsStateController().getRawInsetsState(), display,
                displayStableBounds);
        return display;
    }

    /**
     * Creates insets sources so that we can get the expected stable frame.
     */
    static void configInsetsState(InsetsState state, DisplayContent display,
            Rect stableFrame) {
        final Rect displayFrame = display.getBounds();
        final int dl = displayFrame.left;
        final int dt = displayFrame.top;
        final int dr = displayFrame.right;
        final int db = displayFrame.bottom;
        final int sl = stableFrame.left;
        final int st = stableFrame.top;
        final int sr = stableFrame.right;
        final int sb = stableFrame.bottom;
        final @WindowInsets.Type.InsetsType int statusBarType = WindowInsets.Type.statusBars();
        final @WindowInsets.Type.InsetsType int navBarType = WindowInsets.Type.navigationBars();

        state.setDisplayFrame(displayFrame);
        if (sl > dl) {
            state.getOrCreateSource(InsetsSource.createId(null, 0, statusBarType), statusBarType)
                    .setFrame(dl, dt, sl, db);
        }
        if (st > dt) {
            state.getOrCreateSource(InsetsSource.createId(null, 1, statusBarType), statusBarType)
                    .setFrame(dl, dt, dr, st);
        }
        if (sr < dr) {
            state.getOrCreateSource(InsetsSource.createId(null, 0, navBarType), navBarType)
                    .setFrame(sr, dt, dr, db);
        }
        if (sb < db) {
            state.getOrCreateSource(InsetsSource.createId(null, 1, navBarType), navBarType)
                    .setFrame(dl, sb, dr, db);
        }
        // Recompute config and push to children.
        display.onRequestedOverrideConfigurationChanged(display.getConfiguration());
    }

    class CalculateRequestBuilder {
        private Task mTask;
        private ActivityInfo.WindowLayout mLayout;
        private ActivityRecord mActivity = LaunchParamsModifierTestsBase.this.mActivity;
        private ActivityRecord mSource;
        private ActivityOptions mOptions;
        private ActivityStarter.Request mRequest;
        private int mPhase = PHASE_BOUNDS;
        private LaunchParams mCurrentParams = mCurrent;
        private LaunchParams mOutParams = mResult;

        CalculateRequestBuilder setTask(Task task) {
            mTask = task;
            return this;
        }

        CalculateRequestBuilder setLayout(ActivityInfo.WindowLayout layout) {
            mLayout = layout;
            return this;
        }

        CalculateRequestBuilder setPhase(int phase) {
            mPhase = phase;
            return this;
        }

        CalculateRequestBuilder setActivity(ActivityRecord activity) {
            mActivity = activity;
            return this;
        }

        CalculateRequestBuilder setSource(ActivityRecord source) {
            mSource = source;
            return this;
        }

        CalculateRequestBuilder setOptions(ActivityOptions options) {
            mOptions = options;
            return this;
        }

        CalculateRequestBuilder setRequest(ActivityStarter.Request request) {
            mRequest = request;
            return this;
        }

        @LaunchParamsController.LaunchParamsModifier.Result int calculate() {
            return mTarget.onCalculate(mTask, mLayout, mActivity, mSource, mOptions, mRequest,
                    mPhase, mCurrentParams, mOutParams);
        }
    }

    static class WindowLayoutBuilder {
        private int mWidth = -1;
        private int mHeight = -1;
        private float mWidthFraction = -1f;
        private float mHeightFraction = -1f;
        private int mGravity = Gravity.NO_GRAVITY;
        private int mMinWidth = -1;
        private int mMinHeight = -1;

        WindowLayoutBuilder setWidth(int width) {
            mWidth = width;
            return this;
        }

        WindowLayoutBuilder setHeight(int height) {
            mHeight = height;
            return this;
        }

        WindowLayoutBuilder setWidthFraction(float widthFraction) {
            mWidthFraction = widthFraction;
            return this;
        }

        WindowLayoutBuilder setHeightFraction(float heightFraction) {
            mHeightFraction = heightFraction;
            return this;
        }

        WindowLayoutBuilder setGravity(int gravity) {
            mGravity = gravity;
            return this;
        }

        WindowLayoutBuilder setMinWidth(int minWidth) {
            mMinWidth = minWidth;
            return this;
        }

        WindowLayoutBuilder setMinHeight(int minHeight) {
            mMinHeight = minHeight;
            return this;
        }

        ActivityInfo.WindowLayout build() {
            return new ActivityInfo.WindowLayout(mWidth, mWidthFraction, mHeight, mHeightFraction,
                    mGravity, mMinWidth, mMinHeight);
        }
    }
}
Loading