WallpaperCropActivity.java revision e8d1bf7a439450b9979701909164a6baffbe8bae
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16/* Copied from Launcher3 */
17package com.android.wallpapercropper;
18
19import android.app.ActionBar;
20import android.app.Activity;
21import android.app.WallpaperManager;
22import android.content.Context;
23import android.content.Intent;
24import android.content.SharedPreferences;
25import android.content.res.Configuration;
26import android.content.res.Resources;
27import android.graphics.Bitmap;
28import android.graphics.Bitmap.CompressFormat;
29import android.graphics.BitmapFactory;
30import android.graphics.BitmapRegionDecoder;
31import android.graphics.Canvas;
32import android.graphics.Matrix;
33import android.graphics.Paint;
34import android.graphics.Point;
35import android.graphics.Rect;
36import android.graphics.RectF;
37import android.net.Uri;
38import android.os.AsyncTask;
39import android.os.Bundle;
40import android.util.Log;
41import android.view.Display;
42import android.view.View;
43import android.view.WindowManager;
44
45import com.android.gallery3d.common.Utils;
46import com.android.photos.BitmapRegionTileSource;
47
48import java.io.BufferedInputStream;
49import java.io.ByteArrayInputStream;
50import java.io.ByteArrayOutputStream;
51import java.io.FileNotFoundException;
52import java.io.IOException;
53import java.io.InputStream;
54
55public class WallpaperCropActivity extends Activity {
56    private static final String LOGTAG = "Launcher3.CropActivity";
57
58    protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width";
59    protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height";
60    private static final int DEFAULT_COMPRESS_QUALITY = 90;
61    /**
62     * The maximum bitmap size we allow to be returned through the intent.
63     * Intents have a maximum of 1MB in total size. However, the Bitmap seems to
64     * have some overhead to hit so that we go way below the limit here to make
65     * sure the intent stays below 1MB.We should consider just returning a byte
66     * array instead of a Bitmap instance to avoid overhead.
67     */
68    public static final int MAX_BMAP_IN_INTENT = 750000;
69    private static final float WALLPAPER_SCREENS_SPAN = 2f;
70
71    protected CropView mCropView;
72    protected Uri mUri;
73
74    @Override
75    protected void onCreate(Bundle savedInstanceState) {
76        super.onCreate(savedInstanceState);
77        init();
78    }
79
80    protected void init() {
81        setContentView(R.layout.wallpaper_cropper);
82
83        mCropView = (CropView) findViewById(R.id.cropView);
84
85        Intent cropIntent = this.getIntent();
86        final Uri imageUri = cropIntent.getData();
87
88        mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, 0), null);
89        mCropView.setTouchEnabled(true);
90        // Action bar
91        // Show the custom action bar view
92        final ActionBar actionBar = getActionBar();
93        actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
94        actionBar.getCustomView().setOnClickListener(
95                new View.OnClickListener() {
96                    @Override
97                    public void onClick(View v) {
98                        boolean finishActivityWhenDone = true;
99                        cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
100                    }
101                });
102    }
103
104    public static String getSharedPreferencesKey() {
105        return WallpaperCropActivity.class.getName();
106    }
107
108    // As a ratio of screen height, the total distance we want the parallax effect to span
109    // horizontally
110    private static float wallpaperTravelToScreenWidthRatio(int width, int height) {
111        float aspectRatio = width / (float) height;
112
113        // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
114        // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
115        // We will use these two data points to extrapolate how much the wallpaper parallax effect
116        // to span (ie travel) at any aspect ratio:
117
118        final float ASPECT_RATIO_LANDSCAPE = 16/10f;
119        final float ASPECT_RATIO_PORTRAIT = 10/16f;
120        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
121        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
122
123        // To find out the desired width at different aspect ratios, we use the following two
124        // formulas, where the coefficient on x is the aspect ratio (width/height):
125        //   (16/10)x + y = 1.5
126        //   (10/16)x + y = 1.2
127        // We solve for x and y and end up with a final formula:
128        final float x =
129            (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
130            (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
131        final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
132        return x * aspectRatio + y;
133    }
134
135    static protected Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) {
136        Point minDims = new Point();
137        Point maxDims = new Point();
138        windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
139
140        int maxDim = Math.max(maxDims.x, maxDims.y);
141        final int minDim = Math.min(minDims.x, minDims.y);
142
143        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
144            Point realSize = new Point();
145            windowManager.getDefaultDisplay().getRealSize(realSize);
146            maxDim = Math.max(realSize.x, realSize.y);
147        }
148
149        // We need to ensure that there is enough extra space in the wallpaper
150        // for the intended
151        // parallax effects
152        final int defaultWidth, defaultHeight;
153        if (isScreenLarge(res)) {
154            defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
155            defaultHeight = maxDim;
156        } else {
157            defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
158            defaultHeight = maxDim;
159        }
160        return new Point(defaultWidth, defaultHeight);
161    }
162
163    protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) {
164
165        BitmapCropTask cropTask = new BitmapCropTask(this,
166                filePath, null, 0, 0, true, false, null);
167        final Point bounds = cropTask.getImageBounds();
168        Runnable onEndCrop = new Runnable() {
169            public void run() {
170                updateWallpaperDimensions(bounds.x, bounds.y);
171                if (finishActivityWhenDone) {
172                    setResult(Activity.RESULT_OK);
173                    finish();
174                }
175            }
176        };
177        cropTask.setOnEndRunnable(onEndCrop);
178        cropTask.setNoCrop(true);
179        cropTask.execute();
180    }
181
182    protected void cropImageAndSetWallpaper(
183            Resources res, int resId, final boolean finishActivityWhenDone) {
184        // crop this image and scale it down to the default wallpaper size for
185        // this device
186        Point inSize = mCropView.getSourceDimensions();
187        Point outSize = getDefaultWallpaperSize(getResources(),
188                getWindowManager());
189        RectF crop = getMaxCropRect(
190                inSize.x, inSize.y, outSize.x, outSize.y, false);
191        Runnable onEndCrop = new Runnable() {
192            public void run() {
193                // Passing 0, 0 will cause launcher to revert to using the
194                // default wallpaper size
195                updateWallpaperDimensions(0, 0);
196                if (finishActivityWhenDone) {
197                    setResult(Activity.RESULT_OK);
198                    finish();
199                }
200            }
201        };
202        BitmapCropTask cropTask = new BitmapCropTask(res, resId,
203                crop, outSize.x, outSize.y,
204                true, false, onEndCrop);
205        cropTask.execute();
206    }
207
208    private static boolean isScreenLarge(Resources res) {
209        Configuration config = res.getConfiguration();
210        return config.smallestScreenWidthDp >= 720;
211    }
212
213    protected void cropImageAndSetWallpaper(Uri uri,
214            OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
215     // Get the crop
216        Point inSize = mCropView.getSourceDimensions();
217
218        Point minDims = new Point();
219        Point maxDims = new Point();
220        Display d = getWindowManager().getDefaultDisplay();
221        d.getCurrentSizeRange(minDims, maxDims);
222
223        Point displaySize = new Point();
224        d.getSize(displaySize);
225
226        int maxDim = Math.max(maxDims.x, maxDims.y);
227        final int minDim = Math.min(minDims.x, minDims.y);
228        int defaultWidth;
229        if (isScreenLarge(getResources())) {
230            defaultWidth = (int) (maxDim *
231                    wallpaperTravelToScreenWidthRatio(maxDim, minDim));
232        } else {
233            defaultWidth = Math.max((int)
234                    (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
235        }
236
237        boolean isPortrait = displaySize.x < displaySize.y;
238        int portraitHeight;
239        if (isPortrait) {
240            portraitHeight = mCropView.getHeight();
241        } else {
242            // TODO: how to actually get the proper portrait height?
243            // This is not quite right:
244            portraitHeight = Math.max(maxDims.x, maxDims.y);
245        }
246        if (android.os.Build.VERSION.SDK_INT >=
247                android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
248            Point realSize = new Point();
249            d.getRealSize(realSize);
250            portraitHeight = Math.max(realSize.x, realSize.y);
251        }
252        // Get the crop
253        RectF cropRect = mCropView.getCrop();
254        float cropScale = mCropView.getWidth() / (float) cropRect.width();
255
256        // ADJUST CROP WIDTH
257        // Extend the crop all the way to the right, for parallax
258        float extraSpaceToRight = inSize.x - cropRect.right;
259        // Cap the amount of extra width
260        float maxExtraSpace = defaultWidth / cropScale - cropRect.width();
261        extraSpaceToRight = Math.min(extraSpaceToRight, maxExtraSpace);
262
263        cropRect.right += extraSpaceToRight;
264
265        // ADJUST CROP HEIGHT
266        if (isPortrait) {
267            cropRect.bottom = cropRect.top + portraitHeight / cropScale;
268        } else { // LANDSCAPE
269            float extraPortraitHeight =
270                    portraitHeight / cropScale - cropRect.height();
271            float expandHeight =
272                    Math.min(Math.min(inSize.y - cropRect.bottom, cropRect.top),
273                            extraPortraitHeight / 2);
274            cropRect.top -= expandHeight;
275            cropRect.bottom += expandHeight;
276        }
277        final int outWidth = (int) Math.round(cropRect.width() * cropScale);
278        final int outHeight = (int) Math.round(cropRect.height() * cropScale);
279
280        Runnable onEndCrop = new Runnable() {
281            public void run() {
282                updateWallpaperDimensions(outWidth, outHeight);
283                if (finishActivityWhenDone) {
284                    setResult(Activity.RESULT_OK);
285                    finish();
286                }
287            }
288        };
289        BitmapCropTask cropTask = new BitmapCropTask(uri,
290                cropRect, outWidth, outHeight, true, false, onEndCrop);
291        if (onBitmapCroppedHandler != null) {
292            cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
293        }
294        cropTask.execute();
295    }
296
297    public interface OnBitmapCroppedHandler {
298        public void onBitmapCropped(byte[] imageBytes);
299    }
300
301    protected class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
302        Uri mInUri = null;
303        Context mContext;
304        String mInFilePath;
305        byte[] mInImageBytes;
306        int mInResId = 0;
307        InputStream mInStream;
308        RectF mCropBounds = null;
309        int mOutWidth, mOutHeight;
310        int mRotation = 0; // for now
311        protected final WallpaperManager mWPManager;
312        String mOutputFormat = "jpg"; // for now
313        boolean mSetWallpaper;
314        boolean mSaveCroppedBitmap;
315        Bitmap mCroppedBitmap;
316        Runnable mOnEndRunnable;
317        Resources mResources;
318        OnBitmapCroppedHandler mOnBitmapCroppedHandler;
319        boolean mNoCrop;
320
321        public BitmapCropTask(Context c, String filePath,
322                RectF cropBounds, int outWidth, int outHeight,
323                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
324            mContext = c;
325            mInFilePath = filePath;
326            mWPManager = WallpaperManager.getInstance(getApplicationContext());
327            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
328        }
329
330        public BitmapCropTask(byte[] imageBytes,
331                RectF cropBounds, int outWidth, int outHeight,
332                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
333            mInImageBytes = imageBytes;
334            mWPManager = WallpaperManager.getInstance(getApplicationContext());
335            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
336        }
337
338        public BitmapCropTask(Uri inUri,
339                RectF cropBounds, int outWidth, int outHeight,
340                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
341            mInUri = inUri;
342            mWPManager = WallpaperManager.getInstance(getApplicationContext());
343            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
344        }
345
346        public BitmapCropTask(Resources res, int inResId,
347                RectF cropBounds, int outWidth, int outHeight,
348                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
349            mInResId = inResId;
350            mResources = res;
351            mWPManager = WallpaperManager.getInstance(getApplicationContext());
352            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
353        }
354
355        private void init(RectF cropBounds, int outWidth, int outHeight,
356                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
357            mCropBounds = cropBounds;
358            mOutWidth = outWidth;
359            mOutHeight = outHeight;
360            mSetWallpaper = setWallpaper;
361            mSaveCroppedBitmap = saveCroppedBitmap;
362            mOnEndRunnable = onEndRunnable;
363        }
364
365        public void setOnBitmapCropped(OnBitmapCroppedHandler handler) {
366            mOnBitmapCroppedHandler = handler;
367        }
368
369        public void setNoCrop(boolean value) {
370            mNoCrop = value;
371        }
372
373        public void setOnEndRunnable(Runnable onEndRunnable) {
374            mOnEndRunnable = onEndRunnable;
375        }
376
377        // Helper to setup input stream
378        private void regenerateInputStream() {
379            if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
380                Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
381                        "image byte array given");
382            } else {
383                Utils.closeSilently(mInStream);
384                try {
385                    if (mInUri != null) {
386                        mInStream = new BufferedInputStream(
387                                getContentResolver().openInputStream(mInUri));
388                    } else if (mInFilePath != null) {
389                        mInStream = mContext.openFileInput(mInFilePath);
390                    } else if (mInImageBytes != null) {
391                        mInStream = new BufferedInputStream(
392                                new ByteArrayInputStream(mInImageBytes));
393                    } else {
394                        mInStream = new BufferedInputStream(
395                                mResources.openRawResource(mInResId));
396                    }
397                } catch (FileNotFoundException e) {
398                    Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
399                }
400            }
401        }
402
403        public Point getImageBounds() {
404            regenerateInputStream();
405            if (mInStream != null) {
406                BitmapFactory.Options options = new BitmapFactory.Options();
407                options.inJustDecodeBounds = true;
408                BitmapFactory.decodeStream(mInStream, null, options);
409                if (options.outWidth != 0 && options.outHeight != 0) {
410                    return new Point(options.outWidth, options.outHeight);
411                }
412            }
413            return null;
414        }
415
416        public void setCropBounds(RectF cropBounds) {
417            mCropBounds = cropBounds;
418        }
419
420        public Bitmap getCroppedBitmap() {
421            return mCroppedBitmap;
422        }
423        public boolean cropBitmap() {
424            boolean failure = false;
425
426            regenerateInputStream();
427
428            if (mNoCrop && mInStream != null) {
429                try {
430                    mWPManager.setStream(mInStream);
431                } catch (IOException e) {
432                    Log.w(LOGTAG, "cannot write stream to wallpaper", e);
433                    failure = true;
434                }
435                if (mOnEndRunnable != null) {
436                    mOnEndRunnable.run();
437                }
438                return !failure;
439            }
440            if (mInStream != null) {
441                // Find crop bounds (scaled to original image size)
442                Rect roundedTrueCrop = new Rect();
443                mCropBounds.roundOut(roundedTrueCrop);
444
445                if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
446                    Log.w(LOGTAG, "crop has bad values for full size image");
447                    failure = true;
448                    return false;
449                }
450
451                // See how much we're reducing the size of the image
452                int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / mOutWidth,
453                        roundedTrueCrop.height() / mOutHeight);
454
455                // Attempt to open a region decoder
456                BitmapRegionDecoder decoder = null;
457                try {
458                    decoder = BitmapRegionDecoder.newInstance(mInStream, true);
459                } catch (IOException e) {
460                    Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
461                }
462
463                Bitmap crop = null;
464                if (decoder != null) {
465                    // Do region decoding to get crop bitmap
466                    BitmapFactory.Options options = new BitmapFactory.Options();
467                    if (scaleDownSampleSize > 1) {
468                        options.inSampleSize = scaleDownSampleSize;
469                    }
470                    crop = decoder.decodeRegion(roundedTrueCrop, options);
471                    decoder.recycle();
472                }
473
474                if (crop == null) {
475                    // BitmapRegionDecoder has failed, try to crop in-memory
476                    regenerateInputStream();
477                    Bitmap fullSize = null;
478                    if (mInStream != null) {
479                        BitmapFactory.Options options = new BitmapFactory.Options();
480                        if (scaleDownSampleSize > 1) {
481                            options.inSampleSize = scaleDownSampleSize;
482                        }
483                        fullSize = BitmapFactory.decodeStream(mInStream, null, options);
484                    }
485                    if (fullSize != null) {
486                        crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
487                                roundedTrueCrop.top, roundedTrueCrop.width(),
488                                roundedTrueCrop.height());
489                    }
490                }
491
492                if (crop == null) {
493                    Log.w(LOGTAG, "cannot decode file: " + mInUri.toString());
494                    failure = true;
495                    return false;
496                }
497                if (mOutWidth > 0 && mOutHeight > 0) {
498                    Matrix m = new Matrix();
499                    RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight());
500                    if (mRotation > 0) {
501                        m.setRotate(mRotation);
502                        m.mapRect(cropRect);
503                    }
504                    RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight);
505                    m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
506                    m.preRotate(mRotation);
507                    Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
508                            (int) returnRect.height(), Bitmap.Config.ARGB_8888);
509                    if (tmp != null) {
510                        Canvas c = new Canvas(tmp);
511                        c.drawBitmap(crop, m, new Paint());
512                        crop = tmp;
513                    }
514                } else if (mRotation > 0) {
515                    Matrix m = new Matrix();
516                    m.setRotate(mRotation);
517                    Bitmap tmp = Bitmap.createBitmap(crop, 0, 0, crop.getWidth(),
518                            crop.getHeight(), m, true);
519                    if (tmp != null) {
520                        crop = tmp;
521                    }
522                }
523
524                if (mSaveCroppedBitmap) {
525                    mCroppedBitmap = crop;
526                }
527
528                // Get output compression format
529                CompressFormat cf =
530                        convertExtensionToCompressFormat(getFileExtension(mOutputFormat));
531
532                // Compress to byte array
533                ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
534                if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
535                    // If we need to set to the wallpaper, set it
536                    if (mSetWallpaper && mWPManager != null) {
537                        if (mWPManager == null) {
538                            Log.w(LOGTAG, "no wallpaper manager");
539                            failure = true;
540                        } else {
541                            try {
542                                byte[] outByteArray = tmpOut.toByteArray();
543                                mWPManager.setStream(new ByteArrayInputStream(outByteArray));
544                                if (mOnBitmapCroppedHandler != null) {
545                                    mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
546                                }
547                            } catch (IOException e) {
548                                Log.w(LOGTAG, "cannot write stream to wallpaper", e);
549                                failure = true;
550                            }
551                        }
552                    }
553                    if (mOnEndRunnable != null) {
554                        mOnEndRunnable.run();
555                    }
556                } else {
557                    Log.w(LOGTAG, "cannot compress bitmap");
558                    failure = true;
559                }
560            }
561            return !failure; // True if any of the operations failed
562        }
563
564        @Override
565        protected Boolean doInBackground(Void... params) {
566            return cropBitmap();
567        }
568
569        @Override
570        protected void onPostExecute(Boolean result) {
571            setResult(Activity.RESULT_OK);
572            finish();
573        }
574    }
575
576    protected void updateWallpaperDimensions(int width, int height) {
577        String spKey = getSharedPreferencesKey();
578        SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
579        SharedPreferences.Editor editor = sp.edit();
580        if (width != 0 && height != 0) {
581            editor.putInt(WALLPAPER_WIDTH_KEY, width);
582            editor.putInt(WALLPAPER_HEIGHT_KEY, height);
583        } else {
584            editor.remove(WALLPAPER_WIDTH_KEY);
585            editor.remove(WALLPAPER_HEIGHT_KEY);
586        }
587        editor.commit();
588
589        suggestWallpaperDimension(getResources(),
590                sp, getWindowManager(), WallpaperManager.getInstance(this));
591    }
592
593    static public void suggestWallpaperDimension(Resources res,
594            final SharedPreferences sharedPrefs,
595            WindowManager windowManager,
596            final WallpaperManager wallpaperManager) {
597        final Point defaultWallpaperSize =
598                WallpaperCropActivity.getDefaultWallpaperSize(res, windowManager);
599
600        new Thread("suggestWallpaperDimension") {
601            public void run() {
602                // If we have saved a wallpaper width/height, use that instead
603                int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWallpaperSize.x);
604                int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultWallpaperSize.y);
605                wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight);
606            }
607        }.start();
608    }
609
610
611    protected static RectF getMaxCropRect(
612            int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) {
613        RectF cropRect = new RectF();
614        // Get a crop rect that will fit this
615        if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
616             cropRect.top = 0;
617             cropRect.bottom = inHeight;
618             cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2;
619             cropRect.right = inWidth - cropRect.left;
620             if (leftAligned) {
621                 cropRect.right -= cropRect.left;
622                 cropRect.left = 0;
623             }
624        } else {
625            cropRect.left = 0;
626            cropRect.right = inWidth;
627            cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2;
628            cropRect.bottom = inHeight - cropRect.top;
629        }
630        return cropRect;
631    }
632
633    protected static CompressFormat convertExtensionToCompressFormat(String extension) {
634        return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
635    }
636
637    protected static String getFileExtension(String requestFormat) {
638        String outputFormat = (requestFormat == null)
639                ? "jpg"
640                : requestFormat;
641        outputFormat = outputFormat.toLowerCase();
642        return (outputFormat.equals("png") || outputFormat.equals("gif"))
643                ? "png" // We don't support gif compression.
644                : "jpg";
645    }
646}
647