1a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling/*
2a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * Copyright (C) 2013 The Android Open Source Project
3a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling *
4a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * Licensed under the Apache License, Version 2.0 (the "License");
5a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * you may not use this file except in compliance with the License.
6a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * You may obtain a copy of the License at
7a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling *
8a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling *      http://www.apache.org/licenses/LICENSE-2.0
9a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling *
10a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * Unless required by applicable law or agreed to in writing, software
11a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * distributed under the License is distributed on an "AS IS" BASIS,
12a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * See the License for the specific language governing permissions and
14a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * limitations under the License.
15a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling */
16a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
17a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingpackage com.android.camera.tinyplanet;
18a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
19a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.app.DialogFragment;
20a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.app.ProgressDialog;
21a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.graphics.Bitmap;
22a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.graphics.Bitmap.CompressFormat;
23a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.graphics.BitmapFactory;
24a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.graphics.Canvas;
25a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.graphics.Point;
26a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.graphics.RectF;
27a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.net.Uri;
28a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.os.AsyncTask;
29a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.os.Bundle;
30a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.os.Handler;
31a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.view.Display;
32a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.view.LayoutInflater;
33a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.view.View;
34a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.view.View.OnClickListener;
35a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.view.ViewGroup;
36a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.view.Window;
37a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.widget.Button;
38a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.widget.SeekBar;
39a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport android.widget.SeekBar.OnSeekBarChangeListener;
40a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
41a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport com.adobe.xmp.XMPException;
42a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport com.adobe.xmp.XMPMeta;
43a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport com.android.camera.CameraActivity;
44280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberlingimport com.android.camera.app.CameraApp;
45fd4fc0e52ad69c2d486f5f46c2d465b4c4ba2849Angus Kongimport com.android.camera.app.MediaSaver;
460c8a1a4e65e758beb4f5d48b5110aada49667738Sascha Haeberlingimport com.android.camera.app.MediaSaver.OnMediaSavedListener;
472bca210e5fc8a77685775ffb403096167b017dceAngus Kongimport com.android.camera.debug.Log;
4821a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberlingimport com.android.camera.exif.ExifInterface;
49a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport com.android.camera.tinyplanet.TinyPlanetPreview.PreviewSizeListener;
50a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport com.android.camera.util.XmpUtil;
51a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport com.android.camera2.R;
52a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
53a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport java.io.ByteArrayOutputStream;
54a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport java.io.FileNotFoundException;
5521a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberlingimport java.io.IOException;
56a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport java.io.InputStream;
57a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport java.util.Date;
5821a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberlingimport java.util.TimeZone;
59a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport java.util.concurrent.locks.Lock;
60a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingimport java.util.concurrent.locks.ReentrantLock;
61a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
62a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling/**
63a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * An activity that provides an editor UI to create a TinyPlanet image from a
64a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling * 360 degree stereographically mapped panoramic image.
65a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling */
66a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberlingpublic class TinyPlanetFragment extends DialogFragment implements PreviewSizeListener {
67a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /** Argument to tell the fragment the URI of the original panoramic image. */
68a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public static final String ARGUMENT_URI = "uri";
69a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /** Argument to tell the fragment the title of the original panoramic image. */
70a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public static final String ARGUMENT_TITLE = "title";
71a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
72a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public static final String CROPPED_AREA_IMAGE_WIDTH_PIXELS =
73a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            "CroppedAreaImageWidthPixels";
74a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public static final String CROPPED_AREA_IMAGE_HEIGHT_PIXELS =
75a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            "CroppedAreaImageHeightPixels";
76a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public static final String CROPPED_AREA_FULL_PANO_WIDTH_PIXELS =
77a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            "FullPanoWidthPixels";
78a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public static final String CROPPED_AREA_FULL_PANO_HEIGHT_PIXELS =
79a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            "FullPanoHeightPixels";
80a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public static final String CROPPED_AREA_LEFT =
81a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            "CroppedAreaLeftPixels";
82a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public static final String CROPPED_AREA_TOP =
83a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            "CroppedAreaTopPixels";
84a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public static final String GOOGLE_PANO_NAMESPACE = "http://ns.google.com/photos/1.0/panorama/";
85a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
862bca210e5fc8a77685775ffb403096167b017dceAngus Kong    private static final Log.Tag TAG = new Log.Tag("TinyPlanetActivity");
87a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /** Delay between a value update and the renderer running. */
88a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private static final int RENDER_DELAY_MILLIS = 50;
89a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /** Filename prefix to prepend to the original name for the new file. */
90a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private static final String FILENAME_PREFIX = "TINYPLANET_";
91a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
92a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private Uri mSourceImageUri;
93a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private TinyPlanetPreview mPreview;
94a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private int mPreviewSizePx = 0;
95a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private float mCurrentZoom = 0.5f;
96a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private float mCurrentAngle = 0;
97a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private ProgressDialog mDialog;
98a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
99a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /**
100a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * Lock for the result preview bitmap. We can't change it while we're trying
101a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * to draw it.
102a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     */
1030c8a1a4e65e758beb4f5d48b5110aada49667738Sascha Haeberling    private final Lock mResultLock = new ReentrantLock();
104a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
105a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /** The title of the original panoramic image. */
106a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private String mOriginalTitle = "";
107a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
108a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /** The padded source bitmap. */
109a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private Bitmap mSourceBitmap;
110a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /** The resulting preview bitmap. */
111a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private Bitmap mResultBitmap;
112a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
113a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /** Used to delay-post a tiny planet rendering task. */
1140c8a1a4e65e758beb4f5d48b5110aada49667738Sascha Haeberling    private final Handler mHandler = new Handler();
115a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /** Whether rendering is in progress right now. */
116a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private Boolean mRendering = false;
117a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /**
118a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * Whether we should render one more time after the current rendering run is
119a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * done. This is needed when there was an update to the values during the
120a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * current rendering.
121a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     */
122a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private Boolean mRenderOneMore = false;
123a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
124a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /** Tiny planet data plus size. */
125a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private static final class TinyPlanetImage {
126a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        public final byte[] mJpegData;
127a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        public final int mSize;
128a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
129a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        public TinyPlanetImage(byte[] jpegData, int size) {
130a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            mJpegData = jpegData;
131a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            mSize = size;
132a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
133a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
134a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
135a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /**
136a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * Creates and executes a task to create a tiny planet with the current
137a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * values.
138a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     */
139a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private final Runnable mCreateTinyPlanetRunnable = new Runnable() {
140a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        @Override
141a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        public void run() {
142a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            synchronized (mRendering) {
143a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                if (mRendering) {
144a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    mRenderOneMore = true;
145a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    return;
146a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                }
147a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                mRendering = true;
148a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
149a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
150a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            (new AsyncTask<Void, Void, Void>() {
151a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                @Override
152a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                protected Void doInBackground(Void... params) {
153a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    mResultLock.lock();
154a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    try {
155a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        if (mSourceBitmap == null || mResultBitmap == null) {
156a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                            return null;
157a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        }
158a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        int width = mSourceBitmap.getWidth();
159a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        int height = mSourceBitmap.getHeight();
160a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        TinyPlanetNative.process(mSourceBitmap, width, height, mResultBitmap,
1610c8a1a4e65e758beb4f5d48b5110aada49667738Sascha Haeberling                                mPreviewSizePx, mCurrentZoom, mCurrentAngle);
162a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    } finally {
163a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        mResultLock.unlock();
164a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    }
165a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    return null;
166a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                }
167a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
1680c8a1a4e65e758beb4f5d48b5110aada49667738Sascha Haeberling                @Override
169a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                protected void onPostExecute(Void result) {
170a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    mPreview.setBitmap(mResultBitmap, mResultLock);
171a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    synchronized (mRendering) {
172a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        mRendering = false;
173a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        if (mRenderOneMore) {
174a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                            mRenderOneMore = false;
175a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                            scheduleUpdate();
176a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        }
177a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    }
178a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                }
1791e689c254960f0ced0d19d91067cc5ef703dec64Sascha Haeberling            }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
180a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
181a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    };
182a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
183a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    @Override
184a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public void onCreate(Bundle savedInstanceState) {
185a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        super.onCreate(savedInstanceState);
186a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        setStyle(DialogFragment.STYLE_NORMAL, R.style.Theme_Camera);
187a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
188a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
189a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    @Override
190a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public View onCreateView(LayoutInflater inflater, ViewGroup container,
191a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            Bundle savedInstanceState) {
192a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
193a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        getDialog().setCanceledOnTouchOutside(true);
194a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
195a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        View view = inflater.inflate(R.layout.tinyplanet_editor,
196a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                container, false);
197a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mPreview = (TinyPlanetPreview) view.findViewById(R.id.preview);
198a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mPreview.setPreviewSizeChangeListener(this);
199a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
200a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // Zoom slider setup.
201a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        SeekBar zoomSlider = (SeekBar) view.findViewById(R.id.zoomSlider);
202a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        zoomSlider.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
203a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            @Override
204a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            public void onStopTrackingTouch(SeekBar seekBar) {
205a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                // Do nothing.
206a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
207a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
208a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            @Override
209a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            public void onStartTrackingTouch(SeekBar seekBar) {
210a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                // Do nothing.
211a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
212a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
213a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            @Override
214a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
215a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                onZoomChange(progress);
216a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
217a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        });
218a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
219a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // Rotation slider setup.
220a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        SeekBar angleSlider = (SeekBar) view.findViewById(R.id.angleSlider);
221a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        angleSlider.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
222a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            @Override
223a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            public void onStopTrackingTouch(SeekBar seekBar) {
224a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                // Do nothing.
225a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
226a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
227a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            @Override
228a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            public void onStartTrackingTouch(SeekBar seekBar) {
229a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                // Do nothing.
230a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
231a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
232a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            @Override
233a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
234a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                onAngleChange(progress);
235a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
236a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        });
237a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
238a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        Button createButton = (Button) view.findViewById(R.id.creatTinyPlanetButton);
239a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        createButton.setOnClickListener(new OnClickListener() {
240a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            @Override
241a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            public void onClick(View v) {
242a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                onCreateTinyPlanet();
243a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
244a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        });
245a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
246a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mOriginalTitle = getArguments().getString(ARGUMENT_TITLE);
247a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mSourceImageUri = Uri.parse(getArguments().getString(ARGUMENT_URI));
248a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mSourceBitmap = createPaddedSourceImage(mSourceImageUri, true);
249a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
250a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        if (mSourceBitmap == null) {
251a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            Log.e(TAG, "Could not decode source image.");
252a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            dismiss();
253a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
254a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        return view;
255a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
256a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
257a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /**
258a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * From the given URI this method creates a 360/180 padded image that is
259a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * ready to be made a tiny planet.
260a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     */
261a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private Bitmap createPaddedSourceImage(Uri sourceImageUri, boolean previewSize) {
262a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        InputStream is = getInputStream(sourceImageUri);
263a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        if (is == null) {
264a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            Log.e(TAG, "Could not create input stream for image.");
265a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            dismiss();
266a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
267a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        Bitmap sourceBitmap = BitmapFactory.decodeStream(is);
268a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
269a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        is = getInputStream(sourceImageUri);
270a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        XMPMeta xmp = XmpUtil.extractXMPMeta(is);
271a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
272a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        if (xmp != null) {
273a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            int size = previewSize ? getDisplaySize() : sourceBitmap.getWidth();
274a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            sourceBitmap = createPaddedBitmap(sourceBitmap, xmp, size);
275a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
276a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        return sourceBitmap;
277a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
278a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
279a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /**
280a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * Starts an asynchronous task to create a tiny planet. Once done, will add
281a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * the new image to the filmstrip and dismisses the fragment.
282a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     */
283a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private void onCreateTinyPlanet() {
284a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // Make sure we stop rendering before we create the high-res tiny
285a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // planet.
286a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        synchronized (mRendering) {
287a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            mRenderOneMore = false;
288a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
289a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
290a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        final String savingTinyPlanet = getActivity().getResources().getString(
291a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                R.string.saving_tiny_planet);
292a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        (new AsyncTask<Void, Void, TinyPlanetImage>() {
293a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            @Override
294a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            protected void onPreExecute() {
295a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                mDialog = ProgressDialog.show(getActivity(), null, savingTinyPlanet, true, false);
296a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
297a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
298a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            @Override
299a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            protected TinyPlanetImage doInBackground(Void... params) {
3000c8a1a4e65e758beb4f5d48b5110aada49667738Sascha Haeberling                return createFinalTinyPlanet();
301a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
302a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
303a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            @Override
304a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            protected void onPostExecute(TinyPlanetImage image) {
305a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                // Once created, store the new file and add it to the filmstrip.
306a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                final CameraActivity activity = (CameraActivity) getActivity();
307280fd3edae1dc35ac1015bcc9532477169fad3aaSascha Haeberling                MediaSaver mediaSaver = ((CameraApp) activity.getApplication()).getMediaSaver();
308a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                OnMediaSavedListener doneListener =
309a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        new OnMediaSavedListener() {
310a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                            @Override
311a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                            public void onMediaSaved(Uri uri) {
312a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                                // Add the new photo to the filmstrip and exit
313a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                                // the fragment.
314a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                                activity.notifyNewMedia(uri);
315a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                                mDialog.dismiss();
316a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                                TinyPlanetFragment.this.dismiss();
317a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                            }
318a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        };
319a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                String tinyPlanetTitle = FILENAME_PREFIX + mOriginalTitle;
320fd4fc0e52ad69c2d486f5f46c2d465b4c4ba2849Angus Kong                mediaSaver.addImage(image.mJpegData, tinyPlanetTitle, (new Date()).getTime(),
321a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        null,
322a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        image.mSize, image.mSize, 0, null, doneListener, getActivity()
323a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                                .getContentResolver());
324a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
3251e689c254960f0ced0d19d91067cc5ef703dec64Sascha Haeberling        }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
326a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
327a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
328a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /**
329a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * Creates the high quality tiny planet file and adds it to the media
330a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * service. Don't call this on the UI thread.
331a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     */
3320c8a1a4e65e758beb4f5d48b5110aada49667738Sascha Haeberling    private TinyPlanetImage createFinalTinyPlanet() {
333a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // Free some memory we don't need anymore as we're going to dimiss the
334a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // fragment after the tiny planet creation.
335a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mResultLock.lock();
336a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        try {
337a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            mResultBitmap.recycle();
338a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            mResultBitmap = null;
339a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            mSourceBitmap.recycle();
340a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            mSourceBitmap = null;
341a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        } finally {
342a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            mResultLock.unlock();
343a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
344a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
345a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // Create a high-resolution padded image.
346a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        Bitmap sourceBitmap = createPaddedSourceImage(mSourceImageUri, false);
347a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        int width = sourceBitmap.getWidth();
348a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        int height = sourceBitmap.getHeight();
349a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
350a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        int outputSize = width / 2;
351a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        Bitmap resultBitmap = Bitmap.createBitmap(outputSize, outputSize,
352a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                Bitmap.Config.ARGB_8888);
353a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
354a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        TinyPlanetNative.process(sourceBitmap, width, height, resultBitmap,
355a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                outputSize, mCurrentZoom, mCurrentAngle);
356a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
357a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // Free the sourceImage memory as we don't need it and we need memory
358a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // for the JPEG bytes.
359a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        sourceBitmap.recycle();
360a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        sourceBitmap = null;
361a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
362a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        ByteArrayOutputStream jpeg = new ByteArrayOutputStream();
363a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        resultBitmap.compress(CompressFormat.JPEG, 100, jpeg);
36421a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling        return new TinyPlanetImage(addExif(jpeg.toByteArray()), outputSize);
36521a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling    }
36621a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling
36721a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling    /**
36821a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling     * Adds basic EXIF data to the tiny planet image so it an be rewritten
36921a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling     * later.
37021a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling     *
37121a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling     * @param jpeg the JPEG data of the tiny planet.
37221a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling     * @return The JPEG data containing basic EXIF.
37321a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling     */
37421a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling    private byte[] addExif(byte[] jpeg) {
37521a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling        ExifInterface exif = new ExifInterface();
37621a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling        exif.addDateTimeStampTag(ExifInterface.TAG_DATE_TIME, System.currentTimeMillis(),
37721a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling                TimeZone.getDefault());
37821a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling        ByteArrayOutputStream jpegOut = new ByteArrayOutputStream();
37921a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling        try {
38021a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling            exif.writeExif(jpeg, jpegOut);
38121a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling        } catch (IOException e) {
38221a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling            Log.e(TAG, "Could not write EXIF", e);
38321a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling        }
38421a2e9cb07caba660120b7f5eba6e053a6cb3ac3Sascha Haeberling        return jpegOut.toByteArray();
385a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
386a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
387a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private int getDisplaySize() {
388a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        Display display = getActivity().getWindowManager().getDefaultDisplay();
389a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        Point size = new Point();
390a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        display.getSize(size);
391a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        return Math.min(size.x, size.y);
392a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
393a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
394a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    @Override
395a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    public void onSizeChanged(int sizePx) {
396a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mPreviewSizePx = sizePx;
397a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mResultLock.lock();
398a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        try {
399a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            if (mResultBitmap == null || mResultBitmap.getWidth() != sizePx
400a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    || mResultBitmap.getHeight() != sizePx) {
401a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                if (mResultBitmap != null) {
402a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    mResultBitmap.recycle();
403a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                }
404a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                mResultBitmap = Bitmap.createBitmap(mPreviewSizePx, mPreviewSizePx,
405a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                        Bitmap.Config.ARGB_8888);
406a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
407a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        } finally {
408a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            mResultLock.unlock();
409a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
4100c8a1a4e65e758beb4f5d48b5110aada49667738Sascha Haeberling        scheduleUpdate();
411a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
412a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
413a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private void onZoomChange(int zoom) {
414a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // 1000 needs to be in sync with the max values declared in the layout
415a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        // xml file.
416a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mCurrentZoom = zoom / 1000f;
417a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        scheduleUpdate();
418a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
419a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
420a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private void onAngleChange(int angle) {
421a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mCurrentAngle = (float) Math.toRadians(angle);
422a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        scheduleUpdate();
423a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
424a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
425a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /**
426a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * Delay-post a new preview rendering run.
427a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     */
428a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private void scheduleUpdate() {
429a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mHandler.removeCallbacks(mCreateTinyPlanetRunnable);
430a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        mHandler.postDelayed(mCreateTinyPlanetRunnable, RENDER_DELAY_MILLIS);
431a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
432a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
433a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private InputStream getInputStream(Uri uri) {
434a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        try {
435a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            return getActivity().getContentResolver().openInputStream(uri);
436a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        } catch (FileNotFoundException e) {
437a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            Log.e(TAG, "Could not load source image.", e);
438a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
439a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        return null;
440a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
441a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
442a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    /**
443a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * To create a proper TinyPlanet, the input image must be 2:1 (360:180
444a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     * degrees). So if needed, we pad the source image with black.
445a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling     */
446a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private static Bitmap createPaddedBitmap(Bitmap bitmapIn, XMPMeta xmp, int intermediateWidth) {
447a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        try {
448a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            int croppedAreaWidth =
449a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    getInt(xmp, CROPPED_AREA_IMAGE_WIDTH_PIXELS);
450a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            int croppedAreaHeight =
451a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    getInt(xmp, CROPPED_AREA_IMAGE_HEIGHT_PIXELS);
452a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            int fullPanoWidth =
453a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    getInt(xmp, CROPPED_AREA_FULL_PANO_WIDTH_PIXELS);
454a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            int fullPanoHeight =
455a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    getInt(xmp, CROPPED_AREA_FULL_PANO_HEIGHT_PIXELS);
456a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            int left = getInt(xmp, CROPPED_AREA_LEFT);
457a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            int top = getInt(xmp, CROPPED_AREA_TOP);
458a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
459a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            if (fullPanoWidth == 0 || fullPanoHeight == 0) {
460a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                return bitmapIn;
461a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
462a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            // Make sure the intermediate image has the similar size to the
463a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            // input.
464a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            Bitmap paddedBitmap = null;
465a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            float scale = intermediateWidth / (float) fullPanoWidth;
466a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            while (paddedBitmap == null) {
467a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                try {
468a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    paddedBitmap = Bitmap.createBitmap(
469a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                            (int) (fullPanoWidth * scale), (int) (fullPanoHeight * scale),
470a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                            Bitmap.Config.ARGB_8888);
471a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                } catch (OutOfMemoryError e) {
472a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    System.gc();
473a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                    scale /= 2;
474a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling                }
475a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            }
476a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            Canvas paddedCanvas = new Canvas(paddedBitmap);
477a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
478a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            int right = left + croppedAreaWidth;
479a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            int bottom = top + croppedAreaHeight;
480a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            RectF destRect = new RectF(left * scale, top * scale, right * scale, bottom * scale);
481a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            paddedCanvas.drawBitmap(bitmapIn, null, destRect, null);
482a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            return paddedBitmap;
483a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        } catch (XMPException ex) {
484a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            // Do nothing, just use mSourceBitmap as is.
485a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
486a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        return bitmapIn;
487a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
488a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling
489a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    private static int getInt(XMPMeta xmp, String key) throws XMPException {
490a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        if (xmp.doesPropertyExist(GOOGLE_PANO_NAMESPACE, key)) {
491a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            return xmp.getPropertyInteger(GOOGLE_PANO_NAMESPACE, key);
492a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        } else {
493a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling            return 0;
494a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling        }
495a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling    }
496a5a08d7642a1fdf961b057cc90e76c4c93103c15Sascha Haeberling}
497