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

Commit 874d48ac authored by Kazuki Takise's avatar Kazuki Takise
Browse files

Extract default freeform bounds logic from TaskLPM

This CL extracts getDefaultFreeformBounds from TaskLPM into a
new utility class so it can be used in other LPMs.

The behavior doesn't change except for:
- making the stable bounds and default size overridable.
- allowing usnig different default sizes depending on the
 orientation.

Bug: 185427982
Test: manual
Change-Id: Ie31bae7efdf338db76b482702b7b385ba6667b65
parent 1ff3f115
Loading
Loading
Loading
Loading
+129 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;

import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;

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

/**
 * The static class that defines some utility constants and functions that are shared among launch
 * params modifiers.
 */
class LaunchParamsUtil {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "LaunchParamsUtil" : TAG_ATM;
    private static final boolean DEBUG = false;

    // Screen size of Nexus 5x
    static final int DEFAULT_PORTRAIT_FREEFORM_WIDTH_DP = 412;
    static final int DEFAULT_PORTRAIT_FREEFORM_HEIGHT_DP = 732;

    // One of the most common tablet sizes that are small enough to fit in most large screens.
    private static final int DEFAULT_LANDSCAPE_FREEFORM_WIDTH_DP = 1064;
    private static final int DEFAULT_LANDSCAPE_FREEFORM_HEIGHT_DP = 600;

    private LaunchParamsUtil() {}

    /**
     * Gets centered bounds of width x height. If inOutBounds is not empty, the result bounds
     * centers at its center or displayArea's app bounds center if inOutBounds is empty.
     */
    static void centerBounds(@NonNull TaskDisplayArea displayArea, int width, int height,
            @NonNull Rect inOutBounds) {
        if (inOutBounds.isEmpty()) {
            displayArea.getStableRect(inOutBounds);
        }
        final int left = inOutBounds.centerX() - width / 2;
        final int top = inOutBounds.centerY() - height / 2;
        inOutBounds.set(left, top, left + width, top + height);
    }

    /**
     * Calculate the default size for a freeform environment. |defaultSize| is used as the default
     * DP size, but if this is null, the portrait phone size is used.
     */
    static Size getDefaultFreeformSize(@NonNull ActivityInfo info,
            @NonNull TaskDisplayArea displayArea,
            @NonNull ActivityInfo.WindowLayout layout, int orientation,
            @NonNull Rect stableBounds) {
        // Get window size based on Nexus 5x screen, we assume that this is enough to show content
        // of activities.
        final float density = (float) displayArea.getConfiguration().densityDpi / DENSITY_DEFAULT;
        final int freeformWidthInDp = (orientation == SCREEN_ORIENTATION_LANDSCAPE)
                ? DEFAULT_LANDSCAPE_FREEFORM_WIDTH_DP : DEFAULT_PORTRAIT_FREEFORM_WIDTH_DP;
        final int freeformHeightInDp = (orientation == SCREEN_ORIENTATION_LANDSCAPE)
                ? DEFAULT_LANDSCAPE_FREEFORM_HEIGHT_DP : DEFAULT_PORTRAIT_FREEFORM_HEIGHT_DP;
        final int freeformWidth = (int) (freeformWidthInDp * density + 0.5f);
        final int freeformHeight = (int) (freeformHeightInDp * density + 0.5f);

        // Minimum layout requirements.
        final int layoutMinWidth = (layout == null) ? -1 : layout.minWidth;
        final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight;

        // Max size, which is letterboxing/pillarboxing in displayArea. That's to say the large
        // dimension of default size is the small dimension of displayArea size, and the small
        // dimension of default size is calculated to keep the same aspect ratio as the
        // displayArea's. Here we use stable bounds of displayArea because that indicates the area
        // that isn't occupied by system widgets (e.g. sysbar and navbar).
        final int portraitHeight = Math.min(stableBounds.width(), stableBounds.height());
        final int otherDimension = Math.max(stableBounds.width(), stableBounds.height());
        final int portraitWidth = (portraitHeight * portraitHeight) / otherDimension;
        final int maxWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitHeight
                : portraitWidth;
        final int maxHeight = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitWidth
                : portraitHeight;
        final int width = Math.min(maxWidth, Math.max(freeformWidth, layoutMinWidth));
        final int height = Math.min(maxHeight, Math.max(freeformHeight, layoutMinHeight));
        final float aspectRatio = (float) Math.max(width, height) / (float) Math.min(width, height);

        // Aspect ratio requirements.
        final float minAspectRatio = info.getMinAspectRatio(orientation);
        final float maxAspectRatio = info.getMaxAspectRatio();

        // Adjust the width and height to the aspect ratio requirements.
        int adjWidth = width;
        int adjHeight = height;
        if (minAspectRatio >= 1 && aspectRatio < minAspectRatio) {
            // The aspect ratio is below the minimum, adjust it to the minimum.
            if (orientation == SCREEN_ORIENTATION_LANDSCAPE) {
                // Fix the width, scale the height.
                adjHeight = (int) (adjWidth / minAspectRatio + 0.5f);
            } else {
                // Fix the height, scale the width.
                adjWidth = (int) (adjHeight / minAspectRatio + 0.5f);
            }
        } else if (maxAspectRatio >= 1 && aspectRatio > maxAspectRatio) {
            // The aspect ratio exceeds the maximum, adjust it to the maximum.
            if (orientation == SCREEN_ORIENTATION_LANDSCAPE) {
                // Fix the width, scale the height.
                adjHeight = (int) (adjWidth / maxAspectRatio + 0.5f);
            } else {
                // Fix the height, scale the width.
                adjWidth = (int) (adjHeight / maxAspectRatio + 0.5f);
            }
        }

        return new Size(adjWidth, adjHeight);
    }
}
+8 −89
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.Size;
import android.util.Slog;
import android.view.Gravity;
import android.view.View;
@@ -66,10 +67,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_ATM;
    private static final boolean DEBUG = false;

    // Screen size of Nexus 5x
    private static final int DEFAULT_PORTRAIT_PHONE_WIDTH_DP = 412;
    private static final int DEFAULT_PORTRAIT_PHONE_HEIGHT_DP = 732;

    // Allowance of size matching.
    private static final int EPSILON = 2;

@@ -731,7 +728,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
        }

        // First we get the default size we want.
        getDefaultFreeformSize(root.info, displayArea, layout, orientation, mTmpBounds);
        displayArea.getStableRect(mTmpStableBounds);
        final Size defaultSize = LaunchParamsUtil.getDefaultFreeformSize(root.info, displayArea,
                layout, orientation, mTmpStableBounds);
        mTmpBounds.set(0, 0, defaultSize.getWidth(), defaultSize.getHeight());
        if (hasInitialBounds || sizeMatches(inOutBounds, mTmpBounds)) {
            // We're here because either input parameters specified initial bounds, or the suggested
            // bounds have the same size of the default freeform size. We should use the suggested
@@ -741,8 +741,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
                if (DEBUG) appendLog("freeform-size-orientation-match=" + inOutBounds);
            } else {
                // Meh, orientation doesn't match. Let's rotate inOutBounds in-place.
                centerBounds(displayArea, inOutBounds.height(), inOutBounds.width(),
                        inOutBounds);
                LaunchParamsUtil.centerBounds(displayArea, inOutBounds.height(),
                        inOutBounds.width(), inOutBounds);
                if (DEBUG) appendLog("freeform-orientation-mismatch=" + inOutBounds);
            }
        } else {
@@ -751,7 +751,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
            // to the center of suggested bounds (or the displayArea if no suggested bounds). The
            // default size might be too big to center to source activity bounds in displayArea, so
            // we may need to move it back to the displayArea.
            centerBounds(displayArea, mTmpBounds.width(), mTmpBounds.height(),
            LaunchParamsUtil.centerBounds(displayArea, mTmpBounds.width(), mTmpBounds.height(),
                    inOutBounds);
            adjustBoundsToFitInDisplayArea(displayArea, inOutBounds);
            if (DEBUG) appendLog("freeform-size-mismatch=" + inOutBounds);
@@ -799,87 +799,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
        return orientation;
    }

    private void getDefaultFreeformSize(@NonNull ActivityInfo info,
            @NonNull TaskDisplayArea displayArea,
            @NonNull ActivityInfo.WindowLayout layout, int orientation, @NonNull Rect bounds) {
        // Default size, which is letterboxing/pillarboxing in displayArea. That's to say the large
        // dimension of default size is the small dimension of displayArea size, and the small
        // dimension of default size is calculated to keep the same aspect ratio as the
        // displayArea's. Here we use stable bounds of displayArea because that indicates the area
        // that isn't occupied by system widgets (e.g. sysbar and navbar).
        final Rect stableBounds = mTmpStableBounds;
        displayArea.getStableRect(stableBounds);
        final int portraitHeight = Math.min(stableBounds.width(), stableBounds.height());
        final int otherDimension = Math.max(stableBounds.width(), stableBounds.height());
        final int portraitWidth = (portraitHeight * portraitHeight) / otherDimension;
        final int defaultWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitHeight
                : portraitWidth;
        final int defaultHeight = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitWidth
                : portraitHeight;

        // Get window size based on Nexus 5x screen, we assume that this is enough to show content
        // of activities.
        final float density = (float) displayArea.getConfiguration().densityDpi / DENSITY_DEFAULT;
        final int phonePortraitWidth = (int) (DEFAULT_PORTRAIT_PHONE_WIDTH_DP * density + 0.5f);
        final int phonePortraitHeight = (int) (DEFAULT_PORTRAIT_PHONE_HEIGHT_DP * density + 0.5f);
        final int phoneWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? phonePortraitHeight
                : phonePortraitWidth;
        final int phoneHeight = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? phonePortraitWidth
                : phonePortraitHeight;

        // Minimum layout requirements.
        final int layoutMinWidth = (layout == null) ? -1 : layout.minWidth;
        final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight;

        // Aspect ratio requirements.
        final float minAspectRatio = info.getMinAspectRatio(orientation);
        final float maxAspectRatio = info.getMaxAspectRatio();

        final int width = Math.min(defaultWidth, Math.max(phoneWidth, layoutMinWidth));
        final int height = Math.min(defaultHeight, Math.max(phoneHeight, layoutMinHeight));
        final float aspectRatio = (float) Math.max(width, height) / (float) Math.min(width, height);

        // Adjust the width and height to the aspect ratio requirements.
        int adjWidth = width;
        int adjHeight = height;
        if (minAspectRatio >= 1 && aspectRatio < minAspectRatio) {
            // The aspect ratio is below the minimum, adjust it to the minimum.
            if (orientation == SCREEN_ORIENTATION_LANDSCAPE) {
                // Fix the width, scale the height.
                adjHeight = (int) (adjWidth / minAspectRatio + 0.5f);
            } else {
                // Fix the height, scale the width.
                adjWidth = (int) (adjHeight / minAspectRatio + 0.5f);
            }
        } else if (maxAspectRatio >= 1 && aspectRatio > maxAspectRatio) {
            // The aspect ratio exceeds the maximum, adjust it to the maximum.
            if (orientation == SCREEN_ORIENTATION_LANDSCAPE) {
                // Fix the width, scale the height.
                adjHeight = (int) (adjWidth / maxAspectRatio + 0.5f);
            } else {
                // Fix the height, scale the width.
                adjWidth = (int) (adjHeight / maxAspectRatio + 0.5f);
            }
        }

        bounds.set(0, 0, adjWidth, adjHeight);
        bounds.offset(stableBounds.left, stableBounds.top);
    }

    /**
     * Gets centered bounds of width x height. If inOutBounds is not empty, the result bounds
     * centers at its center or displayArea's app bounds center if inOutBounds is empty.
     */
    private void centerBounds(@NonNull TaskDisplayArea displayArea, int width, int height,
            @NonNull Rect inOutBounds) {
        if (inOutBounds.isEmpty()) {
            displayArea.getStableRect(inOutBounds);
        }
        final int left = inOutBounds.centerX() - width / 2;
        final int top = inOutBounds.centerY() - height / 2;
        inOutBounds.set(left, top, left + width, top + height);
    }

    private void adjustBoundsToFitInDisplayArea(@NonNull TaskDisplayArea displayArea,
            @NonNull Rect inOutBounds) {
        final Rect stableBounds = mTmpStableBounds;