Loading services/core/java/com/android/server/wallpaper/WallpaperCropper.java +39 −42 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); } /** Loading @@ -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); Loading @@ -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); Loading @@ -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); } } Loading @@ -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); Loading @@ -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); Loading @@ -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); } Loading @@ -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); Loading @@ -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); } } } Loading @@ -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); } /** Loading Loading @@ -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)); } } Loading @@ -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; Loading services/core/java/com/android/server/wallpaper/WallpaperDataParser.java +5 −3 Original line number Diff line number Diff line Loading @@ -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; } Loading services/core/java/com/android/server/wallpaper/WallpaperDefaultDisplayInfo.java 0 → 100644 +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); } } services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java +13 −89 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +9 −7 Original line number Diff line number Diff line Loading @@ -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( Loading Loading @@ -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; Loading Loading @@ -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 Loading
services/core/java/com/android/server/wallpaper/WallpaperCropper.java +39 −42 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); } /** Loading @@ -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); Loading @@ -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); Loading @@ -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); } } Loading @@ -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); Loading @@ -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); Loading @@ -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); } Loading @@ -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); Loading @@ -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); } } } Loading @@ -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); } /** Loading Loading @@ -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)); } } Loading @@ -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; Loading
services/core/java/com/android/server/wallpaper/WallpaperDataParser.java +5 −3 Original line number Diff line number Diff line Loading @@ -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; } Loading
services/core/java/com/android/server/wallpaper/WallpaperDefaultDisplayInfo.java 0 → 100644 +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); } }
services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java +13 −89 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +9 −7 Original line number Diff line number Diff line Loading @@ -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( Loading Loading @@ -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; Loading Loading @@ -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