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

Commit 50c69551 authored by Aurélien Pomini's avatar Aurélien Pomini Committed by Android (Google) Code Review
Browse files

Merge changes I51f77d9e,I46e84695 into main

* changes:
  Fix the computation of default crops to match legacy
  Get rid of the field WallpaperData.mSupportsMultCrop
parents 261d4953 831ee149
Loading
Loading
Loading
Loading
+115 −36
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.wallpaper;

import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
import static android.app.WallpaperManager.getOrientation;
import static android.app.WallpaperManager.getRotatedOrientation;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -86,7 +87,7 @@ public class WallpaperCropper {
    public interface WallpaperCropUtils {

        /**
         * Equivalent to {@link #getCrop(Point, Point, SparseArray, boolean)}
         * Equivalent to {@link WallpaperCropper#getCrop(Point, Point, SparseArray, boolean)}
         */
        Rect getCrop(Point displaySize, Point bitmapSize,
                SparseArray<Rect> suggestedCrops, boolean rtl);
@@ -120,16 +121,23 @@ public class WallpaperCropper {
    public Rect getCrop(Point displaySize, Point bitmapSize,
            SparseArray<Rect> suggestedCrops, boolean rtl) {

        // Case 1: if no crops are provided, center align the full image
        int orientation = getOrientation(displaySize);

        // Case 1: if no crops are provided, show the full image (from the left, or right if RTL).
        if (suggestedCrops == null || suggestedCrops.size() == 0) {
            Rect crop = new Rect(0, 0, displaySize.x, displaySize.y);
            float scale = Math.min(
                    ((float) bitmapSize.x) / displaySize.x,
                    ((float) bitmapSize.y) / displaySize.y);
            crop.scale(scale);
            crop.offset((bitmapSize.x - crop.width()) / 2,
                    (bitmapSize.y - crop.height()) / 2);
            return crop;
            Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);

            // 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);
            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 getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD);
        }

        // If any suggested crop is invalid, fallback to case 1
@@ -142,8 +150,6 @@ public class WallpaperCropper {
            }
        }

        int orientation = getOrientation(displaySize);

        // Case 2: if the orientation exists in the suggested crops, adjust the suggested crop
        Rect suggestedCrop = suggestedCrops.get(orientation);
        if (suggestedCrop != null) {
@@ -168,11 +174,21 @@ public class WallpaperCropper {
        suggestedCrop = suggestedCrops.get(unfoldedOrientation);
        suggestedDisplaySize = defaultDisplaySizes.get(unfoldedOrientation);
        if (suggestedCrop != null) {
            // only keep the visible part (without parallax)
            // compute the visible part (without parallax) of the unfolded screen
            Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
            return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, REMOVE);
            // compute the folded crop, at the center of the crop of the unfolded screen
            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);
                // use getAdjustedCrop(parallax=true) to make sure we don't exceed MAX_PARALLAX
                res = getAdjustedCrop(res, bitmapSize, displaySize, true, rtl, ADD);
            }
            return res;
        }


        // 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);
@@ -274,11 +290,8 @@ public class WallpaperCropper {
            if (additionalWidthForParallax > MAX_PARALLAX) {
                int widthToRemove = (int) Math.ceil(
                        (additionalWidthForParallax - MAX_PARALLAX) * screenRatio * crop.height());
                if (rtl) {
                    adjustedCrop.left += widthToRemove;
                } else {
                    adjustedCrop.right -= widthToRemove;
                }
                adjustedCrop.left += widthToRemove / 2;
                adjustedCrop.right -= widthToRemove / 2 + widthToRemove % 2;
            }
        } else {
            // TODO (b/281648899) the third case is not always correct, fix that.
@@ -366,6 +379,24 @@ public class WallpaperCropper {
     */
    SparseArray<Rect> getDefaultCrops(SparseArray<Rect> suggestedCrops, Point bitmapSize) {

        // If the suggested crops is single-element map with (ORIENTATION_UNKNOWN, cropHint),
        // Crop the bitmap using the cropHint and compute the crops for cropped bitmap.
        Rect cropHint = suggestedCrops.get(ORIENTATION_UNKNOWN);
        if (cropHint != null) {
            Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
            if (suggestedCrops.size() != 1 || !bitmapRect.contains(cropHint)) {
                Slog.w(TAG, "Couldn't get default crops from suggested crops " + suggestedCrops
                        + " for bitmap of size " + bitmapSize + "; ignoring suggested crops");
                return getDefaultCrops(new SparseArray<>(), bitmapSize);
            }
            Point cropSize = new Point(cropHint.width(), cropHint.height());
            SparseArray<Rect> relativeDefaultCrops = getDefaultCrops(new SparseArray<>(), cropSize);
            for (int i = 0; i < relativeDefaultCrops.size(); i++) {
                relativeDefaultCrops.valueAt(i).offset(cropHint.left, cropHint.top);
            }
            return relativeDefaultCrops;
        }

        SparseArray<Point> defaultDisplaySizes = mWallpaperDisplayHelper.getDefaultDisplaySizes();
        boolean rtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
                == View.LAYOUT_DIRECTION_RTL;
@@ -422,26 +453,74 @@ public class WallpaperCropper {
        } else {
            boolean needCrop = false;
            boolean needScale;
            boolean multiCrop = multiCrop() && wallpaper.mSupportsMultiCrop;

            Point bitmapSize = new Point(options.outWidth, options.outHeight);
            Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y);

            if (multiCrop()) {
                // Check that the suggested crops per screen orientation are all within the bitmap.
                for (int i = 0; i < wallpaper.mCropHints.size(); i++) {
                    int orientation = wallpaper.mCropHints.keyAt(i);
                    Rect crop = wallpaper.mCropHints.valueAt(i);
                    if (crop.isEmpty() || !bitmapRect.contains(crop)) {
                        Slog.w(TAG, "Invalid crop " + crop + " for orientation " + orientation
                                + " and bitmap size " + bitmapSize + "; clearing suggested crops.");
                        wallpaper.mCropHints.clear();
                        wallpaper.cropHint.set(bitmapRect);
                        break;
                    }
                }
            }
            final Rect cropHint;
            if (multiCrop) {
                SparseArray<Rect> defaultDisplayCrops =
                        getDefaultCrops(wallpaper.mCropHints, bitmapSize);
                // adapt the entries in wallpaper.mCropHints for the actual display

            // A wallpaper with cropHints = Map.of(ORIENTATION_UNKNOWN, rect) is treated like
            // a wallpaper with cropHints = null and  cropHint = rect.
            Rect tempCropHint = wallpaper.mCropHints.get(ORIENTATION_UNKNOWN);
            if (multiCrop() && tempCropHint != null) {
                wallpaper.cropHint.set(tempCropHint);
                wallpaper.mCropHints.clear();
            }
            if (multiCrop() && wallpaper.mCropHints.size() > 0) {
                // Some suggested crops per screen orientation were provided,
                // use them to compute the default crops for this device
                SparseArray<Rect> defaultCrops = getDefaultCrops(wallpaper.mCropHints, bitmapSize);
                // Adapt the provided crops to match the actual crops for the default display
                SparseArray<Rect> updatedCropHints = new SparseArray<>();
                for (int i = 0; i < wallpaper.mCropHints.size(); i++) {
                    int orientation = wallpaper.mCropHints.keyAt(i);
                    Rect defaultCrop = defaultDisplayCrops.get(orientation);
                    Rect defaultCrop = defaultCrops.get(orientation);
                    if (defaultCrop != null) {
                        updatedCropHints.put(orientation, defaultCrop);
                    }
                }
                wallpaper.mCropHints = updatedCropHints;
                cropHint = getTotalCrop(defaultDisplayCrops);

                // Finally, compute the cropHint based on the default crops
                cropHint = getTotalCrop(defaultCrops);
                wallpaper.cropHint.set(cropHint);
                if (DEBUG) {
                    Slog.d(TAG, "Generated default crops for wallpaper: " + defaultCrops
                            + " based on suggested crops: " + wallpaper.mCropHints);
                }
            } else if (multiCrop()) {
                // No crops per screen orientation were provided, but an overall cropHint may be
                // defined in wallpaper.cropHint. Compute the default crops for the sub-image
                // defined by the cropHint, then recompute the cropHint based on the default crops.
                // If the cropHint is empty or invalid, ignore it and use the full image.
                if (wallpaper.cropHint.isEmpty()) wallpaper.cropHint.set(bitmapRect);
                if (!bitmapRect.contains(wallpaper.cropHint)) {
                    Slog.w(TAG, "Ignoring wallpaper.cropHint = " + wallpaper.cropHint
                            + "; not within the bitmap of size " + bitmapSize);
                    wallpaper.cropHint.set(bitmapRect);
                }
                Point cropSize = new Point(wallpaper.cropHint.width(), wallpaper.cropHint.height());
                SparseArray<Rect> defaultCrops = getDefaultCrops(new SparseArray<>(), cropSize);
                cropHint = getTotalCrop(defaultCrops);
                cropHint.offset(wallpaper.cropHint.left, wallpaper.cropHint.top);
                wallpaper.cropHint.set(cropHint);
                if (DEBUG) {
                    Slog.d(TAG, "Generated default crops for wallpaper: " + defaultCrops);
                }
            } else {
                cropHint = new Rect(wallpaper.cropHint);
            }
@@ -455,11 +534,11 @@ public class WallpaperCropper {
            }

            // Empty crop means use the full image
            if (cropHint.isEmpty()) {
            if (!multiCrop() && cropHint.isEmpty()) {
                cropHint.left = cropHint.top = 0;
                cropHint.right = options.outWidth;
                cropHint.bottom = options.outHeight;
            } else {
            } else if (!multiCrop()) {
                // force the crop rect to lie within the measured bounds
                int dx = cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0;
                int dy = cropHint.bottom > options.outHeight
@@ -473,11 +552,11 @@ public class WallpaperCropper {
                if (cropHint.top < 0) {
                    cropHint.top = 0;
                }
            }

            // Don't bother cropping if what we're left with is identity
            needCrop = (options.outHeight > cropHint.height()
                    || options.outWidth > cropHint.width());
            }

            // scale if the crop height winds up not matching the recommended metrics
            needScale = cropHint.height() > wpData.mHeight
@@ -485,7 +564,7 @@ public class WallpaperCropper {
                    || cropHint.width() > GLHelper.getMaxTextureSize();

            //make sure screen aspect ratio is preserved if width is scaled under screen size
            if (needScale && !multiCrop) {
            if (needScale && !multiCrop()) {
                final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
                final int newWidth = (int) (cropHint.width() * scaleByHeight);
                if (newWidth < displayInfo.logicalWidth) {
@@ -543,7 +622,7 @@ public class WallpaperCropper {
                    final Rect estimateCrop = new Rect(cropHint);
                    estimateCrop.scale(1f / options.inSampleSize);
                    float hRatio = (float) wpData.mHeight / estimateCrop.height();
                    if (multiCrop) {
                    if (multiCrop()) {
                        // make sure the crop height is at most the display largest dimension
                        hRatio = (float) mWallpaperDisplayHelper.getDefaultDisplayLargestDimension()
                                / estimateCrop.height();
@@ -557,7 +636,7 @@ public class WallpaperCropper {
                        if (DEBUG) {
                            Slog.w(TAG, "Invalid crop dimensions, trying to adjust.");
                        }
                        if (multiCrop) {
                        if (multiCrop()) {
                            // clear custom crop guidelines, fallback to system default
                            wallpaper.mCropHints.clear();
                            generateCropInternal(wallpaper);
@@ -618,7 +697,7 @@ public class WallpaperCropper {
                        final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
                                safeWidth, safeHeight, true);

                        if (multiCrop) {
                        if (multiCrop()) {
                            wallpaper.mSampleSize =
                                    ((float) cropHint.height()) / finalCrop.getHeight();
                        }
+0 −6
Original line number Diff line number Diff line
@@ -171,11 +171,6 @@ class WallpaperData {
     */
    SparseArray<Rect> mCropHints = new SparseArray<>();

    /**
     * cropHints will be ignored if this flag is false
     */
    boolean mSupportsMultiCrop;

    /**
     * The phone orientation when the wallpaper was set. Only relevant for image wallpapers
     */
@@ -204,7 +199,6 @@ class WallpaperData {
        if (source.mCropHints != null) {
            this.mCropHints = source.mCropHints.clone();
        }
        this.mSupportsMultiCrop = source.mSupportsMultiCrop;
        this.allowBackup = source.allowBackup;
        this.primaryColors = source.primaryColors;
        this.mWallpaperDimAmount = source.mWallpaperDimAmount;
+11 −15
Original line number Diff line number Diff line
@@ -324,10 +324,7 @@ public class WallpaperDataParser {
                getAttributeInt(parser, "totalCropTop", 0),
                getAttributeInt(parser, "totalCropRight", 0),
                getAttributeInt(parser, "totalCropBottom", 0));
        wallpaper.mSupportsMultiCrop = multiCrop() && (
                parser.getAttributeBoolean(null, "supportsMultiCrop", false)
                || mImageWallpaper.equals(wallpaper.wallpaperComponent));
        if (wallpaper.mSupportsMultiCrop) {
        if (multiCrop() && mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) {
            wallpaper.mCropHints = new SparseArray<>();
            for (Pair<Integer, String> pair: screenDimensionPairs()) {
                Rect cropHint = new Rect(
@@ -342,16 +339,14 @@ public class WallpaperDataParser {
            }
            if (wallpaper.mCropHints.size() == 0 && totalCropHint.isEmpty()) {
                // migration case: the crops per screen orientation are not specified.
                int orientation = legacyCropHint.width() < legacyCropHint.height()
                        ? WallpaperManager.PORTRAIT : WallpaperManager.LANDSCAPE;
                if (!legacyCropHint.isEmpty()) {
                    wallpaper.mCropHints.put(orientation, legacyCropHint);
                    wallpaper.cropHint.set(legacyCropHint);
                }
            } else {
                wallpaper.cropHint.set(totalCropHint);
            }
            wallpaper.mSampleSize = parser.getAttributeFloat(null, "sampleSize", 1f);
        } else {
        } else if (!multiCrop()) {
            wallpaper.cropHint.set(legacyCropHint);
        }
        final DisplayData wpData = mWallpaperDisplayHelper
@@ -467,13 +462,12 @@ public class WallpaperDataParser {
        out.startTag(null, tag);
        out.attributeInt(null, "id", wallpaper.wallpaperId);

        out.attributeBoolean(null, "supportsMultiCrop", wallpaper.mSupportsMultiCrop);

        if (multiCrop() && wallpaper.mSupportsMultiCrop) {
        if (multiCrop() && mImageWallpaper.equals(wallpaper.wallpaperComponent)) {
            if (wallpaper.mCropHints == null) {
                Slog.e(TAG, "cropHints should not be null when saved");
                wallpaper.mCropHints = new SparseArray<>();
            }
            Rect rectToPutInLegacyCrop = new Rect(wallpaper.cropHint);
            for (Pair<Integer, String> pair : screenDimensionPairs()) {
                Rect cropHint = wallpaper.mCropHints.get(pair.first);
                if (cropHint == null) continue;
@@ -493,12 +487,14 @@ public class WallpaperDataParser {
                    }
                }
                if (pair.first == orientationToPutInLegacyCrop) {
                    out.attributeInt(null, "cropLeft", cropHint.left);
                    out.attributeInt(null, "cropTop", cropHint.top);
                    out.attributeInt(null, "cropRight", cropHint.right);
                    out.attributeInt(null, "cropBottom", cropHint.bottom);
                    rectToPutInLegacyCrop.set(cropHint);
                }
            }
            out.attributeInt(null, "cropLeft", rectToPutInLegacyCrop.left);
            out.attributeInt(null, "cropTop", rectToPutInLegacyCrop.top);
            out.attributeInt(null, "cropRight", rectToPutInLegacyCrop.right);
            out.attributeInt(null, "cropBottom", rectToPutInLegacyCrop.bottom);

            out.attributeInt(null, "totalCropLeft", wallpaper.cropHint.left);
            out.attributeInt(null, "totalCropTop", wallpaper.cropHint.top);
            out.attributeInt(null, "totalCropRight", wallpaper.cropHint.right);
+31 −35

File changed.

Preview size limit exceeded, changes collapsed.

+36 −18
Original line number Diff line number Diff line
@@ -235,12 +235,11 @@ public class WallpaperCropperTest {
        int expectedWidth = (int) (displaySize.x * (1 + WallpaperCropper.MAX_PARALLAX));
        Point expectedCropSize = new Point(expectedWidth, 1000);
        for (int mode: ALL_MODES) {
            for (boolean rtl: List.of(false, true)) {
                assertThat(WallpaperCropper.getAdjustedCrop(
                    crop, bitmapSize, displaySize, true, false, mode))
                    .isEqualTo(leftOf(crop, expectedCropSize));
            assertThat(WallpaperCropper.getAdjustedCrop(
                    crop, bitmapSize, displaySize, true, true, mode))
                    .isEqualTo(rightOf(crop, expectedCropSize));
                        crop, bitmapSize, displaySize, true, rtl, mode))
                        .isEqualTo(centerOf(crop, expectedCropSize));
            }
        }
    }

@@ -362,11 +361,13 @@ public class WallpaperCropperTest {
    }

    /**
     * Test that {@link WallpaperCropper#getCrop} follows a simple centre-align strategy when
     * no suggested crops are provided.
     * Test that {@link WallpaperCropper#getCrop} uses the full image when no crops are provided.
     * If the image has more width/height ratio than the screen, keep that width for parallax up
     * to {@link WallpaperCropper#MAX_PARALLAX}. If the crop has less width/height ratio, remove the
     * surplus height, on both sides to keep the wallpaper centered.
     */
    @Test
    public void testGetCrop_noSuggestedCrops_centersWallpaper() {
    public void testGetCrop_noSuggestedCrops() {
        setUpWithDisplays(STANDARD_DISPLAY);
        Point bitmapSize = new Point(800, 1000);
        Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
@@ -374,9 +375,11 @@ public class WallpaperCropperTest {

        List<Point> displaySizes = List.of(
                new Point(500, 1000),
                new Point(200, 1000),
                new Point(1000, 500));
        List<Point> expectedCropSizes = List.of(
                new Point(500, 1000),
                new Point(Math.min(800, (int) (500 * (1 + WallpaperCropper.MAX_PARALLAX))), 1000),
                new Point(Math.min(800, (int) (200 * (1 + WallpaperCropper.MAX_PARALLAX))), 1000),
                new Point(800, 400));

        for (int i = 0; i < displaySizes.size(); i++) {
@@ -450,7 +453,8 @@ public class WallpaperCropperTest {
    /**
     * Test that {@link WallpaperCropper#getCrop}, when asked for a folded crop with a suggested
     * crop only for the relative unfolded orientation, creates the folded crop at the center of the
     * unfolded crop, by removing content on two sides to match the folded screen dimensions.
     * unfolded crop, by removing content on two sides to match the folded screen dimensions, and
     * then adds some width for parallax.
     * <p>
     * To simplify, in this test case all crops have the same size as the display (no zoom)
     * and are at the center of the image.
@@ -468,6 +472,7 @@ public class WallpaperCropperTest {
            int unfoldedTwo = getRotatedOrientation(unfoldedOne);
            Rect unfoldedCropOne = centerOf(bitmapRect, mDisplaySizes.get(unfoldedOne));
            Rect unfoldedCropTwo = centerOf(bitmapRect, mDisplaySizes.get(unfoldedTwo));
            List<Rect> unfoldedCrops = List.of(unfoldedCropOne, unfoldedCropTwo);
            SparseArray<Rect> suggestedCrops = new SparseArray<>();
            suggestedCrops.put(unfoldedOne, unfoldedCropOne);
            suggestedCrops.put(unfoldedTwo, unfoldedCropTwo);
@@ -476,15 +481,28 @@ public class WallpaperCropperTest {
            int foldedTwo = getFoldedOrientation(unfoldedTwo);
            Point foldedDisplayOne = mDisplaySizes.get(foldedOne);
            Point foldedDisplayTwo = mDisplaySizes.get(foldedTwo);
            List<Point> foldedDisplays = List.of(foldedDisplayOne, foldedDisplayTwo);

            for (boolean rtl : List.of(false, true)) {
                for (int i = 0; i < 2; i++) {
                    Rect unfoldedCrop = unfoldedCrops.get(i);
                    Point foldedDisplay = foldedDisplays.get(i);
                    Rect expectedCrop = centerOf(unfoldedCrop, foldedDisplay);
                    int maxParallax = (int) (WallpaperCropper.MAX_PARALLAX * unfoldedCrop.width());

                    // the expected behaviour is that we add width for parallax until we reach
                    // either MAX_PARALLAX or the edge of the crop for the unfolded screen.
                    if (rtl) {
                        expectedCrop.left = Math.max(
                                unfoldedCrop.left, expectedCrop.left - maxParallax);
                    } else {
                        expectedCrop.right = Math.min(
                                unfoldedCrop.right, unfoldedCrop.right + maxParallax);
                    }
                    assertThat(mWallpaperCropper.getCrop(
                        foldedDisplayOne, bitmapSize, suggestedCrops, rtl))
                        .isEqualTo(centerOf(unfoldedCropOne, foldedDisplayOne));

                assertThat(mWallpaperCropper.getCrop(
                        foldedDisplayTwo, bitmapSize, suggestedCrops, rtl))
                        .isEqualTo(centerOf(unfoldedCropTwo, foldedDisplayTwo));
                            foldedDisplay, bitmapSize, suggestedCrops, rtl))
                            .isEqualTo(expectedCrop);
                }
            }
        }
    }