Loading services/core/java/com/android/server/wallpaper/WallpaperCropper.java +123 −2 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.DisplayInfo; import android.view.View; import android.window.DesktopExperienceFlags; import com.android.internal.annotations.VisibleForTesting; import com.android.server.utils.TimingsTraceAndSlog; Loading Loading @@ -71,6 +72,13 @@ public class WallpaperCropper { */ @VisibleForTesting static final float MAX_PARALLAX = 1f; /** * For connected displays, if the width or height of the image is smaller than the width or * height of the screen by a factor larger than this amount, the quality is considered too low * and a fallback wallpaper should be used instead. */ @VisibleForTesting static final float CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO = 1.5f; /** * We define three ways to adjust a crop. These modes are used depending on the situation: * - When going from unfolded to folded, we want to remove content Loading Loading @@ -116,6 +124,20 @@ public class WallpaperCropper { public static Rect getCrop(Point displaySize, WallpaperDefaultDisplayInfo defaultDisplayInfo, Point bitmapSize, SparseArray<Rect> suggestedCrops, boolean rtl) { // Case 0: if we're looking for the crop of an external display, use external display logic if (DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_WALLPAPER.isTrue()) { boolean isExternalDisplay = true; for (int i = 0; i < defaultDisplayInfo.defaultDisplaySizes.size(); i++) { if (defaultDisplayInfo.defaultDisplaySizes.valueAt(i).equals(displaySize)) { isExternalDisplay = false; } } if (isExternalDisplay) { return getCropForExternalDisplay( displaySize, defaultDisplayInfo, bitmapSize, suggestedCrops, rtl); } } int orientation = getOrientation(displaySize); // Case 1: if no crops are provided, show the full image (from the left, or right if RTL). Loading Loading @@ -208,7 +230,6 @@ public class WallpaperCropper { 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 = defaultDisplayInfo.getFoldedOrientation(orientation); Loading Loading @@ -832,6 +853,106 @@ public class WallpaperCropper { } } private static Rect getCropForExternalDisplay( Point displaySize, WallpaperDefaultDisplayInfo defaultDisplayInfo, Point bitmapSize, SparseArray<Rect> suggestedCrops, boolean rtl) { // If no custom crops are provided, center-align the image, with parallax if possible if (suggestedCrops == null || suggestedCrops.size() == 0) { Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); return getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD); } // Otherwise, find the custom crop closest to the external display aspect ratio Point closestDisplaySize = null; Rect closestCrop = null; float minDistance = Float.MAX_VALUE; float displayAspectRatio = (float) displaySize.x / displaySize.y; for (int i = 0; i < suggestedCrops.size(); i++) { int orientation = suggestedCrops.keyAt(i); Point suggestedDisplaySize = defaultDisplayInfo.defaultDisplaySizes.get(orientation); if (suggestedDisplaySize != null) { float aspectRatio = (float) suggestedDisplaySize.x / suggestedDisplaySize.y; float distance = Math.abs((float) Math.log(aspectRatio / displayAspectRatio)); if (distance < minDistance) { minDistance = distance; closestCrop = suggestedCrops.valueAt(i); closestDisplaySize = suggestedDisplaySize; } } } if (closestCrop == null) { Slog.w(TAG, "Did not find valid crop to use for external display of size " + displaySize + " and suggestedCrops " + suggestedCrops + ", fallback to center-align."); return getCropForExternalDisplay( displaySize, defaultDisplayInfo, bitmapSize, new SparseArray<>(), rtl ); } // Compute the visible part of that crop (without parallax) Rect noParallax = noParallax(closestCrop, closestDisplaySize, bitmapSize, rtl); // Adjust it to match the external display's aspect ratio Rect crop = getAdjustedCrop(noParallax, bitmapSize, displaySize, false, rtl, ADD); // If the resolution is not good enough, enlarge the crop until we reach the border of the // image or until we reach the required resolution float displayCropRatio = (float) displaySize.y / crop.height(); if (displayCropRatio > CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO) { float targetScale = displayCropRatio / CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO; int targetWidth = Math.round(crop.width() * targetScale); int targetHeight = Math.round(crop.height() * targetScale); int actualWidth = Math.min(targetWidth, bitmapSize.x); int actualHeight = Math.min(targetHeight, bitmapSize.y); float scale = Math.min( (float) actualWidth / crop.width(), (float) actualHeight / crop.height()); int widthToAdd = Math.round(crop.width() * (scale - 1f)); int heightToAdd = Math.round(crop.height() * (scale - 1f)); int widthToAddLeft = widthToAdd / 2; int widthToAddRight = widthToAdd - widthToAddLeft; int heightToAddTop = heightToAdd / 2; int heightToAddBottom = heightToAdd - heightToAddTop; if (crop.left < widthToAddLeft) { widthToAddRight += (widthToAddLeft - crop.left); widthToAddLeft = crop.left; } else if (bitmapSize.x - crop.right < widthToAddRight) { widthToAddLeft += (widthToAddRight - (bitmapSize.x - crop.right)); widthToAddRight = bitmapSize.x - crop.right; } if (crop.top < heightToAddTop) { heightToAddBottom += (heightToAddTop - crop.top); heightToAddTop = crop.top; } else if (bitmapSize.y - crop.bottom < heightToAddBottom) { heightToAddTop += (heightToAddBottom - (bitmapSize.y - crop.bottom)); heightToAddBottom = bitmapSize.y - crop.bottom; } crop.left -= widthToAddLeft; crop.right += widthToAddRight; crop.top -= heightToAddTop; crop.bottom += heightToAddBottom; } // Add some more parallax if we can if (rtl) { crop.left = 0; } else { crop.right = bitmapSize.x; } // Finally, use getAdjustedCrop just to make sure we don't exceed MAX_PARALLAX return getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD); } /** * Returns true if a wallpaper is compatible with a given display with ID, {@code displayId}. * Loading Loading @@ -877,7 +998,7 @@ public class WallpaperCropper { double maxDisplayToImageRatio = Math.max((double) displaySize.x / croppedImageBound.width(), (double) displaySize.y / croppedImageBound.height()); if (maxDisplayToImageRatio > 1.5) { if (maxDisplayToImageRatio > CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO) { return false; } Loading services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java +163 −16 Original line number Diff line number Diff line Loading @@ -38,9 +38,11 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import android.app.Flags; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsEnabled; import android.util.ArraySet; Loading Loading @@ -101,11 +103,6 @@ public class WallpaperCropperTest { private static final Point SQUARE_PORTRAIT_ONE = new Point(1000, 800); private static final Point SQUARE_LANDSCAPE_ONE = new Point(800, 1000); /** * Common device: a single screen of portrait/landscape orientation */ private static final List<Point> STANDARD_DISPLAY = List.of(PORTRAIT_ONE); /** 1: folded: portrait, unfolded: square with w < h */ private static final List<Point> FOLDABLE_ONE = List.of(PORTRAIT_ONE, SQUARE_PORTRAIT_ONE); Loading Loading @@ -454,7 +451,6 @@ public class WallpaperCropperTest { */ @Test public void testGetCrop_noSuggestedCrops() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(STANDARD_DISPLAY); Point bitmapSize = new Point(800, 1000); Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y); SparseArray<Rect> suggestedCrops = new SparseArray<>(); Loading @@ -470,13 +466,14 @@ public class WallpaperCropperTest { for (int i = 0; i < displaySizes.size(); i++) { Point displaySize = displaySizes.get(i); WallpaperDefaultDisplayInfo displayInfo = setUpWithDisplays(List.of(displaySize)); Point expectedCropSize = expectedCropSizes.get(i); for (boolean rtl : List.of(false, true)) { Rect expectedCrop = rtl ? rightOf(bitmapRect, expectedCropSize) : leftOf(bitmapRect, expectedCropSize); assertThat( WallpaperCropper.getCrop( displaySize, defaultDisplayInfo, bitmapSize, suggestedCrops, rtl)) displaySize, displayInfo, bitmapSize, suggestedCrops, rtl)) .isEqualTo(expectedCrop); } } Loading @@ -489,10 +486,10 @@ public class WallpaperCropperTest { */ @Test public void testGetCrop_hasSuggestedCrop() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(STANDARD_DISPLAY); WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(List.of(PORTRAIT_ONE)); Point bitmapSize = new Point(800, 1000); SparseArray<Rect> suggestedCrops = new SparseArray<>(); suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(0, 0, 400, 800)); suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(0, 0, 600, 800)); for (int otherOrientation: List.of(ORIENTATION_LANDSCAPE, ORIENTATION_SQUARE_LANDSCAPE, ORIENTATION_SQUARE_PORTRAIT)) { suggestedCrops.put(otherOrientation, new Rect(0, 0, 10, 10)); Loading @@ -500,13 +497,9 @@ public class WallpaperCropperTest { for (boolean rtl : List.of(false, true)) { assertThat( WallpaperCropper.getCrop(new Point(300, 800), defaultDisplayInfo, bitmapSize, suggestedCrops, rtl)) WallpaperCropper.getCrop(PORTRAIT_ONE, defaultDisplayInfo, bitmapSize, suggestedCrops, rtl)) .isEqualTo(suggestedCrops.get(ORIENTATION_PORTRAIT)); assertThat( WallpaperCropper.getCrop(new Point(500, 800), defaultDisplayInfo, bitmapSize, suggestedCrops, rtl)) .isEqualTo(new Rect(0, 0, 500, 800)); } } Loading @@ -521,7 +514,7 @@ public class WallpaperCropperTest { */ @Test public void testGetCrop_hasRotatedSuggestedCrop() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(STANDARD_DISPLAY); WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(FOLDABLE_ONE); Point bitmapSize = new Point(2000, 1800); Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y); SparseArray<Rect> suggestedCrops = new SparseArray<>(); Loading Loading @@ -726,6 +719,160 @@ public class WallpaperCropperTest { } } /** * Test that {@link WallpaperCropper#getCrop}, when called for an external display (i.e. with * a display size not in the {@link WallpaperDefaultDisplayInfo}) and with no crop provided, * uses the full image similarly to {@link #testGetCrop_noSuggestedCrops()}. */ @Test public void testGetCrop_noSuggestedCrops_externalDisplay() { WallpaperDefaultDisplayInfo displayInfo = setUpWithDisplays(List.of(PORTRAIT_ONE)); Point bitmapSize = new Point(800, 1000); Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y); SparseArray<Rect> suggestedCrops = new SparseArray<>(); List<Point> displaySizes = List.of( new Point(500, 1000), new Point(200, 1000), new Point(1000, 500)); List<Point> expectedCropSizes = List.of( 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++) { Point displaySize = displaySizes.get(i); Point expectedCropSize = expectedCropSizes.get(i); for (boolean rtl : List.of(false, true)) { Rect expectedCrop = rtl ? rightOf(bitmapRect, expectedCropSize) : leftOf(bitmapRect, expectedCropSize); assertThat( WallpaperCropper.getCrop( displaySize, displayInfo, bitmapSize, suggestedCrops, rtl)) .isEqualTo(expectedCrop); } } } /** * Test that {@link WallpaperCropper#getCrop}, called for an external display with only one * suggested crop, properly reuses the suggested crop to get the crop for the external display. */ @Test @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER) public void testGetCrop_hasOneSuggestedCrop_externalDisplay_usesSuggestedCrop() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(FOLDABLE_ONE); Point bitmapSize = new Point(2000, 2000); SparseArray<Rect> suggestedCrops = new SparseArray<>(); // In this example we have a suggested portrait crop with 200px for parallax suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(400, 0, 1600, 1600)); for (boolean rtl : List.of(false, true)) { // Test 1: square screen Rect crop = WallpaperCropper.getCrop(new Point(1000, 1000), defaultDisplayInfo, bitmapSize, suggestedCrops, rtl); assertThat(crop.top).isEqualTo(0); assertThat(crop.bottom).isEqualTo(1600); if (rtl) { // The crop without parallax of the default display is (600, 1600, 1600). // We should add 300px on each side to match the square screen. Then we're allowed // to add width for parallax to the left since rtl is true. assertThat(crop.left).isAtMost(300); assertThat(crop.right).isEqualTo(1900); } else { // The crop without parallax of the default display is (400, 0, 1400, 1600). // We should add 300px on each side to match the square screen. Then we're allowed // to add width for parallax to the right since rtl is false. assertThat(crop.left).isEqualTo(100); assertThat(crop.right).isAtLeast(1500); } // Test 2: landscape screen assertThat( WallpaperCropper.getCrop(new Point(2000, 1000), defaultDisplayInfo, bitmapSize, suggestedCrops, rtl)) // We should use all the available width then remove some height on both sides // of the crop to match the landscape screen, regardless of layout direction. .isEqualTo(new Rect(0, 300, 2000, 1300)); // Test 3: very narrow portrait screen crop = WallpaperCropper.getCrop(new Point(100, 1000), defaultDisplayInfo, bitmapSize, suggestedCrops, rtl); // We should use all the available height to match the external display aspect ratio. assertThat(crop.top).isEqualTo(0); assertThat(crop.bottom).isEqualTo(2000); if (rtl) { // The crop without parallax of the default display is (600, 1600, 1600). // We should remove 400px on each side to match the square screen. Then we're // allowed to add width for parallax to the left since rtl is true. assertThat(crop.left).isAtMost(1000); assertThat(crop.right).isEqualTo(1200); } else { // The crop without parallax of the default display is (400, 0, 1400, 1600). // We should remove 400px on each side to match the square screen. Then we're // allowed to add width for parallax to the right since rtl is false. assertThat(crop.left).isEqualTo(800); assertThat(crop.right).isAtLeast(1000); } } } /** * Test that {@link WallpaperCropper#getCrop}, called for an external display with several * suggested crops, uses the suggested crop for the display closest to the external display in * terms of aspect ratio. */ @Test @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER) public void testGetCrop_multipleSuggestedCrops_externalDisplay_usesClosestDisplayAspectRatio() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays( List.of(new Point(500, 800), new Point(1000, 800))); Point bitmapSize = new Point(3000, 3000); SparseArray<Rect> suggestedCrops = new SparseArray<>(); suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(0, 0, 1, 1)); suggestedCrops.put(ORIENTATION_LANDSCAPE, new Rect(0, 0, 1, 1)); suggestedCrops.put(ORIENTATION_SQUARE_LANDSCAPE, new Rect(0, 0, 2250, 1800)); Rect newCrop = WallpaperCropper.getCrop(new Point(720, 800), defaultDisplayInfo, bitmapSize, suggestedCrops, false); // The device has a aspect ratios of 0.625 for PORTRAIT and 1.25 for LANDSCAPE. In this test // case we're looking for the crop for an aspect ratio of 720/800 = 0.9. We should be using // the SQUARE_LANDSCAPE crop since it is multiplicatively closer to the 0.9 aspect ratio, // since 1.25 / 0.9 < 0.9 / 0.625. So we should reuse the (0, 0, 2250, 1800) crop. To match // the aspect ratio of the new 720 x 800 screen, we should add 700 px of height to the crop. assertThat(newCrop.left).isEqualTo(0); assertThat(newCrop.top).isEqualTo(0); assertThat(newCrop.right).isAtLeast(2250); assertThat(newCrop.bottom).isEqualTo(2500); } /** * Test that {@link WallpaperCropper#getCrop}, when called for a large external display and * a small suggested crop, enlarges the suggested crop until it reaches the required resolution * as per {@link WallpaperCropper#CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO}. */ @Test @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER) public void testGetCrop_externalDisplay_lowResolution_enlargesSuggestedCrop() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(List.of(PORTRAIT_ONE)); Point bitmapSize = new Point(10000, 10000); SparseArray<Rect> suggestedCrops = new SparseArray<>(); suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(4900, 4900, 5100, 5100)); Point newDisplaySize = new Point(10000, 12000); Rect newCrop = WallpaperCropper.getCrop(newDisplaySize, defaultDisplayInfo, bitmapSize, suggestedCrops, false); float displayToImageRatio = WallpaperCropper.CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO; int minExpectedWidth = (int) (0.5f + newDisplaySize.x / displayToImageRatio); int expectedHeight = (int) (0.5f + newDisplaySize.y / displayToImageRatio); assertThat(newCrop.width()).isAtLeast(minExpectedWidth - 1); assertThat(newCrop.height()).isWithin(1).of(expectedHeight); assertThat(newCrop.centerY()).isWithin(1).of(5000); } // Test isWallpaperCompatibleForDisplay always return true for the default display. @Test public void isWallpaperCompatibleForDisplay_defaultDisplay_returnTrue() Loading Loading
services/core/java/com/android/server/wallpaper/WallpaperCropper.java +123 −2 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.DisplayInfo; import android.view.View; import android.window.DesktopExperienceFlags; import com.android.internal.annotations.VisibleForTesting; import com.android.server.utils.TimingsTraceAndSlog; Loading Loading @@ -71,6 +72,13 @@ public class WallpaperCropper { */ @VisibleForTesting static final float MAX_PARALLAX = 1f; /** * For connected displays, if the width or height of the image is smaller than the width or * height of the screen by a factor larger than this amount, the quality is considered too low * and a fallback wallpaper should be used instead. */ @VisibleForTesting static final float CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO = 1.5f; /** * We define three ways to adjust a crop. These modes are used depending on the situation: * - When going from unfolded to folded, we want to remove content Loading Loading @@ -116,6 +124,20 @@ public class WallpaperCropper { public static Rect getCrop(Point displaySize, WallpaperDefaultDisplayInfo defaultDisplayInfo, Point bitmapSize, SparseArray<Rect> suggestedCrops, boolean rtl) { // Case 0: if we're looking for the crop of an external display, use external display logic if (DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_WALLPAPER.isTrue()) { boolean isExternalDisplay = true; for (int i = 0; i < defaultDisplayInfo.defaultDisplaySizes.size(); i++) { if (defaultDisplayInfo.defaultDisplaySizes.valueAt(i).equals(displaySize)) { isExternalDisplay = false; } } if (isExternalDisplay) { return getCropForExternalDisplay( displaySize, defaultDisplayInfo, bitmapSize, suggestedCrops, rtl); } } int orientation = getOrientation(displaySize); // Case 1: if no crops are provided, show the full image (from the left, or right if RTL). Loading Loading @@ -208,7 +230,6 @@ public class WallpaperCropper { 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 = defaultDisplayInfo.getFoldedOrientation(orientation); Loading Loading @@ -832,6 +853,106 @@ public class WallpaperCropper { } } private static Rect getCropForExternalDisplay( Point displaySize, WallpaperDefaultDisplayInfo defaultDisplayInfo, Point bitmapSize, SparseArray<Rect> suggestedCrops, boolean rtl) { // If no custom crops are provided, center-align the image, with parallax if possible if (suggestedCrops == null || suggestedCrops.size() == 0) { Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); return getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD); } // Otherwise, find the custom crop closest to the external display aspect ratio Point closestDisplaySize = null; Rect closestCrop = null; float minDistance = Float.MAX_VALUE; float displayAspectRatio = (float) displaySize.x / displaySize.y; for (int i = 0; i < suggestedCrops.size(); i++) { int orientation = suggestedCrops.keyAt(i); Point suggestedDisplaySize = defaultDisplayInfo.defaultDisplaySizes.get(orientation); if (suggestedDisplaySize != null) { float aspectRatio = (float) suggestedDisplaySize.x / suggestedDisplaySize.y; float distance = Math.abs((float) Math.log(aspectRatio / displayAspectRatio)); if (distance < minDistance) { minDistance = distance; closestCrop = suggestedCrops.valueAt(i); closestDisplaySize = suggestedDisplaySize; } } } if (closestCrop == null) { Slog.w(TAG, "Did not find valid crop to use for external display of size " + displaySize + " and suggestedCrops " + suggestedCrops + ", fallback to center-align."); return getCropForExternalDisplay( displaySize, defaultDisplayInfo, bitmapSize, new SparseArray<>(), rtl ); } // Compute the visible part of that crop (without parallax) Rect noParallax = noParallax(closestCrop, closestDisplaySize, bitmapSize, rtl); // Adjust it to match the external display's aspect ratio Rect crop = getAdjustedCrop(noParallax, bitmapSize, displaySize, false, rtl, ADD); // If the resolution is not good enough, enlarge the crop until we reach the border of the // image or until we reach the required resolution float displayCropRatio = (float) displaySize.y / crop.height(); if (displayCropRatio > CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO) { float targetScale = displayCropRatio / CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO; int targetWidth = Math.round(crop.width() * targetScale); int targetHeight = Math.round(crop.height() * targetScale); int actualWidth = Math.min(targetWidth, bitmapSize.x); int actualHeight = Math.min(targetHeight, bitmapSize.y); float scale = Math.min( (float) actualWidth / crop.width(), (float) actualHeight / crop.height()); int widthToAdd = Math.round(crop.width() * (scale - 1f)); int heightToAdd = Math.round(crop.height() * (scale - 1f)); int widthToAddLeft = widthToAdd / 2; int widthToAddRight = widthToAdd - widthToAddLeft; int heightToAddTop = heightToAdd / 2; int heightToAddBottom = heightToAdd - heightToAddTop; if (crop.left < widthToAddLeft) { widthToAddRight += (widthToAddLeft - crop.left); widthToAddLeft = crop.left; } else if (bitmapSize.x - crop.right < widthToAddRight) { widthToAddLeft += (widthToAddRight - (bitmapSize.x - crop.right)); widthToAddRight = bitmapSize.x - crop.right; } if (crop.top < heightToAddTop) { heightToAddBottom += (heightToAddTop - crop.top); heightToAddTop = crop.top; } else if (bitmapSize.y - crop.bottom < heightToAddBottom) { heightToAddTop += (heightToAddBottom - (bitmapSize.y - crop.bottom)); heightToAddBottom = bitmapSize.y - crop.bottom; } crop.left -= widthToAddLeft; crop.right += widthToAddRight; crop.top -= heightToAddTop; crop.bottom += heightToAddBottom; } // Add some more parallax if we can if (rtl) { crop.left = 0; } else { crop.right = bitmapSize.x; } // Finally, use getAdjustedCrop just to make sure we don't exceed MAX_PARALLAX return getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD); } /** * Returns true if a wallpaper is compatible with a given display with ID, {@code displayId}. * Loading Loading @@ -877,7 +998,7 @@ public class WallpaperCropper { double maxDisplayToImageRatio = Math.max((double) displaySize.x / croppedImageBound.width(), (double) displaySize.y / croppedImageBound.height()); if (maxDisplayToImageRatio > 1.5) { if (maxDisplayToImageRatio > CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO) { return false; } Loading
services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java +163 −16 Original line number Diff line number Diff line Loading @@ -38,9 +38,11 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import android.app.Flags; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsEnabled; import android.util.ArraySet; Loading Loading @@ -101,11 +103,6 @@ public class WallpaperCropperTest { private static final Point SQUARE_PORTRAIT_ONE = new Point(1000, 800); private static final Point SQUARE_LANDSCAPE_ONE = new Point(800, 1000); /** * Common device: a single screen of portrait/landscape orientation */ private static final List<Point> STANDARD_DISPLAY = List.of(PORTRAIT_ONE); /** 1: folded: portrait, unfolded: square with w < h */ private static final List<Point> FOLDABLE_ONE = List.of(PORTRAIT_ONE, SQUARE_PORTRAIT_ONE); Loading Loading @@ -454,7 +451,6 @@ public class WallpaperCropperTest { */ @Test public void testGetCrop_noSuggestedCrops() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(STANDARD_DISPLAY); Point bitmapSize = new Point(800, 1000); Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y); SparseArray<Rect> suggestedCrops = new SparseArray<>(); Loading @@ -470,13 +466,14 @@ public class WallpaperCropperTest { for (int i = 0; i < displaySizes.size(); i++) { Point displaySize = displaySizes.get(i); WallpaperDefaultDisplayInfo displayInfo = setUpWithDisplays(List.of(displaySize)); Point expectedCropSize = expectedCropSizes.get(i); for (boolean rtl : List.of(false, true)) { Rect expectedCrop = rtl ? rightOf(bitmapRect, expectedCropSize) : leftOf(bitmapRect, expectedCropSize); assertThat( WallpaperCropper.getCrop( displaySize, defaultDisplayInfo, bitmapSize, suggestedCrops, rtl)) displaySize, displayInfo, bitmapSize, suggestedCrops, rtl)) .isEqualTo(expectedCrop); } } Loading @@ -489,10 +486,10 @@ public class WallpaperCropperTest { */ @Test public void testGetCrop_hasSuggestedCrop() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(STANDARD_DISPLAY); WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(List.of(PORTRAIT_ONE)); Point bitmapSize = new Point(800, 1000); SparseArray<Rect> suggestedCrops = new SparseArray<>(); suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(0, 0, 400, 800)); suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(0, 0, 600, 800)); for (int otherOrientation: List.of(ORIENTATION_LANDSCAPE, ORIENTATION_SQUARE_LANDSCAPE, ORIENTATION_SQUARE_PORTRAIT)) { suggestedCrops.put(otherOrientation, new Rect(0, 0, 10, 10)); Loading @@ -500,13 +497,9 @@ public class WallpaperCropperTest { for (boolean rtl : List.of(false, true)) { assertThat( WallpaperCropper.getCrop(new Point(300, 800), defaultDisplayInfo, bitmapSize, suggestedCrops, rtl)) WallpaperCropper.getCrop(PORTRAIT_ONE, defaultDisplayInfo, bitmapSize, suggestedCrops, rtl)) .isEqualTo(suggestedCrops.get(ORIENTATION_PORTRAIT)); assertThat( WallpaperCropper.getCrop(new Point(500, 800), defaultDisplayInfo, bitmapSize, suggestedCrops, rtl)) .isEqualTo(new Rect(0, 0, 500, 800)); } } Loading @@ -521,7 +514,7 @@ public class WallpaperCropperTest { */ @Test public void testGetCrop_hasRotatedSuggestedCrop() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(STANDARD_DISPLAY); WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(FOLDABLE_ONE); Point bitmapSize = new Point(2000, 1800); Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y); SparseArray<Rect> suggestedCrops = new SparseArray<>(); Loading Loading @@ -726,6 +719,160 @@ public class WallpaperCropperTest { } } /** * Test that {@link WallpaperCropper#getCrop}, when called for an external display (i.e. with * a display size not in the {@link WallpaperDefaultDisplayInfo}) and with no crop provided, * uses the full image similarly to {@link #testGetCrop_noSuggestedCrops()}. */ @Test public void testGetCrop_noSuggestedCrops_externalDisplay() { WallpaperDefaultDisplayInfo displayInfo = setUpWithDisplays(List.of(PORTRAIT_ONE)); Point bitmapSize = new Point(800, 1000); Rect bitmapRect = new Rect(0, 0, bitmapSize.x, bitmapSize.y); SparseArray<Rect> suggestedCrops = new SparseArray<>(); List<Point> displaySizes = List.of( new Point(500, 1000), new Point(200, 1000), new Point(1000, 500)); List<Point> expectedCropSizes = List.of( 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++) { Point displaySize = displaySizes.get(i); Point expectedCropSize = expectedCropSizes.get(i); for (boolean rtl : List.of(false, true)) { Rect expectedCrop = rtl ? rightOf(bitmapRect, expectedCropSize) : leftOf(bitmapRect, expectedCropSize); assertThat( WallpaperCropper.getCrop( displaySize, displayInfo, bitmapSize, suggestedCrops, rtl)) .isEqualTo(expectedCrop); } } } /** * Test that {@link WallpaperCropper#getCrop}, called for an external display with only one * suggested crop, properly reuses the suggested crop to get the crop for the external display. */ @Test @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER) public void testGetCrop_hasOneSuggestedCrop_externalDisplay_usesSuggestedCrop() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(FOLDABLE_ONE); Point bitmapSize = new Point(2000, 2000); SparseArray<Rect> suggestedCrops = new SparseArray<>(); // In this example we have a suggested portrait crop with 200px for parallax suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(400, 0, 1600, 1600)); for (boolean rtl : List.of(false, true)) { // Test 1: square screen Rect crop = WallpaperCropper.getCrop(new Point(1000, 1000), defaultDisplayInfo, bitmapSize, suggestedCrops, rtl); assertThat(crop.top).isEqualTo(0); assertThat(crop.bottom).isEqualTo(1600); if (rtl) { // The crop without parallax of the default display is (600, 1600, 1600). // We should add 300px on each side to match the square screen. Then we're allowed // to add width for parallax to the left since rtl is true. assertThat(crop.left).isAtMost(300); assertThat(crop.right).isEqualTo(1900); } else { // The crop without parallax of the default display is (400, 0, 1400, 1600). // We should add 300px on each side to match the square screen. Then we're allowed // to add width for parallax to the right since rtl is false. assertThat(crop.left).isEqualTo(100); assertThat(crop.right).isAtLeast(1500); } // Test 2: landscape screen assertThat( WallpaperCropper.getCrop(new Point(2000, 1000), defaultDisplayInfo, bitmapSize, suggestedCrops, rtl)) // We should use all the available width then remove some height on both sides // of the crop to match the landscape screen, regardless of layout direction. .isEqualTo(new Rect(0, 300, 2000, 1300)); // Test 3: very narrow portrait screen crop = WallpaperCropper.getCrop(new Point(100, 1000), defaultDisplayInfo, bitmapSize, suggestedCrops, rtl); // We should use all the available height to match the external display aspect ratio. assertThat(crop.top).isEqualTo(0); assertThat(crop.bottom).isEqualTo(2000); if (rtl) { // The crop without parallax of the default display is (600, 1600, 1600). // We should remove 400px on each side to match the square screen. Then we're // allowed to add width for parallax to the left since rtl is true. assertThat(crop.left).isAtMost(1000); assertThat(crop.right).isEqualTo(1200); } else { // The crop without parallax of the default display is (400, 0, 1400, 1600). // We should remove 400px on each side to match the square screen. Then we're // allowed to add width for parallax to the right since rtl is false. assertThat(crop.left).isEqualTo(800); assertThat(crop.right).isAtLeast(1000); } } } /** * Test that {@link WallpaperCropper#getCrop}, called for an external display with several * suggested crops, uses the suggested crop for the display closest to the external display in * terms of aspect ratio. */ @Test @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER) public void testGetCrop_multipleSuggestedCrops_externalDisplay_usesClosestDisplayAspectRatio() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays( List.of(new Point(500, 800), new Point(1000, 800))); Point bitmapSize = new Point(3000, 3000); SparseArray<Rect> suggestedCrops = new SparseArray<>(); suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(0, 0, 1, 1)); suggestedCrops.put(ORIENTATION_LANDSCAPE, new Rect(0, 0, 1, 1)); suggestedCrops.put(ORIENTATION_SQUARE_LANDSCAPE, new Rect(0, 0, 2250, 1800)); Rect newCrop = WallpaperCropper.getCrop(new Point(720, 800), defaultDisplayInfo, bitmapSize, suggestedCrops, false); // The device has a aspect ratios of 0.625 for PORTRAIT and 1.25 for LANDSCAPE. In this test // case we're looking for the crop for an aspect ratio of 720/800 = 0.9. We should be using // the SQUARE_LANDSCAPE crop since it is multiplicatively closer to the 0.9 aspect ratio, // since 1.25 / 0.9 < 0.9 / 0.625. So we should reuse the (0, 0, 2250, 1800) crop. To match // the aspect ratio of the new 720 x 800 screen, we should add 700 px of height to the crop. assertThat(newCrop.left).isEqualTo(0); assertThat(newCrop.top).isEqualTo(0); assertThat(newCrop.right).isAtLeast(2250); assertThat(newCrop.bottom).isEqualTo(2500); } /** * Test that {@link WallpaperCropper#getCrop}, when called for a large external display and * a small suggested crop, enlarges the suggested crop until it reaches the required resolution * as per {@link WallpaperCropper#CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO}. */ @Test @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER) public void testGetCrop_externalDisplay_lowResolution_enlargesSuggestedCrop() { WallpaperDefaultDisplayInfo defaultDisplayInfo = setUpWithDisplays(List.of(PORTRAIT_ONE)); Point bitmapSize = new Point(10000, 10000); SparseArray<Rect> suggestedCrops = new SparseArray<>(); suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(4900, 4900, 5100, 5100)); Point newDisplaySize = new Point(10000, 12000); Rect newCrop = WallpaperCropper.getCrop(newDisplaySize, defaultDisplayInfo, bitmapSize, suggestedCrops, false); float displayToImageRatio = WallpaperCropper.CONNECTED_DISPLAY_MAX_DISPLAY_TO_IMAGE_RATIO; int minExpectedWidth = (int) (0.5f + newDisplaySize.x / displayToImageRatio); int expectedHeight = (int) (0.5f + newDisplaySize.y / displayToImageRatio); assertThat(newCrop.width()).isAtLeast(minExpectedWidth - 1); assertThat(newCrop.height()).isWithin(1).of(expectedHeight); assertThat(newCrop.centerY()).isWithin(1).of(5000); } // Test isWallpaperCompatibleForDisplay always return true for the default display. @Test public void isWallpaperCompatibleForDisplay_defaultDisplay_returnTrue() Loading