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.res.Configuration;
25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.res.Resources;
26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Bitmap;
27e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Bitmap.CompressFormat;
28e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.BitmapFactory;
29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.BitmapRegionDecoder;
30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Canvas;
31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Matrix;
32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Paint;
33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Point;
34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Rect;
35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.RectF;
36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.net.Uri;
37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.os.AsyncTask;
38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.os.Bundle;
39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Log;
40e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.Display;
41e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.View;
42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.WindowManager;
437b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurkaimport android.widget.Toast;
44e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
45e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.gallery3d.common.Utils;
4669784065ea78550944b7eddb559c0dac952e20e8Michael Jurkaimport com.android.gallery3d.exif.ExifInterface;
47e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.photos.BitmapRegionTileSource;
487b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurkaimport com.android.photos.BitmapRegionTileSource.BitmapSource;
49e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
50e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.BufferedInputStream;
51e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.ByteArrayInputStream;
52e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.ByteArrayOutputStream;
53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.FileNotFoundException;
54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.IOException;
55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.InputStream;
56e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
57e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapublic class WallpaperCropActivity extends Activity {
58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static final String LOGTAG = "Launcher3.CropActivity";
59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width";
61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height";
62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static final int DEFAULT_COMPRESS_QUALITY = 90;
63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    /**
64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     * The maximum bitmap size we allow to be returned through the intent.
65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     * Intents have a maximum of 1MB in total size. However, the Bitmap seems to
66e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     * have some overhead to hit so that we go way below the limit here to make
67e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     * sure the intent stays below 1MB.We should consider just returning a byte
68e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     * array instead of a Bitmap instance to avoid overhead.
69e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka     */
70e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    public static final int MAX_BMAP_IN_INTENT = 750000;
71e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static final float WALLPAPER_SCREENS_SPAN = 2f;
72e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
7335f922d1d038d14f94b584baae324513610f7813Michael Jurka    protected static Point sDefaultWallpaperSize;
7435f922d1d038d14f94b584baae324513610f7813Michael Jurka
75e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected CropView mCropView;
76e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected Uri mUri;
770fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek    private View mSetWallpaperButton;
78e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
79e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    @Override
80e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected void onCreate(Bundle savedInstanceState) {
81e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        super.onCreate(savedInstanceState);
82e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        init();
83e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        if (!enableRotation()) {
84e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT);
85e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        }
86e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
87e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
88e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected void init() {
89e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        setContentView(R.layout.wallpaper_cropper);
90e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
9151efddbd3bb304de2dd47fa8cd1114ac555958bbAlan Viverette        mCropView = findViewById(R.id.cropView);
92e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
9369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        Intent cropIntent = getIntent();
94e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final Uri imageUri = cropIntent.getData();
95e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
9669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        if (imageUri == null) {
9769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity");
9869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            finish();
9969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            return;
10069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        }
10169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
102e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Action bar
103e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Show the custom action bar view
104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final ActionBar actionBar = getActionBar();
105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        actionBar.getCustomView().setOnClickListener(
107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                new View.OnClickListener() {
108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    @Override
109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    public void onClick(View v) {
110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        boolean finishActivityWhenDone = true;
111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
112e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
113e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                });
1140fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek        mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
1155271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka
1165271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        // Load image in background
1177b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka        final BitmapRegionTileSource.UriBitmapSource bitmapSource =
1187b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
1190fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek        mSetWallpaperButton.setVisibility(View.INVISIBLE);
1207b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka        Runnable onLoad = new Runnable() {
1217b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            public void run() {
1227b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
1237b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    Toast.makeText(WallpaperCropActivity.this,
1247b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                            getString(R.string.wallpaper_load_fail),
1257b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                            Toast.LENGTH_LONG).show();
1267b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    finish();
1270fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek                } else {
1280fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek                    mSetWallpaperButton.setVisibility(View.VISIBLE);
1297b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                }
1307b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            }
1317b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka        };
1327b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka        setCropViewTileSource(bitmapSource, true, false, onLoad);
1335271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka    }
1345271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka
135fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal    @Override
136fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal    protected void onDestroy() {
137fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal        if (mCropView != null) {
138fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal            mCropView.destroy();
139fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal        }
140fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal        super.onDestroy();
141fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal    }
142fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal
1437b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka    public void setCropViewTileSource(
1447b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled,
1457b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            final boolean moveToLeft, final Runnable postExecute) {
1465271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        final Context context = WallpaperCropActivity.this;
1475271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        final View progressView = findViewById(R.id.loading);
1485271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
1495271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            protected Void doInBackground(Void...args) {
1505271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                if (!isCancelled()) {
151d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                    try {
152d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                        bitmapSource.loadInBackground();
153d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                    } catch (SecurityException securityException) {
154d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                        if (isDestroyed()) {
155d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // Temporarily granted permissions are revoked when the activity
156d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // finishes, potentially resulting in a SecurityException here.
157d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // Even though {@link #isDestroyed} might also return true in different
158d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // situations where the configuration changes, we are fine with
159d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // catching these cases here as well.
160d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            cancel(false);
161d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                        } else {
162d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            // otherwise it had a different cause and we throw it further
163d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                            throw securityException;
164d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                        }
165d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek                    }
1665271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                }
1675271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                return null;
1685271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            }
1695271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            protected void onPostExecute(Void arg) {
1705271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                if (!isCancelled()) {
1715271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                    progressView.setVisibility(View.INVISIBLE);
1727b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
1737b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        mCropView.setTileSource(
1747b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                                new BitmapRegionTileSource(context, bitmapSource), null);
1757b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        mCropView.setTouchEnabled(touchEnabled);
1767b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        if (moveToLeft) {
1777b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                            mCropView.moveToLeft();
1787b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        }
1795271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                    }
1805271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                }
1817b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                if (postExecute != null) {
1827b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    postExecute.run();
1837b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                }
1845271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            }
1855271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        };
1865271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        // We don't want to show the spinner every time we load an image, because that would be
1875271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        // annoying; instead, only start showing the spinner if loading the image has taken
1885271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        // longer than 1 sec (ie 1000 ms)
1895271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        progressView.postDelayed(new Runnable() {
1905271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            public void run() {
1915271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) {
1925271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                    progressView.setVisibility(View.VISIBLE);
1935271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka                }
1945271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka            }
1955271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        }, 1000);
1965271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka        loadBitmapTask.execute();
197e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka    }
198e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka
199e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka    public boolean enableRotation() {
200e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        return getResources().getBoolean(R.bool.allow_rotation);
201e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
202e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
203e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    public static String getSharedPreferencesKey() {
204e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return WallpaperCropActivity.class.getName();
205e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
206e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
207e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    // As a ratio of screen height, the total distance we want the parallax effect to span
208e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    // horizontally
209e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static float wallpaperTravelToScreenWidthRatio(int width, int height) {
210e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        float aspectRatio = width / (float) height;
211e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
212e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
213e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
214e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // We will use these two data points to extrapolate how much the wallpaper parallax effect
215e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // to span (ie travel) at any aspect ratio:
216e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
217e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float ASPECT_RATIO_LANDSCAPE = 16/10f;
218e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float ASPECT_RATIO_PORTRAIT = 10/16f;
219e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
220e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
221e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
222e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // To find out the desired width at different aspect ratios, we use the following two
223e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // formulas, where the coefficient on x is the aspect ratio (width/height):
224e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        //   (16/10)x + y = 1.5
225e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        //   (10/16)x + y = 1.2
226e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // We solve for x and y and end up with a final formula:
227e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float x =
228e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
229e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
230e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
231e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return x * aspectRatio + y;
232e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
233e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
234e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    static protected Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) {
23535f922d1d038d14f94b584baae324513610f7813Michael Jurka        if (sDefaultWallpaperSize == null) {
23635f922d1d038d14f94b584baae324513610f7813Michael Jurka            Point minDims = new Point();
23735f922d1d038d14f94b584baae324513610f7813Michael Jurka            Point maxDims = new Point();
23835f922d1d038d14f94b584baae324513610f7813Michael Jurka            windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
23935f922d1d038d14f94b584baae324513610f7813Michael Jurka
24035f922d1d038d14f94b584baae324513610f7813Michael Jurka            int maxDim = Math.max(maxDims.x, maxDims.y);
24135f922d1d038d14f94b584baae324513610f7813Michael Jurka            int minDim = Math.max(minDims.x, minDims.y);
24235f922d1d038d14f94b584baae324513610f7813Michael Jurka
24335f922d1d038d14f94b584baae324513610f7813Michael Jurka            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
24435f922d1d038d14f94b584baae324513610f7813Michael Jurka                Point realSize = new Point();
24535f922d1d038d14f94b584baae324513610f7813Michael Jurka                windowManager.getDefaultDisplay().getRealSize(realSize);
24635f922d1d038d14f94b584baae324513610f7813Michael Jurka                maxDim = Math.max(realSize.x, realSize.y);
24735f922d1d038d14f94b584baae324513610f7813Michael Jurka                minDim = Math.min(realSize.x, realSize.y);
24835f922d1d038d14f94b584baae324513610f7813Michael Jurka            }
249e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
25035f922d1d038d14f94b584baae324513610f7813Michael Jurka            // We need to ensure that there is enough extra space in the wallpaper
25135f922d1d038d14f94b584baae324513610f7813Michael Jurka            // for the intended parallax effects
25235f922d1d038d14f94b584baae324513610f7813Michael Jurka            final int defaultWidth, defaultHeight;
25335f922d1d038d14f94b584baae324513610f7813Michael Jurka            if (isScreenLarge(res)) {
25435f922d1d038d14f94b584baae324513610f7813Michael Jurka                defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
25535f922d1d038d14f94b584baae324513610f7813Michael Jurka                defaultHeight = maxDim;
25635f922d1d038d14f94b584baae324513610f7813Michael Jurka            } else {
25735f922d1d038d14f94b584baae324513610f7813Michael Jurka                defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
25835f922d1d038d14f94b584baae324513610f7813Michael Jurka                defaultHeight = maxDim;
25935f922d1d038d14f94b584baae324513610f7813Michael Jurka            }
26035f922d1d038d14f94b584baae324513610f7813Michael Jurka            sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight);
261e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
26235f922d1d038d14f94b584baae324513610f7813Michael Jurka        return sDefaultWallpaperSize;
263e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
264e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
26569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    public static int getRotationFromExif(String path) {
26669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        return getRotationFromExifHelper(path, null, 0, null, null);
26769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    }
26869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
26969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    public static int getRotationFromExif(Context context, Uri uri) {
27069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        return getRotationFromExifHelper(null, null, 0, context, uri);
27169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    }
27269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
27369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    public static int getRotationFromExif(Resources res, int resId) {
27469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        return getRotationFromExifHelper(null, res, resId, null, null);
27569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    }
27669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
27769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    private static int getRotationFromExifHelper(
27869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            String path, Resources res, int resId, Context context, Uri uri) {
27969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        ExifInterface ei = new ExifInterface();
280b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka        InputStream is = null;
281b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka        BufferedInputStream bis = null;
28269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        try {
28369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            if (path != null) {
28469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                ei.readExif(path);
28569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            } else if (uri != null) {
286b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                is = context.getContentResolver().openInputStream(uri);
287b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                bis = new BufferedInputStream(is);
28869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                ei.readExif(bis);
28969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            } else {
290b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                is = res.openRawResource(resId);
291b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                bis = new BufferedInputStream(is);
29269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                ei.readExif(bis);
29369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            }
29469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
29569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            if (ori != null) {
29669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                return ExifInterface.getRotationForOrientationValue(ori.shortValue());
29769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            }
29869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        } catch (IOException e) {
29969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            Log.w(LOGTAG, "Getting exif data failed", e);
3005ab7d37d903f336c5a96f8caca9b36439e76bdb4Selim Cinek        } catch (NullPointerException e) {
3015ab7d37d903f336c5a96f8caca9b36439e76bdb4Selim Cinek            // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid
3025ab7d37d903f336c5a96f8caca9b36439e76bdb4Selim Cinek            Log.w(LOGTAG, "Getting exif data failed", e);
303b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka        } finally {
304b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka            Utils.closeSilently(bis);
305b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka            Utils.closeSilently(is);
30669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        }
30769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        return 0;
30869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka    }
30969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
310e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) {
31169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        int rotation = getRotationFromExif(filePath);
31269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        BitmapCropTask cropTask = new BitmapCropTask(
31369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                this, filePath, null, rotation, 0, 0, true, false, null);
314e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final Point bounds = cropTask.getImageBounds();
315e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Runnable onEndCrop = new Runnable() {
316e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            public void run() {
317e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (finishActivityWhenDone) {
318e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    setResult(Activity.RESULT_OK);
319e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    finish();
320e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
321e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
322e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        };
323e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        cropTask.setOnEndRunnable(onEndCrop);
324e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        cropTask.setNoCrop(true);
325e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        cropTask.execute();
326e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
327e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
328e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected void cropImageAndSetWallpaper(
329e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            Resources res, int resId, final boolean finishActivityWhenDone) {
330e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // crop this image and scale it down to the default wallpaper size for
331e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // this device
33269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        int rotation = getRotationFromExif(res, resId);
333e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Point inSize = mCropView.getSourceDimensions();
334e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Point outSize = getDefaultWallpaperSize(getResources(),
335e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                getWindowManager());
336e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        RectF crop = getMaxCropRect(
337e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                inSize.x, inSize.y, outSize.x, outSize.y, false);
338e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Runnable onEndCrop = new Runnable() {
339e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            public void run() {
340e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (finishActivityWhenDone) {
341e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    setResult(Activity.RESULT_OK);
342e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    finish();
343e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
344e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
345e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        };
346e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        BitmapCropTask cropTask = new BitmapCropTask(this, res, resId,
34769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                crop, rotation, outSize.x, outSize.y, true, false, onEndCrop);
348e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        cropTask.execute();
349e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
350e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
351e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    private static boolean isScreenLarge(Resources res) {
352e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Configuration config = res.getConfiguration();
353e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return config.smallestScreenWidthDp >= 720;
354e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
355e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
356e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected void cropImageAndSetWallpaper(Uri uri,
357e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
3582ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        boolean centerCrop = getResources().getBoolean(R.bool.center_crop);
359e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        // Get the crop
360e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
361e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka
362e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Display d = getWindowManager().getDefaultDisplay();
363e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
364e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Point displaySize = new Point();
365e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        d.getSize(displaySize);
366e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        boolean isPortrait = displaySize.x < displaySize.y;
367eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka
368eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka        Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(),
369eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka                getWindowManager());
370e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Get the crop
371e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        RectF cropRect = mCropView.getCrop();
372912e0df89ce798a5922f87fd65b87bb99f8b4813Selim Cinek
373c41853cee89b391cd2d4d93205ea69c746ccfa4cSelim Cinek        Point inSize = mCropView.getSourceDimensions();
374c41853cee89b391cd2d4d93205ea69c746ccfa4cSelim Cinek
37569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        int cropRotation = mCropView.getImageRotation();
376e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        float cropScale = mCropView.getWidth() / (float) cropRect.width();
377e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
37869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        Matrix rotateMatrix = new Matrix();
37969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        rotateMatrix.setRotate(cropRotation);
38069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        float[] rotatedInSize = new float[] { inSize.x, inSize.y };
38169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        rotateMatrix.mapPoints(rotatedInSize);
38269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        rotatedInSize[0] = Math.abs(rotatedInSize[0]);
38369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        rotatedInSize[1] = Math.abs(rotatedInSize[1]);
38469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
3857718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        // Due to rounding errors in the cropview renderer the edges can be slightly offset
3867718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        // therefore we ensure that the boundaries are sanely defined
3877718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        cropRect.left = Math.max(0, cropRect.left);
3887718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        cropRect.right = Math.min(rotatedInSize[0], cropRect.right);
3897718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        cropRect.top = Math.max(0, cropRect.top);
3907718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek        cropRect.bottom = Math.min(rotatedInSize[1], cropRect.bottom);
3917718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek
392e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // ADJUST CROP WIDTH
393e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Extend the crop all the way to the right, for parallax
394e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        // (or all the way to the left, in RTL)
3952ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        float extraSpace;
3962ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        if (centerCrop) {
3972ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left);
3982ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        } else {
3992ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left;
4002ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        }
401e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Cap the amount of extra width
402eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka        float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width();
403e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        extraSpace = Math.min(extraSpace, maxExtraSpace);
404e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
4052ab14daebac46307d2364733481edc3115bbf98cMichael Jurka        if (centerCrop) {
4062ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            cropRect.left -= extraSpace / 2f;
4072ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            cropRect.right += extraSpace / 2f;
408e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        } else {
4092ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            if (ltr) {
4102ab14daebac46307d2364733481edc3115bbf98cMichael Jurka                cropRect.right += extraSpace;
4112ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            } else {
4122ab14daebac46307d2364733481edc3115bbf98cMichael Jurka                cropRect.left -= extraSpace;
4132ab14daebac46307d2364733481edc3115bbf98cMichael Jurka            }
414e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        }
415e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
416e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // ADJUST CROP HEIGHT
417e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        if (isPortrait) {
418eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka            cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale;
419e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        } else { // LANDSCAPE
420e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            float extraPortraitHeight =
421eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka                    defaultWallpaperSize.y / cropScale - cropRect.height();
422e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            float expandHeight =
42369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top),
424e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            extraPortraitHeight / 2);
425e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.top -= expandHeight;
426e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.bottom += expandHeight;
427e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
428e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final int outWidth = (int) Math.round(cropRect.width() * cropScale);
429e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        final int outHeight = (int) Math.round(cropRect.height() * cropScale);
430e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
431e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Runnable onEndCrop = new Runnable() {
432e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            public void run() {
433e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (finishActivityWhenDone) {
434e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    setResult(Activity.RESULT_OK);
435e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    finish();
436e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
437e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
438e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        };
439e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        BitmapCropTask cropTask = new BitmapCropTask(this, uri,
44069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop);
441e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        if (onBitmapCroppedHandler != null) {
442e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
443e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
444e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        cropTask.execute();
445e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
446e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
447e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    public interface OnBitmapCroppedHandler {
448e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public void onBitmapCropped(byte[] imageBytes);
449e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
450e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
451e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka    protected static class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
452e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Uri mInUri = null;
453e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Context mContext;
454e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        String mInFilePath;
455e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        byte[] mInImageBytes;
456e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        int mInResId = 0;
457e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        RectF mCropBounds = null;
458e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        int mOutWidth, mOutHeight;
45969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        int mRotation;
460e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        String mOutputFormat = "jpg"; // for now
461e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        boolean mSetWallpaper;
462e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        boolean mSaveCroppedBitmap;
463e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Bitmap mCroppedBitmap;
464e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Runnable mOnEndRunnable;
465e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        Resources mResources;
466e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        OnBitmapCroppedHandler mOnBitmapCroppedHandler;
467e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        boolean mNoCrop;
468e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
469e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public BitmapCropTask(Context c, String filePath,
47069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
471e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
472e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mContext = c;
473e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mInFilePath = filePath;
47469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            init(cropBounds, rotation,
47569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
476e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
477e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
478e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public BitmapCropTask(byte[] imageBytes,
47969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
480e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
481e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mInImageBytes = imageBytes;
48269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            init(cropBounds, rotation,
48369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
484e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
485e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
486e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        public BitmapCropTask(Context c, Uri inUri,
48769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
488e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
489e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            mContext = c;
490e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mInUri = inUri;
49169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            init(cropBounds, rotation,
49269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
493e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
494e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
495e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka        public BitmapCropTask(Context c, Resources res, int inResId,
49669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
497e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
498e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            mContext = c;
499e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mInResId = inResId;
500e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mResources = res;
50169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            init(cropBounds, rotation,
50269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
503e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
504e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
50569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka        private void init(RectF cropBounds, int rotation, int outWidth, int outHeight,
506e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
507e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mCropBounds = cropBounds;
50869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka            mRotation = rotation;
509e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOutWidth = outWidth;
510e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOutHeight = outHeight;
511e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mSetWallpaper = setWallpaper;
512e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mSaveCroppedBitmap = saveCroppedBitmap;
513e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOnEndRunnable = onEndRunnable;
514e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
515e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
516e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public void setOnBitmapCropped(OnBitmapCroppedHandler handler) {
517e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOnBitmapCroppedHandler = handler;
518e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
519e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
520e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public void setNoCrop(boolean value) {
521e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mNoCrop = value;
522e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
523e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
524e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public void setOnEndRunnable(Runnable onEndRunnable) {
525e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mOnEndRunnable = onEndRunnable;
526e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
527e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
528e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Helper to setup input stream
5297b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka        private InputStream regenerateInputStream() {
530e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
531e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
532e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        "image byte array given");
533e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            } else {
534e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                try {
535e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    if (mInUri != null) {
5367b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return new BufferedInputStream(
537e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                                mContext.getContentResolver().openInputStream(mInUri));
538e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    } else if (mInFilePath != null) {
5397b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return mContext.openFileInput(mInFilePath);
540e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    } else if (mInImageBytes != null) {
5417b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes));
542e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    } else {
5437b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return new BufferedInputStream(mResources.openRawResource(mInResId));
544e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
545e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                } catch (FileNotFoundException e) {
546e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
547e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
548e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
5497b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            return null;
550e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
551e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
552e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public Point getImageBounds() {
5537b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            InputStream is = regenerateInputStream();
5547b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            if (is != null) {
555e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                BitmapFactory.Options options = new BitmapFactory.Options();
556e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                options.inJustDecodeBounds = true;
5577b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                BitmapFactory.decodeStream(is, null, options);
5587b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                Utils.closeSilently(is);
559e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (options.outWidth != 0 && options.outHeight != 0) {
560e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    return new Point(options.outWidth, options.outHeight);
561e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
562e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
563e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            return null;
564e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
565e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
566e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public void setCropBounds(RectF cropBounds) {
567e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            mCropBounds = cropBounds;
568e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
569e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
570e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public Bitmap getCroppedBitmap() {
571e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            return mCroppedBitmap;
572e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
573e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        public boolean cropBitmap() {
574e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            boolean failure = false;
575e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
576e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
577e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            WallpaperManager wallpaperManager = null;
578e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            if (mSetWallpaper) {
579e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
580e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            }
5817b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka
5827b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka
5837b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            if (mSetWallpaper && mNoCrop) {
584e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                try {
5857b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    InputStream is = regenerateInputStream();
5867b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    if (is != null) {
5877b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        wallpaperManager.setStream(is);
5887b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        Utils.closeSilently(is);
5897b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    }
590e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                } catch (IOException e) {
591e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "cannot write stream to wallpaper", e);
592e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    failure = true;
593e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
594e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                return !failure;
5957b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka            } else {
596e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                // Find crop bounds (scaled to original image size)
597e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                Rect roundedTrueCrop = new Rect();
59869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                Matrix rotateMatrix = new Matrix();
59969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                Matrix inverseRotateMatrix = new Matrix();
600e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka
601e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                Point bounds = getImageBounds();
60269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                if (mRotation > 0) {
60369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    rotateMatrix.setRotate(mRotation);
60469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    inverseRotateMatrix.setRotate(-mRotation);
60569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
60669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    mCropBounds.roundOut(roundedTrueCrop);
60769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    mCropBounds = new RectF(roundedTrueCrop);
60869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
6097b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    if (bounds == null) {
6107b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        Log.w(LOGTAG, "cannot get bounds for image");
6117b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        failure = true;
6127b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return false;
6137b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    }
61469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
61569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    float[] rotatedBounds = new float[] { bounds.x, bounds.y };
61669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    rotateMatrix.mapPoints(rotatedBounds);
61769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    rotatedBounds[0] = Math.abs(rotatedBounds[0]);
61869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    rotatedBounds[1] = Math.abs(rotatedBounds[1]);
61969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
62069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2);
62169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    inverseRotateMatrix.mapRect(mCropBounds);
62269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    mCropBounds.offset(bounds.x/2, bounds.y/2);
62369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
62469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                }
62569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
626e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                mCropBounds.roundOut(roundedTrueCrop);
627e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
628e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
629e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "crop has bad values for full size image");
630e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    failure = true;
631e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    return false;
632e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
633e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
634e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                // See how much we're reducing the size of the image
635b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth,
636b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                        roundedTrueCrop.height() / mOutHeight));
637e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                // Attempt to open a region decoder
638e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                BitmapRegionDecoder decoder = null;
639b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                InputStream is = null;
640e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                try {
641b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                    is = regenerateInputStream();
6427b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    if (is == null) {
6437b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
6447b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        failure = true;
6457b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        return false;
6467b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    }
6477b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    decoder = BitmapRegionDecoder.newInstance(is, false);
6487b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    Utils.closeSilently(is);
649e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                } catch (IOException e) {
650e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
651b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                } finally {
652b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                   Utils.closeSilently(is);
653b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                   is = null;
654e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
655e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
656e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                Bitmap crop = null;
657e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (decoder != null) {
658e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    // Do region decoding to get crop bitmap
659e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    BitmapFactory.Options options = new BitmapFactory.Options();
660e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    if (scaleDownSampleSize > 1) {
661e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        options.inSampleSize = scaleDownSampleSize;
662e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
663e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    crop = decoder.decodeRegion(roundedTrueCrop, options);
664e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    decoder.recycle();
665e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
666e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
667e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (crop == null) {
668e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    // BitmapRegionDecoder has failed, try to crop in-memory
669b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka                    is = regenerateInputStream();
670e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Bitmap fullSize = null;
6717b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                    if (is != null) {
672e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        BitmapFactory.Options options = new BitmapFactory.Options();
673e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        if (scaleDownSampleSize > 1) {
674e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            options.inSampleSize = scaleDownSampleSize;
675e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        }
6767b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        fullSize = BitmapFactory.decodeStream(is, null, options);
6777b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka                        Utils.closeSilently(is);
678e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
679e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    if (fullSize != null) {
680e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        // Find out the true sample size that was used by the decoder
681e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        scaleDownSampleSize = bounds.x / fullSize.getWidth();
68269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mCropBounds.left /= scaleDownSampleSize;
68369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mCropBounds.top /= scaleDownSampleSize;
68469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mCropBounds.bottom /= scaleDownSampleSize;
68569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mCropBounds.right /= scaleDownSampleSize;
68669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mCropBounds.roundOut(roundedTrueCrop);
68769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
688e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        // Adjust values to account for issues related to rounding
689e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        if (roundedTrueCrop.width() > fullSize.getWidth()) {
690e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            // Adjust the width
691e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth();
692e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        }
693e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        if (roundedTrueCrop.right > fullSize.getWidth()) {
694e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            // Adjust the left value
695e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            int adjustment = roundedTrueCrop.left -
696e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                                    Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width());
697e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.left -= adjustment;
698e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.right -= adjustment;
699e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        }
700e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        if (roundedTrueCrop.height() > fullSize.getHeight()) {
701e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            // Adjust the height
702e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight();
703e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        }
704e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        if (roundedTrueCrop.bottom > fullSize.getHeight()) {
705e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            // Adjust the top value
706e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            int adjustment = roundedTrueCrop.top -
707e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                                    Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height());
708e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.top -= adjustment;
709e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                            roundedTrueCrop.bottom -= adjustment;
710e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka                        }
711e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka
712e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
713e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                roundedTrueCrop.top, roundedTrueCrop.width(),
714e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                                roundedTrueCrop.height());
715e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
716e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
717e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
718e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (crop == null) {
719e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "cannot decode file: " + mInUri.toString());
720e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    failure = true;
721e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    return false;
722e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
72369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) {
72469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() };
72569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    rotateMatrix.mapPoints(dimsAfter);
72669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    dimsAfter[0] = Math.abs(dimsAfter[0]);
72769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    dimsAfter[1] = Math.abs(dimsAfter[1]);
72869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
72969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    if (!(mOutWidth > 0 && mOutHeight > 0)) {
73069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mOutWidth = Math.round(dimsAfter[0]);
73169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        mOutHeight = Math.round(dimsAfter[1]);
732e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
73369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
73469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]);
735e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight);
73669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
73769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    Matrix m = new Matrix();
73869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    if (mRotation == 0) {
73969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
74069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    } else {
74169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix m1 = new Matrix();
74269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f);
74369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix m2 = new Matrix();
74469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m2.setRotate(mRotation);
74569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix m3 = new Matrix();
74669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f);
74769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix m4 = new Matrix();
74869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
74969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
75069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix c1 = new Matrix();
75169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        c1.setConcat(m2, m1);
75269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        Matrix c2 = new Matrix();
75369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        c2.setConcat(m4, m3);
75469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                        m.setConcat(c2, c1);
75569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka                    }
75669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka
757e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
758e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            (int) returnRect.height(), Bitmap.Config.ARGB_8888);
759e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    if (tmp != null) {
760e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        Canvas c = new Canvas(tmp);
761e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                        Paint p = new Paint();
762e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                        p.setFilterBitmap(true);
763e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                        c.drawBitmap(crop, m, p);
764e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        crop = tmp;
765e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
766e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
767e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
768e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (mSaveCroppedBitmap) {
769e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    mCroppedBitmap = crop;
770e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
771e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
772e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                // Get output compression format
773e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                CompressFormat cf =
774e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        convertExtensionToCompressFormat(getFileExtension(mOutputFormat));
775e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
776e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                // Compress to byte array
777e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
778e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
779e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    // If we need to set to the wallpaper, set it
780e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                    if (mSetWallpaper && wallpaperManager != null) {
781e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                        try {
782e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                            byte[] outByteArray = tmpOut.toByteArray();
783e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                            wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
784e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                            if (mOnBitmapCroppedHandler != null) {
785e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                                mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
786e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                            }
787e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                        } catch (IOException e) {
788e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                            Log.w(LOGTAG, "cannot write stream to wallpaper", e);
789e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                            failure = true;
790e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                        }
791e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    }
792e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                } else {
793e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    Log.w(LOGTAG, "cannot compress bitmap");
794e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                    failure = true;
795e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                }
796e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            }
797e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            return !failure; // True if any of the operations failed
798e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
799e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
800e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        @Override
801e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        protected Boolean doInBackground(Void... params) {
802e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            return cropBitmap();
803e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
804e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
805e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        @Override
806e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        protected void onPostExecute(Boolean result) {
807e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            if (mOnEndRunnable != null) {
808e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka                mOnEndRunnable.run();
809e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka            }
810e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
811e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
812e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
813e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected static RectF getMaxCropRect(
814e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) {
815e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        RectF cropRect = new RectF();
816e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        // Get a crop rect that will fit this
817e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
818e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             cropRect.top = 0;
819e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             cropRect.bottom = inHeight;
820e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2;
821e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             cropRect.right = inWidth - cropRect.left;
822e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             if (leftAligned) {
823e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                 cropRect.right -= cropRect.left;
824e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                 cropRect.left = 0;
825e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka             }
826e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        } else {
827e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.left = 0;
828e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.right = inWidth;
829e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2;
830e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka            cropRect.bottom = inHeight - cropRect.top;
831e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        }
832e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return cropRect;
833e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
834e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
835e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected static CompressFormat convertExtensionToCompressFormat(String extension) {
836e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
837e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
838e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka
839e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    protected static String getFileExtension(String requestFormat) {
840e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        String outputFormat = (requestFormat == null)
841e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                ? "jpg"
842e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                : requestFormat;
843e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        outputFormat = outputFormat.toLowerCase();
844e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka        return (outputFormat.equals("png") || outputFormat.equals("gif"))
845e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                ? "png" // We don't support gif compression.
846e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka                : "jpg";
847e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka    }
848e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka}
849