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