diff options
| author | 2024-04-15 16:53:28 +0000 | |
|---|---|---|
| committer | 2024-04-15 16:53:28 +0000 | |
| commit | afe50c04c1ee12d054f1d5db8c8c580da71ec9a0 (patch) | |
| tree | 71d021a2de8112064a22adb49e3a70f4d3c4fafc | |
| parent | bab84f3277a017b25dfdd12f7688b961a8e32cd0 (diff) | |
| parent | 60e5458981d53e55762da5bb2c1179175e9d0711 (diff) | |
Merge "Adjust wallpaper during B&R to center align" into 24D1-dev
| -rw-r--r-- | packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java | 303 | ||||
| -rw-r--r-- | packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java | 21 |
2 files changed, 253 insertions, 71 deletions
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index 23e269a67283..cbbce1a74e94 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -19,6 +19,7 @@ package com.android.wallpaperbackup; import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.app.WallpaperManager.ORIENTATION_UNKNOWN; +import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_INELIGIBLE; import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_METADATA; @@ -39,6 +40,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; +import android.graphics.BitmapFactory; import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManager; @@ -109,22 +111,16 @@ public class WallpaperBackupAgent extends BackupAgent { static final String LOCK_WALLPAPER_STAGE = "wallpaper-lock-stage"; @VisibleForTesting static final String WALLPAPER_INFO_STAGE = "wallpaper-info-stage"; - @VisibleForTesting static final String WALLPAPER_BACKUP_DEVICE_INFO_STAGE = "wallpaper-backup-device-info-stage"; - static final String EMPTY_SENTINEL = "empty"; static final String QUOTA_SENTINEL = "quota"; - // Shared preferences constants. static final String PREFS_NAME = "wbprefs.xml"; static final String SYSTEM_GENERATION = "system_gen"; static final String LOCK_GENERATION = "lock_gen"; - /** - * An approximate area threshold to compare device dimension similarity - */ - static final int AREA_THRESHOLD = 50; // TODO (b/327637867): determine appropriate threshold + static final float DEFAULT_ACCEPTABLE_PARALLAX = 0.2f; // If this file exists, it means we exceeded our quota last time private File mQuotaFile; @@ -336,7 +332,6 @@ public class WallpaperBackupAgent extends BackupAgent { mEventLogger.onSystemImageWallpaperBackupFailed(error); } - private void backupLockWallpaperFileIfItExists(SharedPreferences sharedPrefs, boolean lockChanged, int lockGeneration, FullBackupDataOutput data) throws IOException { final File lockImageStage = new File(getFilesDir(), LOCK_WALLPAPER_STAGE); @@ -409,6 +404,16 @@ public class WallpaperBackupAgent extends BackupAgent { } } + private static String readText(TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + String result = ""; + if (parser.next() == XmlPullParser.TEXT) { + result = parser.getText(); + parser.nextTag(); + } + return result; + } + @VisibleForTesting // fullBackupFile is final, so we intercept backups here in tests. protected void backupFile(File file, FullBackupDataOutput data) { @@ -438,18 +443,10 @@ public class WallpaperBackupAgent extends BackupAgent { boolean lockImageStageExists = lockImageStage.exists(); try { - // Parse the device dimensions of the source device and compare with target to - // to identify whether we need to skip the remainder of the restore process + // Parse the device dimensions of the source device Pair<Point, Point> sourceDeviceDimensions = parseDeviceDimensions( deviceDimensionsStage); - Point targetDeviceDimensions = getScreenDimensions(); - if (sourceDeviceDimensions != null && targetDeviceDimensions != null - && isSourceDeviceSignificantlySmallerThanTarget(sourceDeviceDimensions.first, - targetDeviceDimensions)) { - Slog.d(TAG, "The source device is significantly smaller than target"); - } - // First parse the live component name so that we know for logging if we care about // logging errors with the image restore. ComponentName wpService = parseWallpaperComponent(infoStage, "wp"); @@ -466,9 +463,10 @@ public class WallpaperBackupAgent extends BackupAgent { // to back up the original image on the source device, or there was no user-supplied // wallpaper image present. if (lockImageStageExists) { - restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK); + restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK, + sourceDeviceDimensions); } - restoreFromStage(imageStage, infoStage, "wp", sysWhich); + restoreFromStage(imageStage, infoStage, "wp", sysWhich, sourceDeviceDimensions); // And reset to the wallpaper service we should be using if (mLockHasLiveComponent) { @@ -543,16 +541,6 @@ public class WallpaperBackupAgent extends BackupAgent { } } - private static String readText(TypedXmlPullParser parser) - throws IOException, XmlPullParserException { - String result = ""; - if (parser.next() == XmlPullParser.TEXT) { - result = parser.getText(); - parser.nextTag(); - } - return result; - } - @VisibleForTesting void updateWallpaperComponent(ComponentName wpService, int which) throws IOException { @@ -578,10 +566,13 @@ public class WallpaperBackupAgent extends BackupAgent { } } - private void restoreFromStage(File stage, File info, String hintTag, int which) + private void restoreFromStage(File stage, File info, String hintTag, int which, + Pair<Point, Point> sourceDeviceDimensions) throws IOException { if (stage.exists()) { if (multiCrop()) { + // TODO(b/332937943): implement offset adjustment by manually adjusting crop to + // adhere to device aspect ratio SparseArray<Rect> cropHints = parseCropHints(info, hintTag); if (cropHints != null) { Slog.i(TAG, "Got restored wallpaper; applying which=" + which @@ -601,7 +592,6 @@ public class WallpaperBackupAgent extends BackupAgent { } return; } - // Parse the restored info file to find the crop hint. Note that this currently // relies on a priori knowledge of the wallpaper info file schema. Rect cropHint = parseCropHint(info, hintTag); @@ -609,8 +599,33 @@ public class WallpaperBackupAgent extends BackupAgent { Slog.i(TAG, "Got restored wallpaper; applying which=" + which + "; cropHint = " + cropHint); try (FileInputStream in = new FileInputStream(stage)) { - mWallpaperManager.setStream(in, cropHint.isEmpty() ? null : cropHint, true, - which); + + if (sourceDeviceDimensions != null && sourceDeviceDimensions.first != null) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + ParcelFileDescriptor pdf = ParcelFileDescriptor.open(stage, MODE_READ_ONLY); + BitmapFactory.decodeFileDescriptor(pdf.getFileDescriptor(), + null, options); + Point bitmapSize = new Point(options.outWidth, options.outHeight); + Point sourceDeviceSize = new Point(sourceDeviceDimensions.first.x, + sourceDeviceDimensions.first.y); + Point targetDeviceDimensions = getScreenDimensions(); + + // TODO: for now we handle only the case where the target device has smaller + // aspect ratio than the source device i.e. the target device is more narrow + // than the source device + if (isTargetMoreNarrowThanSource(targetDeviceDimensions, + sourceDeviceSize)) { + Rect adjustedCrop = findNewCropfromOldCrop(cropHint, + sourceDeviceDimensions.first, true, targetDeviceDimensions, + bitmapSize, true); + + cropHint.set(adjustedCrop); + } + } + + mWallpaperManager.setStream(in, cropHint.isEmpty() ? null : cropHint, + true, which); // And log the success if ((which & FLAG_SYSTEM) > 0) { @@ -629,6 +644,209 @@ public class WallpaperBackupAgent extends BackupAgent { } } + /** + * This method computes the crop of the stored wallpaper to preserve its center point as the + * user had set it in the previous device. + * + * The algorithm involves first computing the original crop of the user (without parallax). Then + * manually adjusting the user's original crop to respect the current device's aspect ratio + * (thereby preserving the center point). Then finally, adding any leftover image real-estate + * (i.e. space left over on the horizontal axis) to add parallax effect. Parallax is only added + * if was present in the old device's settings. + * + */ + private Rect findNewCropfromOldCrop(Rect oldCrop, Point oldDisplaySize, boolean oldRtl, + Point newDisplaySize, Point bitmapSize, boolean newRtl) { + Rect cropWithoutParallax = withoutParallax(oldCrop, oldDisplaySize, oldRtl, bitmapSize); + oldCrop = oldCrop.isEmpty() ? new Rect(0, 0, bitmapSize.x, bitmapSize.y) : oldCrop; + float oldParallaxAmount = ((float) oldCrop.width() / cropWithoutParallax.width()) - 1; + + Rect newCropWithSameCenterWithoutParallax = sameCenter(newDisplaySize, bitmapSize, + cropWithoutParallax); + + Rect newCrop = newCropWithSameCenterWithoutParallax; + + // calculate the amount of left-over space there is in the image after adjusting the crop + // from the above operation i.e. in a rtl configuration, this is the remaining space in the + // image after subtracting the new crop's right edge coordinate from the image itself, and + // for ltr, its just the new crop's left edge coordinate (as it's the distance from the + // beginning of the image) + int widthAvailableForParallaxOnTheNewDevice = + (newRtl) ? newCrop.left : bitmapSize.x - newCrop.right; + + // calculate relatively how much this available space is as a fraction of the total cropped + // image + float availableParallaxAmount = + (float) widthAvailableForParallaxOnTheNewDevice / newCrop.width(); + + float minAcceptableParallax = Math.min(DEFAULT_ACCEPTABLE_PARALLAX, oldParallaxAmount); + + if (DEBUG) { + Slog.d(TAG, "- cropWithoutParallax: " + cropWithoutParallax); + Slog.d(TAG, "- oldParallaxAmount: " + oldParallaxAmount); + Slog.d(TAG, "- newCropWithSameCenterWithoutParallax: " + + newCropWithSameCenterWithoutParallax); + Slog.d(TAG, "- widthAvailableForParallaxOnTheNewDevice: " + + widthAvailableForParallaxOnTheNewDevice); + Slog.d(TAG, "- availableParallaxAmount: " + availableParallaxAmount); + Slog.d(TAG, "- minAcceptableParallax: " + minAcceptableParallax); + Slog.d(TAG, "- oldCrop: " + oldCrop); + Slog.d(TAG, "- oldDisplaySize: " + oldDisplaySize); + Slog.d(TAG, "- oldRtl: " + oldRtl); + Slog.d(TAG, "- newDisplaySize: " + newDisplaySize); + Slog.d(TAG, "- bitmapSize: " + bitmapSize); + Slog.d(TAG, "- newRtl: " + newRtl); + } + if (availableParallaxAmount >= minAcceptableParallax) { + // but in any case, don't put more parallax than the amount of the old device + float parallaxToAdd = Math.min(availableParallaxAmount, oldParallaxAmount); + + int widthToAddForParallax = (int) (newCrop.width() * parallaxToAdd); + if (DEBUG) { + Slog.d(TAG, "- parallaxToAdd: " + parallaxToAdd); + Slog.d(TAG, "- widthToAddForParallax: " + widthToAddForParallax); + } + if (newRtl) { + newCrop.left -= widthToAddForParallax; + } else { + newCrop.right += widthToAddForParallax; + } + } + return newCrop; + } + + /** + * This method computes the original crop of the user without parallax. + * + * NOTE: When the user sets the wallpaper with a specific crop, there may additional image added + * to the crop to support parallax. In order to determine the user's actual crop the parallax + * must be removed if it exists. + */ + Rect withoutParallax(Rect crop, Point displaySize, boolean rtl, Point bitmapSize) { + // in the case an image's crop is not set, we assume the image itself is cropped + if (crop.isEmpty()) { + crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + } + + if (DEBUG) { + Slog.w(TAG, "- crop: " + crop); + } + + Rect adjustedCrop = new Rect(crop); + float suggestedDisplayRatio = (float) displaySize.x / displaySize.y; + + // here we calculate the width of the wallpaper image such that it has the same aspect ratio + // as the given display i.e. the width of the image on a single page of the device without + // parallax (i.e. displaySize will correspond to the display the crop was originally set on) + int wallpaperWidthWithoutParallax = (int) (0.5f + (float) displaySize.x * crop.height() + / displaySize.y); + // subtracting wallpaperWidthWithoutParallax from the wallpaper crop gives the amount of + // parallax added + int widthToRemove = Math.max(0, crop.width() - wallpaperWidthWithoutParallax); + + if (DEBUG) { + Slog.d(TAG, "- adjustedCrop: " + adjustedCrop); + Slog.d(TAG, "- suggestedDisplayRatio: " + suggestedDisplayRatio); + Slog.d(TAG, "- wallpaperWidthWithoutParallax: " + wallpaperWidthWithoutParallax); + Slog.d(TAG, "- widthToRemove: " + widthToRemove); + } + if (rtl) { + adjustedCrop.left += widthToRemove; + } else { + adjustedCrop.right -= widthToRemove; + } + + if (DEBUG) { + Slog.d(TAG, "- adjustedCrop: " + crop); + } + return adjustedCrop; + } + + /** + * This method computes a new crop based on the given crop in order to preserve the center point + * of the given crop on the provided displaySize. This is only for the case where the device + * displaySize has a smaller aspect ratio than the cropped image. + * + * NOTE: If the width to height ratio is less in the device display than cropped image + * this means the aspect ratios are off and there will be distortions in the image + * if the image is applied to the current display (i.e. the image will be skewed -> + * pixels in the image will not align correctly with the same pixels in the image that are + * above them) + */ + Rect sameCenter(Point displaySize, Point bitmapSize, Rect crop) { + + // in the case an image's crop is not set, we assume the image itself is cropped + if (crop.isEmpty()) { + crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + } + + float screenRatio = (float) displaySize.x / displaySize.y; + float cropRatio = (float) crop.width() / crop.height(); + + Rect adjustedCrop = new Rect(crop); + + if (screenRatio < cropRatio) { + // the screen is more narrow than the image, and as such, the image will need to be + // zoomed in till it fits in the vertical axis. Due to this, we need to manually adjust + // the image's crop in order for it to fit into the screen without having the framework + // do it (since the framework left aligns the image after zooming) + + // Calculate the height of the adjusted wallpaper crop so it respects the aspect ratio + // of the device. To calculate the height, we will use the width of the current crop. + // This is so we find the largest height possible which also respects the device aspect + // ratio. + int heightToAdd = (int) (0.5f + crop.width() / screenRatio - crop.height()); + + // Calculate how much extra image space available that can be used to adjust + // the crop. If this amount is less than heightToAdd, from above, then that means we + // can't use heightToAdd. Instead we will need to use the maximum possible height, which + // is the height of the original bitmap. NOTE: the bitmap height may be different than + // the crop. + // since there is no guarantee to have height available on both sides + // (e.g. the available height might be fully at the bottom), grab the minimum + int availableHeight = 2 * Math.min(crop.top, bitmapSize.y - crop.bottom); + int actualHeightToAdd = Math.min(heightToAdd, availableHeight); + + // half of the additional height is added to the top and bottom of the crop + adjustedCrop.top -= actualHeightToAdd / 2 + actualHeightToAdd % 2; + adjustedCrop.bottom += actualHeightToAdd / 2; + + // Calculate the width of the adjusted crop. Initially we used the fixed width of the + // crop to calculate the heightToAdd, but since this height may be invalid (based on + // the calculation above) we calculate the width again instead of using the fixed width, + // using the adjustedCrop's updated height. + int widthToRemove = (int) (0.5f + crop.width() - adjustedCrop.height() * screenRatio); + + // half of the additional width is subtracted from the left and right side of the crop + int widthToRemoveLeft = widthToRemove / 2; + int widthToRemoveRight = widthToRemove / 2 + widthToRemove % 2; + + adjustedCrop.left += widthToRemoveLeft; + adjustedCrop.right -= widthToRemoveRight; + + if (DEBUG) { + Slog.d(TAG, "cropRatio: " + cropRatio); + Slog.d(TAG, "screenRatio: " + screenRatio); + Slog.d(TAG, "heightToAdd: " + heightToAdd); + Slog.d(TAG, "actualHeightToAdd: " + actualHeightToAdd); + Slog.d(TAG, "availableHeight: " + availableHeight); + Slog.d(TAG, "widthToRemove: " + widthToRemove); + Slog.d(TAG, "adjustedCrop: " + adjustedCrop); + } + + return adjustedCrop; + } + + return adjustedCrop; + } + + private boolean isTargetMoreNarrowThanSource(Point targetDisplaySize, Point srcDisplaySize) { + float targetScreenRatio = (float) targetDisplaySize.x / targetDisplaySize.y; + float srcScreenRatio = (float) srcDisplaySize.x / srcDisplaySize.y; + + return (targetScreenRatio < srcScreenRatio); + } + private void logRestoreErrorIfNoLiveComponent(int which, String error) { if (mSystemHasLiveComponent) { return; @@ -644,6 +862,7 @@ public class WallpaperBackupAgent extends BackupAgent { mEventLogger.onLockImageWallpaperRestoreFailed(error); } } + private Rect parseCropHint(File wallpaperInfo, String sectionTag) { Rect cropHint = new Rect(); try (FileInputStream stream = new FileInputStream(wallpaperInfo)) { @@ -681,7 +900,7 @@ public class WallpaperBackupAgent extends BackupAgent { if (type != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (!sectionTag.equals(tag)) continue; - for (Pair<Integer, String> pair: List.of( + for (Pair<Integer, String> pair : List.of( new Pair<>(WallpaperManager.PORTRAIT, "Portrait"), new Pair<>(WallpaperManager.LANDSCAPE, "Landscape"), new Pair<>(WallpaperManager.SQUARE_PORTRAIT, "SquarePortrait"), @@ -907,22 +1126,6 @@ public class WallpaperBackupAgent extends BackupAgent { return internalDisplays; } - /** - * This method compares the source and target dimensions, and returns true if there is a - * significant difference in area between them and the source dimensions are smaller than the - * target dimensions. - * - * @param sourceDimensions is the dimensions of the source device - * @param targetDimensions is the dimensions of the target device - */ - @VisibleForTesting - boolean isSourceDeviceSignificantlySmallerThanTarget(Point sourceDimensions, - Point targetDimensions) { - int rawAreaDelta = (targetDimensions.x * targetDimensions.y) - - (sourceDimensions.x * sourceDimensions.y); - return rawAreaDelta > AREA_THRESHOLD; - } - @VisibleForTesting boolean isDeviceInRestore() { try { diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java index ec9223c7d667..3ecdf3f101a5 100644 --- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java +++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java @@ -59,7 +59,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.graphics.Point; import android.graphics.Rect; import android.os.FileUtils; import android.os.ParcelFileDescriptor; @@ -841,26 +840,6 @@ public class WallpaperBackupAgentTest { testParseCropHints(testMap); } - @Test - public void test_sourceDimensionsAreLargerThanTarget() { - // source device is larger than target, expecting to get false - Point sourceDimensions = new Point(2208, 1840); - Point targetDimensions = new Point(1080, 2092); - boolean isSourceSmaller = mWallpaperBackupAgent - .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions); - assertThat(isSourceSmaller).isEqualTo(false); - } - - @Test - public void test_sourceDimensionsMuchSmallerThanTarget() { - // source device is smaller than target, expecting to get true - Point sourceDimensions = new Point(1080, 2092); - Point targetDimensions = new Point(2208, 1840); - boolean isSourceSmaller = mWallpaperBackupAgent - .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions); - assertThat(isSourceSmaller).isEqualTo(true); - } - private void testParseCropHints(Map<Integer, Rect> testMap) throws Exception { assumeTrue(multiCrop()); mockRestoredStaticWallpaperFile(testMap); |