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
17package com.android.gallery3d.filtershow.crop;
18
19import android.app.ActionBar;
20import android.app.Activity;
21import android.app.WallpaperManager;
22import android.content.Context;
23import android.content.Intent;
24import android.content.res.Configuration;
25import android.graphics.Bitmap;
26import android.graphics.Bitmap.CompressFormat;
27import android.graphics.BitmapFactory;
28import android.graphics.BitmapRegionDecoder;
29import android.graphics.Canvas;
30import android.graphics.Matrix;
31import android.graphics.Paint;
32import android.graphics.Rect;
33import android.graphics.RectF;
34import android.net.Uri;
35import android.os.AsyncTask;
36import android.os.Bundle;
37import android.provider.MediaStore;
38import android.util.DisplayMetrics;
39import android.util.Log;
40import android.view.View;
41import android.view.View.OnClickListener;
42import android.view.WindowManager;
43import android.widget.Toast;
44
45import com.android.gallery3d.R;
46import com.android.gallery3d.common.Utils;
47import com.android.gallery3d.filtershow.cache.ImageLoader;
48import com.android.gallery3d.filtershow.tools.SaveImage;
49
50import java.io.ByteArrayInputStream;
51import java.io.ByteArrayOutputStream;
52import java.io.FileNotFoundException;
53import java.io.IOException;
54import java.io.InputStream;
55import java.io.OutputStream;
56
57/**
58 * Activity for cropping an image.
59 */
60public class CropActivity extends Activity {
61    private static final String LOGTAG = "CropActivity";
62    public static final String CROP_ACTION = "com.android.camera.action.CROP";
63    private CropExtras mCropExtras = null;
64    private LoadBitmapTask mLoadBitmapTask = null;
65
66    private int mOutputX = 0;
67    private int mOutputY = 0;
68    private Bitmap mOriginalBitmap = null;
69    private RectF mOriginalBounds = null;
70    private int mOriginalRotation = 0;
71    private Uri mSourceUri = null;
72    private CropView mCropView = null;
73    private View mSaveButton = null;
74    private boolean finalIOGuard = false;
75
76    private static final int SELECT_PICTURE = 1; // request code for picker
77
78    private static final int DEFAULT_COMPRESS_QUALITY = 90;
79    /**
80     * The maximum bitmap size we allow to be returned through the intent.
81     * Intents have a maximum of 1MB in total size. However, the Bitmap seems to
82     * have some overhead to hit so that we go way below the limit here to make
83     * sure the intent stays below 1MB.We should consider just returning a byte
84     * array instead of a Bitmap instance to avoid overhead.
85     */
86    public static final int MAX_BMAP_IN_INTENT = 750000;
87
88    // Flags
89    private static final int DO_SET_WALLPAPER = 1;
90    private static final int DO_RETURN_DATA = 1 << 1;
91    private static final int DO_EXTRA_OUTPUT = 1 << 2;
92
93    private static final int FLAG_CHECK = DO_SET_WALLPAPER | DO_RETURN_DATA | DO_EXTRA_OUTPUT;
94
95    @Override
96    public void onCreate(Bundle savedInstanceState) {
97        super.onCreate(savedInstanceState);
98        Intent intent = getIntent();
99        setResult(RESULT_CANCELED, new Intent());
100        mCropExtras = getExtrasFromIntent(intent);
101        if (mCropExtras != null && mCropExtras.getShowWhenLocked()) {
102            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
103        }
104
105        setContentView(R.layout.crop_activity);
106        mCropView = (CropView) findViewById(R.id.cropView);
107
108        ActionBar actionBar = getActionBar();
109        if (actionBar != null) {
110            actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
111            actionBar.setCustomView(R.layout.filtershow_actionbar);
112
113            View mSaveButton = actionBar.getCustomView();
114            mSaveButton.setOnClickListener(new OnClickListener() {
115                @Override
116                public void onClick(View view) {
117                    startFinishOutput();
118                }
119            });
120        }
121        if (intent.getData() != null) {
122            mSourceUri = intent.getData();
123            startLoadBitmap(mSourceUri);
124        } else {
125            pickImage();
126        }
127    }
128
129    private void enableSave(boolean enable) {
130        if (mSaveButton != null) {
131            mSaveButton.setEnabled(enable);
132        }
133    }
134
135    @Override
136    protected void onDestroy() {
137        if (mLoadBitmapTask != null) {
138            mLoadBitmapTask.cancel(false);
139        }
140        super.onDestroy();
141    }
142
143    @Override
144    public void onConfigurationChanged (Configuration newConfig) {
145        super.onConfigurationChanged(newConfig);
146        mCropView.configChanged();
147    }
148
149    /**
150     * Opens a selector in Gallery to chose an image for use when none was given
151     * in the CROP intent.
152     */
153    private void pickImage() {
154        Intent intent = new Intent();
155        intent.setType("image/*");
156        intent.setAction(Intent.ACTION_GET_CONTENT);
157        startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
158                SELECT_PICTURE);
159    }
160
161    /**
162     * Callback for pickImage().
163     */
164    @Override
165    public void onActivityResult(int requestCode, int resultCode, Intent data) {
166        if (resultCode == RESULT_OK && requestCode == SELECT_PICTURE) {
167            mSourceUri = data.getData();
168            startLoadBitmap(mSourceUri);
169        }
170    }
171
172    /**
173     * Gets screen size metric.
174     */
175    private int getScreenImageSize() {
176        DisplayMetrics outMetrics = new DisplayMetrics();
177        getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
178        return (int) Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
179    }
180
181    /**
182     * Method that loads a bitmap in an async task.
183     */
184    private void startLoadBitmap(Uri uri) {
185        if (uri != null) {
186            enableSave(false);
187            final View loading = findViewById(R.id.loading);
188            loading.setVisibility(View.VISIBLE);
189            mLoadBitmapTask = new LoadBitmapTask();
190            mLoadBitmapTask.execute(uri);
191        } else {
192            cannotLoadImage();
193            done();
194        }
195    }
196
197    /**
198     * Method called on UI thread with loaded bitmap.
199     */
200    private void doneLoadBitmap(Bitmap bitmap, RectF bounds, int orientation) {
201        final View loading = findViewById(R.id.loading);
202        loading.setVisibility(View.GONE);
203        mOriginalBitmap = bitmap;
204        mOriginalBounds = bounds;
205        mOriginalRotation = orientation;
206        if (bitmap != null && bitmap.getWidth() != 0 && bitmap.getHeight() != 0) {
207            RectF imgBounds = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
208            mCropView.initialize(bitmap, imgBounds, imgBounds, orientation);
209            if (mCropExtras != null) {
210                int aspectX = mCropExtras.getAspectX();
211                int aspectY = mCropExtras.getAspectY();
212                mOutputX = mCropExtras.getOutputX();
213                mOutputY = mCropExtras.getOutputY();
214                if (mOutputX > 0 && mOutputY > 0) {
215                    mCropView.applyAspect(mOutputX, mOutputY);
216
217                }
218                float spotX = mCropExtras.getSpotlightX();
219                float spotY = mCropExtras.getSpotlightY();
220                if (spotX > 0 && spotY > 0) {
221                    mCropView.setWallpaperSpotlight(spotX, spotY);
222                }
223                if (aspectX > 0 && aspectY > 0) {
224                    mCropView.applyAspect(aspectX, aspectY);
225                }
226            }
227            enableSave(true);
228        } else {
229            Log.w(LOGTAG, "could not load image for cropping");
230            cannotLoadImage();
231            setResult(RESULT_CANCELED, new Intent());
232            done();
233        }
234    }
235
236    /**
237     * Display toast for image loading failure.
238     */
239    private void cannotLoadImage() {
240        CharSequence text = getString(R.string.cannot_load_image);
241        Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
242        toast.show();
243    }
244
245    /**
246     * AsyncTask for loading a bitmap into memory.
247     *
248     * @see #startLoadBitmap(Uri)
249     * @see #doneLoadBitmap(Bitmap)
250     */
251    private class LoadBitmapTask extends AsyncTask<Uri, Void, Bitmap> {
252        int mBitmapSize;
253        Context mContext;
254        Rect mOriginalBounds;
255        int mOrientation;
256
257        public LoadBitmapTask() {
258            mBitmapSize = getScreenImageSize();
259            mContext = getApplicationContext();
260            mOriginalBounds = new Rect();
261            mOrientation = 0;
262        }
263
264        @Override
265        protected Bitmap doInBackground(Uri... params) {
266            Uri uri = params[0];
267            Bitmap bmap = ImageLoader.loadConstrainedBitmap(uri, mContext, mBitmapSize,
268                    mOriginalBounds, false);
269            mOrientation = ImageLoader.getMetadataRotation(mContext, uri);
270            return bmap;
271        }
272
273        @Override
274        protected void onPostExecute(Bitmap result) {
275            doneLoadBitmap(result, new RectF(mOriginalBounds), mOrientation);
276        }
277    }
278
279    protected void startFinishOutput() {
280        if (finalIOGuard) {
281            return;
282        } else {
283            finalIOGuard = true;
284        }
285        enableSave(false);
286        Uri destinationUri = null;
287        int flags = 0;
288        if (mOriginalBitmap != null && mCropExtras != null) {
289            if (mCropExtras.getExtraOutput() != null) {
290                destinationUri = mCropExtras.getExtraOutput();
291                if (destinationUri != null) {
292                    flags |= DO_EXTRA_OUTPUT;
293                }
294            }
295            if (mCropExtras.getSetAsWallpaper()) {
296                flags |= DO_SET_WALLPAPER;
297            }
298            if (mCropExtras.getReturnData()) {
299                flags |= DO_RETURN_DATA;
300            }
301        }
302        if (flags == 0) {
303            destinationUri = SaveImage.makeAndInsertUri(this, mSourceUri);
304            if (destinationUri != null) {
305                flags |= DO_EXTRA_OUTPUT;
306            }
307        }
308        if ((flags & FLAG_CHECK) != 0 && mOriginalBitmap != null) {
309            RectF photo = new RectF(0, 0, mOriginalBitmap.getWidth(), mOriginalBitmap.getHeight());
310            RectF crop = getBitmapCrop(photo);
311            startBitmapIO(flags, mOriginalBitmap, mSourceUri, destinationUri, crop,
312                    photo, mOriginalBounds,
313                    (mCropExtras == null) ? null : mCropExtras.getOutputFormat(), mOriginalRotation);
314            return;
315        }
316        setResult(RESULT_CANCELED, new Intent());
317        done();
318        return;
319    }
320
321    private void startBitmapIO(int flags, Bitmap currentBitmap, Uri sourceUri, Uri destUri,
322            RectF cropBounds, RectF photoBounds, RectF currentBitmapBounds, String format,
323            int rotation) {
324        if (cropBounds == null || photoBounds == null || currentBitmap == null
325                || currentBitmap.getWidth() == 0 || currentBitmap.getHeight() == 0
326                || cropBounds.width() == 0 || cropBounds.height() == 0 || photoBounds.width() == 0
327                || photoBounds.height() == 0) {
328            return; // fail fast
329        }
330        if ((flags & FLAG_CHECK) == 0) {
331            return; // no output options
332        }
333        if ((flags & DO_SET_WALLPAPER) != 0) {
334            Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show();
335        }
336
337        final View loading = findViewById(R.id.loading);
338        loading.setVisibility(View.VISIBLE);
339        BitmapIOTask ioTask = new BitmapIOTask(sourceUri, destUri, format, flags, cropBounds,
340                photoBounds, currentBitmapBounds, rotation, mOutputX, mOutputY);
341        ioTask.execute(currentBitmap);
342    }
343
344    private void doneBitmapIO(boolean success, Intent intent) {
345        final View loading = findViewById(R.id.loading);
346        loading.setVisibility(View.GONE);
347        if (success) {
348            setResult(RESULT_OK, intent);
349        } else {
350            setResult(RESULT_CANCELED, intent);
351        }
352        done();
353    }
354
355    private class BitmapIOTask extends AsyncTask<Bitmap, Void, Boolean> {
356
357        private final WallpaperManager mWPManager;
358        InputStream mInStream = null;
359        OutputStream mOutStream = null;
360        String mOutputFormat = null;
361        Uri mOutUri = null;
362        Uri mInUri = null;
363        int mFlags = 0;
364        RectF mCrop = null;
365        RectF mPhoto = null;
366        RectF mOrig = null;
367        Intent mResultIntent = null;
368        int mRotation = 0;
369
370        // Helper to setup input stream
371        private void regenerateInputStream() {
372            if (mInUri == null) {
373                Log.w(LOGTAG, "cannot read original file, no input URI given");
374            } else {
375                Utils.closeSilently(mInStream);
376                try {
377                    mInStream = getContentResolver().openInputStream(mInUri);
378                } catch (FileNotFoundException e) {
379                    Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
380                }
381            }
382        }
383
384        public BitmapIOTask(Uri sourceUri, Uri destUri, String outputFormat, int flags,
385                RectF cropBounds, RectF photoBounds, RectF originalBitmapBounds, int rotation,
386                int outputX, int outputY) {
387            mOutputFormat = outputFormat;
388            mOutStream = null;
389            mOutUri = destUri;
390            mInUri = sourceUri;
391            mFlags = flags;
392            mCrop = cropBounds;
393            mPhoto = photoBounds;
394            mOrig = originalBitmapBounds;
395            mWPManager = WallpaperManager.getInstance(getApplicationContext());
396            mResultIntent = new Intent();
397            mRotation = (rotation < 0) ? -rotation : rotation;
398            mRotation %= 360;
399            mRotation = 90 * (int) (mRotation / 90);  // now mRotation is a multiple of 90
400            mOutputX = outputX;
401            mOutputY = outputY;
402
403            if ((flags & DO_EXTRA_OUTPUT) != 0) {
404                if (mOutUri == null) {
405                    Log.w(LOGTAG, "cannot write file, no output URI given");
406                } else {
407                    try {
408                        mOutStream = getContentResolver().openOutputStream(mOutUri);
409                    } catch (FileNotFoundException e) {
410                        Log.w(LOGTAG, "cannot write file: " + mOutUri.toString(), e);
411                    }
412                }
413            }
414
415            if ((flags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0) {
416                regenerateInputStream();
417            }
418        }
419
420        @Override
421        protected Boolean doInBackground(Bitmap... params) {
422            boolean failure = false;
423            Bitmap img = params[0];
424
425            // Set extra for crop bounds
426            if (mCrop != null && mPhoto != null && mOrig != null) {
427                RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig);
428                Matrix m = new Matrix();
429                m.setRotate(mRotation);
430                m.mapRect(trueCrop);
431                if (trueCrop != null) {
432                    Rect rounded = new Rect();
433                    trueCrop.roundOut(rounded);
434                    mResultIntent.putExtra(CropExtras.KEY_CROPPED_RECT, rounded);
435                }
436            }
437
438            // Find the small cropped bitmap that is returned in the intent
439            if ((mFlags & DO_RETURN_DATA) != 0) {
440                assert (img != null);
441                Bitmap ret = getCroppedImage(img, mCrop, mPhoto);
442                if (ret != null) {
443                    ret = getDownsampledBitmap(ret, MAX_BMAP_IN_INTENT);
444                }
445                if (ret == null) {
446                    Log.w(LOGTAG, "could not downsample bitmap to return in data");
447                    failure = true;
448                } else {
449                    if (mRotation > 0) {
450                        Matrix m = new Matrix();
451                        m.setRotate(mRotation);
452                        Bitmap tmp = Bitmap.createBitmap(ret, 0, 0, ret.getWidth(),
453                                ret.getHeight(), m, true);
454                        if (tmp != null) {
455                            ret = tmp;
456                        }
457                    }
458                    mResultIntent.putExtra(CropExtras.KEY_DATA, ret);
459                }
460            }
461
462            // Do the large cropped bitmap and/or set the wallpaper
463            if ((mFlags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0 && mInStream != null) {
464                // Find crop bounds (scaled to original image size)
465                RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig);
466                if (trueCrop == null) {
467                    Log.w(LOGTAG, "cannot find crop for full size image");
468                    failure = true;
469                    return false;
470                }
471                Rect roundedTrueCrop = new Rect();
472                trueCrop.roundOut(roundedTrueCrop);
473
474                if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
475                    Log.w(LOGTAG, "crop has bad values for full size image");
476                    failure = true;
477                    return false;
478                }
479
480                // Attempt to open a region decoder
481                BitmapRegionDecoder decoder = null;
482                try {
483                    decoder = BitmapRegionDecoder.newInstance(mInStream, true);
484                } catch (IOException e) {
485                    Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
486                }
487
488                Bitmap crop = null;
489                if (decoder != null) {
490                    // Do region decoding to get crop bitmap
491                    BitmapFactory.Options options = new BitmapFactory.Options();
492                    options.inMutable = true;
493                    crop = decoder.decodeRegion(roundedTrueCrop, options);
494                    decoder.recycle();
495                }
496
497                if (crop == null) {
498                    // BitmapRegionDecoder has failed, try to crop in-memory
499                    regenerateInputStream();
500                    Bitmap fullSize = null;
501                    if (mInStream != null) {
502                        fullSize = BitmapFactory.decodeStream(mInStream);
503                    }
504                    if (fullSize != null) {
505                        crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
506                                roundedTrueCrop.top, roundedTrueCrop.width(),
507                                roundedTrueCrop.height());
508                    }
509                }
510
511                if (crop == null) {
512                    Log.w(LOGTAG, "cannot decode file: " + mInUri.toString());
513                    failure = true;
514                    return false;
515                }
516                if (mOutputX > 0 && mOutputY > 0) {
517                    Matrix m = new Matrix();
518                    RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight());
519                    if (mRotation > 0) {
520                        m.setRotate(mRotation);
521                        m.mapRect(cropRect);
522                    }
523                    RectF returnRect = new RectF(0, 0, mOutputX, mOutputY);
524                    m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
525                    m.preRotate(mRotation);
526                    Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
527                            (int) returnRect.height(), Bitmap.Config.ARGB_8888);
528                    if (tmp != null) {
529                        Canvas c = new Canvas(tmp);
530                        c.drawBitmap(crop, m, new Paint());
531                        crop = tmp;
532                    }
533                } else if (mRotation > 0) {
534                    Matrix m = new Matrix();
535                    m.setRotate(mRotation);
536                    Bitmap tmp = Bitmap.createBitmap(crop, 0, 0, crop.getWidth(),
537                            crop.getHeight(), m, true);
538                    if (tmp != null) {
539                        crop = tmp;
540                    }
541                }
542                // Get output compression format
543                CompressFormat cf =
544                        convertExtensionToCompressFormat(getFileExtension(mOutputFormat));
545
546                // If we only need to output to a URI, compress straight to file
547                if (mFlags == DO_EXTRA_OUTPUT) {
548                    if (mOutStream == null
549                            || !crop.compress(cf, DEFAULT_COMPRESS_QUALITY, mOutStream)) {
550                        Log.w(LOGTAG, "failed to compress bitmap to file: " + mOutUri.toString());
551                        failure = true;
552                    } else {
553                        mResultIntent.setData(mOutUri);
554                    }
555                } else {
556                    // Compress to byte array
557                    ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
558                    if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
559
560                        // If we need to output to a Uri, write compressed
561                        // bitmap out
562                        if ((mFlags & DO_EXTRA_OUTPUT) != 0) {
563                            if (mOutStream == null) {
564                                Log.w(LOGTAG,
565                                        "failed to compress bitmap to file: " + mOutUri.toString());
566                                failure = true;
567                            } else {
568                                try {
569                                    mOutStream.write(tmpOut.toByteArray());
570                                    mResultIntent.setData(mOutUri);
571                                } catch (IOException e) {
572                                    Log.w(LOGTAG,
573                                            "failed to compress bitmap to file: "
574                                                    + mOutUri.toString(), e);
575                                    failure = true;
576                                }
577                            }
578                        }
579
580                        // If we need to set to the wallpaper, set it
581                        if ((mFlags & DO_SET_WALLPAPER) != 0 && mWPManager != null) {
582                            if (mWPManager == null) {
583                                Log.w(LOGTAG, "no wallpaper manager");
584                                failure = true;
585                            } else {
586                                try {
587                                    mWPManager.setStream(new ByteArrayInputStream(tmpOut
588                                            .toByteArray()));
589                                } catch (IOException e) {
590                                    Log.w(LOGTAG, "cannot write stream to wallpaper", e);
591                                    failure = true;
592                                }
593                            }
594                        }
595                    } else {
596                        Log.w(LOGTAG, "cannot compress bitmap");
597                        failure = true;
598                    }
599                }
600            }
601            return !failure; // True if any of the operations failed
602        }
603
604        @Override
605        protected void onPostExecute(Boolean result) {
606            Utils.closeSilently(mOutStream);
607            Utils.closeSilently(mInStream);
608            doneBitmapIO(result.booleanValue(), mResultIntent);
609        }
610
611    }
612
613    private void done() {
614        finish();
615    }
616
617    protected static Bitmap getCroppedImage(Bitmap image, RectF cropBounds, RectF photoBounds) {
618        RectF imageBounds = new RectF(0, 0, image.getWidth(), image.getHeight());
619        RectF crop = CropMath.getScaledCropBounds(cropBounds, photoBounds, imageBounds);
620        if (crop == null) {
621            return null;
622        }
623        Rect intCrop = new Rect();
624        crop.roundOut(intCrop);
625        return Bitmap.createBitmap(image, intCrop.left, intCrop.top, intCrop.width(),
626                intCrop.height());
627    }
628
629    protected static Bitmap getDownsampledBitmap(Bitmap image, int max_size) {
630        if (image == null || image.getWidth() == 0 || image.getHeight() == 0 || max_size < 16) {
631            throw new IllegalArgumentException("Bad argument to getDownsampledBitmap()");
632        }
633        int shifts = 0;
634        int size = CropMath.getBitmapSize(image);
635        while (size > max_size) {
636            shifts++;
637            size /= 4;
638        }
639        Bitmap ret = Bitmap.createScaledBitmap(image, image.getWidth() >> shifts,
640                image.getHeight() >> shifts, true);
641        if (ret == null) {
642            return null;
643        }
644        // Handle edge case for rounding.
645        if (CropMath.getBitmapSize(ret) > max_size) {
646            return Bitmap.createScaledBitmap(ret, ret.getWidth() >> 1, ret.getHeight() >> 1, true);
647        }
648        return ret;
649    }
650
651    /**
652     * Gets the crop extras from the intent, or null if none exist.
653     */
654    protected static CropExtras getExtrasFromIntent(Intent intent) {
655        Bundle extras = intent.getExtras();
656        if (extras != null) {
657            return new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0),
658                    extras.getInt(CropExtras.KEY_OUTPUT_Y, 0),
659                    extras.getBoolean(CropExtras.KEY_SCALE, true) &&
660                            extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false),
661                    extras.getInt(CropExtras.KEY_ASPECT_X, 0),
662                    extras.getInt(CropExtras.KEY_ASPECT_Y, 0),
663                    extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false),
664                    extras.getBoolean(CropExtras.KEY_RETURN_DATA, false),
665                    (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT),
666                    extras.getString(CropExtras.KEY_OUTPUT_FORMAT),
667                    extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false),
668                    extras.getFloat(CropExtras.KEY_SPOTLIGHT_X),
669                    extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y));
670        }
671        return null;
672    }
673
674    protected static CompressFormat convertExtensionToCompressFormat(String extension) {
675        return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
676    }
677
678    protected static String getFileExtension(String requestFormat) {
679        String outputFormat = (requestFormat == null)
680                ? "jpg"
681                : requestFormat;
682        outputFormat = outputFormat.toLowerCase();
683        return (outputFormat.equals("png") || outputFormat.equals("gif"))
684                ? "png" // We don't support gif compression.
685                : "jpg";
686    }
687
688    private RectF getBitmapCrop(RectF imageBounds) {
689        RectF crop = mCropView.getCrop();
690        RectF photo = mCropView.getPhoto();
691        if (crop == null || photo == null) {
692            Log.w(LOGTAG, "could not get crop");
693            return null;
694        }
695        RectF scaledCrop = CropMath.getScaledCropBounds(crop, photo, imageBounds);
696        return scaledCrop;
697    }
698}
699