WallpaperCropActivitypublic class WallpaperCropActivity extends android.app.Activity
Fields Summary |
---|
private static final String | LOGTAG | protected static final String | WALLPAPER_WIDTH_KEY | protected static final String | WALLPAPER_HEIGHT_KEY | private static final int | DEFAULT_COMPRESS_QUALITY | public static final int | MAX_BMAP_IN_INTENTThe 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
have some overhead to hit so that we go way below the limit here to make
sure the intent stays below 1MB.We should consider just returning a byte
array instead of a Bitmap instance to avoid overhead. | private static final float | WALLPAPER_SCREENS_SPAN | protected static android.graphics.Point | sDefaultWallpaperSize | protected CropView | mCropView | protected android.net.Uri | mUri | private android.view.View | mSetWallpaperButton |
Methods Summary |
---|
protected static android.graphics.Bitmap.CompressFormat | convertExtensionToCompressFormat(java.lang.String extension)
return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
| protected void | cropImageAndSetWallpaper(android.content.res.Resources res, int resId, boolean finishActivityWhenDone)
// crop this image and scale it down to the default wallpaper size for
// this device
int rotation = getRotationFromExif(res, resId);
Point inSize = mCropView.getSourceDimensions();
Point outSize = getDefaultWallpaperSize(getResources(),
getWindowManager());
RectF crop = getMaxCropRect(
inSize.x, inSize.y, outSize.x, outSize.y, false);
Runnable onEndCrop = new Runnable() {
public void run() {
if (finishActivityWhenDone) {
setResult(Activity.RESULT_OK);
finish();
}
}
};
BitmapCropTask cropTask = new BitmapCropTask(this, res, resId,
crop, rotation, outSize.x, outSize.y, true, false, onEndCrop);
cropTask.execute();
| protected void | cropImageAndSetWallpaper(android.net.Uri uri, com.android.wallpapercropper.WallpaperCropActivity$OnBitmapCroppedHandler onBitmapCroppedHandler, boolean finishActivityWhenDone)
boolean centerCrop = getResources().getBoolean(R.bool.center_crop);
// Get the crop
boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
Display d = getWindowManager().getDefaultDisplay();
Point displaySize = new Point();
d.getSize(displaySize);
boolean isPortrait = displaySize.x < displaySize.y;
Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(),
getWindowManager());
// Get the crop
RectF cropRect = mCropView.getCrop();
Point inSize = mCropView.getSourceDimensions();
int cropRotation = mCropView.getImageRotation();
float cropScale = mCropView.getWidth() / (float) cropRect.width();
Matrix rotateMatrix = new Matrix();
rotateMatrix.setRotate(cropRotation);
float[] rotatedInSize = new float[] { inSize.x, inSize.y };
rotateMatrix.mapPoints(rotatedInSize);
rotatedInSize[0] = Math.abs(rotatedInSize[0]);
rotatedInSize[1] = Math.abs(rotatedInSize[1]);
// Due to rounding errors in the cropview renderer the edges can be slightly offset
// therefore we ensure that the boundaries are sanely defined
cropRect.left = Math.max(0, cropRect.left);
cropRect.right = Math.min(rotatedInSize[0], cropRect.right);
cropRect.top = Math.max(0, cropRect.top);
cropRect.bottom = Math.min(rotatedInSize[1], cropRect.bottom);
// ADJUST CROP WIDTH
// Extend the crop all the way to the right, for parallax
// (or all the way to the left, in RTL)
float extraSpace;
if (centerCrop) {
extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left);
} else {
extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left;
}
// Cap the amount of extra width
float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width();
extraSpace = Math.min(extraSpace, maxExtraSpace);
if (centerCrop) {
cropRect.left -= extraSpace / 2f;
cropRect.right += extraSpace / 2f;
} else {
if (ltr) {
cropRect.right += extraSpace;
} else {
cropRect.left -= extraSpace;
}
}
// ADJUST CROP HEIGHT
if (isPortrait) {
cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale;
} else { // LANDSCAPE
float extraPortraitHeight =
defaultWallpaperSize.y / cropScale - cropRect.height();
float expandHeight =
Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top),
extraPortraitHeight / 2);
cropRect.top -= expandHeight;
cropRect.bottom += expandHeight;
}
final int outWidth = (int) Math.round(cropRect.width() * cropScale);
final int outHeight = (int) Math.round(cropRect.height() * cropScale);
Runnable onEndCrop = new Runnable() {
public void run() {
if (finishActivityWhenDone) {
setResult(Activity.RESULT_OK);
finish();
}
}
};
BitmapCropTask cropTask = new BitmapCropTask(this, uri,
cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop);
if (onBitmapCroppedHandler != null) {
cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
}
cropTask.execute();
| public boolean | enableRotation()
return getResources().getBoolean(R.bool.allow_rotation);
| protected static android.graphics.Point | getDefaultWallpaperSize(android.content.res.Resources res, android.view.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;
| protected static java.lang.String | getFileExtension(java.lang.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";
| protected static android.graphics.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;
| public static int | getRotationFromExif(android.content.Context context, android.net.Uri uri)
return getRotationFromExifHelper(null, null, 0, context, uri);
| public static int | getRotationFromExif(android.content.res.Resources res, int resId)
return getRotationFromExifHelper(null, res, resId, null, null);
| public static int | getRotationFromExif(java.lang.String path)
return getRotationFromExifHelper(path, null, 0, null, null);
| private static int | getRotationFromExifHelper(java.lang.String path, android.content.res.Resources res, int resId, android.content.Context context, android.net.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;
| public static java.lang.String | getSharedPreferencesKey()
return WallpaperCropActivity.class.getName();
| protected void | init()
setContentView(R.layout.wallpaper_cropper);
mCropView = (CropView) findViewById(R.id.cropView);
Intent cropIntent = getIntent();
final Uri imageUri = cropIntent.getData();
if (imageUri == null) {
Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity");
finish();
return;
}
// Action bar
// Show the custom action bar view
final ActionBar actionBar = getActionBar();
actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
actionBar.getCustomView().setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean finishActivityWhenDone = true;
cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
}
});
mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
// Load image in background
final BitmapRegionTileSource.UriBitmapSource bitmapSource =
new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
mSetWallpaperButton.setVisibility(View.INVISIBLE);
Runnable onLoad = new Runnable() {
public void run() {
if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
Toast.makeText(WallpaperCropActivity.this,
getString(R.string.wallpaper_load_fail),
Toast.LENGTH_LONG).show();
finish();
} else {
mSetWallpaperButton.setVisibility(View.VISIBLE);
}
}
};
setCropViewTileSource(bitmapSource, true, false, onLoad);
| private static boolean | isScreenLarge(android.content.res.Resources res)
Configuration config = res.getConfiguration();
return config.smallestScreenWidthDp >= 720;
| protected void | onCreate(android.os.Bundle savedInstanceState)
super.onCreate(savedInstanceState);
init();
if (!enableRotation()) {
setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT);
}
| protected void | onDestroy()
if (mCropView != null) {
mCropView.destroy();
}
super.onDestroy();
| public void | setCropViewTileSource(com.android.photos.BitmapRegionTileSource.BitmapSource bitmapSource, boolean touchEnabled, boolean moveToLeft, java.lang.Runnable postExecute)
final Context context = WallpaperCropActivity.this;
final View progressView = findViewById(R.id.loading);
final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
protected Void doInBackground(Void...args) {
if (!isCancelled()) {
try {
bitmapSource.loadInBackground();
} catch (SecurityException securityException) {
if (isDestroyed()) {
// Temporarily granted permissions are revoked when the activity
// finishes, potentially resulting in a SecurityException here.
// Even though {@link #isDestroyed} might also return true in different
// situations where the configuration changes, we are fine with
// catching these cases here as well.
cancel(false);
} else {
// otherwise it had a different cause and we throw it further
throw securityException;
}
}
}
return null;
}
protected void onPostExecute(Void arg) {
if (!isCancelled()) {
progressView.setVisibility(View.INVISIBLE);
if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
mCropView.setTileSource(
new BitmapRegionTileSource(context, bitmapSource), null);
mCropView.setTouchEnabled(touchEnabled);
if (moveToLeft) {
mCropView.moveToLeft();
}
}
}
if (postExecute != null) {
postExecute.run();
}
}
};
// We don't want to show the spinner every time we load an image, because that would be
// annoying; instead, only start showing the spinner if loading the image has taken
// longer than 1 sec (ie 1000 ms)
progressView.postDelayed(new Runnable() {
public void run() {
if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) {
progressView.setVisibility(View.VISIBLE);
}
}
}, 1000);
loadBitmapTask.execute();
| protected void | setWallpaper(java.lang.String filePath, boolean finishActivityWhenDone)
int rotation = getRotationFromExif(filePath);
BitmapCropTask cropTask = new BitmapCropTask(
this, filePath, null, rotation, 0, 0, true, false, null);
final Point bounds = cropTask.getImageBounds();
Runnable onEndCrop = new Runnable() {
public void run() {
if (finishActivityWhenDone) {
setResult(Activity.RESULT_OK);
finish();
}
}
};
cropTask.setOnEndRunnable(onEndCrop);
cropTask.setNoCrop(true);
cropTask.execute();
| 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;
|
|