WallpaperCropActivity.java revision fabf1f23d64d717790e68b78ad75e1466426be65
1e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/*
2e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Copyright (C) 2013 The Android Open Source Project
3e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka *
4e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * you may not use this file except in compliance with the License.
6e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * You may obtain a copy of the License at
7e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka *
8e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka *
10e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Unless required by applicable law or agreed to in writing, software
11e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * See the License for the specific language governing permissions and
14e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * limitations under the License.
15e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */
16e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/* Copied from Launcher3 */
17e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapackage com.android.wallpapercropper;
18e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
19e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.app.ActionBar;
20e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.app.Activity;
21e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.app.WallpaperManager;
22e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.Context;
23e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.Intent;
24e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.SharedPreferences;
25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.res.Configuration;
26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.res.Resources;
27e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Bitmap;
28e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Bitmap.CompressFormat;
29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.BitmapFactory;
30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.BitmapRegionDecoder;
31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Canvas;
32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Matrix;
33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Paint;
34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Point;
35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Rect;
36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.RectF;
37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.net.Uri;
38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.os.AsyncTask;
39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.os.Bundle;
40e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Log;
41e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.Display;
42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.View;
43e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.WindowManager;
447b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurkaimport android.widget.Toast;
45e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
46e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.gallery3d.common.Utils;
4769784065ea78550944b7eddb559c0dac952e20e8Michael Jurkaimport com.android.gallery3d.exif.ExifInterface;
48e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.photos.BitmapRegionTileSource;
497b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurkaimport com.android.photos.BitmapRegionTileSource.BitmapSource;
50e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
51e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.BufferedInputStream;
52e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.ByteArrayInputStream;
53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.ByteArrayOutputStream;
54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.FileNotFoundException;
55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.IOException;
56e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.InputStream;
57e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapublic class WallpaperCropActivity extends Activity {
59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static final String LOGTAG = "Launcher3.CropActivity";
60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width";
62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height";
63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static final int DEFAULT_COMPRESS_QUALITY = 90;
64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    /**
65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     * The maximum bitmap size we allow to be returned through the intent.
66e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     * Intents have a maximum of 1MB in total size. However, the Bitmap seems to
67e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     * have some overhead to hit so that we go way below the limit here to make
68e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     * sure the intent stays below 1MB.We should consider just returning a byte
69e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     * array instead of a Bitmap instance to avoid overhead.
70e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     */
71e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    public static final int MAX_BMAP_IN_INTENT = 750000;
72e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static final float WALLPAPER_SCREENS_SPAN = 2f;
73e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
7435f922d1d038d14f94b584baae324513610f7813Michael Jurka    protected static Point sDefaultWallpaperSize;
7535f922d1d038d14f94b584baae324513610f7813Michael Jurka
76e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected CropView mCropView;
77e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected Uri mUri;
780fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek    private View mSetWallpaperButton;
79e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
80e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    @Override
81e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected void onCreate(Bundle savedInstanceState) {
82e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        super.onCreate(savedInstanceState);
83e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        init();
84e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        if (!enableRotation()) {
85e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT);
86e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        }
87e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
88e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
89e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected void init() {
90e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        setContentView(R.layout.wallpaper_cropper);
91e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
92e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        mCropView = (CropView) findViewById(R.id.cropView);
93e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
9469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        Intent cropIntent = getIntent();
95e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final Uri imageUri = cropIntent.getData();
96e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
9769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        if (imageUri == null) {
9869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity");
9969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            finish();
10069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            return;
10169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        }
10269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
103e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Action bar
104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Show the custom action bar view
105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final ActionBar actionBar = getActionBar();
106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        actionBar.getCustomView().setOnClickListener(
108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                new View.OnClickListener() {
109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    @Override
110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    public void onClick(View v) {
111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        boolean finishActivityWhenDone = true;
112e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
113e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
114e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                });
1150fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek        mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
1165271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka
1175271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        // Load image in background
1187b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka        final BitmapRegionTileSource.UriBitmapSource bitmapSource =
1197b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
1200fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek        mSetWallpaperButton.setVisibility(View.INVISIBLE);
1217b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka        Runnable onLoad = new Runnable() {
1227b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            public void run() {
1237b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
1247b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    Toast.makeText(WallpaperCropActivity.this,
1257b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                            getString(R.string.wallpaper_load_fail),
1267b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                            Toast.LENGTH_LONG).show();
1277b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    finish();
1280fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek                } else {
1290fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek                    mSetWallpaperButton.setVisibility(View.VISIBLE);
1307b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                }
1317b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            }
1327b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka        };
1337b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka        setCropViewTileSource(bitmapSource, true, false, onLoad);
1345271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka    }
1355271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka
136fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal    @Override
137fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal    protected void onDestroy() {
138fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal        if (mCropView != null) {
139fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal            mCropView.destroy();
140fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal        }
141fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal        super.onDestroy();
142fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal    }
143fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal
1447b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka    public void setCropViewTileSource(
1457b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled,
1467b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            final boolean moveToLeft, final Runnable postExecute) {
1475271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        final Context context = WallpaperCropActivity.this;
1485271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        final View progressView = findViewById(R.id.loading);
1495271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
1505271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            protected Void doInBackground(Void...args) {
1515271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                if (!isCancelled()) {
152d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                    try {
153d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                        bitmapSource.loadInBackground();
154d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                    } catch (SecurityException securityException) {
155d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                        if (isDestroyed()) {
156d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // Temporarily granted permissions are revoked when the activity
157d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // finishes, potentially resulting in a SecurityException here.
158d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // Even though {@link #isDestroyed} might also return true in different
159d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // situations where the configuration changes, we are fine with
160d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // catching these cases here as well.
161d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            cancel(false);
162d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                        } else {
163d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // otherwise it had a different cause and we throw it further
164d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            throw securityException;
165d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                        }
166d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                    }
1675271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                }
1685271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                return null;
1695271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            }
1705271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            protected void onPostExecute(Void arg) {
1715271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                if (!isCancelled()) {
1725271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                    progressView.setVisibility(View.INVISIBLE);
1737b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
1747b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        mCropView.setTileSource(
1757b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                                new BitmapRegionTileSource(context, bitmapSource), null);
1767b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        mCropView.setTouchEnabled(touchEnabled);
1777b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        if (moveToLeft) {
1787b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                            mCropView.moveToLeft();
1797b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        }
1805271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                    }
1815271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                }
1827b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                if (postExecute != null) {
1837b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    postExecute.run();
1847b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                }
1855271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            }
1865271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        };
1875271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        // We don't want to show the spinner every time we load an image, because that would be
1885271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        // annoying; instead, only start showing the spinner if loading the image has taken
1895271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        // longer than 1 sec (ie 1000 ms)
1905271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        progressView.postDelayed(new Runnable() {
1915271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            public void run() {
1925271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) {
1935271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                    progressView.setVisibility(View.VISIBLE);
1945271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                }
1955271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            }
1965271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        }, 1000);
1975271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        loadBitmapTask.execute();
198e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka    }
199e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka
200e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka    public boolean enableRotation() {
201e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        return getResources().getBoolean(R.bool.allow_rotation);
202e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
203e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
204e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    public static String getSharedPreferencesKey() {
205e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return WallpaperCropActivity.class.getName();
206e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
207e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
208e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    // As a ratio of screen height, the total distance we want the parallax effect to span
209e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    // horizontally
210e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static float wallpaperTravelToScreenWidthRatio(int width, int height) {
211e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        float aspectRatio = width / (float) height;
212e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
213e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
214e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
215e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // We will use these two data points to extrapolate how much the wallpaper parallax effect
216e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // to span (ie travel) at any aspect ratio:
217e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
218e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float ASPECT_RATIO_LANDSCAPE = 16/10f;
219e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float ASPECT_RATIO_PORTRAIT = 10/16f;
220e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
221e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
222e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
223e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // To find out the desired width at different aspect ratios, we use the following two
224e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // formulas, where the coefficient on x is the aspect ratio (width/height):
225e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        //   (16/10)x + y = 1.5
226e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        //   (10/16)x + y = 1.2
227e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // We solve for x and y and end up with a final formula:
228e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float x =
229e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
230e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
231e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
232e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return x * aspectRatio + y;
233e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
234e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
235e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    static protected Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) {
23635f922d1d038d14f94b584baae324513610f7813Michael Jurka        if (sDefaultWallpaperSize == null) {
23735f922d1d038d14f94b584baae324513610f7813Michael Jurka            Point minDims = new Point();
23835f922d1d038d14f94b584baae324513610f7813Michael Jurka            Point maxDims = new Point();
23935f922d1d038d14f94b584baae324513610f7813Michael Jurka            windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
24035f922d1d038d14f94b584baae324513610f7813Michael Jurka
24135f922d1d038d14f94b584baae324513610f7813Michael Jurka            int maxDim = Math.max(maxDims.x, maxDims.y);
24235f922d1d038d14f94b584baae324513610f7813Michael Jurka            int minDim = Math.max(minDims.x, minDims.y);
24335f922d1d038d14f94b584baae324513610f7813Michael Jurka
24435f922d1d038d14f94b584baae324513610f7813Michael Jurka            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
24535f922d1d038d14f94b584baae324513610f7813Michael Jurka                Point realSize = new Point();
24635f922d1d038d14f94b584baae324513610f7813Michael Jurka                windowManager.getDefaultDisplay().getRealSize(realSize);
24735f922d1d038d14f94b584baae324513610f7813Michael Jurka                maxDim = Math.max(realSize.x, realSize.y);
24835f922d1d038d14f94b584baae324513610f7813Michael Jurka                minDim = Math.min(realSize.x, realSize.y);
24935f922d1d038d14f94b584baae324513610f7813Michael Jurka            }
250e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
25135f922d1d038d14f94b584baae324513610f7813Michael Jurka            // We need to ensure that there is enough extra space in the wallpaper
25235f922d1d038d14f94b584baae324513610f7813Michael Jurka            // for the intended parallax effects
25335f922d1d038d14f94b584baae324513610f7813Michael Jurka            final int defaultWidth, defaultHeight;
25435f922d1d038d14f94b584baae324513610f7813Michael Jurka            if (isScreenLarge(res)) {
25535f922d1d038d14f94b584baae324513610f7813Michael Jurka                defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
25635f922d1d038d14f94b584baae324513610f7813Michael Jurka                defaultHeight = maxDim;
25735f922d1d038d14f94b584baae324513610f7813Michael Jurka            } else {
25835f922d1d038d14f94b584baae324513610f7813Michael Jurka                defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
25935f922d1d038d14f94b584baae324513610f7813Michael Jurka                defaultHeight = maxDim;
26035f922d1d038d14f94b584baae324513610f7813Michael Jurka            }
26135f922d1d038d14f94b584baae324513610f7813Michael Jurka            sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight);
262e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
26335f922d1d038d14f94b584baae324513610f7813Michael Jurka        return sDefaultWallpaperSize;
264e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
265e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
26669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    public static int getRotationFromExif(String path) {
26769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        return getRotationFromExifHelper(path, null, 0, null, null);
26869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    }
26969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
27069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    public static int getRotationFromExif(Context context, Uri uri) {
27169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        return getRotationFromExifHelper(null, null, 0, context, uri);
27269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    }
27369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
27469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    public static int getRotationFromExif(Resources res, int resId) {
27569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        return getRotationFromExifHelper(null, res, resId, null, null);
27669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    }
27769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
27869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    private static int getRotationFromExifHelper(
27969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            String path, Resources res, int resId, Context context, Uri uri) {
28069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        ExifInterface ei = new ExifInterface();
281b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka        InputStream is = null;
282b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka        BufferedInputStream bis = null;
28369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        try {
28469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            if (path != null) {
28569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                ei.readExif(path);
28669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            } else if (uri != null) {
287b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                is = context.getContentResolver().openInputStream(uri);
288b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                bis = new BufferedInputStream(is);
28969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                ei.readExif(bis);
29069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            } else {
291b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                is = res.openRawResource(resId);
292b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                bis = new BufferedInputStream(is);
29369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                ei.readExif(bis);
29469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            }
29569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
29669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            if (ori != null) {
29769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                return ExifInterface.getRotationForOrientationValue(ori.shortValue());
29869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            }
29969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        } catch (IOException e) {
30069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            Log.w(LOGTAG, "Getting exif data failed", e);
3015ab7d37d903f336c5a96f8caca9b36439e76bdb4Selim Cinek        } catch (NullPointerException e) {
3025ab7d37d903f336c5a96f8caca9b36439e76bdb4Selim Cinek            // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid
3035ab7d37d903f336c5a96f8caca9b36439e76bdb4Selim Cinek            Log.w(LOGTAG, "Getting exif data failed", e);
304b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka        } finally {
305b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka            Utils.closeSilently(bis);
306b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka            Utils.closeSilently(is);
30769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        }
30869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        return 0;
30969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    }
31069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
311e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) {
31269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        int rotation = getRotationFromExif(filePath);
31369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        BitmapCropTask cropTask = new BitmapCropTask(
31469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                this, filePath, null, rotation, 0, 0, true, false, null);
315e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final Point bounds = cropTask.getImageBounds();
316e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Runnable onEndCrop = new Runnable() {
317e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            public void run() {
318e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (finishActivityWhenDone) {
319e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    setResult(Activity.RESULT_OK);
320e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    finish();
321e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
322e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
323e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        };
324e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        cropTask.setOnEndRunnable(onEndCrop);
325e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        cropTask.setNoCrop(true);
326e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        cropTask.execute();
327e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
328e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
329e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected void cropImageAndSetWallpaper(
330e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            Resources res, int resId, final boolean finishActivityWhenDone) {
331e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // crop this image and scale it down to the default wallpaper size for
332e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // this device
33369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        int rotation = getRotationFromExif(res, resId);
334e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Point inSize = mCropView.getSourceDimensions();
335e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Point outSize = getDefaultWallpaperSize(getResources(),
336e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                getWindowManager());
337e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        RectF crop = getMaxCropRect(
338e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                inSize.x, inSize.y, outSize.x, outSize.y, false);
339e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Runnable onEndCrop = new Runnable() {
340e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            public void run() {
341e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (finishActivityWhenDone) {
342e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    setResult(Activity.RESULT_OK);
343e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    finish();
344e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
345e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
346e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        };
347e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        BitmapCropTask cropTask = new BitmapCropTask(this, res, resId,
34869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                crop, rotation, outSize.x, outSize.y, true, false, onEndCrop);
349e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        cropTask.execute();
350e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
351e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
352e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static boolean isScreenLarge(Resources res) {
353e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Configuration config = res.getConfiguration();
354e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return config.smallestScreenWidthDp >= 720;
355e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
356e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
357e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected void cropImageAndSetWallpaper(Uri uri,
358e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
3592ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        boolean centerCrop = getResources().getBoolean(R.bool.center_crop);
360e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        // Get the crop
361e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
362e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka
363e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Display d = getWindowManager().getDefaultDisplay();
364e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
365e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Point displaySize = new Point();
366e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        d.getSize(displaySize);
367e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        boolean isPortrait = displaySize.x < displaySize.y;
368eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka
369eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka        Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(),
370eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka                getWindowManager());
371e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Get the crop
372e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        RectF cropRect = mCropView.getCrop();
373912e0df89ce798a5922f87fd65b87bb99f8b4813Selim Cinek
374c41853cee89b391cd2d4d93205ea69c746ccfa4cSelim Cinek        Point inSize = mCropView.getSourceDimensions();
375c41853cee89b391cd2d4d93205ea69c746ccfa4cSelim Cinek
37669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        int cropRotation = mCropView.getImageRotation();
377e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        float cropScale = mCropView.getWidth() / (float) cropRect.width();
378e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
37969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        Matrix rotateMatrix = new Matrix();
38069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        rotateMatrix.setRotate(cropRotation);
38169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        float[] rotatedInSize = new float[] { inSize.x, inSize.y };
38269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        rotateMatrix.mapPoints(rotatedInSize);
38369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        rotatedInSize[0] = Math.abs(rotatedInSize[0]);
38469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        rotatedInSize[1] = Math.abs(rotatedInSize[1]);
38569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
3867718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        // Due to rounding errors in the cropview renderer the edges can be slightly offset
3877718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        // therefore we ensure that the boundaries are sanely defined
3887718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        cropRect.left = Math.max(0, cropRect.left);
3897718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        cropRect.right = Math.min(rotatedInSize[0], cropRect.right);
3907718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        cropRect.top = Math.max(0, cropRect.top);
3917718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        cropRect.bottom = Math.min(rotatedInSize[1], cropRect.bottom);
3927718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek
393e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // ADJUST CROP WIDTH
394e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Extend the crop all the way to the right, for parallax
395e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        // (or all the way to the left, in RTL)
3962ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        float extraSpace;
3972ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        if (centerCrop) {
3982ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left);
3992ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        } else {
4002ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left;
4012ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        }
402e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Cap the amount of extra width
403eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka        float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width();
404e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        extraSpace = Math.min(extraSpace, maxExtraSpace);
405e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
4062ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        if (centerCrop) {
4072ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            cropRect.left -= extraSpace / 2f;
4082ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            cropRect.right += extraSpace / 2f;
409e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        } else {
4102ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            if (ltr) {
4112ab14daebac46307d2364733481edc3115bbf98cMichael Jurka                cropRect.right += extraSpace;
4122ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            } else {
4132ab14daebac46307d2364733481edc3115bbf98cMichael Jurka                cropRect.left -= extraSpace;
4142ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            }
415e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        }
416e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
417e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // ADJUST CROP HEIGHT
418e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        if (isPortrait) {
419eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka            cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale;
420e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        } else { // LANDSCAPE
421e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            float extraPortraitHeight =
422eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka                    defaultWallpaperSize.y / cropScale - cropRect.height();
423e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            float expandHeight =
42469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top),
425e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            extraPortraitHeight / 2);
426e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.top -= expandHeight;
427e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.bottom += expandHeight;
428e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
429e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final int outWidth = (int) Math.round(cropRect.width() * cropScale);
430e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final int outHeight = (int) Math.round(cropRect.height() * cropScale);
431e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
432e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Runnable onEndCrop = new Runnable() {
433e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            public void run() {
434e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (finishActivityWhenDone) {
435e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    setResult(Activity.RESULT_OK);
436e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    finish();
437e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
438e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
439e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        };
440e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        BitmapCropTask cropTask = new BitmapCropTask(this, uri,
44169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop);
442e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        if (onBitmapCroppedHandler != null) {
443e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
444e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
445e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        cropTask.execute();
446e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
447e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
448e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    public interface OnBitmapCroppedHandler {
449e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public void onBitmapCropped(byte[] imageBytes);
450e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
451e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
452e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka    protected static class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
453e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Uri mInUri = null;
454e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Context mContext;
455e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        String mInFilePath;
456e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        byte[] mInImageBytes;
457e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        int mInResId = 0;
458e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        RectF mCropBounds = null;
459e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        int mOutWidth, mOutHeight;
46069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        int mRotation;
461e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        String mOutputFormat = "jpg"; // for now
462e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        boolean mSetWallpaper;
463e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        boolean mSaveCroppedBitmap;
464e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Bitmap mCroppedBitmap;
465e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Runnable mOnEndRunnable;
466e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Resources mResources;
467e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        OnBitmapCroppedHandler mOnBitmapCroppedHandler;
468e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        boolean mNoCrop;
469e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
470e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public BitmapCropTask(Context c, String filePath,
47169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
472e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
473e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mContext = c;
474e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mInFilePath = filePath;
47569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            init(cropBounds, rotation,
47669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
477e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
478e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
479e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public BitmapCropTask(byte[] imageBytes,
48069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
481e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
482e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mInImageBytes = imageBytes;
48369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            init(cropBounds, rotation,
48469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
485e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
486e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
487e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        public BitmapCropTask(Context c, Uri inUri,
48869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
489e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
490e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            mContext = c;
491e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mInUri = inUri;
49269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            init(cropBounds, rotation,
49369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
494e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
495e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
496e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        public BitmapCropTask(Context c, Resources res, int inResId,
49769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
498e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
499e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            mContext = c;
500e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mInResId = inResId;
501e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mResources = res;
50269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            init(cropBounds, rotation,
50369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
504e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
505e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
50669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        private void init(RectF cropBounds, int rotation, int outWidth, int outHeight,
507e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
508e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mCropBounds = cropBounds;
50969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            mRotation = rotation;
510e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOutWidth = outWidth;
511e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOutHeight = outHeight;
512e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mSetWallpaper = setWallpaper;
513e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mSaveCroppedBitmap = saveCroppedBitmap;
514e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOnEndRunnable = onEndRunnable;
515e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
516e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
517e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public void setOnBitmapCropped(OnBitmapCroppedHandler handler) {
518e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOnBitmapCroppedHandler = handler;
519e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
520e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
521e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public void setNoCrop(boolean value) {
522e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mNoCrop = value;
523e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
524e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
525e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public void setOnEndRunnable(Runnable onEndRunnable) {
526e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOnEndRunnable = onEndRunnable;
527e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
528e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
529e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Helper to setup input stream
5307b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka        private InputStream regenerateInputStream() {
531e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
532e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
533e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        "image byte array given");
534e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            } else {
535e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                try {
536e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    if (mInUri != null) {
5377b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return new BufferedInputStream(
538e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                                mContext.getContentResolver().openInputStream(mInUri));
539e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    } else if (mInFilePath != null) {
5407b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return mContext.openFileInput(mInFilePath);
541e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    } else if (mInImageBytes != null) {
5427b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes));
543e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    } else {
5447b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return new BufferedInputStream(mResources.openRawResource(mInResId));
545e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
546e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                } catch (FileNotFoundException e) {
547e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
548e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
549e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
5507b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            return null;
551e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
552e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
553e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public Point getImageBounds() {
5547b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            InputStream is = regenerateInputStream();
5557b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            if (is != null) {
556e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                BitmapFactory.Options options = new BitmapFactory.Options();
557e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                options.inJustDecodeBounds = true;
5587b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                BitmapFactory.decodeStream(is, null, options);
5597b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                Utils.closeSilently(is);
560e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (options.outWidth != 0 && options.outHeight != 0) {
561e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    return new Point(options.outWidth, options.outHeight);
562e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
563e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
564e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            return null;
565e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
566e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
567e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public void setCropBounds(RectF cropBounds) {
568e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mCropBounds = cropBounds;
569e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
570e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
571e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public Bitmap getCroppedBitmap() {
572e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            return mCroppedBitmap;
573e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
574e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public boolean cropBitmap() {
575e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            boolean failure = false;
576e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
577e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
578e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            WallpaperManager wallpaperManager = null;
579e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            if (mSetWallpaper) {
580e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
581e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            }
5827b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka
5837b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka
5847b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            if (mSetWallpaper && mNoCrop) {
585e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                try {
5867b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    InputStream is = regenerateInputStream();
5877b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    if (is != null) {
5887b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        wallpaperManager.setStream(is);
5897b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        Utils.closeSilently(is);
5907b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    }
591e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                } catch (IOException e) {
592e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "cannot write stream to wallpaper", e);
593e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    failure = true;
594e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
595e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                return !failure;
5967b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            } else {
597e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                // Find crop bounds (scaled to original image size)
598e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                Rect roundedTrueCrop = new Rect();
59969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                Matrix rotateMatrix = new Matrix();
60069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                Matrix inverseRotateMatrix = new Matrix();
601e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka
602e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                Point bounds = getImageBounds();
60369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                if (mRotation > 0) {
60469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    rotateMatrix.setRotate(mRotation);
60569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    inverseRotateMatrix.setRotate(-mRotation);
60669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
60769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    mCropBounds.roundOut(roundedTrueCrop);
60869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    mCropBounds = new RectF(roundedTrueCrop);
60969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
6107b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    if (bounds == null) {
6117b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        Log.w(LOGTAG, "cannot get bounds for image");
6127b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        failure = true;
6137b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return false;
6147b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    }
61569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
61669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    float[] rotatedBounds = new float[] { bounds.x, bounds.y };
61769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    rotateMatrix.mapPoints(rotatedBounds);
61869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    rotatedBounds[0] = Math.abs(rotatedBounds[0]);
61969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    rotatedBounds[1] = Math.abs(rotatedBounds[1]);
62069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
62169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2);
62269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    inverseRotateMatrix.mapRect(mCropBounds);
62369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    mCropBounds.offset(bounds.x/2, bounds.y/2);
62469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
62569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                }
62669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
627e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                mCropBounds.roundOut(roundedTrueCrop);
628e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
629e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
630e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "crop has bad values for full size image");
631e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    failure = true;
632e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    return false;
633e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
634e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
635e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                // See how much we're reducing the size of the image
636b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth,
637b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                        roundedTrueCrop.height() / mOutHeight));
638e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                // Attempt to open a region decoder
639e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                BitmapRegionDecoder decoder = null;
640b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                InputStream is = null;
641e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                try {
642b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                    is = regenerateInputStream();
6437b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    if (is == null) {
6447b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
6457b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        failure = true;
6467b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return false;
6477b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    }
6487b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    decoder = BitmapRegionDecoder.newInstance(is, false);
6497b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    Utils.closeSilently(is);
650e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                } catch (IOException e) {
651e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
652b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                } finally {
653b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                   Utils.closeSilently(is);
654b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                   is = null;
655e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
656e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
657e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                Bitmap crop = null;
658e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (decoder != null) {
659e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    // Do region decoding to get crop bitmap
660e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    BitmapFactory.Options options = new BitmapFactory.Options();
661e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    if (scaleDownSampleSize > 1) {
662e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        options.inSampleSize = scaleDownSampleSize;
663e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
664e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    crop = decoder.decodeRegion(roundedTrueCrop, options);
665e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    decoder.recycle();
666e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
667e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
668e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (crop == null) {
669e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    // BitmapRegionDecoder has failed, try to crop in-memory
670b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                    is = regenerateInputStream();
671e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Bitmap fullSize = null;
6727b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    if (is != null) {
673e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        BitmapFactory.Options options = new BitmapFactory.Options();
674e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        if (scaleDownSampleSize > 1) {
675e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            options.inSampleSize = scaleDownSampleSize;
676e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        }
6777b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        fullSize = BitmapFactory.decodeStream(is, null, options);
6787b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        Utils.closeSilently(is);
679e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
680e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    if (fullSize != null) {
681e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        // Find out the true sample size that was used by the decoder
682e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        scaleDownSampleSize = bounds.x / fullSize.getWidth();
68369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mCropBounds.left /= scaleDownSampleSize;
68469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mCropBounds.top /= scaleDownSampleSize;
68569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mCropBounds.bottom /= scaleDownSampleSize;
68669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mCropBounds.right /= scaleDownSampleSize;
68769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mCropBounds.roundOut(roundedTrueCrop);
68869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
689e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        // Adjust values to account for issues related to rounding
690e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        if (roundedTrueCrop.width() > fullSize.getWidth()) {
691e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            // Adjust the width
692e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth();
693e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        }
694e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        if (roundedTrueCrop.right > fullSize.getWidth()) {
695e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            // Adjust the left value
696e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            int adjustment = roundedTrueCrop.left -
697e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                                    Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width());
698e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.left -= adjustment;
699e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.right -= adjustment;
700e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        }
701e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        if (roundedTrueCrop.height() > fullSize.getHeight()) {
702e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            // Adjust the height
703e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight();
704e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        }
705e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        if (roundedTrueCrop.bottom > fullSize.getHeight()) {
706e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            // Adjust the top value
707e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            int adjustment = roundedTrueCrop.top -
708e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                                    Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height());
709e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.top -= adjustment;
710e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.bottom -= adjustment;
711e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        }
712e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka
713e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
714e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                roundedTrueCrop.top, roundedTrueCrop.width(),
715e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                roundedTrueCrop.height());
716e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
717e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
718e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
719e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (crop == null) {
720e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "cannot decode file: " + mInUri.toString());
721e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    failure = true;
722e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    return false;
723e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
72469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) {
72569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() };
72669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    rotateMatrix.mapPoints(dimsAfter);
72769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    dimsAfter[0] = Math.abs(dimsAfter[0]);
72869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    dimsAfter[1] = Math.abs(dimsAfter[1]);
72969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
73069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    if (!(mOutWidth > 0 && mOutHeight > 0)) {
73169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mOutWidth = Math.round(dimsAfter[0]);
73269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mOutHeight = Math.round(dimsAfter[1]);
733e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
73469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
73569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]);
736e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight);
73769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
73869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    Matrix m = new Matrix();
73969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    if (mRotation == 0) {
74069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
74169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    } else {
74269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix m1 = new Matrix();
74369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f);
74469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix m2 = new Matrix();
74569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m2.setRotate(mRotation);
74669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix m3 = new Matrix();
74769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f);
74869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix m4 = new Matrix();
74969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
75069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
75169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix c1 = new Matrix();
75269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        c1.setConcat(m2, m1);
75369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix c2 = new Matrix();
75469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        c2.setConcat(m4, m3);
75569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m.setConcat(c2, c1);
75669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    }
75769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
758e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
759e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            (int) returnRect.height(), Bitmap.Config.ARGB_8888);
760e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    if (tmp != null) {
761e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        Canvas c = new Canvas(tmp);
762e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                        Paint p = new Paint();
763e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                        p.setFilterBitmap(true);
764e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                        c.drawBitmap(crop, m, p);
765e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        crop = tmp;
766e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
767e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
768e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
769e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (mSaveCroppedBitmap) {
770e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mCroppedBitmap = crop;
771e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
772e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
773e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                // Get output compression format
774e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                CompressFormat cf =
775e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        convertExtensionToCompressFormat(getFileExtension(mOutputFormat));
776e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
777e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                // Compress to byte array
778e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
779e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
780e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    // If we need to set to the wallpaper, set it
781e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                    if (mSetWallpaper && wallpaperManager != null) {
782e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                        try {
783e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                            byte[] outByteArray = tmpOut.toByteArray();
784e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                            wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
785e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                            if (mOnBitmapCroppedHandler != null) {
786e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                                mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
787e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            }
788e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                        } catch (IOException e) {
789e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                            Log.w(LOGTAG, "cannot write stream to wallpaper", e);
790e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                            failure = true;
791e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        }
792e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
793e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                } else {
794e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "cannot compress bitmap");
795e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    failure = true;
796e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
797e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
798e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            return !failure; // True if any of the operations failed
799e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
800e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
801e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        @Override
802e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        protected Boolean doInBackground(Void... params) {
803e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            return cropBitmap();
804e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
805e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
806e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        @Override
807e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        protected void onPostExecute(Boolean result) {
808e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            if (mOnEndRunnable != null) {
809e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                mOnEndRunnable.run();
810e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            }
811e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
812e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
813e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
814e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected static RectF getMaxCropRect(
815e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) {
816e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        RectF cropRect = new RectF();
817e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Get a crop rect that will fit this
818e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
819e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             cropRect.top = 0;
820e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             cropRect.bottom = inHeight;
821e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2;
822e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             cropRect.right = inWidth - cropRect.left;
823e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             if (leftAligned) {
824e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                 cropRect.right -= cropRect.left;
825e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                 cropRect.left = 0;
826e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             }
827e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        } else {
828e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.left = 0;
829e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.right = inWidth;
830e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2;
831e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.bottom = inHeight - cropRect.top;
832e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
833e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return cropRect;
834e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
835e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
836e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected static CompressFormat convertExtensionToCompressFormat(String extension) {
837e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
838e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
839e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
840e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected static String getFileExtension(String requestFormat) {
841e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        String outputFormat = (requestFormat == null)
842e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                ? "jpg"
843e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                : requestFormat;
844e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        outputFormat = outputFormat.toLowerCase();
845e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return (outputFormat.equals("png") || outputFormat.equals("gif"))
846e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                ? "png" // We don't support gif compression.
847e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                : "jpg";
848e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
849e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka}
850