Merge "Move wallpaper crop logic in a new class WallpaperCropper"
This commit is contained in:
committed by
Android (Google) Code Review
commit
17fc1ea27e
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static com.android.server.wallpaper.WallpaperUtils.RECORD_FILE;
|
||||
import static com.android.server.wallpaper.WallpaperUtils.RECORD_LOCK_FILE;
|
||||
import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER;
|
||||
import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.ImageDecoder;
|
||||
import android.graphics.Rect;
|
||||
import android.os.FileUtils;
|
||||
import android.os.SELinux;
|
||||
import android.util.Slog;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
import com.android.server.utils.TimingsTraceAndSlog;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
/**
|
||||
* Helper file for wallpaper cropping
|
||||
* Meant to have a single instance, only used by the WallpaperManagerService
|
||||
*/
|
||||
class WallpaperCropper {
|
||||
|
||||
private static final String TAG = WallpaperCropper.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
private static final boolean DEBUG_CROP = true;
|
||||
|
||||
private final WallpaperDisplayHelper mWallpaperDisplayHelper;
|
||||
|
||||
WallpaperCropper(WallpaperDisplayHelper wallpaperDisplayHelper) {
|
||||
mWallpaperDisplayHelper = wallpaperDisplayHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
|
||||
* for display.
|
||||
*
|
||||
* This will generate the crop and write it in the file
|
||||
*/
|
||||
void generateCrop(WallpaperData wallpaper) {
|
||||
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
|
||||
t.traceBegin("WPMS.generateCrop");
|
||||
generateCropInternal(wallpaper);
|
||||
t.traceEnd();
|
||||
}
|
||||
|
||||
private void generateCropInternal(WallpaperData wallpaper) {
|
||||
boolean success = false;
|
||||
|
||||
// Only generate crop for default display.
|
||||
final WallpaperDisplayHelper.DisplayData wpData =
|
||||
mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
|
||||
final Rect cropHint = new Rect(wallpaper.cropHint);
|
||||
final DisplayInfo displayInfo = mWallpaperDisplayHelper.getDisplayInfo(DEFAULT_DISPLAY);
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
|
||||
+ Integer.toHexString(wallpaper.mWhich)
|
||||
+ " to " + wallpaper.cropFile.getName()
|
||||
+ " crop=(" + cropHint.width() + 'x' + cropHint.height()
|
||||
+ ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
|
||||
}
|
||||
|
||||
// Analyse the source; needed in multiple cases
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
|
||||
if (options.outWidth <= 0 || options.outHeight <= 0) {
|
||||
Slog.w(TAG, "Invalid wallpaper data");
|
||||
success = false;
|
||||
} else {
|
||||
boolean needCrop = false;
|
||||
boolean needScale;
|
||||
|
||||
// Empty crop means use the full image
|
||||
if (cropHint.isEmpty()) {
|
||||
cropHint.left = cropHint.top = 0;
|
||||
cropHint.right = options.outWidth;
|
||||
cropHint.bottom = options.outHeight;
|
||||
} else {
|
||||
// force the crop rect to lie within the measured bounds
|
||||
int dx = cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0;
|
||||
int dy = cropHint.bottom > options.outHeight
|
||||
? options.outHeight - cropHint.bottom : 0;
|
||||
cropHint.offset(dx, dy);
|
||||
|
||||
// If the crop hint was larger than the image we just overshot. Patch things up.
|
||||
if (cropHint.left < 0) {
|
||||
cropHint.left = 0;
|
||||
}
|
||||
if (cropHint.top < 0) {
|
||||
cropHint.top = 0;
|
||||
}
|
||||
|
||||
// Don't bother cropping if what we're left with is identity
|
||||
needCrop = (options.outHeight > cropHint.height()
|
||||
|| options.outWidth > cropHint.width());
|
||||
}
|
||||
|
||||
// scale if the crop height winds up not matching the recommended metrics
|
||||
needScale = cropHint.height() > wpData.mHeight
|
||||
|| cropHint.height() > GLHelper.getMaxTextureSize()
|
||||
|| cropHint.width() > GLHelper.getMaxTextureSize();
|
||||
|
||||
//make sure screen aspect ratio is preserved if width is scaled under screen size
|
||||
if (needScale) {
|
||||
final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
|
||||
final int newWidth = (int) (cropHint.width() * scaleByHeight);
|
||||
if (newWidth < displayInfo.logicalWidth) {
|
||||
final float screenAspectRatio =
|
||||
(float) displayInfo.logicalHeight / (float) displayInfo.logicalWidth;
|
||||
cropHint.bottom = (int) (cropHint.width() * screenAspectRatio);
|
||||
needCrop = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_CROP) {
|
||||
Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
|
||||
Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
|
||||
Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
|
||||
Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
|
||||
}
|
||||
|
||||
if (!needCrop && !needScale) {
|
||||
// Simple case: the nominal crop fits what we want, so we take
|
||||
// the whole thing and just copy the image file directly.
|
||||
|
||||
// TODO: It is not accurate to estimate bitmap size without decoding it,
|
||||
// may be we can try to remove this optimized way in the future,
|
||||
// that means, we will always go into the 'else' block.
|
||||
|
||||
success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
|
||||
|
||||
if (!success) {
|
||||
wallpaper.cropFile.delete();
|
||||
// TODO: fall back to default wallpaper in this case
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
long estimateSize = (long) options.outWidth * options.outHeight * 4;
|
||||
Slog.v(TAG, "Null crop of new wallpaper, estimate size="
|
||||
+ estimateSize + ", success=" + success);
|
||||
}
|
||||
} else {
|
||||
// Fancy case: crop and scale. First, we decode and scale down if appropriate.
|
||||
FileOutputStream f = null;
|
||||
BufferedOutputStream bos = null;
|
||||
try {
|
||||
// This actually downsamples only by powers of two, but that's okay; we do
|
||||
// a proper scaling blit later. This is to minimize transient RAM use.
|
||||
// We calculate the largest power-of-two under the actual ratio rather than
|
||||
// just let the decode take care of it because we also want to remap where the
|
||||
// cropHint rectangle lies in the decoded [super]rect.
|
||||
final int actualScale = cropHint.height() / wpData.mHeight;
|
||||
int scale = 1;
|
||||
while (2 * scale <= actualScale) {
|
||||
scale *= 2;
|
||||
}
|
||||
options.inSampleSize = scale;
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
final Rect estimateCrop = new Rect(cropHint);
|
||||
estimateCrop.scale(1f / options.inSampleSize);
|
||||
final float hRatio = (float) wpData.mHeight / estimateCrop.height();
|
||||
final int destHeight = (int) (estimateCrop.height() * hRatio);
|
||||
final int destWidth = (int) (estimateCrop.width() * hRatio);
|
||||
|
||||
// We estimated an invalid crop, try to adjust the cropHint to get a valid one.
|
||||
if (destWidth > GLHelper.getMaxTextureSize()) {
|
||||
int newHeight = (int) (wpData.mHeight / hRatio);
|
||||
int newWidth = (int) (wpData.mWidth / hRatio);
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Invalid crop dimensions, trying to adjust.");
|
||||
}
|
||||
|
||||
estimateCrop.set(cropHint);
|
||||
estimateCrop.left += (cropHint.width() - newWidth) / 2;
|
||||
estimateCrop.top += (cropHint.height() - newHeight) / 2;
|
||||
estimateCrop.right = estimateCrop.left + newWidth;
|
||||
estimateCrop.bottom = estimateCrop.top + newHeight;
|
||||
cropHint.set(estimateCrop);
|
||||
estimateCrop.scale(1f / options.inSampleSize);
|
||||
}
|
||||
|
||||
// We've got the safe cropHint; now we want to scale it properly to
|
||||
// the desired rectangle.
|
||||
// That's a height-biased operation: make it fit the hinted height.
|
||||
final int safeHeight = (int) (estimateCrop.height() * hRatio);
|
||||
final int safeWidth = (int) (estimateCrop.width() * hRatio);
|
||||
|
||||
if (DEBUG_CROP) {
|
||||
Slog.v(TAG, "Decode parameters:");
|
||||
Slog.v(TAG, " cropHint=" + cropHint + ", estimateCrop=" + estimateCrop);
|
||||
Slog.v(TAG, " down sampling=" + options.inSampleSize
|
||||
+ ", hRatio=" + hRatio);
|
||||
Slog.v(TAG, " dest=" + destWidth + "x" + destHeight);
|
||||
Slog.v(TAG, " safe=" + safeWidth + "x" + safeHeight);
|
||||
Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize());
|
||||
}
|
||||
|
||||
//Create a record file and will delete if ImageDecoder work well.
|
||||
final String recordName =
|
||||
(wallpaper.wallpaperFile.getName().equals(WALLPAPER)
|
||||
? RECORD_FILE : RECORD_LOCK_FILE);
|
||||
final File record = new File(getWallpaperDir(wallpaper.userId), recordName);
|
||||
record.createNewFile();
|
||||
Slog.v(TAG, "record path =" + record.getPath()
|
||||
+ ", record name =" + record.getName());
|
||||
|
||||
final ImageDecoder.Source srcData =
|
||||
ImageDecoder.createSource(wallpaper.wallpaperFile);
|
||||
final int sampleSize = scale;
|
||||
Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
|
||||
decoder.setTargetSampleSize(sampleSize);
|
||||
decoder.setCrop(estimateCrop);
|
||||
});
|
||||
|
||||
record.delete();
|
||||
|
||||
if (cropped == null) {
|
||||
Slog.e(TAG, "Could not decode new wallpaper");
|
||||
} else {
|
||||
// We are safe to create final crop with safe dimensions now.
|
||||
final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
|
||||
safeWidth, safeHeight, true);
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Final extract:");
|
||||
Slog.v(TAG, " dims: w=" + wpData.mWidth
|
||||
+ " h=" + wpData.mHeight);
|
||||
Slog.v(TAG, " out: w=" + finalCrop.getWidth()
|
||||
+ " h=" + finalCrop.getHeight());
|
||||
}
|
||||
|
||||
f = new FileOutputStream(wallpaper.cropFile);
|
||||
bos = new BufferedOutputStream(f, 32 * 1024);
|
||||
finalCrop.compress(Bitmap.CompressFormat.PNG, 100, bos);
|
||||
// don't rely on the implicit flush-at-close when noting success
|
||||
bos.flush();
|
||||
success = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (DEBUG) {
|
||||
Slog.e(TAG, "Error decoding crop", e);
|
||||
}
|
||||
} finally {
|
||||
IoUtils.closeQuietly(bos);
|
||||
IoUtils.closeQuietly(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
Slog.e(TAG, "Unable to apply new wallpaper");
|
||||
wallpaper.cropFile.delete();
|
||||
}
|
||||
|
||||
if (wallpaper.cropFile.exists()) {
|
||||
boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.wm.WindowManagerInternal;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
@@ -36,7 +35,6 @@ import java.util.function.Consumer;
|
||||
*/
|
||||
class WallpaperDisplayHelper {
|
||||
|
||||
@VisibleForTesting
|
||||
static final class DisplayData {
|
||||
int mWidth = -1;
|
||||
int mHeight = -1;
|
||||
|
||||
@@ -74,7 +74,6 @@ import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ImageDecoder;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.display.DisplayManager;
|
||||
@@ -109,7 +108,6 @@ import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.Xml;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -132,7 +130,6 @@ import libcore.io.IoUtils;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
@@ -308,7 +305,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
|
||||
}
|
||||
loadSettingsLocked(wallpaper.userId, true);
|
||||
}
|
||||
generateCrop(wallpaper);
|
||||
mWallpaperCropper.generateCrop(wallpaper);
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Crop done; invoking completion callback");
|
||||
}
|
||||
@@ -593,233 +590,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
|
||||
return colors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
|
||||
* for display.
|
||||
*/
|
||||
void generateCrop(WallpaperData wallpaper) {
|
||||
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
|
||||
t.traceBegin("WPMS.generateCrop");
|
||||
generateCropInternal(wallpaper);
|
||||
t.traceEnd();
|
||||
}
|
||||
|
||||
private void generateCropInternal(WallpaperData wallpaper) {
|
||||
boolean success = false;
|
||||
|
||||
// Only generate crop for default display.
|
||||
final DisplayData wpData = mWallpaperDisplayHelper.getDisplayDataOrCreate(DEFAULT_DISPLAY);
|
||||
final Rect cropHint = new Rect(wallpaper.cropHint);
|
||||
final DisplayInfo displayInfo = mWallpaperDisplayHelper.getDisplayInfo(DEFAULT_DISPLAY);
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
|
||||
+ Integer.toHexString(wallpaper.mWhich)
|
||||
+ " to " + wallpaper.cropFile.getName()
|
||||
+ " crop=(" + cropHint.width() + 'x' + cropHint.height()
|
||||
+ ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
|
||||
}
|
||||
|
||||
// Analyse the source; needed in multiple cases
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
|
||||
if (options.outWidth <= 0 || options.outHeight <= 0) {
|
||||
Slog.w(TAG, "Invalid wallpaper data");
|
||||
success = false;
|
||||
} else {
|
||||
boolean needCrop = false;
|
||||
boolean needScale = false;
|
||||
|
||||
// Empty crop means use the full image
|
||||
if (cropHint.isEmpty()) {
|
||||
cropHint.left = cropHint.top = 0;
|
||||
cropHint.right = options.outWidth;
|
||||
cropHint.bottom = options.outHeight;
|
||||
} else {
|
||||
// force the crop rect to lie within the measured bounds
|
||||
cropHint.offset(
|
||||
(cropHint.right > options.outWidth ? options.outWidth - cropHint.right : 0),
|
||||
(cropHint.bottom > options.outHeight ? options.outHeight - cropHint.bottom : 0));
|
||||
|
||||
// If the crop hint was larger than the image we just overshot. Patch things up.
|
||||
if (cropHint.left < 0) {
|
||||
cropHint.left = 0;
|
||||
}
|
||||
if (cropHint.top < 0) {
|
||||
cropHint.top = 0;
|
||||
}
|
||||
|
||||
// Don't bother cropping if what we're left with is identity
|
||||
needCrop = (options.outHeight > cropHint.height()
|
||||
|| options.outWidth > cropHint.width());
|
||||
}
|
||||
|
||||
// scale if the crop height winds up not matching the recommended metrics
|
||||
needScale = cropHint.height() > wpData.mHeight
|
||||
|| cropHint.height() > GLHelper.getMaxTextureSize()
|
||||
|| cropHint.width() > GLHelper.getMaxTextureSize();
|
||||
|
||||
//make sure screen aspect ratio is preserved if width is scaled under screen size
|
||||
if (needScale) {
|
||||
final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
|
||||
final int newWidth = (int) (cropHint.width() * scaleByHeight);
|
||||
if (newWidth < displayInfo.logicalWidth) {
|
||||
final float screenAspectRatio =
|
||||
(float) displayInfo.logicalHeight / (float) displayInfo.logicalWidth;
|
||||
cropHint.bottom = (int) (cropHint.width() * screenAspectRatio);
|
||||
needCrop = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_CROP) {
|
||||
Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
|
||||
Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
|
||||
Slog.v(TAG, "meas: w=" + options.outWidth + " h=" + options.outHeight);
|
||||
Slog.v(TAG, "crop?=" + needCrop + " scale?=" + needScale);
|
||||
}
|
||||
|
||||
if (!needCrop && !needScale) {
|
||||
// Simple case: the nominal crop fits what we want, so we take
|
||||
// the whole thing and just copy the image file directly.
|
||||
|
||||
// TODO: It is not accurate to estimate bitmap size without decoding it,
|
||||
// may be we can try to remove this optimized way in the future,
|
||||
// that means, we will always go into the 'else' block.
|
||||
|
||||
success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
|
||||
|
||||
if (!success) {
|
||||
wallpaper.cropFile.delete();
|
||||
// TODO: fall back to default wallpaper in this case
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
long estimateSize = (long) options.outWidth * options.outHeight * 4;
|
||||
Slog.v(TAG, "Null crop of new wallpaper, estimate size="
|
||||
+ estimateSize + ", success=" + success);
|
||||
}
|
||||
} else {
|
||||
// Fancy case: crop and scale. First, we decode and scale down if appropriate.
|
||||
FileOutputStream f = null;
|
||||
BufferedOutputStream bos = null;
|
||||
try {
|
||||
// This actually downsamples only by powers of two, but that's okay; we do
|
||||
// a proper scaling blit later. This is to minimize transient RAM use.
|
||||
// We calculate the largest power-of-two under the actual ratio rather than
|
||||
// just let the decode take care of it because we also want to remap where the
|
||||
// cropHint rectangle lies in the decoded [super]rect.
|
||||
final int actualScale = cropHint.height() / wpData.mHeight;
|
||||
int scale = 1;
|
||||
while (2 * scale <= actualScale) {
|
||||
scale *= 2;
|
||||
}
|
||||
options.inSampleSize = scale;
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
final Rect estimateCrop = new Rect(cropHint);
|
||||
estimateCrop.scale(1f / options.inSampleSize);
|
||||
final float hRatio = (float) wpData.mHeight / estimateCrop.height();
|
||||
final int destHeight = (int) (estimateCrop.height() * hRatio);
|
||||
final int destWidth = (int) (estimateCrop.width() * hRatio);
|
||||
|
||||
// We estimated an invalid crop, try to adjust the cropHint to get a valid one.
|
||||
if (destWidth > GLHelper.getMaxTextureSize()) {
|
||||
int newHeight = (int) (wpData.mHeight / hRatio);
|
||||
int newWidth = (int) (wpData.mWidth / hRatio);
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Invalid crop dimensions, trying to adjust.");
|
||||
}
|
||||
|
||||
estimateCrop.set(cropHint);
|
||||
estimateCrop.left += (cropHint.width() - newWidth) / 2;
|
||||
estimateCrop.top += (cropHint.height() - newHeight) / 2;
|
||||
estimateCrop.right = estimateCrop.left + newWidth;
|
||||
estimateCrop.bottom = estimateCrop.top + newHeight;
|
||||
cropHint.set(estimateCrop);
|
||||
estimateCrop.scale(1f / options.inSampleSize);
|
||||
}
|
||||
|
||||
// We've got the safe cropHint; now we want to scale it properly to
|
||||
// the desired rectangle.
|
||||
// That's a height-biased operation: make it fit the hinted height.
|
||||
final int safeHeight = (int) (estimateCrop.height() * hRatio);
|
||||
final int safeWidth = (int) (estimateCrop.width() * hRatio);
|
||||
|
||||
if (DEBUG_CROP) {
|
||||
Slog.v(TAG, "Decode parameters:");
|
||||
Slog.v(TAG, " cropHint=" + cropHint + ", estimateCrop=" + estimateCrop);
|
||||
Slog.v(TAG, " down sampling=" + options.inSampleSize
|
||||
+ ", hRatio=" + hRatio);
|
||||
Slog.v(TAG, " dest=" + destWidth + "x" + destHeight);
|
||||
Slog.v(TAG, " safe=" + safeWidth + "x" + safeHeight);
|
||||
Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize());
|
||||
}
|
||||
|
||||
//Create a record file and will delete if ImageDecoder work well.
|
||||
final String recordName =
|
||||
(wallpaper.wallpaperFile.getName().equals(WALLPAPER)
|
||||
? RECORD_FILE : RECORD_LOCK_FILE);
|
||||
final File record = new File(getWallpaperDir(wallpaper.userId), recordName);
|
||||
record.createNewFile();
|
||||
Slog.v(TAG, "record path =" + record.getPath()
|
||||
+ ", record name =" + record.getName());
|
||||
|
||||
final ImageDecoder.Source srcData =
|
||||
ImageDecoder.createSource(wallpaper.wallpaperFile);
|
||||
final int sampleSize = scale;
|
||||
Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
|
||||
decoder.setTargetSampleSize(sampleSize);
|
||||
decoder.setCrop(estimateCrop);
|
||||
});
|
||||
|
||||
record.delete();
|
||||
|
||||
if (cropped == null) {
|
||||
Slog.e(TAG, "Could not decode new wallpaper");
|
||||
} else {
|
||||
// We are safe to create final crop with safe dimensions now.
|
||||
final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
|
||||
safeWidth, safeHeight, true);
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Final extract:");
|
||||
Slog.v(TAG, " dims: w=" + wpData.mWidth
|
||||
+ " h=" + wpData.mHeight);
|
||||
Slog.v(TAG, " out: w=" + finalCrop.getWidth()
|
||||
+ " h=" + finalCrop.getHeight());
|
||||
}
|
||||
|
||||
f = new FileOutputStream(wallpaper.cropFile);
|
||||
bos = new BufferedOutputStream(f, 32*1024);
|
||||
finalCrop.compress(Bitmap.CompressFormat.PNG, 100, bos);
|
||||
bos.flush(); // don't rely on the implicit flush-at-close when noting success
|
||||
success = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (DEBUG) {
|
||||
Slog.e(TAG, "Error decoding crop", e);
|
||||
}
|
||||
} finally {
|
||||
IoUtils.closeQuietly(bos);
|
||||
IoUtils.closeQuietly(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
Slog.e(TAG, "Unable to apply new wallpaper");
|
||||
wallpaper.cropFile.delete();
|
||||
}
|
||||
|
||||
if (wallpaper.cropFile.exists()) {
|
||||
boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
private final WindowManagerInternal mWindowManagerInternal;
|
||||
private final IPackageManager mIPackageManager;
|
||||
@@ -912,6 +682,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
|
||||
|
||||
@VisibleForTesting
|
||||
final WallpaperDisplayHelper mWallpaperDisplayHelper;
|
||||
final WallpaperCropper mWallpaperCropper;
|
||||
|
||||
private boolean supportsMultiDisplay(WallpaperConnection connection) {
|
||||
if (connection != null) {
|
||||
@@ -1567,6 +1338,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
|
||||
DisplayManager dm = mContext.getSystemService(DisplayManager.class);
|
||||
dm.registerDisplayListener(mDisplayListener, null /* handler */);
|
||||
mWallpaperDisplayHelper = new WallpaperDisplayHelper(dm, mWindowManagerInternal);
|
||||
mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
|
||||
mActivityManager = mContext.getSystemService(ActivityManager.class);
|
||||
mMonitor = new MyPackageMonitor();
|
||||
mColorsChangedListeners = new SparseArray<>();
|
||||
@@ -1617,7 +1389,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "No crop; regenerating from source");
|
||||
}
|
||||
generateCrop(wallpaper);
|
||||
mWallpaperCropper.generateCrop(wallpaper);
|
||||
}
|
||||
// Still nothing? Fall back to default.
|
||||
if (!wallpaper.cropExists()) {
|
||||
@@ -3479,7 +3251,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
|
||||
mWallpaperMap.put(userId, wallpaper);
|
||||
if (!wallpaper.cropExists()) {
|
||||
if (wallpaper.sourceExists()) {
|
||||
generateCrop(wallpaper);
|
||||
mWallpaperCropper.generateCrop(wallpaper);
|
||||
} else {
|
||||
Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
|
||||
}
|
||||
@@ -3700,7 +3472,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
|
||||
if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
|
||||
+ " id=" + wallpaper.wallpaperId);
|
||||
if (success) {
|
||||
generateCrop(wallpaper); // based on the new image + metadata
|
||||
mWallpaperCropper.generateCrop(wallpaper); // based on the new image + metadata
|
||||
bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
|
||||
wallpaper, null);
|
||||
}
|
||||
|
||||
@@ -427,7 +427,8 @@ public class WallpaperManagerServiceTests {
|
||||
doReturn(true).when(mService)
|
||||
.bindWallpaperComponentLocked(any(), anyBoolean(), anyBoolean(), any(), any());
|
||||
doNothing().when(mService).saveSettingsLocked(wallpaper.userId);
|
||||
doNothing().when(mService).generateCrop(wallpaper);
|
||||
spyOn(mService.mWallpaperCropper);
|
||||
doNothing().when(mService.mWallpaperCropper).generateCrop(wallpaper);
|
||||
|
||||
// timestamps of {ACTION_WALLPAPER_CHANGED, onWallpaperColorsChanged}
|
||||
final long[] timestamps = new long[2];
|
||||
|
||||
Reference in New Issue
Block a user