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

Commit 7e2d8841 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use a static function to get wallpaper crop bounds" into main

parents 411af4be 5761a0a0
Loading
Loading
Loading
Loading
+39 −42
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.server.wallpaper;

import static android.app.WallpaperManager.ORIENTATION_LANDSCAPE;
import static android.app.WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE;
import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
import static android.app.WallpaperManager.getOrientation;
import static android.app.WallpaperManager.getRotatedOrientation;
@@ -85,20 +84,11 @@ public class WallpaperCropper {

    private final WallpaperDisplayHelper mWallpaperDisplayHelper;

    /**
     * Helpers exposed to the window manager part (WallpaperController)
     */
    public interface WallpaperCropUtils {

        /**
         * Equivalent to {@link WallpaperCropper#getCrop(Point, Point, SparseArray, boolean)}
         */
        Rect getCrop(Point displaySize, Point bitmapSize,
                SparseArray<Rect> suggestedCrops, boolean rtl);
    }
    private final WallpaperDefaultDisplayInfo mDefaultDisplayInfo;

    WallpaperCropper(WallpaperDisplayHelper wallpaperDisplayHelper) {
        mWallpaperDisplayHelper = wallpaperDisplayHelper;
        mDefaultDisplayInfo = mWallpaperDisplayHelper.getDefaultDisplayInfo();
    }

    /**
@@ -117,15 +107,15 @@ public class WallpaperCropper {
     * </ul>
     *
     * @param displaySize        The dimensions of the surface where we want to render the wallpaper
     * @param defaultDisplayInfo The default display info
     * @param bitmapSize         The dimensions of the wallpaper bitmap
     * @param rtl                Whether the device is right-to-left
     * @param suggestedCrops     An optional list of user-defined crops for some orientations.
     *                        If there is a suggested crop for
     *
     * @return  A Rect indicating how to crop the bitmap for the current display.
     */
    public Rect getCrop(Point displaySize, Point bitmapSize,
            SparseArray<Rect> suggestedCrops, boolean rtl) {
    public static Rect getCrop(Point displaySize, WallpaperDefaultDisplayInfo defaultDisplayInfo,
            Point bitmapSize, SparseArray<Rect> suggestedCrops, boolean rtl) {

        int orientation = getOrientation(displaySize);

@@ -135,23 +125,24 @@ public class WallpaperCropper {

            // The first exception is if the device is a foldable and we're on the folded screen.
            // In that case, show the center of what's on the unfolded screen.
            int unfoldedOrientation = mWallpaperDisplayHelper.getUnfoldedOrientation(orientation);
            int unfoldedOrientation = defaultDisplayInfo.getUnfoldedOrientation(orientation);
            if (unfoldedOrientation != ORIENTATION_UNKNOWN) {
                // Let the system know that we're showing the full image on the unfolded screen
                SparseArray<Rect> newSuggestedCrops = new SparseArray<>();
                newSuggestedCrops.put(unfoldedOrientation, crop);
                // This will fall into "Case 4" of this function and center the folded screen
                return getCrop(displaySize, bitmapSize, newSuggestedCrops, rtl);
                return getCrop(displaySize, defaultDisplayInfo, bitmapSize, newSuggestedCrops,
                        rtl);
            }

            // The second exception is if we're on tablet and we're on portrait mode.
            // In that case, center the wallpaper relatively to landscape and put some parallax.
            boolean isTablet = mWallpaperDisplayHelper.isLargeScreen()
                    && !mWallpaperDisplayHelper.isFoldable();
            boolean isTablet = defaultDisplayInfo.isLargeScreen && !defaultDisplayInfo.isFoldable;
            if (isTablet && displaySize.x < displaySize.y) {
                Point rotatedDisplaySize = new Point(displaySize.y, displaySize.x);
                // compute the crop on landscape (without parallax)
                Rect landscapeCrop = getCrop(rotatedDisplaySize, bitmapSize, suggestedCrops, rtl);
                Rect landscapeCrop = getCrop(rotatedDisplaySize, defaultDisplayInfo, bitmapSize,
                        suggestedCrops, rtl);
                landscapeCrop = noParallax(landscapeCrop, rotatedDisplaySize, bitmapSize, rtl);
                // compute the crop on portrait at the center of the landscape crop
                crop = getAdjustedCrop(landscapeCrop, bitmapSize, displaySize, false, rtl, ADD);
@@ -173,7 +164,8 @@ public class WallpaperCropper {
            if (testCrop == null || testCrop.left < 0 || testCrop.top < 0
                    || testCrop.right > bitmapSize.x || testCrop.bottom > bitmapSize.y) {
                Slog.w(TAG, "invalid crop: " + testCrop + " for bitmap size: " + bitmapSize);
                return getCrop(displaySize, bitmapSize, new SparseArray<>(), rtl);
                return getCrop(displaySize, defaultDisplayInfo, bitmapSize, new SparseArray<>(),
                        rtl);
            }
        }

@@ -185,10 +177,9 @@ public class WallpaperCropper {

        // Case 3: if we have the 90° rotated orientation in the suggested crops, reuse it and
        // trying to preserve the zoom level and the center of the image
        SparseArray<Point> defaultDisplaySizes = mWallpaperDisplayHelper.getDefaultDisplaySizes();
        int rotatedOrientation = getRotatedOrientation(orientation);
        suggestedCrop = suggestedCrops.get(rotatedOrientation);
        Point suggestedDisplaySize = defaultDisplaySizes.get(rotatedOrientation);
        Point suggestedDisplaySize = defaultDisplayInfo.defaultDisplaySizes.get(rotatedOrientation);
        if (suggestedCrop != null) {
            // only keep the visible part (without parallax)
            Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
@@ -197,9 +188,9 @@ public class WallpaperCropper {

        // Case 4: if the device is a foldable, if we're looking for a folded orientation and have
        // the suggested crop of the relative unfolded orientation, reuse it by removing content.
        int unfoldedOrientation = mWallpaperDisplayHelper.getUnfoldedOrientation(orientation);
        int unfoldedOrientation = defaultDisplayInfo.getUnfoldedOrientation(orientation);
        suggestedCrop = suggestedCrops.get(unfoldedOrientation);
        suggestedDisplaySize = defaultDisplaySizes.get(unfoldedOrientation);
        suggestedDisplaySize = defaultDisplayInfo.defaultDisplaySizes.get(unfoldedOrientation);
        if (suggestedCrop != null) {
            // compute the visible part (without parallax) of the unfolded screen
            Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
@@ -207,8 +198,11 @@ public class WallpaperCropper {
            Rect res = getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, REMOVE);
            // if we removed some width, add it back to add a parallax effect
            if (res.width() < adjustedCrop.width()) {
                if (rtl) res.left = Math.min(res.left, adjustedCrop.left);
                else res.right = Math.max(res.right, adjustedCrop.right);
                if (rtl) {
                    res.left = Math.min(res.left, adjustedCrop.left);
                } else {
                    res.right = Math.max(res.right, adjustedCrop.right);
                }
                // use getAdjustedCrop(parallax=true) to make sure we don't exceed MAX_PARALLAX
                res = getAdjustedCrop(res, bitmapSize, displaySize, true, rtl, ADD);
            }
@@ -218,9 +212,9 @@ public class WallpaperCropper {

        // Case 5: if the device is a foldable, if we're looking for an unfolded orientation and
        // have the suggested crop of the relative folded orientation, reuse it by adding content.
        int foldedOrientation = mWallpaperDisplayHelper.getFoldedOrientation(orientation);
        int foldedOrientation = defaultDisplayInfo.getFoldedOrientation(orientation);
        suggestedCrop = suggestedCrops.get(foldedOrientation);
        suggestedDisplaySize = defaultDisplaySizes.get(foldedOrientation);
        suggestedDisplaySize = defaultDisplayInfo.defaultDisplaySizes.get(foldedOrientation);
        if (suggestedCrop != null) {
            // only keep the visible part (without parallax)
            Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
@@ -229,17 +223,19 @@ public class WallpaperCropper {

        // Case 6: for a foldable device, try to combine case 3 + case 4 or 5:
        // rotate, then fold or unfold
        Point rotatedDisplaySize = defaultDisplaySizes.get(rotatedOrientation);
        Point rotatedDisplaySize = defaultDisplayInfo.defaultDisplaySizes.get(rotatedOrientation);
        if (rotatedDisplaySize != null) {
            int rotatedFolded = mWallpaperDisplayHelper.getFoldedOrientation(rotatedOrientation);
            int rotateUnfolded = mWallpaperDisplayHelper.getUnfoldedOrientation(rotatedOrientation);
            int rotatedFolded = defaultDisplayInfo.getFoldedOrientation(rotatedOrientation);
            int rotateUnfolded = defaultDisplayInfo.getUnfoldedOrientation(rotatedOrientation);
            for (int suggestedOrientation : new int[]{rotatedFolded, rotateUnfolded}) {
                suggestedCrop = suggestedCrops.get(suggestedOrientation);
                if (suggestedCrop != null) {
                    Rect rotatedCrop = getCrop(rotatedDisplaySize, bitmapSize, suggestedCrops, rtl);
                    Rect rotatedCrop = getCrop(rotatedDisplaySize, defaultDisplayInfo, bitmapSize,
                            suggestedCrops, rtl);
                    SparseArray<Rect> rotatedCropMap = new SparseArray<>();
                    rotatedCropMap.put(rotatedOrientation, rotatedCrop);
                    return getCrop(displaySize, bitmapSize, rotatedCropMap, rtl);
                    return getCrop(displaySize, defaultDisplayInfo, bitmapSize, rotatedCropMap,
                            rtl);
                }
            }
        }
@@ -248,8 +244,8 @@ public class WallpaperCropper {
        Slog.w(TAG, "Could not find a proper default crop for display: " + displaySize
                + ", bitmap size: " + bitmapSize + ", suggested crops: " + suggestedCrops
                + ", orientation: " + orientation + ", rtl: " + rtl
                + ", defaultDisplaySizes: " + defaultDisplaySizes);
        return getCrop(displaySize, bitmapSize, new SparseArray<>(), rtl);
                + ", defaultDisplaySizes: " + defaultDisplayInfo.defaultDisplaySizes);
        return getCrop(displaySize, defaultDisplayInfo, bitmapSize, new SparseArray<>(), rtl);
    }

    /**
@@ -445,7 +441,7 @@ public class WallpaperCropper {
            Rect suggestedCrop = suggestedCrops.get(orientation);
            if (suggestedCrop != null) {
                adjustedSuggestedCrops.put(orientation,
                        getCrop(displaySize, bitmapSize, suggestedCrops, rtl));
                        getCrop(displaySize, mDefaultDisplayInfo, bitmapSize, suggestedCrops, rtl));
            }
        }

@@ -455,7 +451,8 @@ public class WallpaperCropper {
            int orientation = defaultDisplaySizes.keyAt(i);
            if (result.contains(orientation)) continue;
            Point displaySize = defaultDisplaySizes.valueAt(i);
            Rect newCrop = getCrop(displaySize, bitmapSize, adjustedSuggestedCrops, rtl);
            Rect newCrop = getCrop(displaySize, mDefaultDisplayInfo, bitmapSize,
                    adjustedSuggestedCrops, rtl);
            result.put(orientation, newCrop);
        }
        return result;
+5 −3
Original line number Diff line number Diff line
@@ -542,9 +542,11 @@ public class WallpaperDataParser {
                // to support back compatibility in B&R, save the crops for one orientation in the
                // legacy "cropLeft", "cropTop", "cropRight", "cropBottom" entries
                int orientationToPutInLegacyCrop = wallpaper.mOrientationWhenSet;
                if (mWallpaperDisplayHelper.isFoldable()) {
                    int unfoldedOrientation = mWallpaperDisplayHelper
                            .getUnfoldedOrientation(orientationToPutInLegacyCrop);
                WallpaperDefaultDisplayInfo defaultDisplayInfo =
                        mWallpaperDisplayHelper.getDefaultDisplayInfo();
                if (defaultDisplayInfo.isFoldable) {
                    int unfoldedOrientation = defaultDisplayInfo.getUnfoldedOrientation(
                            orientationToPutInLegacyCrop);
                    if (unfoldedOrientation != ORIENTATION_UNKNOWN) {
                        orientationToPutInLegacyCrop = unfoldedOrientation;
                    }
+207 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.wallpaper;

import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
import static android.app.WallpaperManager.getRotatedOrientation;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;

import android.app.WallpaperManager;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.SparseArray;
import android.view.WindowManager;
import android.view.WindowMetrics;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;


/**  A data class for the default display attributes used in wallpaper related operations. */
public final class WallpaperDefaultDisplayInfo {
    /**
     * A data class representing the screen orientations for a foldable device in the folded and
     * unfolded states.
     */
    @VisibleForTesting
    static final class FoldableOrientations {
        @WallpaperManager.ScreenOrientation
        public final int foldedOrientation;
        @WallpaperManager.ScreenOrientation
        public final int unfoldedOrientation;

        FoldableOrientations(int foldedOrientation, int unfoldedOrientation) {
            this.foldedOrientation = foldedOrientation;
            this.unfoldedOrientation = unfoldedOrientation;
        }

        @Override
        public boolean equals(Object other) {
            if (this == other) return true;
            if (!(other instanceof FoldableOrientations that)) return false;
            return foldedOrientation == that.foldedOrientation
                    && unfoldedOrientation == that.unfoldedOrientation;
        }

        @Override
        public int hashCode() {
            return Objects.hash(foldedOrientation, unfoldedOrientation);
        }
    }

    public final SparseArray<Point> defaultDisplaySizes;
    public final boolean isLargeScreen;
    public final boolean isFoldable;
    @VisibleForTesting
    final List<FoldableOrientations> foldableOrientations;

    public WallpaperDefaultDisplayInfo() {
        this.defaultDisplaySizes = new SparseArray<>();
        this.isLargeScreen = false;
        this.isFoldable = false;
        this.foldableOrientations = Collections.emptyList();
    }

    public WallpaperDefaultDisplayInfo(WindowManager windowManager, Resources resources) {
        Set<WindowMetrics> metrics = windowManager.getPossibleMaximumWindowMetrics(DEFAULT_DISPLAY);
        boolean isFoldable = resources.getIntArray(R.array.config_foldedDeviceStates).length > 0;
        if (isFoldable) {
            this.foldableOrientations = getFoldableOrientations(metrics);
        } else {
            this.foldableOrientations = Collections.emptyList();
        }
        this.defaultDisplaySizes = getDisplaySizes(metrics);
        this.isLargeScreen = isLargeScreen(metrics);
        this.isFoldable = isFoldable;
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) return true;
        if (!(other instanceof WallpaperDefaultDisplayInfo that)) return false;
        return isLargeScreen == that.isLargeScreen && isFoldable == that.isFoldable
                && defaultDisplaySizes.contentEquals(that.defaultDisplaySizes)
                && Objects.equals(foldableOrientations, that.foldableOrientations);
    }

    @Override
    public int hashCode() {
        return 31 * Objects.hash(isLargeScreen, isFoldable, foldableOrientations)
                + defaultDisplaySizes.contentHashCode();
    }

    /**
     * Returns the folded orientation corresponds to the {@code unfoldedOrientation} found in
     * {@link #foldableOrientations}. If not found, returns
     * {@link WallpaperManager.ORIENTATION_UNKNOWN}.
     */
    public int getFoldedOrientation(int unfoldedOrientation) {
        for (FoldableOrientations orientations : foldableOrientations) {
            if (orientations.unfoldedOrientation == unfoldedOrientation) {
                return orientations.foldedOrientation;
            }
        }
        return ORIENTATION_UNKNOWN;
    }

    /**
     * Returns the unfolded orientation corresponds to the {@code foldedOrientation} found in
     * {@link #foldableOrientations}. If not found, returns
     * {@link WallpaperManager.ORIENTATION_UNKNOWN}.
     */
    public int getUnfoldedOrientation(int foldedOrientation) {
        for (FoldableOrientations orientations : foldableOrientations) {
            if (orientations.foldedOrientation == foldedOrientation) {
                return orientations.unfoldedOrientation;
            }
        }
        return ORIENTATION_UNKNOWN;
    }

    private static SparseArray<Point> getDisplaySizes(Set<WindowMetrics> displayMetrics) {
        SparseArray<Point> displaySizes = new SparseArray<>();
        for (WindowMetrics metric : displayMetrics) {
            Rect bounds = metric.getBounds();
            Point displaySize = new Point(bounds.width(), bounds.height());
            Point reversedDisplaySize = new Point(displaySize.y, displaySize.x);
            for (Point point : List.of(displaySize, reversedDisplaySize)) {
                int orientation = WallpaperManager.getOrientation(point);
                // don't add an entry if there is already a larger display of the same orientation
                Point display = displaySizes.get(orientation);
                if (display == null || display.x * display.y < point.x * point.y) {
                    displaySizes.put(orientation, point);
                }
            }
        }
        return displaySizes;
    }

    private static boolean isLargeScreen(Set<WindowMetrics> displayMetrics) {
        float smallestWidth = Float.MAX_VALUE;
        for (WindowMetrics metric : displayMetrics) {
            Rect bounds = metric.getBounds();
            smallestWidth = Math.min(smallestWidth, bounds.width() / metric.getDensity());
        }
        return smallestWidth >= LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
    }

    /**
     * Determines all potential foldable orientations, populating {@code
     * outFoldableOrientationPairs} with pairs of (folded orientation, unfolded orientation). If
     * {@code defaultDisplayMetrics} isn't for foldable, {@code outFoldableOrientationPairs} will
     * not be populated.
     */
    private static List<FoldableOrientations> getFoldableOrientations(
            Set<WindowMetrics> defaultDisplayMetrics) {
        if (defaultDisplayMetrics.size() != 2) {
            return Collections.emptyList();
        }
        List<FoldableOrientations> foldableOrientations = new ArrayList<>();
        float surface = 0;
        int firstOrientation = -1;
        for (WindowMetrics metric : defaultDisplayMetrics) {
            Rect bounds = metric.getBounds();
            Point displaySize = new Point(bounds.width(), bounds.height());

            int orientation = WallpaperManager.getOrientation(displaySize);
            float newSurface = displaySize.x * displaySize.y
                    / (metric.getDensity() * metric.getDensity());
            if (surface <= 0) {
                surface = newSurface;
                firstOrientation = orientation;
            } else {
                FoldableOrientations orientations = (newSurface > surface)
                        ? new FoldableOrientations(firstOrientation, orientation)
                        : new FoldableOrientations(orientation, firstOrientation);
                FoldableOrientations rotatedOrientations = new FoldableOrientations(
                        getRotatedOrientation(orientations.foldedOrientation),
                        getRotatedOrientation(orientations.unfoldedOrientation));
                foldableOrientations.add(orientations);
                foldableOrientations.add(rotatedOrientations);
            }
        }
        return Collections.unmodifiableList(foldableOrientations);
    }
}
+13 −89

File changed.

Preview size limit exceeded, changes collapsed.

+9 −7
Original line number Diff line number Diff line
@@ -1666,12 +1666,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
        DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
        displayManager.registerDisplayListener(mDisplayListener, null /* handler */);
        WindowManager windowManager = mContext.getSystemService(WindowManager.class);
        boolean isFoldable = mContext.getResources()
                .getIntArray(R.array.config_foldedDeviceStates).length > 0;
        mWallpaperDisplayHelper = new WallpaperDisplayHelper(
                displayManager, windowManager, mWindowManagerInternal, isFoldable);
                displayManager, windowManager, mWindowManagerInternal, mContext.getResources());
        mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
        mWindowManagerInternal.setWallpaperCropUtils(mWallpaperCropper::getCrop);
        mActivityManager = mContext.getSystemService(ActivityManager.class);

        if (mContext.getResources().getBoolean(
@@ -2510,9 +2507,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
            List<Rect> result = new ArrayList<>();
            boolean rtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
                    == View.LAYOUT_DIRECTION_RTL;
            WallpaperDefaultDisplayInfo defaultDisplayInfo =
                    mWallpaperDisplayHelper.getDefaultDisplayInfo();
            for (Point displaySize : displaySizes) {
                result.add(mWallpaperCropper.getCrop(
                        displaySize, croppedBitmapSize, adjustedRelativeSuggestedCrops, rtl));
                result.add(WallpaperCropper.getCrop(displaySize, defaultDisplayInfo,
                        croppedBitmapSize, adjustedRelativeSuggestedCrops, rtl));
            }
            if (originalBitmap) result = WallpaperCropper.getOriginalCropHints(wallpaper, result);
            return result;
@@ -2548,8 +2547,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
        List<Rect> result = new ArrayList<>();
        boolean rtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
                == View.LAYOUT_DIRECTION_RTL;
        WallpaperDefaultDisplayInfo defaultDisplayInfo =
                mWallpaperDisplayHelper.getDefaultDisplayInfo();
        for (Point displaySize : displaySizes) {
            result.add(mWallpaperCropper.getCrop(displaySize, bitmapSize, defaultCrops, rtl));
            result.add(WallpaperCropper.getCrop(displaySize, defaultDisplayInfo, bitmapSize,
                    defaultCrops, rtl));
        }
        return result;
    }
Loading