Merge "Moving some files and methods around" into ub-launcher3-master
This commit is contained in:
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* Copyright (C) 2015 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.gallery3d.common;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapRegionDecoder;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
|
||||
|
||||
public interface OnBitmapCroppedHandler {
|
||||
public void onBitmapCropped(byte[] imageBytes);
|
||||
}
|
||||
|
||||
private static final int DEFAULT_COMPRESS_QUALITY = 90;
|
||||
private static final String LOGTAG = "BitmapCropTask";
|
||||
|
||||
Uri mInUri = null;
|
||||
Context mContext;
|
||||
String mInFilePath;
|
||||
byte[] mInImageBytes;
|
||||
int mInResId = 0;
|
||||
RectF mCropBounds = null;
|
||||
int mOutWidth, mOutHeight;
|
||||
int mRotation;
|
||||
boolean mSetWallpaper;
|
||||
boolean mSaveCroppedBitmap;
|
||||
Bitmap mCroppedBitmap;
|
||||
Runnable mOnEndRunnable;
|
||||
Resources mResources;
|
||||
BitmapCropTask.OnBitmapCroppedHandler mOnBitmapCroppedHandler;
|
||||
boolean mNoCrop;
|
||||
|
||||
public BitmapCropTask(Context c, String filePath,
|
||||
RectF cropBounds, int rotation, int outWidth, int outHeight,
|
||||
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
|
||||
mContext = c;
|
||||
mInFilePath = filePath;
|
||||
init(cropBounds, rotation,
|
||||
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
|
||||
}
|
||||
|
||||
public BitmapCropTask(byte[] imageBytes,
|
||||
RectF cropBounds, int rotation, int outWidth, int outHeight,
|
||||
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
|
||||
mInImageBytes = imageBytes;
|
||||
init(cropBounds, rotation,
|
||||
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
|
||||
}
|
||||
|
||||
public BitmapCropTask(Context c, Uri inUri,
|
||||
RectF cropBounds, int rotation, int outWidth, int outHeight,
|
||||
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
|
||||
mContext = c;
|
||||
mInUri = inUri;
|
||||
init(cropBounds, rotation,
|
||||
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
|
||||
}
|
||||
|
||||
public BitmapCropTask(Context c, Resources res, int inResId,
|
||||
RectF cropBounds, int rotation, int outWidth, int outHeight,
|
||||
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
|
||||
mContext = c;
|
||||
mInResId = inResId;
|
||||
mResources = res;
|
||||
init(cropBounds, rotation,
|
||||
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
|
||||
}
|
||||
|
||||
private void init(RectF cropBounds, int rotation, int outWidth, int outHeight,
|
||||
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
|
||||
mCropBounds = cropBounds;
|
||||
mRotation = rotation;
|
||||
mOutWidth = outWidth;
|
||||
mOutHeight = outHeight;
|
||||
mSetWallpaper = setWallpaper;
|
||||
mSaveCroppedBitmap = saveCroppedBitmap;
|
||||
mOnEndRunnable = onEndRunnable;
|
||||
}
|
||||
|
||||
public void setOnBitmapCropped(BitmapCropTask.OnBitmapCroppedHandler handler) {
|
||||
mOnBitmapCroppedHandler = handler;
|
||||
}
|
||||
|
||||
public void setNoCrop(boolean value) {
|
||||
mNoCrop = value;
|
||||
}
|
||||
|
||||
public void setOnEndRunnable(Runnable onEndRunnable) {
|
||||
mOnEndRunnable = onEndRunnable;
|
||||
}
|
||||
|
||||
// Helper to setup input stream
|
||||
private InputStream regenerateInputStream() {
|
||||
if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
|
||||
Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
|
||||
"image byte array given");
|
||||
} else {
|
||||
try {
|
||||
if (mInUri != null) {
|
||||
return new BufferedInputStream(
|
||||
mContext.getContentResolver().openInputStream(mInUri));
|
||||
} else if (mInFilePath != null) {
|
||||
return mContext.openFileInput(mInFilePath);
|
||||
} else if (mInImageBytes != null) {
|
||||
return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes));
|
||||
} else {
|
||||
return new BufferedInputStream(mResources.openRawResource(mInResId));
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Point getImageBounds() {
|
||||
InputStream is = regenerateInputStream();
|
||||
if (is != null) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
Utils.closeSilently(is);
|
||||
if (options.outWidth != 0 && options.outHeight != 0) {
|
||||
return new Point(options.outWidth, options.outHeight);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setCropBounds(RectF cropBounds) {
|
||||
mCropBounds = cropBounds;
|
||||
}
|
||||
|
||||
public Bitmap getCroppedBitmap() {
|
||||
return mCroppedBitmap;
|
||||
}
|
||||
public boolean cropBitmap() {
|
||||
boolean failure = false;
|
||||
|
||||
|
||||
WallpaperManager wallpaperManager = null;
|
||||
if (mSetWallpaper) {
|
||||
wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
|
||||
}
|
||||
|
||||
|
||||
if (mSetWallpaper && mNoCrop) {
|
||||
try {
|
||||
InputStream is = regenerateInputStream();
|
||||
if (is != null) {
|
||||
wallpaperManager.setStream(is);
|
||||
Utils.closeSilently(is);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, "cannot write stream to wallpaper", e);
|
||||
failure = true;
|
||||
}
|
||||
return !failure;
|
||||
} else {
|
||||
// Find crop bounds (scaled to original image size)
|
||||
Rect roundedTrueCrop = new Rect();
|
||||
Matrix rotateMatrix = new Matrix();
|
||||
Matrix inverseRotateMatrix = new Matrix();
|
||||
|
||||
Point bounds = getImageBounds();
|
||||
if (mRotation > 0) {
|
||||
rotateMatrix.setRotate(mRotation);
|
||||
inverseRotateMatrix.setRotate(-mRotation);
|
||||
|
||||
mCropBounds.roundOut(roundedTrueCrop);
|
||||
mCropBounds = new RectF(roundedTrueCrop);
|
||||
|
||||
if (bounds == null) {
|
||||
Log.w(LOGTAG, "cannot get bounds for image");
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
float[] rotatedBounds = new float[] { bounds.x, bounds.y };
|
||||
rotateMatrix.mapPoints(rotatedBounds);
|
||||
rotatedBounds[0] = Math.abs(rotatedBounds[0]);
|
||||
rotatedBounds[1] = Math.abs(rotatedBounds[1]);
|
||||
|
||||
mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2);
|
||||
inverseRotateMatrix.mapRect(mCropBounds);
|
||||
mCropBounds.offset(bounds.x/2, bounds.y/2);
|
||||
|
||||
}
|
||||
|
||||
mCropBounds.roundOut(roundedTrueCrop);
|
||||
|
||||
if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
|
||||
Log.w(LOGTAG, "crop has bad values for full size image");
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// See how much we're reducing the size of the image
|
||||
int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth,
|
||||
roundedTrueCrop.height() / mOutHeight));
|
||||
// Attempt to open a region decoder
|
||||
BitmapRegionDecoder decoder = null;
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = regenerateInputStream();
|
||||
if (is == null) {
|
||||
Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
decoder = BitmapRegionDecoder.newInstance(is, false);
|
||||
Utils.closeSilently(is);
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
|
||||
} finally {
|
||||
Utils.closeSilently(is);
|
||||
is = null;
|
||||
}
|
||||
|
||||
Bitmap crop = null;
|
||||
if (decoder != null) {
|
||||
// Do region decoding to get crop bitmap
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
if (scaleDownSampleSize > 1) {
|
||||
options.inSampleSize = scaleDownSampleSize;
|
||||
}
|
||||
crop = decoder.decodeRegion(roundedTrueCrop, options);
|
||||
decoder.recycle();
|
||||
}
|
||||
|
||||
if (crop == null) {
|
||||
// BitmapRegionDecoder has failed, try to crop in-memory
|
||||
is = regenerateInputStream();
|
||||
Bitmap fullSize = null;
|
||||
if (is != null) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
if (scaleDownSampleSize > 1) {
|
||||
options.inSampleSize = scaleDownSampleSize;
|
||||
}
|
||||
fullSize = BitmapFactory.decodeStream(is, null, options);
|
||||
Utils.closeSilently(is);
|
||||
}
|
||||
if (fullSize != null) {
|
||||
// Find out the true sample size that was used by the decoder
|
||||
scaleDownSampleSize = bounds.x / fullSize.getWidth();
|
||||
mCropBounds.left /= scaleDownSampleSize;
|
||||
mCropBounds.top /= scaleDownSampleSize;
|
||||
mCropBounds.bottom /= scaleDownSampleSize;
|
||||
mCropBounds.right /= scaleDownSampleSize;
|
||||
mCropBounds.roundOut(roundedTrueCrop);
|
||||
|
||||
// Adjust values to account for issues related to rounding
|
||||
if (roundedTrueCrop.width() > fullSize.getWidth()) {
|
||||
// Adjust the width
|
||||
roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth();
|
||||
}
|
||||
if (roundedTrueCrop.right > fullSize.getWidth()) {
|
||||
// Adjust the left value
|
||||
int adjustment = roundedTrueCrop.left -
|
||||
Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width());
|
||||
roundedTrueCrop.left -= adjustment;
|
||||
roundedTrueCrop.right -= adjustment;
|
||||
}
|
||||
if (roundedTrueCrop.height() > fullSize.getHeight()) {
|
||||
// Adjust the height
|
||||
roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight();
|
||||
}
|
||||
if (roundedTrueCrop.bottom > fullSize.getHeight()) {
|
||||
// Adjust the top value
|
||||
int adjustment = roundedTrueCrop.top -
|
||||
Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height());
|
||||
roundedTrueCrop.top -= adjustment;
|
||||
roundedTrueCrop.bottom -= adjustment;
|
||||
}
|
||||
|
||||
crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
|
||||
roundedTrueCrop.top, roundedTrueCrop.width(),
|
||||
roundedTrueCrop.height());
|
||||
}
|
||||
}
|
||||
|
||||
if (crop == null) {
|
||||
Log.w(LOGTAG, "cannot decode file: " + mInUri.toString());
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) {
|
||||
float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() };
|
||||
rotateMatrix.mapPoints(dimsAfter);
|
||||
dimsAfter[0] = Math.abs(dimsAfter[0]);
|
||||
dimsAfter[1] = Math.abs(dimsAfter[1]);
|
||||
|
||||
if (!(mOutWidth > 0 && mOutHeight > 0)) {
|
||||
mOutWidth = Math.round(dimsAfter[0]);
|
||||
mOutHeight = Math.round(dimsAfter[1]);
|
||||
}
|
||||
|
||||
RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]);
|
||||
RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight);
|
||||
|
||||
Matrix m = new Matrix();
|
||||
if (mRotation == 0) {
|
||||
m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
|
||||
} else {
|
||||
Matrix m1 = new Matrix();
|
||||
m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f);
|
||||
Matrix m2 = new Matrix();
|
||||
m2.setRotate(mRotation);
|
||||
Matrix m3 = new Matrix();
|
||||
m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f);
|
||||
Matrix m4 = new Matrix();
|
||||
m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
|
||||
|
||||
Matrix c1 = new Matrix();
|
||||
c1.setConcat(m2, m1);
|
||||
Matrix c2 = new Matrix();
|
||||
c2.setConcat(m4, m3);
|
||||
m.setConcat(c2, c1);
|
||||
}
|
||||
|
||||
Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
|
||||
(int) returnRect.height(), Bitmap.Config.ARGB_8888);
|
||||
if (tmp != null) {
|
||||
Canvas c = new Canvas(tmp);
|
||||
Paint p = new Paint();
|
||||
p.setFilterBitmap(true);
|
||||
c.drawBitmap(crop, m, p);
|
||||
crop = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSaveCroppedBitmap) {
|
||||
mCroppedBitmap = crop;
|
||||
}
|
||||
|
||||
// Compress to byte array
|
||||
ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
|
||||
if (crop.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
|
||||
// If we need to set to the wallpaper, set it
|
||||
if (mSetWallpaper && wallpaperManager != null) {
|
||||
try {
|
||||
byte[] outByteArray = tmpOut.toByteArray();
|
||||
wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
|
||||
if (mOnBitmapCroppedHandler != null) {
|
||||
mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, "cannot write stream to wallpaper", e);
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(LOGTAG, "cannot compress bitmap");
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
return !failure; // True if any of the operations failed
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
return cropBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (mOnEndRunnable != null) {
|
||||
mOnEndRunnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,83 +16,32 @@
|
||||
|
||||
package com.android.gallery3d.common;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.util.FloatMath;
|
||||
import android.graphics.Point;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import com.android.gallery3d.exif.ExifInterface;
|
||||
import com.android.launcher3.WallpaperCropActivity;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class BitmapUtils {
|
||||
|
||||
private static final String TAG = "BitmapUtils";
|
||||
private static final int DEFAULT_JPEG_QUALITY = 90;
|
||||
public static final int UNCONSTRAINED = -1;
|
||||
|
||||
private BitmapUtils(){}
|
||||
|
||||
/*
|
||||
* Compute the sample size as a function of minSideLength
|
||||
* and maxNumOfPixels.
|
||||
* minSideLength is used to specify that minimal width or height of a
|
||||
* bitmap.
|
||||
* maxNumOfPixels is used to specify the maximal size in pixels that is
|
||||
* tolerable in terms of memory usage.
|
||||
*
|
||||
* The function returns a sample size based on the constraints.
|
||||
* Both size and minSideLength can be passed in as UNCONSTRAINED,
|
||||
* which indicates no care of the corresponding constraint.
|
||||
* The functions prefers returning a sample size that
|
||||
* generates a smaller bitmap, unless minSideLength = UNCONSTRAINED.
|
||||
*
|
||||
* Also, the function rounds up the sample size to a power of 2 or multiple
|
||||
* of 8 because BitmapFactory only honors sample size this way.
|
||||
* For example, BitmapFactory downsamples an image by 2 even though the
|
||||
* request is 3. So we round up the sample size to avoid OOM.
|
||||
*/
|
||||
public static int computeSampleSize(int width, int height,
|
||||
int minSideLength, int maxNumOfPixels) {
|
||||
int initialSize = computeInitialSampleSize(
|
||||
width, height, minSideLength, maxNumOfPixels);
|
||||
|
||||
return initialSize <= 8
|
||||
? Utils.nextPowerOf2(initialSize)
|
||||
: (initialSize + 7) / 8 * 8;
|
||||
}
|
||||
|
||||
private static int computeInitialSampleSize(int w, int h,
|
||||
int minSideLength, int maxNumOfPixels) {
|
||||
if (maxNumOfPixels == UNCONSTRAINED
|
||||
&& minSideLength == UNCONSTRAINED) return 1;
|
||||
|
||||
int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
|
||||
(int) FloatMath.ceil(FloatMath.sqrt((float) (w * h) / maxNumOfPixels));
|
||||
|
||||
if (minSideLength == UNCONSTRAINED) {
|
||||
return lowerBound;
|
||||
} else {
|
||||
int sampleSize = Math.min(w / minSideLength, h / minSideLength);
|
||||
return Math.max(sampleSize, lowerBound);
|
||||
}
|
||||
}
|
||||
|
||||
// This computes a sample size which makes the longer side at least
|
||||
// minSideLength long. If that's not possible, return 1.
|
||||
public static int computeSampleSizeLarger(int w, int h,
|
||||
int minSideLength) {
|
||||
int initialSize = Math.max(w / minSideLength, h / minSideLength);
|
||||
if (initialSize <= 1) return 1;
|
||||
|
||||
return initialSize <= 8
|
||||
? Utils.prevPowerOf2(initialSize)
|
||||
: initialSize / 8 * 8;
|
||||
}
|
||||
|
||||
// Find the min x that 1 / x >= scale
|
||||
public static int computeSampleSizeLarger(float scale) {
|
||||
int initialSize = (int) FloatMath.floor(1f / scale);
|
||||
int initialSize = (int) Math.floor(1f / scale);
|
||||
if (initialSize <= 1) return 1;
|
||||
|
||||
return initialSize <= 8
|
||||
@@ -100,15 +49,6 @@ public class BitmapUtils {
|
||||
: initialSize / 8 * 8;
|
||||
}
|
||||
|
||||
// Find the max x that 1 / x <= scale.
|
||||
public static int computeSampleSize(float scale) {
|
||||
Utils.assertTrue(scale > 0);
|
||||
int initialSize = Math.max(1, (int) FloatMath.ceil(1 / scale));
|
||||
return initialSize <= 8
|
||||
? Utils.nextPowerOf2(initialSize)
|
||||
: (initialSize + 7) / 8 * 8;
|
||||
}
|
||||
|
||||
public static Bitmap resizeBitmapByScale(
|
||||
Bitmap bitmap, float scale, boolean recycle) {
|
||||
int width = Math.round(bitmap.getWidth() * scale);
|
||||
@@ -132,77 +72,104 @@ public class BitmapUtils {
|
||||
return config;
|
||||
}
|
||||
|
||||
public static Bitmap resizeDownBySideLength(
|
||||
Bitmap bitmap, int maxLength, boolean recycle) {
|
||||
int srcWidth = bitmap.getWidth();
|
||||
int srcHeight = bitmap.getHeight();
|
||||
float scale = Math.min(
|
||||
(float) maxLength / srcWidth, (float) maxLength / srcHeight);
|
||||
if (scale >= 1.0f) return bitmap;
|
||||
return resizeBitmapByScale(bitmap, scale, recycle);
|
||||
/**
|
||||
* As a ratio of screen height, the total distance we want the parallax effect to span
|
||||
* horizontally
|
||||
*/
|
||||
public static float wallpaperTravelToScreenWidthRatio(int width, int height) {
|
||||
float aspectRatio = width / (float) height;
|
||||
|
||||
// At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
|
||||
// At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
|
||||
// We will use these two data points to extrapolate how much the wallpaper parallax effect
|
||||
// to span (ie travel) at any aspect ratio:
|
||||
|
||||
final float ASPECT_RATIO_LANDSCAPE = 16/10f;
|
||||
final float ASPECT_RATIO_PORTRAIT = 10/16f;
|
||||
final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
|
||||
final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
|
||||
|
||||
// To find out the desired width at different aspect ratios, we use the following two
|
||||
// formulas, where the coefficient on x is the aspect ratio (width/height):
|
||||
// (16/10)x + y = 1.5
|
||||
// (10/16)x + y = 1.2
|
||||
// We solve for x and y and end up with a final formula:
|
||||
final float x =
|
||||
(WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
|
||||
(ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
|
||||
final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
|
||||
return x * aspectRatio + y;
|
||||
}
|
||||
|
||||
public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
|
||||
int w = bitmap.getWidth();
|
||||
int h = bitmap.getHeight();
|
||||
if (w == size && h == size) return bitmap;
|
||||
private static Point sDefaultWallpaperSize;
|
||||
|
||||
// scale the image so that the shorter side equals to the target;
|
||||
// the longer side will be center-cropped.
|
||||
float scale = (float) size / Math.min(w, h);
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
public static Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) {
|
||||
if (sDefaultWallpaperSize == null) {
|
||||
Point minDims = new Point();
|
||||
Point maxDims = new Point();
|
||||
windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
|
||||
|
||||
Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
|
||||
int width = Math.round(scale * bitmap.getWidth());
|
||||
int height = Math.round(scale * bitmap.getHeight());
|
||||
Canvas canvas = new Canvas(target);
|
||||
canvas.translate((size - width) / 2f, (size - height) / 2f);
|
||||
canvas.scale(scale, scale);
|
||||
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
|
||||
canvas.drawBitmap(bitmap, 0, 0, paint);
|
||||
if (recycle) bitmap.recycle();
|
||||
return target;
|
||||
}
|
||||
int maxDim = Math.max(maxDims.x, maxDims.y);
|
||||
int minDim = Math.max(minDims.x, minDims.y);
|
||||
|
||||
public static void recycleSilently(Bitmap bitmap) {
|
||||
if (bitmap == null) return;
|
||||
try {
|
||||
bitmap.recycle();
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, "unable recycle bitmap", t);
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
Point realSize = new Point();
|
||||
windowManager.getDefaultDisplay().getRealSize(realSize);
|
||||
maxDim = Math.max(realSize.x, realSize.y);
|
||||
minDim = Math.min(realSize.x, realSize.y);
|
||||
}
|
||||
|
||||
// We need to ensure that there is enough extra space in the wallpaper
|
||||
// for the intended parallax effects
|
||||
final int defaultWidth, defaultHeight;
|
||||
if (res.getConfiguration().smallestScreenWidthDp >= 720) {
|
||||
defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
|
||||
defaultHeight = maxDim;
|
||||
} else {
|
||||
defaultWidth = Math.max((int) (minDim * WallpaperCropActivity.WALLPAPER_SCREENS_SPAN), maxDim);
|
||||
defaultHeight = maxDim;
|
||||
}
|
||||
sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight);
|
||||
}
|
||||
return sDefaultWallpaperSize;
|
||||
}
|
||||
|
||||
public static Bitmap rotateBitmap(Bitmap source, int rotation, boolean recycle) {
|
||||
if (rotation == 0) return source;
|
||||
int w = source.getWidth();
|
||||
int h = source.getHeight();
|
||||
Matrix m = new Matrix();
|
||||
m.postRotate(rotation);
|
||||
Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, w, h, m, true);
|
||||
if (recycle) source.recycle();
|
||||
return bitmap;
|
||||
public static int getRotationFromExif(Context context, Uri uri) {
|
||||
return BitmapUtils.getRotationFromExifHelper(null, 0, context, uri);
|
||||
}
|
||||
|
||||
public static byte[] compressToBytes(Bitmap bitmap) {
|
||||
return compressToBytes(bitmap, DEFAULT_JPEG_QUALITY);
|
||||
public static int getRotationFromExif(Resources res, int resId) {
|
||||
return BitmapUtils.getRotationFromExifHelper(res, resId, null, null);
|
||||
}
|
||||
|
||||
public static byte[] compressToBytes(Bitmap bitmap, int quality) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(65536);
|
||||
bitmap.compress(CompressFormat.JPEG, quality, baos);
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
public static boolean isSupportedByRegionDecoder(String mimeType) {
|
||||
if (mimeType == null) return false;
|
||||
mimeType = mimeType.toLowerCase();
|
||||
return mimeType.startsWith("image/") &&
|
||||
(!mimeType.equals("image/gif") && !mimeType.endsWith("bmp"));
|
||||
}
|
||||
|
||||
public static boolean isRotationSupported(String mimeType) {
|
||||
if (mimeType == null) return false;
|
||||
mimeType = mimeType.toLowerCase();
|
||||
return mimeType.equals("image/jpeg");
|
||||
private static int getRotationFromExifHelper(Resources res, int resId, Context context, Uri uri) {
|
||||
ExifInterface ei = new ExifInterface();
|
||||
InputStream is = null;
|
||||
BufferedInputStream bis = null;
|
||||
try {
|
||||
if (uri != null) {
|
||||
is = context.getContentResolver().openInputStream(uri);
|
||||
bis = new BufferedInputStream(is);
|
||||
ei.readExif(bis);
|
||||
} else {
|
||||
is = res.openRawResource(resId);
|
||||
bis = new BufferedInputStream(is);
|
||||
ei.readExif(bis);
|
||||
}
|
||||
Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
|
||||
if (ori != null) {
|
||||
return ExifInterface.getRotationForOrientationValue(ori.shortValue());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Getting exif data failed", e);
|
||||
} catch (NullPointerException e) {
|
||||
// Sometimes the ExifInterface has an internal NPE if Exif data isn't valid
|
||||
Log.w(TAG, "Getting exif data failed", e);
|
||||
} finally {
|
||||
Utils.closeSilently(bis);
|
||||
Utils.closeSilently(is);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,32 +16,16 @@
|
||||
|
||||
package com.android.gallery3d.common;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.graphics.RectF;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
|
||||
public class Utils {
|
||||
private static final String TAG = "Utils";
|
||||
private static final String DEBUG_TAG = "GalleryDebug";
|
||||
|
||||
private static final long POLY64REV = 0x95AC9329AC4BC9B5L;
|
||||
private static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL;
|
||||
|
||||
private static long[] sCrcTable = new long[256];
|
||||
|
||||
private static final boolean IS_DEBUG_BUILD =
|
||||
Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug");
|
||||
|
||||
private static final String MASK_STRING = "********************************";
|
||||
|
||||
// Throws AssertionError if the input is false.
|
||||
public static void assertTrue(boolean cond) {
|
||||
@@ -50,28 +34,6 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
// Throws AssertionError with the message. We had a method having the form
|
||||
// assertTrue(boolean cond, String message, Object ... args);
|
||||
// However a call to that method will cause memory allocation even if the
|
||||
// condition is false (due to autoboxing generated by "Object ... args"),
|
||||
// so we don't use that anymore.
|
||||
public static void fail(String message, Object ... args) {
|
||||
throw new AssertionError(
|
||||
args.length == 0 ? message : String.format(message, args));
|
||||
}
|
||||
|
||||
// Throws NullPointerException if the input is null.
|
||||
public static <T> T checkNotNull(T object) {
|
||||
if (object == null) throw new NullPointerException();
|
||||
return object;
|
||||
}
|
||||
|
||||
// Returns true if two input Object are both null or equal
|
||||
// to each other.
|
||||
public static boolean equals(Object a, Object b) {
|
||||
return (a == b) || (a == null ? false : a.equals(b));
|
||||
}
|
||||
|
||||
// Returns the next power of two.
|
||||
// Returns the input if it is already power of 2.
|
||||
// Throws IllegalArgumentException if the input is <= 0 or
|
||||
@@ -102,87 +64,6 @@ public class Utils {
|
||||
return x;
|
||||
}
|
||||
|
||||
// Returns the input value x clamped to the range [min, max].
|
||||
public static float clamp(float x, float min, float max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
// Returns the input value x clamped to the range [min, max].
|
||||
public static long clamp(long x, long min, long max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
public static boolean isOpaque(int color) {
|
||||
return color >>> 24 == 0xFF;
|
||||
}
|
||||
|
||||
public static void swap(int[] array, int i, int j) {
|
||||
int temp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function thats returns a 64-bit crc for string
|
||||
*
|
||||
* @param in input string
|
||||
* @return a 64-bit crc value
|
||||
*/
|
||||
public static final long crc64Long(String in) {
|
||||
if (in == null || in.length() == 0) {
|
||||
return 0;
|
||||
}
|
||||
return crc64Long(getBytes(in));
|
||||
}
|
||||
|
||||
static {
|
||||
// http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c
|
||||
long part;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
part = i;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
long x = ((int) part & 1) != 0 ? POLY64REV : 0;
|
||||
part = (part >> 1) ^ x;
|
||||
}
|
||||
sCrcTable[i] = part;
|
||||
}
|
||||
}
|
||||
|
||||
public static final long crc64Long(byte[] buffer) {
|
||||
long crc = INITIALCRC;
|
||||
for (int k = 0, n = buffer.length; k < n; ++k) {
|
||||
crc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
public static byte[] getBytes(String in) {
|
||||
byte[] result = new byte[in.length() * 2];
|
||||
int output = 0;
|
||||
for (char ch : in.toCharArray()) {
|
||||
result[output++] = (byte) (ch & 0xFF);
|
||||
result[output++] = (byte) (ch >> 8);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void closeSilently(Closeable c) {
|
||||
if (c == null) return;
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException t) {
|
||||
Log.w(TAG, "close fail ", t);
|
||||
}
|
||||
}
|
||||
|
||||
public static int compare(long a, long b) {
|
||||
return a < b ? -1 : a == b ? 0 : 1;
|
||||
}
|
||||
|
||||
public static int ceilLog2(float value) {
|
||||
int i;
|
||||
for (i = 0; i < 31; i++) {
|
||||
@@ -199,6 +80,15 @@ public class Utils {
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
public static void closeSilently(Closeable c) {
|
||||
if (c == null) return;
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException t) {
|
||||
Log.w(TAG, "close fail ", t);
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeSilently(ParcelFileDescriptor fd) {
|
||||
try {
|
||||
if (fd != null) fd.close();
|
||||
@@ -215,126 +105,25 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static float interpolateAngle(
|
||||
float source, float target, float progress) {
|
||||
// interpolate the angle from source to target
|
||||
// We make the difference in the range of [-179, 180], this is the
|
||||
// shortest path to change source to target.
|
||||
float diff = target - source;
|
||||
if (diff < 0) diff += 360f;
|
||||
if (diff > 180) diff -= 360f;
|
||||
|
||||
float result = source + diff * progress;
|
||||
return result < 0 ? result + 360f : result;
|
||||
}
|
||||
|
||||
public static float interpolateScale(
|
||||
float source, float target, float progress) {
|
||||
return source + progress * (target - source);
|
||||
}
|
||||
|
||||
public static String ensureNotNull(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
public static float parseFloatSafely(String content, float defaultValue) {
|
||||
if (content == null) return defaultValue;
|
||||
try {
|
||||
return Float.parseFloat(content);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
public static RectF getMaxCropRect(
|
||||
int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) {
|
||||
RectF cropRect = new RectF();
|
||||
// Get a crop rect that will fit this
|
||||
if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
|
||||
cropRect.top = 0;
|
||||
cropRect.bottom = inHeight;
|
||||
cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2;
|
||||
cropRect.right = inWidth - cropRect.left;
|
||||
if (leftAligned) {
|
||||
cropRect.right -= cropRect.left;
|
||||
cropRect.left = 0;
|
||||
}
|
||||
} else {
|
||||
cropRect.left = 0;
|
||||
cropRect.right = inWidth;
|
||||
cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2;
|
||||
cropRect.bottom = inHeight - cropRect.top;
|
||||
}
|
||||
}
|
||||
|
||||
public static int parseIntSafely(String content, int defaultValue) {
|
||||
if (content == null) return defaultValue;
|
||||
try {
|
||||
return Integer.parseInt(content);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isNullOrEmpty(String exifMake) {
|
||||
return TextUtils.isEmpty(exifMake);
|
||||
}
|
||||
|
||||
public static void waitWithoutInterrupt(Object object) {
|
||||
try {
|
||||
object.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Log.w(TAG, "unexpected interrupt: " + object);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean handleInterrruptedException(Throwable e) {
|
||||
// A helper to deal with the interrupt exception
|
||||
// If an interrupt detected, we will setup the bit again.
|
||||
if (e instanceof InterruptedIOException
|
||||
|| e instanceof InterruptedException) {
|
||||
Thread.currentThread().interrupt();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String with special XML characters escaped.
|
||||
*/
|
||||
public static String escapeXml(String s) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0, len = s.length(); i < len; ++i) {
|
||||
char c = s.charAt(i);
|
||||
switch (c) {
|
||||
case '<': sb.append("<"); break;
|
||||
case '>': sb.append(">"); break;
|
||||
case '\"': sb.append("""); break;
|
||||
case '\'': sb.append("'"); break;
|
||||
case '&': sb.append("&"); break;
|
||||
default: sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getUserAgent(Context context) {
|
||||
PackageInfo packageInfo;
|
||||
try {
|
||||
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new IllegalStateException("getPackageInfo failed");
|
||||
}
|
||||
return String.format("%s/%s; %s/%s/%s/%s; %s/%s/%s",
|
||||
packageInfo.packageName,
|
||||
packageInfo.versionName,
|
||||
Build.BRAND,
|
||||
Build.DEVICE,
|
||||
Build.MODEL,
|
||||
Build.ID,
|
||||
Build.VERSION.SDK_INT,
|
||||
Build.VERSION.RELEASE,
|
||||
Build.VERSION.INCREMENTAL);
|
||||
}
|
||||
|
||||
public static String[] copyOf(String[] source, int newSize) {
|
||||
String[] result = new String[newSize];
|
||||
newSize = Math.min(source.length, newSize);
|
||||
System.arraycopy(source, 0, result, 0, newSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Mask information for debugging only. It returns <code>info.toString()</code> directly
|
||||
// for debugging build (i.e., 'eng' and 'userdebug') and returns a mask ("****")
|
||||
// in release build to protect the information (e.g. for privacy issue).
|
||||
public static String maskDebugInfo(Object info) {
|
||||
if (info == null) return null;
|
||||
String s = info.toString();
|
||||
int length = Math.min(s.length(), MASK_STRING.length());
|
||||
return IS_DEBUG_BUILD ? s : MASK_STRING.substring(0, length);
|
||||
}
|
||||
|
||||
// This method should be ONLY used for debugging.
|
||||
public static void debug(String message, Object ... args) {
|
||||
Log.v(DEBUG_TAG, String.format(message, args));
|
||||
return cropRect;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import java.util.WeakHashMap;
|
||||
// If a BasicTexture is loaded into GL memory, it has a GL texture id.
|
||||
public abstract class BasicTexture implements Texture {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "BasicTexture";
|
||||
protected static final int UNSPECIFIED = -1;
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ import android.opengl.GLUtils;
|
||||
import android.opengl.Matrix;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.gallery3d.util.IntArray;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.gallery3d.util;
|
||||
package com.android.gallery3d.glrenderer;
|
||||
|
||||
public class IntArray {
|
||||
private static final int INIT_CAPACITY = 8;
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.WallpaperManager;
|
||||
@@ -24,43 +25,30 @@ import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapRegionDecoder;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.gallery3d.common.BitmapCropTask;
|
||||
import com.android.gallery3d.common.BitmapUtils;
|
||||
import com.android.gallery3d.common.Utils;
|
||||
import com.android.gallery3d.exif.ExifInterface;
|
||||
import com.android.photos.BitmapRegionTileSource;
|
||||
import com.android.photos.BitmapRegionTileSource.BitmapSource;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class WallpaperCropActivity extends Activity {
|
||||
private static final String LOGTAG = "Launcher3.CropActivity";
|
||||
|
||||
protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width";
|
||||
protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height";
|
||||
private static final int DEFAULT_COMPRESS_QUALITY = 90;
|
||||
|
||||
/**
|
||||
* The maximum bitmap size we allow to be returned through the intent.
|
||||
* Intents have a maximum of 1MB in total size. However, the Bitmap seems to
|
||||
@@ -69,9 +57,7 @@ public class WallpaperCropActivity extends Activity {
|
||||
* array instead of a Bitmap instance to avoid overhead.
|
||||
*/
|
||||
public static final int MAX_BMAP_IN_INTENT = 750000;
|
||||
private static final float WALLPAPER_SCREENS_SPAN = 2f;
|
||||
|
||||
protected static Point sDefaultWallpaperSize;
|
||||
public static final float WALLPAPER_SCREENS_SPAN = 2f;
|
||||
|
||||
protected CropView mCropView;
|
||||
protected Uri mUri;
|
||||
@@ -205,111 +191,8 @@ public class WallpaperCropActivity extends Activity {
|
||||
return LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY;
|
||||
}
|
||||
|
||||
// As a ratio of screen height, the total distance we want the parallax effect to span
|
||||
// horizontally
|
||||
private static float wallpaperTravelToScreenWidthRatio(int width, int height) {
|
||||
float aspectRatio = width / (float) height;
|
||||
|
||||
// At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
|
||||
// At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
|
||||
// We will use these two data points to extrapolate how much the wallpaper parallax effect
|
||||
// to span (ie travel) at any aspect ratio:
|
||||
|
||||
final float ASPECT_RATIO_LANDSCAPE = 16/10f;
|
||||
final float ASPECT_RATIO_PORTRAIT = 10/16f;
|
||||
final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
|
||||
final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
|
||||
|
||||
// To find out the desired width at different aspect ratios, we use the following two
|
||||
// formulas, where the coefficient on x is the aspect ratio (width/height):
|
||||
// (16/10)x + y = 1.5
|
||||
// (10/16)x + y = 1.2
|
||||
// We solve for x and y and end up with a final formula:
|
||||
final float x =
|
||||
(WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
|
||||
(ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
|
||||
final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
|
||||
return x * aspectRatio + y;
|
||||
}
|
||||
|
||||
static protected Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) {
|
||||
if (sDefaultWallpaperSize == null) {
|
||||
Point minDims = new Point();
|
||||
Point maxDims = new Point();
|
||||
windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
|
||||
|
||||
int maxDim = Math.max(maxDims.x, maxDims.y);
|
||||
int minDim = Math.max(minDims.x, minDims.y);
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
Point realSize = new Point();
|
||||
windowManager.getDefaultDisplay().getRealSize(realSize);
|
||||
maxDim = Math.max(realSize.x, realSize.y);
|
||||
minDim = Math.min(realSize.x, realSize.y);
|
||||
}
|
||||
|
||||
// We need to ensure that there is enough extra space in the wallpaper
|
||||
// for the intended parallax effects
|
||||
final int defaultWidth, defaultHeight;
|
||||
if (isScreenLarge(res)) {
|
||||
defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
|
||||
defaultHeight = maxDim;
|
||||
} else {
|
||||
defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
|
||||
defaultHeight = maxDim;
|
||||
}
|
||||
sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight);
|
||||
}
|
||||
return sDefaultWallpaperSize;
|
||||
}
|
||||
|
||||
public static int getRotationFromExif(String path) {
|
||||
return getRotationFromExifHelper(path, null, 0, null, null);
|
||||
}
|
||||
|
||||
public static int getRotationFromExif(Context context, Uri uri) {
|
||||
return getRotationFromExifHelper(null, null, 0, context, uri);
|
||||
}
|
||||
|
||||
public static int getRotationFromExif(Resources res, int resId) {
|
||||
return getRotationFromExifHelper(null, res, resId, null, null);
|
||||
}
|
||||
|
||||
private static int getRotationFromExifHelper(
|
||||
String path, Resources res, int resId, Context context, Uri uri) {
|
||||
ExifInterface ei = new ExifInterface();
|
||||
InputStream is = null;
|
||||
BufferedInputStream bis = null;
|
||||
try {
|
||||
if (path != null) {
|
||||
ei.readExif(path);
|
||||
} else if (uri != null) {
|
||||
is = context.getContentResolver().openInputStream(uri);
|
||||
bis = new BufferedInputStream(is);
|
||||
ei.readExif(bis);
|
||||
} else {
|
||||
is = res.openRawResource(resId);
|
||||
bis = new BufferedInputStream(is);
|
||||
ei.readExif(bis);
|
||||
}
|
||||
Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
|
||||
if (ori != null) {
|
||||
return ExifInterface.getRotationForOrientationValue(ori.shortValue());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, "Getting exif data failed", e);
|
||||
} catch (NullPointerException e) {
|
||||
// Sometimes the ExifInterface has an internal NPE if Exif data isn't valid
|
||||
Log.w(LOGTAG, "Getting exif data failed", e);
|
||||
} finally {
|
||||
Utils.closeSilently(bis);
|
||||
Utils.closeSilently(is);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone) {
|
||||
int rotation = getRotationFromExif(this, uri);
|
||||
int rotation = BitmapUtils.getRotationFromExif(this, uri);
|
||||
BitmapCropTask cropTask = new BitmapCropTask(
|
||||
this, uri, null, rotation, 0, 0, true, false, null);
|
||||
final Point bounds = cropTask.getImageBounds();
|
||||
@@ -331,11 +214,11 @@ public class WallpaperCropActivity extends Activity {
|
||||
Resources res, int resId, final boolean finishActivityWhenDone) {
|
||||
// crop this image and scale it down to the default wallpaper size for
|
||||
// this device
|
||||
int rotation = getRotationFromExif(res, resId);
|
||||
int rotation = BitmapUtils.getRotationFromExif(res, resId);
|
||||
Point inSize = mCropView.getSourceDimensions();
|
||||
Point outSize = getDefaultWallpaperSize(getResources(),
|
||||
Point outSize = BitmapUtils.getDefaultWallpaperSize(getResources(),
|
||||
getWindowManager());
|
||||
RectF crop = getMaxCropRect(
|
||||
RectF crop = Utils.getMaxCropRect(
|
||||
inSize.x, inSize.y, outSize.x, outSize.y, false);
|
||||
Runnable onEndCrop = new Runnable() {
|
||||
public void run() {
|
||||
@@ -353,13 +236,9 @@ public class WallpaperCropActivity extends Activity {
|
||||
cropTask.execute();
|
||||
}
|
||||
|
||||
private static boolean isScreenLarge(Resources res) {
|
||||
Configuration config = res.getConfiguration();
|
||||
return config.smallestScreenWidthDp >= 720;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
protected void cropImageAndSetWallpaper(Uri uri,
|
||||
OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
|
||||
BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
|
||||
boolean centerCrop = getResources().getBoolean(R.bool.center_crop);
|
||||
// Get the crop
|
||||
boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
|
||||
@@ -370,7 +249,7 @@ public class WallpaperCropActivity extends Activity {
|
||||
d.getSize(displaySize);
|
||||
boolean isPortrait = displaySize.x < displaySize.y;
|
||||
|
||||
Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(),
|
||||
Point defaultWallpaperSize = BitmapUtils.getDefaultWallpaperSize(getResources(),
|
||||
getWindowManager());
|
||||
// Get the crop
|
||||
RectF cropRect = mCropView.getCrop();
|
||||
@@ -452,372 +331,6 @@ public class WallpaperCropActivity extends Activity {
|
||||
cropTask.execute();
|
||||
}
|
||||
|
||||
public interface OnBitmapCroppedHandler {
|
||||
public void onBitmapCropped(byte[] imageBytes);
|
||||
}
|
||||
|
||||
protected static class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
|
||||
Uri mInUri = null;
|
||||
Context mContext;
|
||||
String mInFilePath;
|
||||
byte[] mInImageBytes;
|
||||
int mInResId = 0;
|
||||
RectF mCropBounds = null;
|
||||
int mOutWidth, mOutHeight;
|
||||
int mRotation;
|
||||
String mOutputFormat = "jpg"; // for now
|
||||
boolean mSetWallpaper;
|
||||
boolean mSaveCroppedBitmap;
|
||||
Bitmap mCroppedBitmap;
|
||||
Runnable mOnEndRunnable;
|
||||
Resources mResources;
|
||||
OnBitmapCroppedHandler mOnBitmapCroppedHandler;
|
||||
boolean mNoCrop;
|
||||
|
||||
public BitmapCropTask(Context c, String filePath,
|
||||
RectF cropBounds, int rotation, int outWidth, int outHeight,
|
||||
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
|
||||
mContext = c;
|
||||
mInFilePath = filePath;
|
||||
init(cropBounds, rotation,
|
||||
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
|
||||
}
|
||||
|
||||
public BitmapCropTask(byte[] imageBytes,
|
||||
RectF cropBounds, int rotation, int outWidth, int outHeight,
|
||||
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
|
||||
mInImageBytes = imageBytes;
|
||||
init(cropBounds, rotation,
|
||||
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
|
||||
}
|
||||
|
||||
public BitmapCropTask(Context c, Uri inUri,
|
||||
RectF cropBounds, int rotation, int outWidth, int outHeight,
|
||||
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
|
||||
mContext = c;
|
||||
mInUri = inUri;
|
||||
init(cropBounds, rotation,
|
||||
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
|
||||
}
|
||||
|
||||
public BitmapCropTask(Context c, Resources res, int inResId,
|
||||
RectF cropBounds, int rotation, int outWidth, int outHeight,
|
||||
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
|
||||
mContext = c;
|
||||
mInResId = inResId;
|
||||
mResources = res;
|
||||
init(cropBounds, rotation,
|
||||
outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
|
||||
}
|
||||
|
||||
private void init(RectF cropBounds, int rotation, int outWidth, int outHeight,
|
||||
boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
|
||||
mCropBounds = cropBounds;
|
||||
mRotation = rotation;
|
||||
mOutWidth = outWidth;
|
||||
mOutHeight = outHeight;
|
||||
mSetWallpaper = setWallpaper;
|
||||
mSaveCroppedBitmap = saveCroppedBitmap;
|
||||
mOnEndRunnable = onEndRunnable;
|
||||
}
|
||||
|
||||
public void setOnBitmapCropped(OnBitmapCroppedHandler handler) {
|
||||
mOnBitmapCroppedHandler = handler;
|
||||
}
|
||||
|
||||
public void setNoCrop(boolean value) {
|
||||
mNoCrop = value;
|
||||
}
|
||||
|
||||
public void setOnEndRunnable(Runnable onEndRunnable) {
|
||||
mOnEndRunnable = onEndRunnable;
|
||||
}
|
||||
|
||||
// Helper to setup input stream
|
||||
private InputStream regenerateInputStream() {
|
||||
if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
|
||||
Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
|
||||
"image byte array given");
|
||||
} else {
|
||||
try {
|
||||
if (mInUri != null) {
|
||||
return new BufferedInputStream(
|
||||
mContext.getContentResolver().openInputStream(mInUri));
|
||||
} else if (mInFilePath != null) {
|
||||
return mContext.openFileInput(mInFilePath);
|
||||
} else if (mInImageBytes != null) {
|
||||
return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes));
|
||||
} else {
|
||||
return new BufferedInputStream(mResources.openRawResource(mInResId));
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Point getImageBounds() {
|
||||
InputStream is = regenerateInputStream();
|
||||
if (is != null) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
Utils.closeSilently(is);
|
||||
if (options.outWidth != 0 && options.outHeight != 0) {
|
||||
return new Point(options.outWidth, options.outHeight);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setCropBounds(RectF cropBounds) {
|
||||
mCropBounds = cropBounds;
|
||||
}
|
||||
|
||||
public Bitmap getCroppedBitmap() {
|
||||
return mCroppedBitmap;
|
||||
}
|
||||
public boolean cropBitmap() {
|
||||
boolean failure = false;
|
||||
|
||||
|
||||
WallpaperManager wallpaperManager = null;
|
||||
if (mSetWallpaper) {
|
||||
wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
|
||||
}
|
||||
|
||||
|
||||
if (mSetWallpaper && mNoCrop) {
|
||||
try {
|
||||
InputStream is = regenerateInputStream();
|
||||
if (is != null) {
|
||||
wallpaperManager.setStream(is);
|
||||
Utils.closeSilently(is);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, "cannot write stream to wallpaper", e);
|
||||
failure = true;
|
||||
}
|
||||
return !failure;
|
||||
} else {
|
||||
// Find crop bounds (scaled to original image size)
|
||||
Rect roundedTrueCrop = new Rect();
|
||||
Matrix rotateMatrix = new Matrix();
|
||||
Matrix inverseRotateMatrix = new Matrix();
|
||||
|
||||
Point bounds = getImageBounds();
|
||||
if (mRotation > 0) {
|
||||
rotateMatrix.setRotate(mRotation);
|
||||
inverseRotateMatrix.setRotate(-mRotation);
|
||||
|
||||
mCropBounds.roundOut(roundedTrueCrop);
|
||||
mCropBounds = new RectF(roundedTrueCrop);
|
||||
|
||||
if (bounds == null) {
|
||||
Log.w(LOGTAG, "cannot get bounds for image");
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
float[] rotatedBounds = new float[] { bounds.x, bounds.y };
|
||||
rotateMatrix.mapPoints(rotatedBounds);
|
||||
rotatedBounds[0] = Math.abs(rotatedBounds[0]);
|
||||
rotatedBounds[1] = Math.abs(rotatedBounds[1]);
|
||||
|
||||
mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2);
|
||||
inverseRotateMatrix.mapRect(mCropBounds);
|
||||
mCropBounds.offset(bounds.x/2, bounds.y/2);
|
||||
|
||||
}
|
||||
|
||||
mCropBounds.roundOut(roundedTrueCrop);
|
||||
|
||||
if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
|
||||
Log.w(LOGTAG, "crop has bad values for full size image");
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// See how much we're reducing the size of the image
|
||||
int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth,
|
||||
roundedTrueCrop.height() / mOutHeight));
|
||||
// Attempt to open a region decoder
|
||||
BitmapRegionDecoder decoder = null;
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = regenerateInputStream();
|
||||
if (is == null) {
|
||||
Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
decoder = BitmapRegionDecoder.newInstance(is, false);
|
||||
Utils.closeSilently(is);
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
|
||||
} finally {
|
||||
Utils.closeSilently(is);
|
||||
is = null;
|
||||
}
|
||||
|
||||
Bitmap crop = null;
|
||||
if (decoder != null) {
|
||||
// Do region decoding to get crop bitmap
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
if (scaleDownSampleSize > 1) {
|
||||
options.inSampleSize = scaleDownSampleSize;
|
||||
}
|
||||
crop = decoder.decodeRegion(roundedTrueCrop, options);
|
||||
decoder.recycle();
|
||||
}
|
||||
|
||||
if (crop == null) {
|
||||
// BitmapRegionDecoder has failed, try to crop in-memory
|
||||
is = regenerateInputStream();
|
||||
Bitmap fullSize = null;
|
||||
if (is != null) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
if (scaleDownSampleSize > 1) {
|
||||
options.inSampleSize = scaleDownSampleSize;
|
||||
}
|
||||
fullSize = BitmapFactory.decodeStream(is, null, options);
|
||||
Utils.closeSilently(is);
|
||||
}
|
||||
if (fullSize != null) {
|
||||
// Find out the true sample size that was used by the decoder
|
||||
scaleDownSampleSize = bounds.x / fullSize.getWidth();
|
||||
mCropBounds.left /= scaleDownSampleSize;
|
||||
mCropBounds.top /= scaleDownSampleSize;
|
||||
mCropBounds.bottom /= scaleDownSampleSize;
|
||||
mCropBounds.right /= scaleDownSampleSize;
|
||||
mCropBounds.roundOut(roundedTrueCrop);
|
||||
|
||||
// Adjust values to account for issues related to rounding
|
||||
if (roundedTrueCrop.width() > fullSize.getWidth()) {
|
||||
// Adjust the width
|
||||
roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth();
|
||||
}
|
||||
if (roundedTrueCrop.right > fullSize.getWidth()) {
|
||||
// Adjust the left value
|
||||
int adjustment = roundedTrueCrop.left -
|
||||
Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width());
|
||||
roundedTrueCrop.left -= adjustment;
|
||||
roundedTrueCrop.right -= adjustment;
|
||||
}
|
||||
if (roundedTrueCrop.height() > fullSize.getHeight()) {
|
||||
// Adjust the height
|
||||
roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight();
|
||||
}
|
||||
if (roundedTrueCrop.bottom > fullSize.getHeight()) {
|
||||
// Adjust the top value
|
||||
int adjustment = roundedTrueCrop.top -
|
||||
Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height());
|
||||
roundedTrueCrop.top -= adjustment;
|
||||
roundedTrueCrop.bottom -= adjustment;
|
||||
}
|
||||
|
||||
crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
|
||||
roundedTrueCrop.top, roundedTrueCrop.width(),
|
||||
roundedTrueCrop.height());
|
||||
}
|
||||
}
|
||||
|
||||
if (crop == null) {
|
||||
Log.w(LOGTAG, "cannot decode file: " + mInUri.toString());
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) {
|
||||
float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() };
|
||||
rotateMatrix.mapPoints(dimsAfter);
|
||||
dimsAfter[0] = Math.abs(dimsAfter[0]);
|
||||
dimsAfter[1] = Math.abs(dimsAfter[1]);
|
||||
|
||||
if (!(mOutWidth > 0 && mOutHeight > 0)) {
|
||||
mOutWidth = Math.round(dimsAfter[0]);
|
||||
mOutHeight = Math.round(dimsAfter[1]);
|
||||
}
|
||||
|
||||
RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]);
|
||||
RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight);
|
||||
|
||||
Matrix m = new Matrix();
|
||||
if (mRotation == 0) {
|
||||
m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
|
||||
} else {
|
||||
Matrix m1 = new Matrix();
|
||||
m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f);
|
||||
Matrix m2 = new Matrix();
|
||||
m2.setRotate(mRotation);
|
||||
Matrix m3 = new Matrix();
|
||||
m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f);
|
||||
Matrix m4 = new Matrix();
|
||||
m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
|
||||
|
||||
Matrix c1 = new Matrix();
|
||||
c1.setConcat(m2, m1);
|
||||
Matrix c2 = new Matrix();
|
||||
c2.setConcat(m4, m3);
|
||||
m.setConcat(c2, c1);
|
||||
}
|
||||
|
||||
Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
|
||||
(int) returnRect.height(), Bitmap.Config.ARGB_8888);
|
||||
if (tmp != null) {
|
||||
Canvas c = new Canvas(tmp);
|
||||
Paint p = new Paint();
|
||||
p.setFilterBitmap(true);
|
||||
c.drawBitmap(crop, m, p);
|
||||
crop = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSaveCroppedBitmap) {
|
||||
mCroppedBitmap = crop;
|
||||
}
|
||||
|
||||
// Get output compression format
|
||||
CompressFormat cf =
|
||||
convertExtensionToCompressFormat(getFileExtension(mOutputFormat));
|
||||
|
||||
// Compress to byte array
|
||||
ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
|
||||
if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
|
||||
// If we need to set to the wallpaper, set it
|
||||
if (mSetWallpaper && wallpaperManager != null) {
|
||||
try {
|
||||
byte[] outByteArray = tmpOut.toByteArray();
|
||||
wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
|
||||
if (mOnBitmapCroppedHandler != null) {
|
||||
mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, "cannot write stream to wallpaper", e);
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(LOGTAG, "cannot compress bitmap");
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
return !failure; // True if any of the operations failed
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
return cropBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (mOnEndRunnable != null) {
|
||||
mOnEndRunnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateWallpaperDimensions(int width, int height) {
|
||||
String spKey = getSharedPreferencesKey();
|
||||
SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
|
||||
@@ -835,11 +348,11 @@ public class WallpaperCropActivity extends Activity {
|
||||
sp, getWindowManager(), WallpaperManager.getInstance(this), true);
|
||||
}
|
||||
|
||||
static public void suggestWallpaperDimension(Resources res,
|
||||
public static void suggestWallpaperDimension(Resources res,
|
||||
final SharedPreferences sharedPrefs,
|
||||
WindowManager windowManager,
|
||||
final WallpaperManager wallpaperManager, boolean fallBackToDefaults) {
|
||||
final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager);
|
||||
final Point defaultWallpaperSize = BitmapUtils.getDefaultWallpaperSize(res, windowManager);
|
||||
// If we have saved a wallpaper width/height, use that instead
|
||||
|
||||
int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, -1);
|
||||
@@ -859,40 +372,4 @@ public class WallpaperCropActivity extends Activity {
|
||||
wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight);
|
||||
}
|
||||
}
|
||||
|
||||
protected static RectF getMaxCropRect(
|
||||
int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) {
|
||||
RectF cropRect = new RectF();
|
||||
// Get a crop rect that will fit this
|
||||
if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
|
||||
cropRect.top = 0;
|
||||
cropRect.bottom = inHeight;
|
||||
cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2;
|
||||
cropRect.right = inWidth - cropRect.left;
|
||||
if (leftAligned) {
|
||||
cropRect.right -= cropRect.left;
|
||||
cropRect.left = 0;
|
||||
}
|
||||
} else {
|
||||
cropRect.left = 0;
|
||||
cropRect.right = inWidth;
|
||||
cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2;
|
||||
cropRect.bottom = inHeight - cropRect.top;
|
||||
}
|
||||
return cropRect;
|
||||
}
|
||||
|
||||
protected static CompressFormat convertExtensionToCompressFormat(String extension) {
|
||||
return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
|
||||
}
|
||||
|
||||
protected static String getFileExtension(String requestFormat) {
|
||||
String outputFormat = (requestFormat == null)
|
||||
? "jpg"
|
||||
: requestFormat;
|
||||
outputFormat = outputFormat.toLowerCase();
|
||||
return (outputFormat.equals("png") || outputFormat.equals("gif"))
|
||||
? "png" // We don't support gif compression.
|
||||
: "jpg";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.gallery3d.common.BitmapCropTask;
|
||||
import com.android.gallery3d.common.BitmapUtils;
|
||||
import com.android.gallery3d.common.Utils;
|
||||
import com.android.photos.BitmapRegionTileSource;
|
||||
import com.android.photos.BitmapRegionTileSource.BitmapSource;
|
||||
|
||||
@@ -170,7 +173,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
|
||||
@Override
|
||||
public void onSave(final WallpaperPickerActivity a) {
|
||||
boolean finishActivityWhenDone = true;
|
||||
OnBitmapCroppedHandler h = new OnBitmapCroppedHandler() {
|
||||
BitmapCropTask.OnBitmapCroppedHandler h = new BitmapCropTask.OnBitmapCroppedHandler() {
|
||||
public void onBitmapCropped(byte[] imageBytes) {
|
||||
Point thumbSize = getDefaultThumbnailSize(a.getResources());
|
||||
// rotation is set to 0 since imageBytes has already been correctly rotated
|
||||
@@ -236,9 +239,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
|
||||
BitmapRegionTileSource source = new BitmapRegionTileSource(a, bitmapSource);
|
||||
CropView v = a.getCropView();
|
||||
v.setTileSource(source, null);
|
||||
Point wallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize(
|
||||
Point wallpaperSize = BitmapUtils.getDefaultWallpaperSize(
|
||||
a.getResources(), a.getWindowManager());
|
||||
RectF crop = WallpaperCropActivity.getMaxCropRect(
|
||||
RectF crop = Utils.getMaxCropRect(
|
||||
source.getImageWidth(), source.getImageHeight(),
|
||||
wallpaperSize.x, wallpaperSize.y, false);
|
||||
v.setScale(wallpaperSize.x / crop.width());
|
||||
@@ -807,7 +810,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
|
||||
rotatedBounds[0] = Math.abs(rotatedBounds[0]);
|
||||
rotatedBounds[1] = Math.abs(rotatedBounds[1]);
|
||||
|
||||
RectF cropRect = WallpaperCropActivity.getMaxCropRect(
|
||||
RectF cropRect = Utils.getMaxCropRect(
|
||||
(int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned);
|
||||
cropTask.setCropBounds(cropRect);
|
||||
|
||||
@@ -834,7 +837,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
|
||||
new AsyncTask<Void, Bitmap, Bitmap>() {
|
||||
protected Bitmap doInBackground(Void...args) {
|
||||
try {
|
||||
int rotation = WallpaperCropActivity.getRotationFromExif(context, uri);
|
||||
int rotation = BitmapUtils.getRotationFromExif(context, uri);
|
||||
return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false);
|
||||
} catch (SecurityException securityException) {
|
||||
if (isDestroyed()) {
|
||||
@@ -1004,7 +1007,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
|
||||
} else {
|
||||
Resources res = getResources();
|
||||
Point defaultThumbSize = getDefaultThumbnailSize(res);
|
||||
int rotation = WallpaperCropActivity.getRotationFromExif(res, resId);
|
||||
int rotation = BitmapUtils.getRotationFromExif(res, resId);
|
||||
thumb = createThumbnail(
|
||||
defaultThumbSize, this, null, null, sysRes, resId, rotation, false);
|
||||
if (thumb != null) {
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.util;
|
||||
package com.android.photos.views;
|
||||
|
||||
/**
|
||||
* Helper class for crating pools of objects. An example use looks like this:
|
||||
@@ -23,8 +23,6 @@ import android.graphics.RectF;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.LongSparseArray;
|
||||
import android.util.Pools.Pool;
|
||||
import android.util.Pools.SynchronizedPool;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@@ -32,6 +30,8 @@ import com.android.gallery3d.common.Utils;
|
||||
import com.android.gallery3d.glrenderer.BasicTexture;
|
||||
import com.android.gallery3d.glrenderer.GLCanvas;
|
||||
import com.android.gallery3d.glrenderer.UploadedTexture;
|
||||
import com.android.photos.views.Pools.Pool;
|
||||
import com.android.photos.views.Pools.SynchronizedPool;
|
||||
|
||||
/**
|
||||
* Handles laying out, decoding, and drawing of tiles in GL
|
||||
|
||||
Reference in New Issue
Block a user