19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2011 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage com.android.camera.panorama;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.ActivityBase;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.CameraDisabledException;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.CameraHardwareException;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.CameraHolder;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.Exif;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.MenuHelper;
256139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughesimport com.android.camera.ModePicker;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.OnClickAttr;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.R;
28becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chanimport com.android.camera.RotateDialogController;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.ShutterButton;
30becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chanimport com.android.camera.Storage;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.Thumbnail;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.Util;
336139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughesimport com.android.camera.ui.PopupManager;
344af85345e798c37ab026e93a1ae250d8bca4fc40Elliott Hughesimport com.android.camera.ui.Rotatable;
354af85345e798c37ab026e93a1ae250d8bca4fc40Elliott Hughesimport com.android.camera.ui.RotateImageView;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.ui.RotateLayout;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.camera.ui.SharePopup;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.ContentResolver;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.AssetFileDescriptor;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.pm.ActivityInfo;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Bitmap;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.BitmapFactory;
4699a64f4b1d2519b71558fdc62721686445865b60Jesse Wilsonimport android.graphics.ImageFormat;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.PixelFormat;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.SurfaceTexture;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.YuvImage;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.hardware.Camera.Parameters;
5284c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischerimport android.hardware.Camera.Size;
5384c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischerimport android.hardware.Sensor;
5484c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischerimport android.hardware.SensorManager;
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.media.ExifInterface;
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.media.MediaActionSound;
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.Uri;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Bundle;
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Handler;
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Message;
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.ParcelFileDescriptor;
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.PowerManager;
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.Gravity;
65d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughesimport android.view.Menu;
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MenuItem;
67d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughesimport android.view.OrientationEventListener;
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
69d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughesimport android.view.ViewGroup;
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.Window;
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.WindowManager;
72d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughesimport android.widget.ImageView;
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.widget.TextView;
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
75d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughesimport java.io.ByteArrayOutputStream;
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File;
77577ec9eb3a661de96a2cbe9ec918eda082fb7659Erikimport java.io.IOException;
78577ec9eb3a661de96a2cbe9ec918eda082fb7659Erikimport java.util.List;
79577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik
80577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik/**
81577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik * Activity to handle panorama capturing.
82577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik */
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class PanoramaActivity extends ActivityBase implements
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ModePicker.OnModeChangeListener, SurfaceTexture.OnFrameAvailableListener,
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ShutterButton.OnShutterButtonListener,
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MosaicRendererSurfaceViewRenderer.MosaicSurfaceCreateListener {
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int DEFAULT_SWEEP_ANGLE = 160;
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int DEFAULT_BLEND_MODE = Mosaic.BLENDTYPE_HORIZONTAL;
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final int DEFAULT_CAPTURE_PIXELS = 960 * 720;
90d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_LOW_RES_FINAL_MOSAIC_READY = 1;
92d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes    private static final int MSG_RESET_TO_PREVIEW_WITH_THUMBNAIL = 2;
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_GENERATE_FINAL_MOSAIC_ERROR = 3;
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_RESET_TO_PREVIEW = 4;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_CLEAR_SCREEN_DELAY = 5;
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9784c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer    private static final int SCREEN_DELAY = 2 * 60 * 1000;
9884c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer
99fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes    private static final String TAG = "PanoramaActivity";
10084c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer    private static final int PREVIEW_STOPPED = 0;
101d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes    private static final int PREVIEW_ACTIVE = 1;
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CAPTURE_STATE_VIEWFINDER = 0;
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CAPTURE_STATE_MOSAIC = 1;
1045669ce59aad19f17e1e89913e7483fb93b8aa0cfEric Fischer
1055669ce59aad19f17e1e89913e7483fb93b8aa0cfEric Fischer    // Speed is in unit of deg/sec
106fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes    private static final float PANNING_SPEED_THRESHOLD = 20f;
1075669ce59aad19f17e1e89913e7483fb93b8aa0cfEric Fischer
108d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes    // Ratio of nanosecond to second
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final float NS2S = 1.0f / 1000000000.0f;
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mPausing;
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private View mPanoLayout;
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private View mCaptureLayout;
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private View mReviewLayout;
11699a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    private ImageView mReview;
117fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes    private RotateLayout mCaptureIndicator;
1186139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughes    private PanoProgressBar mPanoProgressBar;
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private PanoProgressBar mSavingProgressBar;
120fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes    private View mFastIndicationBorder;
1216139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughes    private View mLeftIndicator;
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private View mRightIndicator;
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private MosaicRendererSurfaceView mMosaicView;
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private TextView mTooFastPrompt;
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private ShutterButton mShutterButton;
12699a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    private Object mWaitObject = new Object();
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mPreparePreviewString;
129fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes    private String mDialogTitle;
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mDialogOkString;
131d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes    private String mDialogPanoramaFailedString;
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mIndicatorColor;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mIndicatorColorFast;
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mCompassValueX;
13799a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    private float mCompassValueY;
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mCompassValueXStart;
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mCompassValueYStart;
140fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes    private float mCompassValueXStartBuffer;
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mCompassValueYStartBuffer;
142d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes    private int mCompassThreshold;
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mTraversedAngleX;
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mTraversedAngleY;
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private long mTimestamp;
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private RotateImageView mThumbnailView;
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Thumbnail mThumbnail;
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SharePopup mSharePopup;
1505bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer
1515bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer    private int mPreviewWidth;
152fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes    private int mPreviewHeight;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mCameraState;
154d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes    private int mCaptureState;
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SensorManager mSensorManager;
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Sensor mSensor;
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private PowerManager.WakeLock mPartialWakeLock;
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private ModePicker mModePicker;
1595bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer    private MosaicFrameProcessor mMosaicFrameProcessor;
1605bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer    private long mTimeTaken;
161fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes    private Handler mMainHandler;
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SurfaceTexture mSurfaceTexture;
163d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes    private boolean mThreadRunning;
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mCancelComputation;
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float[] mTransformMatrix;
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mHorizontalViewAngle;
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mVerticalViewAngle;
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Prefer FOCUS_MODE_INFINITY to FOCUS_MODE_CONTINUOUS_VIDEO because of
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // getting a better image quality by the former.
1715bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer    private String mTargetFocusMode = Parameters.FOCUS_MODE_INFINITY;
1725bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer
173fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes    private PanoOrientationEventListener mOrientationEventListener;
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // The value could be 0, 90, 180, 270 for the 4 different orientations measured in clockwise
175d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes    // respectively.
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mDeviceOrientation;
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mDeviceOrientationAtCapture;
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mCameraOrientation;
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mOrientationCompensation;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private RotateDialogController mRotateDialog;
1825bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer
1835bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer    private MediaActionSound mCameraSound;
1845bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer
1855bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer    private class MosaicJpeg {
1865bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer        public MosaicJpeg(byte[] data, int width, int height) {
1875bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer            this.data = data;
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.width = width;
189fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes            this.height = height;
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.isValid = true;
191d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes        }
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19308153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes        public MosaicJpeg() {
19408153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes            this.data = null;
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.width = 0;
19608153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes            this.height = 0;
19708153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes            this.isValid = false;
19808153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes        }
19908153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes
20008153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes        public final byte[] data;
20108153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes        public final int width;
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public final int height;
20308153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes        public final boolean isValid;
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class PanoOrientationEventListener extends OrientationEventListener {
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public PanoOrientationEventListener(Context context) {
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(context);
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
211fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes        @Override
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void onOrientationChanged(int orientation) {
213d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes            // We keep the last known orientation. So if the user first orient
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the camera then point the camera to floor or sky, we still have
2154af85345e798c37ab026e93a1ae250d8bca4fc40Elliott Hughes            // the correct orientation.
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (orientation == ORIENTATION_UNKNOWN) return;
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDeviceOrientation = Util.roundOrientation(orientation, mDeviceOrientation);
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // When the screen is unlocked, display rotation may change. Always
2195bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer            // calculate the up-to-date orientationCompensation.
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int orientationCompensation = mDeviceOrientation
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    + Util.getDisplayRotation(PanoramaActivity.this);
2225bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer            if (mOrientationCompensation != orientationCompensation) {
2235bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer                mOrientationCompensation = orientationCompensation;
2245bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer                setOrientationIndicator(mOrientationCompensation);
2255bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer            }
2265bd644caf73e76750feef1a82b8817d32f5367fcEric Fischer        }
227fbf37c79bdedbdd7b82ad7d5862dd82e3c068590Elliott Hughes    }
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
229d3c01012d8d329e541214e8207272ab5cc9ca004Elliott Hughes    private void setOrientationIndicator(int degree) {
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSharePopup != null) mSharePopup.setOrientation(degree);
23108153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes    }
23208153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
23408153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes    public boolean onCreateOptionsMenu(Menu menu) {
23508153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes        super.onCreateOptionsMenu(menu);
23608153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes
23708153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes        addBaseMenuItems(menu);
23808153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes        return true;
23908153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes    }
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
24108153ee01e6aa67061f628bc3fed66c6400dfd1cElliott Hughes    @Override
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onPrepareOptionsMenu(Menu menu) {
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.onPrepareOptionsMenu(menu);
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Only show the menu when idle.
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean idle = (mCaptureState == CAPTURE_STATE_VIEWFINDER && !mThreadRunning);
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < menu.size(); i++) {
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            MenuItem item = menu.getItem(i);
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            item.setVisible(idle);
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            item.setEnabled(idle);
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void addBaseMenuItems(Menu menu) {
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MenuHelper.addSwitchModeMenuItem(menu, ModePicker.MODE_CAMERA, new Runnable() {
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void run() {
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                switchToOtherMode(ModePicker.MODE_CAMERA);
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MenuHelper.addSwitchModeMenuItem(menu, ModePicker.MODE_VIDEO, new Runnable() {
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void run() {
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                switchToOtherMode(ModePicker.MODE_VIDEO);
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onCreate(Bundle icicle) {
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.onCreate(icicle);
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Window window = getWindow();
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Util.enterLightsOutMode(window);
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Util.initializeScreenBrightness(window, getContentResolver());
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        createContentView();
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
27999a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mSensor == null) {
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Panorama");
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOrientationEventListener = new PanoOrientationEventListener(this);
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTransformMatrix = new float[16];
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPreparePreviewString =
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                getResources().getString(R.string.pano_dialog_prepare_preview);
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDialogTitle = getResources().getString(R.string.pano_dialog_title);
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDialogOkString = getResources().getString(R.string.dialog_ok);
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDialogPanoramaFailedString =
29599a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                getResources().getString(R.string.pano_dialog_panorama_failed);
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCameraSound = new MediaActionSound();
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMainHandler = new Handler() {
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @Override
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void handleMessage(Message msg) {
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                switch (msg.what) {
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case MSG_LOW_RES_FINAL_MOSAIC_READY:
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        onBackgroundThreadFinished();
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        showFinalMosaic((Bitmap) msg.obj);
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        saveHighResMosaic();
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case MSG_RESET_TO_PREVIEW_WITH_THUMBNAIL:
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        onBackgroundThreadFinished();
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // If the activity is paused, save the thumbnail to the file here.
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // If not, it will be saved in onPause.
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mPausing) saveThumbnailToFile();
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // Set the thumbnail bitmap here because mThumbnailView must be accessed
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // from the UI thread.
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        updateThumbnailButton();
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // Share popup may still have the reference to the old thumbnail. Clear it.
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mSharePopup = null;
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        resetToPreview();
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case MSG_GENERATE_FINAL_MOSAIC_ERROR:
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        onBackgroundThreadFinished();
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mPausing) {
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            resetToPreview();
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } else {
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            mRotateDialog.showAlertDialog(
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    mDialogTitle, mDialogPanoramaFailedString,
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    mDialogOkString, new Runnable() {
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        @Override
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        public void run() {
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                            resetToPreview();
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        }},
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    null, null);
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case MSG_RESET_TO_PREVIEW:
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        onBackgroundThreadFinished();
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        resetToPreview();
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    case MSG_CLEAR_SCREEN_DELAY:
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        getWindow().clearFlags(WindowManager.LayoutParams.
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                FLAG_KEEP_SCREEN_ON);
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        break;
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                clearMosaicFrameProcessorIfNeeded();
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
3475acc6e521494f1ea21c793d0b56667dae107a1beElliott Hughes    }
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void setupCamera() throws CameraHardwareException, CameraDisabledException {
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        openCamera();
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Parameters parameters = mCameraDevice.getParameters();
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setupCaptureParams(parameters);
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        configureCamera(parameters);
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void releaseCamera() {
35799a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        if (mCameraDevice != null) {
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCameraDevice.setPreviewCallbackWithBuffer(null);
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            CameraHolder.instance().release();
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCameraDevice = null;
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCameraState = PREVIEW_STOPPED;
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void openCamera() throws CameraHardwareException, CameraDisabledException {
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int backCameraId = CameraHolder.instance().getBackCameraId();
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCameraDevice = Util.openCamera(this, backCameraId);
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCameraOrientation = Util.getCameraOrientation(backCameraId);
36999a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    }
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean findBestPreviewSize(List<Size> supportedSizes, boolean need4To3,
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            boolean needSmaller) {
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int pixelsDiff = DEFAULT_CAPTURE_PIXELS;
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean hasFound = false;
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Size size : supportedSizes) {
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int h = size.height;
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int w = size.width;
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // we only want 4:3 format.
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int d = DEFAULT_CAPTURE_PIXELS - h * w;
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (needSmaller && d < 0) { // no bigger preview than 960x720.
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (need4To3 && (h * 4 != w * 3)) {
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            d = Math.abs(d);
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (d < pixelsDiff) {
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPreviewWidth = w;
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPreviewHeight = h;
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                pixelsDiff = d;
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                hasFound = true;
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return hasFound;
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void setupCaptureParams(Parameters parameters) {
39899a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        List<Size> supportedSizes = parameters.getSupportedPreviewSizes();
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!findBestPreviewSize(supportedSizes, true, true)) {
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w(TAG, "No 4:3 ratio preview size supported.");
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!findBestPreviewSize(supportedSizes, false, true)) {
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.w(TAG, "Can't find a supported preview size smaller than 960x720.");
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                findBestPreviewSize(supportedSizes, false, false);
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Log.v(TAG, "preview h = " + mPreviewHeight + " , w = " + mPreviewWidth);
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        parameters.setPreviewSize(mPreviewWidth, mPreviewHeight);
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        List<int[]> frameRates = parameters.getSupportedPreviewFpsRange();
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int last = frameRates.size() - 1;
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int minFps = (frameRates.get(last))[Parameters.PREVIEW_FPS_MIN_INDEX];
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int maxFps = (frameRates.get(last))[Parameters.PREVIEW_FPS_MAX_INDEX];
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        parameters.setPreviewFpsRange(minFps, maxFps);
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Log.v(TAG, "preview fps: " + minFps + ", " + maxFps);
41599a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson
4165acc6e521494f1ea21c793d0b56667dae107a1beElliott Hughes        List<String> supportedFocusModes = parameters.getSupportedFocusModes();
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (supportedFocusModes.indexOf(mTargetFocusMode) >= 0) {
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            parameters.setFocusMode(mTargetFocusMode);
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Use the default focus mode and log a message
4215acc6e521494f1ea21c793d0b56667dae107a1beElliott Hughes            Log.w(TAG, "Cannot set the focus mode to " + mTargetFocusMode +
4225acc6e521494f1ea21c793d0b56667dae107a1beElliott Hughes                  " becuase the mode is not supported.");
4235acc6e521494f1ea21c793d0b56667dae107a1beElliott Hughes        }
4245acc6e521494f1ea21c793d0b56667dae107a1beElliott Hughes
4255acc6e521494f1ea21c793d0b56667dae107a1beElliott Hughes        parameters.setRecordingHint(false);
4265acc6e521494f1ea21c793d0b56667dae107a1beElliott Hughes
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mHorizontalViewAngle = parameters.getHorizontalViewAngle();
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVerticalViewAngle =  parameters.getVerticalViewAngle();
4295acc6e521494f1ea21c793d0b56667dae107a1beElliott Hughes    }
4305acc6e521494f1ea21c793d0b56667dae107a1beElliott Hughes
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getPreviewBufSize() {
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PixelFormat pixelInfo = new PixelFormat();
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        PixelFormat.getPixelFormatInfo(mCameraDevice.getParameters().getPreviewFormat(), pixelInfo);
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // TODO: remove this extra 32 byte after the driver bug is fixed.
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return (mPreviewWidth * mPreviewHeight * pixelInfo.bitsPerPixel / 8) + 32;
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
43799a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson
438c3ff72f5bd5896ad2584e0cc0582cbd2ad8191f2Elliott Hughes    private void configureCamera(Parameters parameters) {
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCameraDevice.setParameters(parameters);
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
441c3ff72f5bd5896ad2584e0cc0582cbd2ad8191f2Elliott Hughes
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean switchToOtherMode(int mode) {
443c3ff72f5bd5896ad2584e0cc0582cbd2ad8191f2Elliott Hughes        if (isFinishing()) {
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
446c3ff72f5bd5896ad2584e0cc0582cbd2ad8191f2Elliott Hughes        MenuHelper.gotoMode(mode, this);
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        finish();
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onModeChanged(int mode) {
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mode != ModePicker.MODE_PANORAMA) {
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return switchToOtherMode(mode);
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
45599a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson            return true;
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onMosaicSurfaceChanged() {
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        runOnUiThread(new Runnable() {
46225d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK            @Override
46325d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK            public void run() {
46425d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK                // If panorama is generating low res or high res mosaic, it
46525d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK                // means users exit and come back to panorama. Do not start the
46625d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK                // preview. Preview will be started after final mosaic is
46725d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK                // generated.
46825d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK                if (!mPausing && !mThreadRunning) {
46925d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK                    startCameraPreview();
47025d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK                }
47125d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK            }
47225d8b05fb77e8ee755aacd27c1c2d3b237537b7aJozef BABJAK        });
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onMosaicSurfaceCreated(final int textureID) {
47753f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey        runOnUiThread(new Runnable() {
47853f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey            @Override
47953f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey            public void run() {
48053f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey                if (mSurfaceTexture != null) {
48153f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey                    mSurfaceTexture.release();
48253f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey                }
48353f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey                mSurfaceTexture = new SurfaceTexture(textureID);
48453f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey                if (!mPausing) {
48553f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey                    mSurfaceTexture.setOnFrameAvailableListener(PanoramaActivity.this);
48653f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey                }
48753f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey            }
48853f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey        });
48953f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey    }
49053f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey
49153f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey    public void runViewFinder() {
49253f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey        mMosaicView.setWarping(false);
49353f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey        // Call preprocess to render it to low-res and high-res RGB textures.
49453f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey        mMosaicView.preprocess(mTransformMatrix);
49553f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey        mMosaicView.setReady();
49653f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey        mMosaicView.requestRender();
49753f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey    }
49853f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey
49953f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey    public void runMosaicCapture() {
50053f6e8a9b95a1fe5229295ade4989339d577de08Jeff Sharkey        mMosaicView.setWarping(true);
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Call preprocess to render it to low-res and high-res RGB textures.
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMosaicView.preprocess(mTransformMatrix);
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Lock the conditional variable to ensure the order of transferGPUtoCPU and
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // mMosaicFrame.processFrame().
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMosaicView.lockPreviewReadyFlag();
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Now, transfer the textures from GPU to CPU memory for processing
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMosaicView.transferGPUtoCPU();
50899a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        // Wait on the condition variable (will be opened when GPU->CPU transfer is done).
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMosaicView.waitUntilPreviewReady();
5102eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes        mMosaicFrameProcessor.processFrame();
5112eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes    }
5122eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes
51399a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    public synchronized void onFrameAvailable(SurfaceTexture surface) {
5142eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes        /* This function may be called by some random thread,
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * so let's be safe and use synchronize. No OpenGL calls can be done here.
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Frames might still be available after the activity is paused. If we call onFrameAvailable
5182eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes        // after pausing, the GL thread will crash.
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mPausing) return;
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Updating the texture should be done in the GL thread which mMosaicView is attached.
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMosaicView.queueEvent(new Runnable() {
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @Override
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void run() {
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Check if the activity is paused here can speed up the onPause() process.
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mPausing) return;
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSurfaceTexture.updateTexImage();
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSurfaceTexture.getTransformMatrix(mTransformMatrix);
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Update the transformation matrix for mosaic pre-process.
5322eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes        if (mCaptureState == CAPTURE_STATE_VIEWFINDER) {
5332eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes            runViewFinder();
5342eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes        } else {
5352eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes            runMosaicCapture();
5362eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes        }
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5382eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void hideDirectionIndicators() {
540315a7c0335fb54beced23b1703c10563ce02ee82Roozbeh Pournader        mLeftIndicator.setVisibility(View.GONE);
5412eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes        mRightIndicator.setVisibility(View.GONE);
5422eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes    }
5432eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes
5442eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes    private void showDirectionIndicators(int direction) {
5452eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes        switch (direction) {
5462eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes            case PanoProgressBar.DIRECTION_NONE:
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mLeftIndicator.setVisibility(View.VISIBLE);
5482eda18485cefc5dbc946d7eb32486a7bd67bf8afElliott Hughes                mRightIndicator.setVisibility(View.VISIBLE);
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case PanoProgressBar.DIRECTION_LEFT:
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mLeftIndicator.setVisibility(View.VISIBLE);
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mRightIndicator.setVisibility(View.GONE);
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case PanoProgressBar.DIRECTION_RIGHT:
55599a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                mLeftIndicator.setVisibility(View.GONE);
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mRightIndicator.setVisibility(View.VISIBLE);
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void startCapture() {
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Reset values so we can do this again.
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCancelComputation = false;
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTimeTaken = System.currentTimeMillis();
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCaptureState = CAPTURE_STATE_MOSAIC;
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mShutterButton.setBackgroundResource(R.drawable.btn_shutter_pan_recording);
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCaptureIndicator.setVisibility(View.VISIBLE);
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        showDirectionIndicators(PanoProgressBar.DIRECTION_NONE);
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mThumbnailView.setEnabled(false);
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCompassValueXStart = mCompassValueXStartBuffer;
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCompassValueYStart = mCompassValueYStartBuffer;
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTimestamp = 0;
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMosaicFrameProcessor.setProgressListener(new MosaicFrameProcessor.ProgressListener() {
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @Override
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void onProgress(boolean isFinished, float panningRateX, float panningRateY,
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    float progressX, float progressY) {
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float accumulatedHorizontalAngle = progressX * mHorizontalViewAngle;
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                float accumulatedVerticalAngle = progressY * mVerticalViewAngle;
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (isFinished
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        || (Math.abs(accumulatedHorizontalAngle) >= DEFAULT_SWEEP_ANGLE)
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        || (Math.abs(accumulatedVerticalAngle) >= DEFAULT_SWEEP_ANGLE)) {
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    stopCapture(false);
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    float panningRateXInDegree = panningRateX * mHorizontalViewAngle;
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    float panningRateYInDegree = panningRateY * mVerticalViewAngle;
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    updateProgress(panningRateXInDegree, panningRateYInDegree,
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            accumulatedHorizontalAngle, accumulatedVerticalAngle);
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mModePicker != null) mModePicker.setEnabled(false);
59599a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPanoProgressBar.reset();
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // TODO: calculate the indicator width according to different devices to reflect the actual
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // angle of view of the camera device.
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPanoProgressBar.setIndicatorWidth(20);
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPanoProgressBar.setMaxProgress(DEFAULT_SWEEP_ANGLE);
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPanoProgressBar.setVisibility(View.VISIBLE);
60299a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        mDeviceOrientationAtCapture = mDeviceOrientation;
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        keepScreenOn();
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void stopCapture(boolean aborted) {
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCaptureState = CAPTURE_STATE_VIEWFINDER;
608becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan        mCaptureIndicator.setVisibility(View.GONE);
609becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan        hideTooFastIndication();
610becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan        hideDirectionIndicators();
611becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan        mThumbnailView.setEnabled(true);
612becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan
613becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan        mMosaicFrameProcessor.setProgressListener(null);
614becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan        stopCameraPreview();
615becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan
616becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan        mSurfaceTexture.setOnFrameAvailableListener(null);
617577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik
618becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan        if (!aborted && !mThreadRunning) {
619becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan            mRotateDialog.showWaitingDialog(mPreparePreviewString);
620becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan            runBackgroundThread(new Thread() {
621becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan                @Override
622577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik                public void run() {
623becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan                    MosaicJpeg jpeg = generateFinalMosaic(false);
624becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan
625becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan                    if (jpeg != null && jpeg.isValid) {
626becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan                        Bitmap bitmap = null;
627becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan                        bitmap = BitmapFactory.decodeByteArray(jpeg.data, 0, jpeg.data.length);
628becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan                        mMainHandler.sendMessage(mMainHandler.obtainMessage(
629becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan                                MSG_LOW_RES_FINAL_MOSAIC_READY, bitmap));
630becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan                    } else {
631becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan                        mMainHandler.sendMessage(mMainHandler.obtainMessage(
632becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan                                MSG_RESET_TO_PREVIEW));
633577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik                    }
634577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik                }
635577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik            });
636577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        }
637577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        // do we have to wait for the thread to complete before enabling this?
638577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        if (mModePicker != null) mModePicker.setEnabled(true);
639577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        keepScreenOnAwhile();
640577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik    }
641577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik
642577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik    private void showTooFastIndication() {
643577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        mTooFastPrompt.setVisibility(View.VISIBLE);
644577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        mFastIndicationBorder.setVisibility(View.VISIBLE);
645577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        mPanoProgressBar.setIndicatorColor(mIndicatorColorFast);
646577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        mLeftIndicator.setEnabled(true);
647577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        mRightIndicator.setEnabled(true);
648577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik    }
649577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik
650577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik    private void hideTooFastIndication() {
651577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        mTooFastPrompt.setVisibility(View.GONE);
652577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        mFastIndicationBorder.setVisibility(View.GONE);
653577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        mPanoProgressBar.setIndicatorColor(mIndicatorColor);
654577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        mLeftIndicator.setEnabled(false);
655577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        mRightIndicator.setEnabled(false);
65699a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    }
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void updateProgress(float panningRateXInDegree, float panningRateYInDegree,
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float progressHorizontalAngle, float progressVerticalAngle) {
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMosaicView.setReady();
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMosaicView.requestRender();
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // TODO: Now we just display warning message by the panning speed.
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Since we only support horizontal panning, we should display a warning message
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // in UI when there're significant vertical movements.
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if ((Math.abs(panningRateXInDegree) > PANNING_SPEED_THRESHOLD)
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            || (Math.abs(panningRateYInDegree) > PANNING_SPEED_THRESHOLD)) {
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            showTooFastIndication();
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            hideTooFastIndication();
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int angleInMajorDirection =
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                (Math.abs(progressHorizontalAngle) > Math.abs(progressVerticalAngle))
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ? (int) progressHorizontalAngle
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                : (int) progressVerticalAngle;
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPanoProgressBar.setProgress((angleInMajorDirection));
67799a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    }
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void createContentView() {
68099a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        setContentView(R.layout.panorama);
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCaptureState = CAPTURE_STATE_VIEWFINDER;
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Resources appRes = getResources();
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCaptureLayout = (View) findViewById(R.id.pano_capture_layout);
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPanoProgressBar = (PanoProgressBar) findViewById(R.id.pano_pan_progress_bar);
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPanoProgressBar.setBackgroundColor(appRes.getColor(R.color.pano_progress_empty));
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPanoProgressBar.setDoneColor(appRes.getColor(R.color.pano_progress_done));
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mIndicatorColor = appRes.getColor(R.color.pano_progress_indication);
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mIndicatorColorFast = appRes.getColor(R.color.pano_progress_indication_fast);
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPanoProgressBar.setIndicatorColor(mIndicatorColor);
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPanoProgressBar.setOnDirectionChangeListener(
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                new PanoProgressBar.OnDirectionChangeListener () {
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    @Override
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    public void onDirectionChange(int direction) {
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mCaptureState == CAPTURE_STATE_MOSAIC) {
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            showDirectionIndicators(direction);
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
70199a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                });
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLeftIndicator = (ImageView) findViewById(R.id.pano_pan_left_indicator);
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mRightIndicator = (ImageView) findViewById(R.id.pano_pan_right_indicator);
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mLeftIndicator.setEnabled(false);
70699a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        mRightIndicator.setEnabled(false);
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTooFastPrompt = (TextView) findViewById(R.id.pano_capture_too_fast_textview);
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mFastIndicationBorder = (View) findViewById(R.id.pano_speed_indication_border);
70999a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSavingProgressBar = (PanoProgressBar) findViewById(R.id.pano_saving_progress_bar);
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSavingProgressBar.setIndicatorWidth(0);
7126139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughes        mSavingProgressBar.setMaxProgress(100);
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSavingProgressBar.setBackgroundColor(appRes.getColor(R.color.pano_progress_empty));
7146139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughes        mSavingProgressBar.setDoneColor(appRes.getColor(R.color.pano_progress_indication));
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCaptureIndicator = (RotateLayout) findViewById(R.id.pano_capture_indicator);
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mThumbnailView = (RotateImageView) findViewById(R.id.thumbnail);
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mThumbnailView.enableFilter(false);
72099a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mReviewLayout = (View) findViewById(R.id.pano_review_layout);
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mReview = (ImageView) findViewById(R.id.pano_reviewarea);
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMosaicView = (MosaicRendererSurfaceView) findViewById(R.id.pano_renderer);
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMosaicView.getRenderer().setMosaicSurfaceCreateListener(this);
72599a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mModePicker = (ModePicker) findViewById(R.id.mode_picker);
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mModePicker.setVisibility(View.VISIBLE);
72884c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer        mModePicker.setOnModeChangeListener(this);
72984c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer        mModePicker.setCurrentMode(ModePicker.MODE_PANORAMA);
73084c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer
73199a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        mShutterButton = (ShutterButton) findViewById(R.id.shutter_button);
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mShutterButton.setBackgroundResource(R.drawable.btn_shutter_pan);
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mShutterButton.setOnShutterButtonListener(this);
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
73599a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        mPanoLayout = findViewById(R.id.pano_layout);
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mRotateDialog = new RotateDialogController(this, R.layout.rotate_dialog);
73884c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer
73984c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer        if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
74084c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer            Rotatable[] rotateLayout = {
74199a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                    (Rotatable) findViewById(R.id.pano_pan_progress_bar_layout),
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    (Rotatable) findViewById(R.id.pano_capture_too_fast_textview_layout),
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    (Rotatable) findViewById(R.id.pano_review_saving_indication_layout),
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    (Rotatable) findViewById(R.id.pano_saving_progress_bar_layout),
74599a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                    (Rotatable) findViewById(R.id.pano_review_cancel_button_layout),
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    (Rotatable) findViewById(R.id.pano_rotate_reviewarea),
74784c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer                    (Rotatable) mRotateDialog,
74884c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer                    (Rotatable) mCaptureIndicator,
74984c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer                    (Rotatable) mModePicker,
75084c863d9af1270a2ccd2ccd2b8c1a753826dfe6dEric Fischer                    (Rotatable) mThumbnailView};
75199a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson            for (Rotatable r : rotateLayout) {
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                r.setOrientation(270);
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
75899a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    public void onShutterButtonClick() {
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // If mSurfaceTexture == null then GL setup is not finished yet.
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // No buttons can be pressed.
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mPausing || mThreadRunning || mSurfaceTexture == null) return;
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Since this button will stay on the screen when capturing, we need to check the state
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // right now.
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (mCaptureState) {
76599a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson            case CAPTURE_STATE_VIEWFINDER:
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mCameraSound.play(MediaActionSound.START_VIDEO_RECORDING);
767577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik                startCapture();
768577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik                break;
769577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik            case CAPTURE_STATE_MOSAIC:
77099a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                mCameraSound.play(MediaActionSound.STOP_VIDEO_RECORDING);
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                stopCapture(false);
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
77699a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    public void onShutterButtonFocus(boolean pressed) {
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void reportProgress() {
78099a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        mSavingProgressBar.reset();
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSavingProgressBar.setRightIncreasing(true);
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Thread t = new Thread() {
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @Override
78499a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson            public void run() {
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (mThreadRunning) {
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    final int progress = mMosaicFrameProcessor.reportProgress(
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            true, mCancelComputation);
78899a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    try {
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        synchronized (mWaitObject) {
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            mWaitObject.wait(50);
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
79399a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                    } catch (InterruptedException e) {
79499a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                        throw new RuntimeException("Panorama reportProgress failed", e);
79599a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                    }
79699a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                    // Update the progress bar
79799a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                    runOnUiThread(new Runnable() {
79899a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                        public void run() {
79999a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                            mSavingProgressBar.setProgress(progress);
80099a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                        }
80199a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                    });
80299a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                }
80399a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson            }
80499a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        };
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        t.start();
806becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan    }
807becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan
808becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan    private void initThumbnailButton() {
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Load the thumbnail from the disk.
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mThumbnail == null) {
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mThumbnail = Thumbnail.loadFrom(new File(getFilesDir(), Thumbnail.LAST_THUMB_FILENAME));
812577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        }
813577ec9eb3a661de96a2cbe9ec918eda082fb7659Erik        updateThumbnailButton();
81499a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    }
815becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void updateThumbnailButton() {
817becfc9de9e18ef216c2b537cd2829f1d2d55404fMichael Chan        // Update last image if URI is invalid and the storage is ready.
81812285f3d9f756226c13ef93054b87e0d487e1409Elliott Hughes        ContentResolver contentResolver = getContentResolver();
819f9818d33f94686ee9c80697ec79ef557a7b77176Elliott Hughes        if ((mThumbnail == null || !Util.isUriValid(mThumbnail.getUri(), contentResolver))) {
820f9818d33f94686ee9c80697ec79ef557a7b77176Elliott Hughes            mThumbnail = Thumbnail.getLastThumbnail(contentResolver);
82112285f3d9f756226c13ef93054b87e0d487e1409Elliott Hughes        }
822f9818d33f94686ee9c80697ec79ef557a7b77176Elliott Hughes        if (mThumbnail != null) {
82312285f3d9f756226c13ef93054b87e0d487e1409Elliott Hughes            mThumbnailView.setBitmap(mThumbnail.getBitmap());
82412285f3d9f756226c13ef93054b87e0d487e1409Elliott Hughes        } else {
82512285f3d9f756226c13ef93054b87e0d487e1409Elliott Hughes            mThumbnailView.setBitmap(null);
8266139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughes        }
8276139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughes    }
8286139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughes
8296139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughes    private void saveThumbnailToFile() {
8306139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughes        if (mThumbnail != null && !mThumbnail.fromFile()) {
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mThumbnail.saveTo(new File(getFilesDir(), Thumbnail.LAST_THUMB_FILENAME));
8326139e64cd6f86c4e3983de976cb0aa976b0d313cElliott Hughes        }
8339a2ada418b0a53cb10e009398116ff8cd914d71eSungmin Choi    }
8349a2ada418b0a53cb10e009398116ff8cd914d71eSungmin Choi
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void saveHighResMosaic() {
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        runBackgroundThread(new Thread() {
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            @Override
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void run() {
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPartialWakeLock.acquire();
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                MosaicJpeg jpeg;
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    jpeg = generateFinalMosaic(true);
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } finally {
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mPartialWakeLock.release();
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (jpeg == null) {  // Cancelled by user.
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mMainHandler.sendEmptyMessage(MSG_RESET_TO_PREVIEW);
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (!jpeg.isValid) {  // Error when generating mosaic.
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mMainHandler.sendEmptyMessage(MSG_GENERATE_FINAL_MOSAIC_ERROR);
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // The panorama image returned from the library is orientated based on the
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // natural orientation of a camera. We need to set an orientation for the image
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // in its EXIF header, so the image can be displayed correctly.
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // The orientation is calculated from compensating the
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // device orientation at capture and the camera orientation respective to
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // the natural orientation of the device.
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int orientation = (mDeviceOrientationAtCapture + mCameraOrientation) % 360;
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Uri uri = savePanorama(jpeg.data, jpeg.width, jpeg.height, orientation);
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (uri != null) {
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // Create a thumbnail whose width or height is equal or bigger
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // than the screen's width or height.
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int widthRatio = (int) Math.ceil((double) jpeg.width
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                / mPanoLayout.getWidth());
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int heightRatio = (int) Math.ceil((double) jpeg.height
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                / mPanoLayout.getHeight());
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int inSampleSize = Integer.highestOneBit(
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                Math.max(widthRatio, heightRatio));
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mThumbnail = Thumbnail.createThumbnail(
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                jpeg.data, orientation, inSampleSize, uri);
87199a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson                        Util.broadcastNewPicture(PanoramaActivity.this, uri);
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mMainHandler.sendMessage(
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            mMainHandler.obtainMessage(MSG_RESET_TO_PREVIEW_WITH_THUMBNAIL));
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        });
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        reportProgress();
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void runBackgroundThread(Thread thread) {
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mThreadRunning = true;
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        thread.start();
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
88699a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    private void onBackgroundThreadFinished() {
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mThreadRunning = false;
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mRotateDialog.dismissDialog();
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
89199a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson    private void cancelHighResComputation() {
89299a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        mCancelComputation = true;
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        synchronized (mWaitObject) {
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mWaitObject.notify();
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8986a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques    @OnClickAttr
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onCancelButtonClicked(View v) {
900ca3363775e03e0f90bdf111d3e5c54b3ab16df57Steve Pomeroy        if (mPausing || mSurfaceTexture == null) return;
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        cancelHighResComputation();
9026a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques    }
9036a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques
9046a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques    @OnClickAttr
9056a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques    public void onThumbnailClicked(View v) {
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mPausing || mThreadRunning || mSurfaceTexture == null) return;
9076a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        showSharePopup();
9086a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques    }
9096a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void showSharePopup() {
9116a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        if (mThumbnail == null) return;
9126a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        Uri uri = mThumbnail.getUri();
9136a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        if (mSharePopup == null || !uri.equals(mSharePopup.getUri())) {
9146a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques            // The orientation compensation is set to 0 here because we only support landscape.
9156a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques            mSharePopup = new SharePopup(this, uri, mThumbnail.getBitmap(),
9166a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques                    mOrientationCompensation,
9176a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques                    findViewById(R.id.frame_layout));
9186a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        }
9196a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        mSharePopup.showAtLocation(mThumbnailView, Gravity.NO_GRAVITY, 0, 0);
9206a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques    }
9216a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques
9226a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques    private void reset() {
9236a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        mCaptureState = CAPTURE_STATE_VIEWFINDER;
9246a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques
9256a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        mReviewLayout.setVisibility(View.GONE);
9266a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        mShutterButton.setBackgroundResource(R.drawable.btn_shutter_pan);
9276a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        mPanoProgressBar.setVisibility(View.GONE);
9286a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        mCaptureLayout.setVisibility(View.VISIBLE);
9296a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        mMosaicFrameProcessor.reset();
9306a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques
9316a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        mSurfaceTexture.setOnFrameAvailableListener(this);
9326a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques    }
9336a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques
9346a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques    private void resetToPreview() {
9356a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        reset();
9366a84af0949b341593b4b25ed9bb365bd60ff1254David Sobreira Marques        if (!mPausing) startCameraPreview();
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void showFinalMosaic(Bitmap bitmap) {
94099a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        if (bitmap != null) {
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mReview.setImageBitmap(bitmap);
94299a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson        }
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mCaptureLayout.setVisibility(View.GONE);
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mReviewLayout.setVisibility(View.VISIBLE);
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri savePanorama(byte[] jpegData, int width, int height, int orientation) {
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (jpegData != null) {
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String filename = PanoUtil.createName(
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    getResources().getString(R.string.pano_file_name_format), mTimeTaken);
95199a64f4b1d2519b71558fdc62721686445865b60Jesse Wilson            Uri uri = Storage.addImage(getContentResolver(), filename, mTimeTaken, null,
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    orientation, jpegData, width, height);
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (uri != null && orientation != 0) {
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String filepath = Storage.generateFilepath(filename);
955                try {
956                    // Save the orientation in EXIF.
957                    ExifInterface exif = new ExifInterface(filepath);
958                    exif.setAttribute(ExifInterface.TAG_ORIENTATION,
959                            getExifOrientation(orientation));
960                    exif.saveAttributes();
961                } catch (IOException e) {
962                    Log.e(TAG, "cannot set exif data: " + filepath);
963                }
964            }
965            return uri;
966        }
967        return null;
968    }
969
970    private static String getExifOrientation(int orientation) {
971        switch (orientation) {
972            case 0:
973                return String.valueOf(ExifInterface.ORIENTATION_NORMAL);
974            case 90:
975                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_90);
976            case 180:
977                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_180);
978            case 270:
979                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_270);
980            default:
981                throw new AssertionError("invalid: " + orientation);
982        }
983    }
984
985    private void clearMosaicFrameProcessorIfNeeded() {
986        if (!mPausing || mThreadRunning) return;
987        mMosaicFrameProcessor.clear();
988    }
989
990    private void initMosaicFrameProcessorIfNeeded() {
991        if (mPausing || mThreadRunning) return;
992        if (mMosaicFrameProcessor == null) {
993            // Start the activity for the first time.
994            mMosaicFrameProcessor = new MosaicFrameProcessor(
995                    mPreviewWidth, mPreviewHeight, getPreviewBufSize());
996        }
997        mMosaicFrameProcessor.initialize();
998    }
999
1000    @Override
1001    protected void onPause() {
1002        super.onPause();
1003
1004        mPausing = true;
1005        // Stop the capturing first.
1006        if (mCaptureState == CAPTURE_STATE_MOSAIC) {
1007            stopCapture(true);
1008            reset();
1009        }
1010        if (mSharePopup != null) mSharePopup.dismiss();
1011
1012        saveThumbnailToFile();
1013
1014        releaseCamera();
1015        mMosaicView.onPause();
1016        clearMosaicFrameProcessorIfNeeded();
1017        mOrientationEventListener.disable();
1018        resetScreenOn();
1019        mCameraSound.release();
1020        System.gc();
1021    }
1022
1023    @Override
1024    protected void doOnResume() {
1025        mPausing = false;
1026        mOrientationEventListener.enable();
1027
1028        mCaptureState = CAPTURE_STATE_VIEWFINDER;
1029        try {
1030            setupCamera();
1031
1032            // Camera must be initialized before MosaicFrameProcessor is initialized.
1033            // The preview size has to be decided by camera device.
1034            initMosaicFrameProcessorIfNeeded();
1035            mMosaicView.onResume();
1036
1037            initThumbnailButton();
1038            keepScreenOnAwhile();
1039        } catch (CameraHardwareException e) {
1040            Util.showErrorAndFinish(this, R.string.cannot_connect_camera);
1041            return;
1042        } catch (CameraDisabledException e) {
1043            Util.showErrorAndFinish(this, R.string.camera_disabled);
1044            return;
1045        }
1046        // Dismiss open menu if exists.
1047        PopupManager.getInstance(this).notifyShowPopup(null);
1048    }
1049
1050    /**
1051     * Generate the final mosaic image.
1052     *
1053     * @param highRes flag to indicate whether we want to get a high-res version.
1054     * @return a MosaicJpeg with its isValid flag set to true if successful; null if the generation
1055     *         process is cancelled; and a MosaicJpeg with its isValid flag set to false if there
1056     *         is an error in generating the final mosaic.
1057     */
1058    public MosaicJpeg generateFinalMosaic(boolean highRes) {
1059        int mosaicReturnCode = mMosaicFrameProcessor.createMosaic(highRes);
1060        if (mosaicReturnCode == Mosaic.MOSAIC_RET_CANCELLED) {
1061            return null;
1062        } else if (mosaicReturnCode == Mosaic.MOSAIC_RET_ERROR) {
1063            return new MosaicJpeg();
1064        }
1065
1066        byte[] imageData = mMosaicFrameProcessor.getFinalMosaicNV21();
1067        if (imageData == null) {
1068            Log.e(TAG, "getFinalMosaicNV21() returned null.");
1069            return new MosaicJpeg();
1070        }
1071
1072        int len = imageData.length - 8;
1073        int width = (imageData[len + 0] << 24) + ((imageData[len + 1] & 0xFF) << 16)
1074                + ((imageData[len + 2] & 0xFF) << 8) + (imageData[len + 3] & 0xFF);
1075        int height = (imageData[len + 4] << 24) + ((imageData[len + 5] & 0xFF) << 16)
1076                + ((imageData[len + 6] & 0xFF) << 8) + (imageData[len + 7] & 0xFF);
1077        Log.v(TAG, "ImLength = " + (len) + ", W = " + width + ", H = " + height);
1078
1079        if (width <= 0 || height <= 0) {
1080            // TODO: pop up a error meesage indicating that the final result is not generated.
1081            Log.e(TAG, "width|height <= 0!!, len = " + (len) + ", W = " + width + ", H = " +
1082                    height);
1083            return new MosaicJpeg();
1084        }
1085
1086        YuvImage yuvimage = new YuvImage(imageData, ImageFormat.NV21, width, height, null);
1087        ByteArrayOutputStream out = new ByteArrayOutputStream();
1088        yuvimage.compressToJpeg(new Rect(0, 0, width, height), 100, out);
1089        try {
1090            out.close();
1091        } catch (Exception e) {
1092            Log.e(TAG, "Exception in storing final mosaic", e);
1093            return new MosaicJpeg();
1094        }
1095        return new MosaicJpeg(out.toByteArray(), width, height);
1096    }
1097
1098    private void setPreviewTexture(SurfaceTexture surface) {
1099        try {
1100            mCameraDevice.setPreviewTexture(surface);
1101        } catch (Throwable ex) {
1102            releaseCamera();
1103            throw new RuntimeException("setPreviewTexture failed", ex);
1104        }
1105    }
1106
1107    private void startCameraPreview() {
1108        // If we're previewing already, stop the preview first (this will blank
1109        // the screen).
1110        if (mCameraState != PREVIEW_STOPPED) stopCameraPreview();
1111
1112        // Set the display orientation to 0, so that the underlying mosaic library
1113        // can always get undistorted mPreviewWidth x mPreviewHeight image data
1114        // from SurfaceTexture.
1115        mCameraDevice.setDisplayOrientation(0);
1116
1117        setPreviewTexture(mSurfaceTexture);
1118
1119        try {
1120            Log.v(TAG, "startPreview");
1121            mCameraDevice.startPreview();
1122        } catch (Throwable ex) {
1123            releaseCamera();
1124            throw new RuntimeException("startPreview failed", ex);
1125        }
1126        mCameraState = PREVIEW_ACTIVE;
1127    }
1128
1129    private void stopCameraPreview() {
1130        if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1131            Log.v(TAG, "stopPreview");
1132            mCameraDevice.stopPreview();
1133        }
1134        mCameraState = PREVIEW_STOPPED;
1135    }
1136
1137    @Override
1138    public void onUserInteraction() {
1139        super.onUserInteraction();
1140        if (mCaptureState != CAPTURE_STATE_MOSAIC) keepScreenOnAwhile();
1141    }
1142
1143    @Override
1144    public void onBackPressed() {
1145        // If panorama is generating low res or high res mosaic, ignore back
1146        // key. So the activity will not be destroyed.
1147        if (mThreadRunning) return;
1148        super.onBackPressed();
1149    }
1150
1151    private void resetScreenOn() {
1152        mMainHandler.removeMessages(MSG_CLEAR_SCREEN_DELAY);
1153        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1154    }
1155
1156    private void keepScreenOnAwhile() {
1157        mMainHandler.removeMessages(MSG_CLEAR_SCREEN_DELAY);
1158        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1159        mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1160    }
1161
1162    private void keepScreenOn() {
1163        mMainHandler.removeMessages(MSG_CLEAR_SCREEN_DELAY);
1164        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1165    }
1166}
1167