WallpaperCropActivity.java revision b24af13c8359963ecdea420a1b395149e7477f83
1f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka/*
2f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Copyright (C) 2013 The Android Open Source Project
3f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *
4f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * you may not use this file except in compliance with the License.
6f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * You may obtain a copy of the License at
7f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *
8f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *
10f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Unless required by applicable law or agreed to in writing, software
11f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * See the License for the specific language governing permissions and
14f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * limitations under the License.
15f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka */
16f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
17f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkapackage com.android.launcher3;
18f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
19f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.app.ActionBar;
20f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.app.Activity;
21f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.app.WallpaperManager;
22f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.content.Context;
23f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.content.Intent;
24f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.content.SharedPreferences;
25f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.content.res.Configuration;
26f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.content.res.Resources;
27f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Bitmap;
28f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Bitmap.CompressFormat;
29f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.BitmapFactory;
30f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.BitmapRegionDecoder;
31f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Canvas;
32f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Matrix;
33f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Paint;
34f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Point;
35f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Rect;
36f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.RectF;
37f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.net.Uri;
38f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.os.AsyncTask;
39f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.os.Bundle;
40f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.util.Log;
41f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.view.Display;
42f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.view.View;
43f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.view.WindowManager;
44f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.widget.Toast;
45f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
46f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport com.android.gallery3d.common.Utils;
47f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport com.android.gallery3d.exif.ExifInterface;
48f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport com.android.photos.BitmapRegionTileSource;
49f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport com.android.photos.BitmapRegionTileSource.BitmapSource;
50f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
51f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport java.io.BufferedInputStream;
52f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport java.io.ByteArrayInputStream;
53f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport java.io.ByteArrayOutputStream;
54f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport java.io.FileNotFoundException;
55f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport java.io.IOException;
56f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport java.io.InputStream;
57f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
58f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkapublic class WallpaperCropActivity extends Activity {
59f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private static final String LOGTAG = "Launcher3.CropActivity";
60f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
61f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width";
62f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height";
63f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private static final int DEFAULT_COMPRESS_QUALITY = 90;
64f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    /**
65f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     * The maximum bitmap size we allow to be returned through the intent.
66f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     * Intents have a maximum of 1MB in total size. However, the Bitmap seems to
67f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     * have some overhead to hit so that we go way below the limit here to make
68f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     * sure the intent stays below 1MB.We should consider just returning a byte
69f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     * array instead of a Bitmap instance to avoid overhead.
70f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka     */
71f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public static final int MAX_BMAP_IN_INTENT = 750000;
72f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private static final float WALLPAPER_SCREENS_SPAN = 2f;
73f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
74f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected static Point sDefaultWallpaperSize;
75f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
76f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected CropView mCropView;
77f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected Uri mUri;
7812d9d42afb5bd750ab52e38cd66f6081f4bd76bfSelim Cinek    protected View mSetWallpaperButton;
79f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
80f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
81f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected void onCreate(Bundle savedInstanceState) {
82f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        super.onCreate(savedInstanceState);
83f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        init();
84f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (!enableRotation()) {
85f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT);
86f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
87f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
88f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
89f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected void init() {
90f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        setContentView(R.layout.wallpaper_cropper);
91f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
92f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mCropView = (CropView) findViewById(R.id.cropView);
93f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
94f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Intent cropIntent = getIntent();
95f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final Uri imageUri = cropIntent.getData();
96f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
97f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (imageUri == null) {
98f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity");
99f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            finish();
100f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return;
101f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
102f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
103f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // Action bar
104f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // Show the custom action bar view
105f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final ActionBar actionBar = getActionBar();
106f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
107f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        actionBar.getCustomView().setOnClickListener(
108f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                new View.OnClickListener() {
109f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    @Override
110f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    public void onClick(View v) {
111f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        boolean finishActivityWhenDone = true;
112f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
113f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
114f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                });
115c192530433ab03c02462635e9ffbe3781b666e13Michael Jurka        mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
116f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
117f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // Load image in background
118f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final BitmapRegionTileSource.UriBitmapSource bitmapSource =
119f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
12012d9d42afb5bd750ab52e38cd66f6081f4bd76bfSelim Cinek        mSetWallpaperButton.setEnabled(false);
121f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Runnable onLoad = new Runnable() {
122f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            public void run() {
123f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
124f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Toast.makeText(WallpaperCropActivity.this,
125f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            getString(R.string.wallpaper_load_fail),
126f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            Toast.LENGTH_LONG).show();
127f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    finish();
128c192530433ab03c02462635e9ffbe3781b666e13Michael Jurka                } else {
12912d9d42afb5bd750ab52e38cd66f6081f4bd76bfSelim Cinek                    mSetWallpaperButton.setEnabled(true);
130f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
131f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
132f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        };
133f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        setCropViewTileSource(bitmapSource, true, false, onLoad);
134f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
135f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
136f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public void setCropViewTileSource(
137f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled,
138f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            final boolean moveToLeft, final Runnable postExecute) {
139f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final Context context = WallpaperCropActivity.this;
140f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final View progressView = findViewById(R.id.loading);
141f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
142f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            protected Void doInBackground(Void...args) {
143f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (!isCancelled()) {
144d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                    try {
145d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                        bitmapSource.loadInBackground();
146d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                    } catch (SecurityException securityException) {
147d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                        if (isDestroyed()) {
148d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                            // Temporarily granted permissions are revoked when the activity
149d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                            // finishes, potentially resulting in a SecurityException here.
150d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                            // Even though {@link #isDestroyed} might also return true in different
151d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                            // situations where the configuration changes, we are fine with
152d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                            // catching these cases here as well.
153d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                            cancel(false);
154d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                        } else {
155d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                            // otherwise it had a different cause and we throw it further
156d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                            throw securityException;
157d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                        }
158d444a550ec1c8992fbac0a036b293fc4759d25c1Selim Cinek                    }
159f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
160f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                return null;
161f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
162f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            protected void onPostExecute(Void arg) {
163f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (!isCancelled()) {
164f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    progressView.setVisibility(View.INVISIBLE);
165f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
166f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        mCropView.setTileSource(
167f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                                new BitmapRegionTileSource(context, bitmapSource), null);
168f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        mCropView.setTouchEnabled(touchEnabled);
169f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        if (moveToLeft) {
170f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            mCropView.moveToLeft();
171f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        }
172f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
173f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
174f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (postExecute != null) {
175f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    postExecute.run();
176f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
177f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
178f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        };
179f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // We don't want to show the spinner every time we load an image, because that would be
180f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // annoying; instead, only start showing the spinner if loading the image has taken
181f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // longer than 1 sec (ie 1000 ms)
182f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        progressView.postDelayed(new Runnable() {
183f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            public void run() {
184f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) {
185f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    progressView.setVisibility(View.VISIBLE);
186f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
187f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
188f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }, 1000);
189f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        loadBitmapTask.execute();
190f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
191f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
192f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public boolean enableRotation() {
193f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return getResources().getBoolean(R.bool.allow_rotation);
194f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
195f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
196f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public static String getSharedPreferencesKey() {
197f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return WallpaperCropActivity.class.getName();
198f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
199f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
200f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    // As a ratio of screen height, the total distance we want the parallax effect to span
201f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    // horizontally
202f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private static float wallpaperTravelToScreenWidthRatio(int width, int height) {
203f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        float aspectRatio = width / (float) height;
204f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
205f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
206f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
207f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // We will use these two data points to extrapolate how much the wallpaper parallax effect
208f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // to span (ie travel) at any aspect ratio:
209f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
210f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final float ASPECT_RATIO_LANDSCAPE = 16/10f;
211f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final float ASPECT_RATIO_PORTRAIT = 10/16f;
212f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
213f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
214f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
215f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // To find out the desired width at different aspect ratios, we use the following two
216f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // formulas, where the coefficient on x is the aspect ratio (width/height):
217f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        //   (16/10)x + y = 1.5
218f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        //   (10/16)x + y = 1.2
219f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // We solve for x and y and end up with a final formula:
220f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final float x =
221f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
222f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
223f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
224f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return x * aspectRatio + y;
225f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
226f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
227f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    static protected Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) {
228f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (sDefaultWallpaperSize == null) {
229f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Point minDims = new Point();
230f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Point maxDims = new Point();
231f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
232f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
233f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            int maxDim = Math.max(maxDims.x, maxDims.y);
234f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            int minDim = Math.max(minDims.x, minDims.y);
235f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
236f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
237f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Point realSize = new Point();
238f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                windowManager.getDefaultDisplay().getRealSize(realSize);
239f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                maxDim = Math.max(realSize.x, realSize.y);
240f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                minDim = Math.min(realSize.x, realSize.y);
241f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
242f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
243f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            // We need to ensure that there is enough extra space in the wallpaper
244f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            // for the intended parallax effects
245f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            final int defaultWidth, defaultHeight;
246f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (isScreenLarge(res)) {
247f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
248f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                defaultHeight = maxDim;
249f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            } else {
250f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
251f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                defaultHeight = maxDim;
252f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
253f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight);
254f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
255f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return sDefaultWallpaperSize;
256f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
257f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
258f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public static int getRotationFromExif(String path) {
259f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return getRotationFromExifHelper(path, null, 0, null, null);
260f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
261f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
262f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public static int getRotationFromExif(Context context, Uri uri) {
263f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return getRotationFromExifHelper(null, null, 0, context, uri);
264f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
265f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
266f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public static int getRotationFromExif(Resources res, int resId) {
267f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return getRotationFromExifHelper(null, res, resId, null, null);
268f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
269f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
270f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private static int getRotationFromExifHelper(
271f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            String path, Resources res, int resId, Context context, Uri uri) {
272f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        ExifInterface ei = new ExifInterface();
273f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        InputStream is = null;
274f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        BufferedInputStream bis = null;
275f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        try {
276f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (path != null) {
277f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                ei.readExif(path);
278f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            } else if (uri != null) {
279f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                is = context.getContentResolver().openInputStream(uri);
280f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                bis = new BufferedInputStream(is);
281f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                ei.readExif(bis);
282f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            } else {
283f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                is = res.openRawResource(resId);
284f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                bis = new BufferedInputStream(is);
285f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                ei.readExif(bis);
286f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
287f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
288f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (ori != null) {
289f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                return ExifInterface.getRotationForOrientationValue(ori.shortValue());
290f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
291f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        } catch (IOException e) {
292f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Log.w(LOGTAG, "Getting exif data failed", e);
2936a56c7c9a4a8d8c3f76202e4f9e23f95068b41b8Michael Jurka        } catch (NullPointerException e) {
2946a56c7c9a4a8d8c3f76202e4f9e23f95068b41b8Michael Jurka            // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid
2956a56c7c9a4a8d8c3f76202e4f9e23f95068b41b8Michael Jurka            Log.w(LOGTAG, "Getting exif data failed", e);
296f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        } finally {
297f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Utils.closeSilently(bis);
298f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Utils.closeSilently(is);
299f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
300f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return 0;
301f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
302f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
303f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) {
304f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int rotation = getRotationFromExif(filePath);
305f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        BitmapCropTask cropTask = new BitmapCropTask(
306f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                this, filePath, null, rotation, 0, 0, true, false, null);
307f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final Point bounds = cropTask.getImageBounds();
308f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Runnable onEndCrop = new Runnable() {
309f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            public void run() {
310f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                updateWallpaperDimensions(bounds.x, bounds.y);
311f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (finishActivityWhenDone) {
312f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    setResult(Activity.RESULT_OK);
313f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    finish();
314f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
315f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
316f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        };
317f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        cropTask.setOnEndRunnable(onEndCrop);
318f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        cropTask.setNoCrop(true);
319f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        cropTask.execute();
320f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
321f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
322f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected void cropImageAndSetWallpaper(
323f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Resources res, int resId, final boolean finishActivityWhenDone) {
324f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // crop this image and scale it down to the default wallpaper size for
325f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // this device
326f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int rotation = getRotationFromExif(res, resId);
327f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Point inSize = mCropView.getSourceDimensions();
328f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Point outSize = getDefaultWallpaperSize(getResources(),
329f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                getWindowManager());
330f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        RectF crop = getMaxCropRect(
331f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                inSize.x, inSize.y, outSize.x, outSize.y, false);
332f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Runnable onEndCrop = new Runnable() {
333f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            public void run() {
334f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                // Passing 0, 0 will cause launcher to revert to using the
335f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                // default wallpaper size
336f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                updateWallpaperDimensions(0, 0);
337f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (finishActivityWhenDone) {
338f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    setResult(Activity.RESULT_OK);
339f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    finish();
340f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
341f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
342f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        };
343f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        BitmapCropTask cropTask = new BitmapCropTask(this, res, resId,
344f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                crop, rotation, outSize.x, outSize.y, true, false, onEndCrop);
345f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        cropTask.execute();
346f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
347f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
348f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private static boolean isScreenLarge(Resources res) {
349f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Configuration config = res.getConfiguration();
350f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return config.smallestScreenWidthDp >= 720;
351f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
352f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
353f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected void cropImageAndSetWallpaper(Uri uri,
354f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
355942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka        boolean centerCrop = getResources().getBoolean(R.bool.center_crop);
356f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // Get the crop
357f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
358f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
359f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Display d = getWindowManager().getDefaultDisplay();
360f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
361f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Point displaySize = new Point();
362f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        d.getSize(displaySize);
363f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        boolean isPortrait = displaySize.x < displaySize.y;
364f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
365f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(),
366f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                getWindowManager());
367f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // Get the crop
368f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        RectF cropRect = mCropView.getCrop();
369b24af13c8359963ecdea420a1b395149e7477f83Selim Cinek
370b24af13c8359963ecdea420a1b395149e7477f83Selim Cinek        // due to rounding errors in the cropview renderer the edges can be slightly offset
371b24af13c8359963ecdea420a1b395149e7477f83Selim Cinek        // therefore we ensure that the boundaries are sanely defined
372b24af13c8359963ecdea420a1b395149e7477f83Selim Cinek        cropRect.left = Math.max(0, cropRect.left);
373b24af13c8359963ecdea420a1b395149e7477f83Selim Cinek        cropRect.right = Math.min(mCropView.getWidth(), cropRect.right);
374b24af13c8359963ecdea420a1b395149e7477f83Selim Cinek        cropRect.top = Math.max(0, cropRect.top);
375b24af13c8359963ecdea420a1b395149e7477f83Selim Cinek        cropRect.bottom = Math.min(mCropView.getHeight(), cropRect.bottom);
376b24af13c8359963ecdea420a1b395149e7477f83Selim Cinek
377f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int cropRotation = mCropView.getImageRotation();
378f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        float cropScale = mCropView.getWidth() / (float) cropRect.width();
379f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
380f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Point inSize = mCropView.getSourceDimensions();
381f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Matrix rotateMatrix = new Matrix();
382f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        rotateMatrix.setRotate(cropRotation);
383f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        float[] rotatedInSize = new float[] { inSize.x, inSize.y };
384f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        rotateMatrix.mapPoints(rotatedInSize);
385f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        rotatedInSize[0] = Math.abs(rotatedInSize[0]);
386f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        rotatedInSize[1] = Math.abs(rotatedInSize[1]);
387f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
388f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // ADJUST CROP WIDTH
389f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // Extend the crop all the way to the right, for parallax
390f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // (or all the way to the left, in RTL)
391942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka        float extraSpace;
392942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka        if (centerCrop) {
393942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka            extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left);
394942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka        } else {
395942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka            extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left;
396942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka        }
397f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // Cap the amount of extra width
398f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width();
399f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        extraSpace = Math.min(extraSpace, maxExtraSpace);
400f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
401942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka        if (centerCrop) {
402942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka            cropRect.left -= extraSpace / 2f;
403942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka            cropRect.right += extraSpace / 2f;
404f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        } else {
405942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka            if (ltr) {
406942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka                cropRect.right += extraSpace;
407942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka            } else {
408942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka                cropRect.left -= extraSpace;
409942b9eefdc1a918ccc259a54bc61cb7fbfb03bfdMichael Jurka            }
410f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
411f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
412f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // ADJUST CROP HEIGHT
413f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (isPortrait) {
414f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale;
415f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        } else { // LANDSCAPE
416f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            float extraPortraitHeight =
417f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    defaultWallpaperSize.y / cropScale - cropRect.height();
418f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            float expandHeight =
419f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top),
420f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            extraPortraitHeight / 2);
421f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            cropRect.top -= expandHeight;
422f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            cropRect.bottom += expandHeight;
423f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
424f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final int outWidth = (int) Math.round(cropRect.width() * cropScale);
425f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final int outHeight = (int) Math.round(cropRect.height() * cropScale);
426f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
427f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Runnable onEndCrop = new Runnable() {
428f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            public void run() {
429f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                updateWallpaperDimensions(outWidth, outHeight);
430f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (finishActivityWhenDone) {
431f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    setResult(Activity.RESULT_OK);
432f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    finish();
433f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
434f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
435f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        };
436f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        BitmapCropTask cropTask = new BitmapCropTask(this, uri,
437f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop);
438f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (onBitmapCroppedHandler != null) {
439f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
440f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
441f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        cropTask.execute();
442f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
443f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
444f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public interface OnBitmapCroppedHandler {
445f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public void onBitmapCropped(byte[] imageBytes);
446f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
447f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
448f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected static class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
449f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Uri mInUri = null;
450f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Context mContext;
451f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        String mInFilePath;
452f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        byte[] mInImageBytes;
453f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int mInResId = 0;
454f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        RectF mCropBounds = null;
455f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int mOutWidth, mOutHeight;
456f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int mRotation;
457f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        String mOutputFormat = "jpg"; // for now
458f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        boolean mSetWallpaper;
459f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        boolean mSaveCroppedBitmap;
460f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Bitmap mCroppedBitmap;
461f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Runnable mOnEndRunnable;
462f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Resources mResources;
463f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        OnBitmapCroppedHandler mOnBitmapCroppedHandler;
464f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        boolean mNoCrop;
465f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
466f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public BitmapCropTask(Context c, String filePath,
467f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
468f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
469f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mContext = c;
470f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mInFilePath = filePath;
471f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            init(cropBounds, rotation,
472f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
473f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
474f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
475f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public BitmapCropTask(byte[] imageBytes,
476f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
477f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
478f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mInImageBytes = imageBytes;
479f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            init(cropBounds, rotation,
480f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
481f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
482f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
483f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public BitmapCropTask(Context c, Uri inUri,
484f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
485f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
486f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mContext = c;
487f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mInUri = inUri;
488f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            init(cropBounds, rotation,
489f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
490f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
491f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
492f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public BitmapCropTask(Context c, Resources res, int inResId,
493f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                RectF cropBounds, int rotation, int outWidth, int outHeight,
494f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
495f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mContext = c;
496f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mInResId = inResId;
497f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mResources = res;
498f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            init(cropBounds, rotation,
499f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
500f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
501f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
502f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        private void init(RectF cropBounds, int rotation, int outWidth, int outHeight,
503f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
504f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mCropBounds = cropBounds;
505f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mRotation = rotation;
506f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mOutWidth = outWidth;
507f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mOutHeight = outHeight;
508f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mSetWallpaper = setWallpaper;
509f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mSaveCroppedBitmap = saveCroppedBitmap;
510f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mOnEndRunnable = onEndRunnable;
511f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
512f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
513f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public void setOnBitmapCropped(OnBitmapCroppedHandler handler) {
514f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mOnBitmapCroppedHandler = handler;
515f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
516f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
517f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public void setNoCrop(boolean value) {
518f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mNoCrop = value;
519f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
520f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
521f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public void setOnEndRunnable(Runnable onEndRunnable) {
522f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mOnEndRunnable = onEndRunnable;
523f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
524f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
525f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // Helper to setup input stream
526f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        private InputStream regenerateInputStream() {
527f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
528f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
529f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        "image byte array given");
530f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            } else {
531f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                try {
532f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (mInUri != null) {
533f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        return new BufferedInputStream(
534f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                                mContext.getContentResolver().openInputStream(mInUri));
535f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    } else if (mInFilePath != null) {
536f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        return mContext.openFileInput(mInFilePath);
537f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    } else if (mInImageBytes != null) {
538f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes));
539f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    } else {
540f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        return new BufferedInputStream(mResources.openRawResource(mInResId));
541f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
542f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                } catch (FileNotFoundException e) {
543f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
544f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
545f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
546f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return null;
547f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
548f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
549f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public Point getImageBounds() {
550f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            InputStream is = regenerateInputStream();
551f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (is != null) {
552f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                BitmapFactory.Options options = new BitmapFactory.Options();
553f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                options.inJustDecodeBounds = true;
554f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                BitmapFactory.decodeStream(is, null, options);
555f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Utils.closeSilently(is);
556f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (options.outWidth != 0 && options.outHeight != 0) {
557f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    return new Point(options.outWidth, options.outHeight);
558f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
559f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
560f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return null;
561f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
562f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
563f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public void setCropBounds(RectF cropBounds) {
564f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mCropBounds = cropBounds;
565f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
566f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
567f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public Bitmap getCroppedBitmap() {
568f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return mCroppedBitmap;
569f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
570f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public boolean cropBitmap() {
571f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            boolean failure = false;
572f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
573f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
574f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            WallpaperManager wallpaperManager = null;
575f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (mSetWallpaper) {
576f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
577f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
578f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
579f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
580f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (mSetWallpaper && mNoCrop) {
581f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                try {
582f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    InputStream is = regenerateInputStream();
583f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (is != null) {
584f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        wallpaperManager.setStream(is);
585f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Utils.closeSilently(is);
586f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
587f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                } catch (IOException e) {
588f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Log.w(LOGTAG, "cannot write stream to wallpaper", e);
589f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    failure = true;
590f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
591f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                return !failure;
592f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            } else {
593f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                // Find crop bounds (scaled to original image size)
594f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Rect roundedTrueCrop = new Rect();
595f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Matrix rotateMatrix = new Matrix();
596f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Matrix inverseRotateMatrix = new Matrix();
597f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
598f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Point bounds = getImageBounds();
599f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (mRotation > 0) {
600f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    rotateMatrix.setRotate(mRotation);
601f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    inverseRotateMatrix.setRotate(-mRotation);
602f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
603f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    mCropBounds.roundOut(roundedTrueCrop);
604f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    mCropBounds = new RectF(roundedTrueCrop);
605f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
606f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (bounds == null) {
607f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Log.w(LOGTAG, "cannot get bounds for image");
608f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        failure = true;
609f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        return false;
610f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
611f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
612f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    float[] rotatedBounds = new float[] { bounds.x, bounds.y };
613f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    rotateMatrix.mapPoints(rotatedBounds);
614f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    rotatedBounds[0] = Math.abs(rotatedBounds[0]);
615f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    rotatedBounds[1] = Math.abs(rotatedBounds[1]);
616f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
617f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2);
618f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    inverseRotateMatrix.mapRect(mCropBounds);
619f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    mCropBounds.offset(bounds.x/2, bounds.y/2);
620f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
621f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
622f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
623f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                mCropBounds.roundOut(roundedTrueCrop);
624f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
625f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
626f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Log.w(LOGTAG, "crop has bad values for full size image");
627f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    failure = true;
628f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    return false;
629f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
630f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
631f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                // See how much we're reducing the size of the image
632f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth,
633f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        roundedTrueCrop.height() / mOutHeight));
634f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                // Attempt to open a region decoder
635f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                BitmapRegionDecoder decoder = null;
636f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                InputStream is = null;
637f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                try {
638f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    is = regenerateInputStream();
639f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (is == null) {
640f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
641f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        failure = true;
642f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        return false;
643f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
644f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    decoder = BitmapRegionDecoder.newInstance(is, false);
645f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Utils.closeSilently(is);
646f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                } catch (IOException e) {
647f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
648f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                } finally {
649f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                   Utils.closeSilently(is);
650f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                   is = null;
651f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
652f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
653f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Bitmap crop = null;
654f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (decoder != null) {
655f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    // Do region decoding to get crop bitmap
656f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    BitmapFactory.Options options = new BitmapFactory.Options();
657f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (scaleDownSampleSize > 1) {
658f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        options.inSampleSize = scaleDownSampleSize;
659f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
660f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    crop = decoder.decodeRegion(roundedTrueCrop, options);
661f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    decoder.recycle();
662f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
663f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
664f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (crop == null) {
665f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    // BitmapRegionDecoder has failed, try to crop in-memory
666f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    is = regenerateInputStream();
667f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Bitmap fullSize = null;
668f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (is != null) {
669f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        BitmapFactory.Options options = new BitmapFactory.Options();
670f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        if (scaleDownSampleSize > 1) {
671f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            options.inSampleSize = scaleDownSampleSize;
672f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        }
673f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        fullSize = BitmapFactory.decodeStream(is, null, options);
674f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Utils.closeSilently(is);
675f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
676f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (fullSize != null) {
677f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        // Find out the true sample size that was used by the decoder
678f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        scaleDownSampleSize = bounds.x / fullSize.getWidth();
679f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        mCropBounds.left /= scaleDownSampleSize;
680f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        mCropBounds.top /= scaleDownSampleSize;
681f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        mCropBounds.bottom /= scaleDownSampleSize;
682f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        mCropBounds.right /= scaleDownSampleSize;
683f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        mCropBounds.roundOut(roundedTrueCrop);
684f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
685f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        // Adjust values to account for issues related to rounding
686f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        if (roundedTrueCrop.width() > fullSize.getWidth()) {
687f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            // Adjust the width
688f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth();
689f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        }
690f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        if (roundedTrueCrop.right > fullSize.getWidth()) {
691f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            // Adjust the left value
692f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            int adjustment = roundedTrueCrop.left -
693f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                                    Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width());
694f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            roundedTrueCrop.left -= adjustment;
695f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            roundedTrueCrop.right -= adjustment;
696f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        }
697f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        if (roundedTrueCrop.height() > fullSize.getHeight()) {
698f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            // Adjust the height
699f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight();
700f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        }
701f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        if (roundedTrueCrop.bottom > fullSize.getHeight()) {
702f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            // Adjust the top value
703f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            int adjustment = roundedTrueCrop.top -
704f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                                    Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height());
705f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            roundedTrueCrop.top -= adjustment;
706f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            roundedTrueCrop.bottom -= adjustment;
707f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        }
708f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
709f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
710f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                                roundedTrueCrop.top, roundedTrueCrop.width(),
711f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                                roundedTrueCrop.height());
712f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
713f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
714f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
715f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (crop == null) {
716f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Log.w(LOGTAG, "cannot decode file: " + mInUri.toString());
717f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    failure = true;
718f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    return false;
719f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
720f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) {
721f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() };
722f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    rotateMatrix.mapPoints(dimsAfter);
723f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    dimsAfter[0] = Math.abs(dimsAfter[0]);
724f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    dimsAfter[1] = Math.abs(dimsAfter[1]);
725f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
726f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (!(mOutWidth > 0 && mOutHeight > 0)) {
727f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        mOutWidth = Math.round(dimsAfter[0]);
728f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        mOutHeight = Math.round(dimsAfter[1]);
729f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
730f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
731f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]);
732f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight);
733f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
734f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Matrix m = new Matrix();
735f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (mRotation == 0) {
736f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
737f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    } else {
738f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Matrix m1 = new Matrix();
739f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f);
740f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Matrix m2 = new Matrix();
741f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        m2.setRotate(mRotation);
742f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Matrix m3 = new Matrix();
743f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f);
744f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Matrix m4 = new Matrix();
745f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
746f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
747f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Matrix c1 = new Matrix();
748f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        c1.setConcat(m2, m1);
749f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Matrix c2 = new Matrix();
750f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        c2.setConcat(m4, m3);
751f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        m.setConcat(c2, c1);
752f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
753f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
754f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
755f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            (int) returnRect.height(), Bitmap.Config.ARGB_8888);
756f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (tmp != null) {
757f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Canvas c = new Canvas(tmp);
758f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        Paint p = new Paint();
759f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        p.setFilterBitmap(true);
760f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        c.drawBitmap(crop, m, p);
761f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        crop = tmp;
762f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
763f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
764f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
765f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (mSaveCroppedBitmap) {
766f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    mCroppedBitmap = crop;
767f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
768f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
769f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                // Get output compression format
770f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                CompressFormat cf =
771f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        convertExtensionToCompressFormat(getFileExtension(mOutputFormat));
772f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
773f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                // Compress to byte array
774f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
775f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
776f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    // If we need to set to the wallpaper, set it
777f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    if (mSetWallpaper && wallpaperManager != null) {
778f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        try {
779f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            byte[] outByteArray = tmpOut.toByteArray();
780f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
781f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            if (mOnBitmapCroppedHandler != null) {
782f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                                mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
783f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            }
784f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        } catch (IOException e) {
785f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            Log.w(LOGTAG, "cannot write stream to wallpaper", e);
786f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                            failure = true;
787f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        }
788f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    }
789f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                } else {
790f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Log.w(LOGTAG, "cannot compress bitmap");
791f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    failure = true;
792f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
793f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
794f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return !failure; // True if any of the operations failed
795f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
796f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
797f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        @Override
798f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        protected Boolean doInBackground(Void... params) {
799f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return cropBitmap();
800f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
801f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
802f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        @Override
803f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        protected void onPostExecute(Boolean result) {
804f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (mOnEndRunnable != null) {
805f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                mOnEndRunnable.run();
806f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
807f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
808f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
809f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
810f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected void updateWallpaperDimensions(int width, int height) {
811f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        String spKey = getSharedPreferencesKey();
812f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
813f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        SharedPreferences.Editor editor = sp.edit();
814f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (width != 0 && height != 0) {
815f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            editor.putInt(WALLPAPER_WIDTH_KEY, width);
816f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            editor.putInt(WALLPAPER_HEIGHT_KEY, height);
817f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        } else {
818f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            editor.remove(WALLPAPER_WIDTH_KEY);
819f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            editor.remove(WALLPAPER_HEIGHT_KEY);
820f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
821f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        editor.commit();
822f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
823f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        suggestWallpaperDimension(getResources(),
824f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                sp, getWindowManager(), WallpaperManager.getInstance(this));
825f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
826f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
827f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    static public void suggestWallpaperDimension(Resources res,
828f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            final SharedPreferences sharedPrefs,
829f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            WindowManager windowManager,
830f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            final WallpaperManager wallpaperManager) {
831f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager);
832f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // If we have saved a wallpaper width/height, use that instead
833f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWallpaperSize.x);
834f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultWallpaperSize.y);
835f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (savedWidth != wallpaperManager.getDesiredMinimumWidth() ||
836f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                savedHeight != wallpaperManager.getDesiredMinimumHeight()) {
837f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight);
838f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
839f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
840f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
841f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected static RectF getMaxCropRect(
842f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) {
843f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        RectF cropRect = new RectF();
844f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        // Get a crop rect that will fit this
845f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
846f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka             cropRect.top = 0;
847f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka             cropRect.bottom = inHeight;
848f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka             cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2;
849f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka             cropRect.right = inWidth - cropRect.left;
850f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka             if (leftAligned) {
851f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                 cropRect.right -= cropRect.left;
852f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                 cropRect.left = 0;
853f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka             }
854f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        } else {
855f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            cropRect.left = 0;
856f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            cropRect.right = inWidth;
857f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2;
858f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            cropRect.bottom = inHeight - cropRect.top;
859f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
860f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return cropRect;
861f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
862f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
863f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected static CompressFormat convertExtensionToCompressFormat(String extension) {
864f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
865f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
866f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
867f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    protected static String getFileExtension(String requestFormat) {
868f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        String outputFormat = (requestFormat == null)
869f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                ? "jpg"
870f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                : requestFormat;
871f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        outputFormat = outputFormat.toLowerCase();
872f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return (outputFormat.equals("png") || outputFormat.equals("gif"))
873f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                ? "png" // We don't support gif compression.
874f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                : "jpg";
875f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
876f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka}
877