PanoramaModule.java revision ce1d8396924bf4ca9be076cfdf9594c4d33ebd9b
1ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb/*
2ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb * Copyright (C) 2011 The Android Open Source Project
3ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb *
4ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb * Licensed under the Apache License, Version 2.0 (the "License");
5ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb * you may not use this file except in compliance with the License.
6ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb * You may obtain a copy of the License at
7ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb *
8ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb *      http://www.apache.org/licenses/LICENSE-2.0
9ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb *
10ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb * Unless required by applicable law or agreed to in writing, software
11ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb * distributed under the License is distributed on an "AS IS" BASIS,
12ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb * See the License for the specific language governing permissions and
14ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb * limitations under the License.
15ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb */
16ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
17ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbpackage com.android.camera;
18ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
19ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.annotation.TargetApi;
20ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.content.ContentResolver;
21ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.content.Context;
22ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.content.Intent;
23ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.content.res.Configuration;
24ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.content.res.Resources;
25ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.graphics.Bitmap;
26ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.graphics.BitmapFactory;
27ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.graphics.ImageFormat;
28ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.graphics.PixelFormat;
29ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.graphics.Rect;
30ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.graphics.SurfaceTexture;
31ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.graphics.YuvImage;
32ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.graphics.drawable.Drawable;
33ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.hardware.Camera.Parameters;
34ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.hardware.Camera.Size;
35ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.media.ExifInterface;
36ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.net.Uri;
37ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.os.AsyncTask;
38ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.os.Handler;
39ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.os.Message;
40ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.os.PowerManager;
41ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.util.Log;
42ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.view.KeyEvent;
43ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.view.LayoutInflater;
44ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.view.MotionEvent;
45ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.view.OrientationEventListener;
46ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.view.View;
47ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.view.View.OnClickListener;
48ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.view.ViewGroup;
49ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.view.WindowManager;
50ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.widget.ImageView;
51ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.widget.LinearLayout;
52ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport android.widget.TextView;
53ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
54ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport com.android.camera.CameraManager.CameraProxy;
55ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport com.android.camera.ui.LayoutChangeNotifier;
56ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport com.android.camera.ui.LayoutNotifyView;
57ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport com.android.camera.ui.PopupManager;
58ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport com.android.camera.ui.Rotatable;
59ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport com.android.camera.ui.RotateLayout;
60ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport com.android.gallery3d.common.ApiHelper;
61ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport com.android.gallery3d.ui.GLRootView;
62ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
63ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport java.io.ByteArrayOutputStream;
64ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport java.io.IOException;
65ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport java.text.DateFormat;
66ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport java.text.SimpleDateFormat;
67ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport java.util.List;
68ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbimport java.util.TimeZone;
69ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
70ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb/**
71ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb * Activity to handle panorama capturing.
72ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb */
73ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) // uses SurfaceTexture
74ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolbpublic class PanoramaModule implements CameraModule,
75ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        SurfaceTexture.OnFrameAvailableListener,
76ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        ShutterButton.OnShutterButtonListener,
77ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        LayoutChangeNotifier.Listener {
78ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
79ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public static final int DEFAULT_SWEEP_ANGLE = 160;
80ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public static final int DEFAULT_BLEND_MODE = Mosaic.BLENDTYPE_HORIZONTAL;
81ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public static final int DEFAULT_CAPTURE_PIXELS = 960 * 720;
82ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
83ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final int MSG_LOW_RES_FINAL_MOSAIC_READY = 1;
84ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final int MSG_RESET_TO_PREVIEW_WITH_THUMBNAIL = 2;
85ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final int MSG_GENERATE_FINAL_MOSAIC_ERROR = 3;
86ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final int MSG_RESET_TO_PREVIEW = 4;
87ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final int MSG_CLEAR_SCREEN_DELAY = 5;
88ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
89ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final int SCREEN_DELAY = 2 * 60 * 1000;
90ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
91ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final String TAG = "CAM PanoModule";
92ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final int PREVIEW_STOPPED = 0;
93ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final int PREVIEW_ACTIVE = 1;
94ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final int CAPTURE_STATE_VIEWFINDER = 0;
95ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final int CAPTURE_STATE_MOSAIC = 1;
96ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
97ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final String GPS_DATE_FORMAT_STR = "yyyy:MM:dd";
98ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final String GPS_TIME_FORMAT_STR = "kk/1,mm/1,ss/1";
99ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final String DATETIME_FORMAT_STR = "yyyy:MM:dd kk:mm:ss";
100ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
101ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    // Speed is in unit of deg/sec
102ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static final float PANNING_SPEED_THRESHOLD = 25f;
103ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
104ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private ContentResolver mContentResolver;
105ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
106ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private GLRootView mGLRootView;
107ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private ViewGroup mPanoLayout;
108ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private LinearLayout mCaptureLayout;
109ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private View mReviewLayout;
110ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private ImageView mReview;
111ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private RotateLayout mCaptureIndicator;
112ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private PanoProgressBar mPanoProgressBar;
113ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private PanoProgressBar mSavingProgressBar;
114ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private LayoutNotifyView mPreviewArea;
115ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private View mLeftIndicator;
116ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private View mRightIndicator;
117ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private MosaicPreviewRenderer mMosaicPreviewRenderer;
118ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private TextView mTooFastPrompt;
119ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private ShutterButton mShutterButton;
120ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private Object mWaitObject = new Object();
121ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
122ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private DateFormat mGPSDateStampFormat;
123ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private DateFormat mGPSTimeStampFormat;
124ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private DateFormat mDateTimeStampFormat;
125ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
126ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private String mPreparePreviewString;
127ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private String mDialogTitle;
128ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private String mDialogOkString;
129ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private String mDialogPanoramaFailedString;
130ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private String mDialogWaitingPreviousString;
131ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
132ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private int mIndicatorColor;
133ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private int mIndicatorColorFast;
134ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
135ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private boolean mUsingFrontCamera;
136ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private int mPreviewWidth;
137ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private int mPreviewHeight;
138ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private int mCameraState;
139ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private int mCaptureState;
140ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private PowerManager.WakeLock mPartialWakeLock;
141ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private MosaicFrameProcessor mMosaicFrameProcessor;
142ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private boolean mMosaicFrameProcessorInitialized;
143ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private AsyncTask <Void, Void, Void> mWaitProcessorTask;
144ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private long mTimeTaken;
145ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private Handler mMainHandler;
146ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private SurfaceTexture mCameraTexture;
147ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private boolean mThreadRunning;
148ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private boolean mCancelComputation;
149ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private float mHorizontalViewAngle;
150ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private float mVerticalViewAngle;
151ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
152ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    // Prefer FOCUS_MODE_INFINITY to FOCUS_MODE_CONTINUOUS_VIDEO because of
153ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    // getting a better image quality by the former.
154ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private String mTargetFocusMode = Parameters.FOCUS_MODE_INFINITY;
155ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
156ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private PanoOrientationEventListener mOrientationEventListener;
157ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    // The value could be 0, 90, 180, 270 for the 4 different orientations measured in clockwise
158ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    // respectively.
159ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private int mDeviceOrientation;
160ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private int mDeviceOrientationAtCapture;
161ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private int mCameraOrientation;
162ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private int mOrientationCompensation;
163ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
164ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private RotateDialogController mRotateDialog;
165ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
166ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private SoundClips.Player mSoundPlayer;
167ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
168ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private Runnable mOnFrameAvailableRunnable;
169ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
170ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private CameraActivity mActivity;
171ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private View mRootView;
172ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private CameraProxy mCameraDevice;
173ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private boolean mOpenCameraFail;
174ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private boolean mCameraDisabled;
175ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private boolean mPaused;
176ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
177ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private class SetupCameraThread extends Thread {
178ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        @Override
179ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        public void run() {
180ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            try {
181ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                setupCamera();
182ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            } catch (CameraHardwareException e) {
183ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mOpenCameraFail = true;
184ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            } catch (CameraDisabledException e) {
185ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mCameraDisabled = true;
186ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
187ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
188ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
189ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
190ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private class MosaicJpeg {
191ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        public MosaicJpeg(byte[] data, int width, int height) {
192ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            this.data = data;
193ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            this.width = width;
194ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            this.height = height;
195ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            this.isValid = true;
196ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
197ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
198ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        public MosaicJpeg() {
199ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            this.data = null;
200ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            this.width = 0;
201ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            this.height = 0;
202ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            this.isValid = false;
203ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
204ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
205ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        public final byte[] data;
206ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        public final int width;
207ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        public final int height;
208ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        public final boolean isValid;
209ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
210ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
211ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private class PanoOrientationEventListener extends OrientationEventListener {
212ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        public PanoOrientationEventListener(Context context) {
213ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            super(context);
214ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
215ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
216ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        @Override
217ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        public void onOrientationChanged(int orientation) {
218ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // We keep the last known orientation. So if the user first orient
219ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // the camera then point the camera to floor or sky, we still have
220ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // the correct orientation.
221ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            if (orientation == ORIENTATION_UNKNOWN) return;
222ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mDeviceOrientation = Util.roundOrientation(orientation, mDeviceOrientation);
223ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // When the screen is unlocked, display rotation may change. Always
224ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // calculate the up-to-date orientationCompensation.
225ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            int orientationCompensation = mDeviceOrientation
226ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    + Util.getDisplayRotation(mActivity) % 360;
227ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            if (mOrientationCompensation != orientationCompensation) {
228ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mOrientationCompensation = orientationCompensation;
229ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
230ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
231ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
232ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
233ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
234ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void init(CameraActivity activity, View parent, boolean reuseScreenNail) {
235ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity = activity;
236ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mRootView = (ViewGroup) parent;
237ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
238ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        createContentView();
239ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
240ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mContentResolver = mActivity.getContentResolver();
241ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (reuseScreenNail) {
242ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mActivity.reuseCameraScreenNail(true);
243ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        } else {
244ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mActivity.createCameraScreenNail(true);
245ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
246ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
247ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // This runs in UI thread.
248ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mOnFrameAvailableRunnable = new Runnable() {
249ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            @Override
250ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            public void run() {
251ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                // Frames might still be available after the activity is paused.
252ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                // If we call onFrameAvailable after pausing, the GL thread will crash.
253ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                if (mPaused) return;
254ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
255ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                if (mGLRootView.getVisibility() != View.VISIBLE) {
256ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    mMosaicPreviewRenderer.showPreviewFrameSync();
257ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    mGLRootView.setVisibility(View.VISIBLE);
258ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                } else {
259ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    if (mCaptureState == CAPTURE_STATE_VIEWFINDER) {
260ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        mMosaicPreviewRenderer.showPreviewFrame();
261ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    } else {
262ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        mMosaicPreviewRenderer.alignFrame();
263ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        mMosaicFrameProcessor.processFrame();
264ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    }
265ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                }
266ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
267ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        };
268ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
269ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mGPSDateStampFormat = new SimpleDateFormat(GPS_DATE_FORMAT_STR);
270ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mGPSTimeStampFormat = new SimpleDateFormat(GPS_TIME_FORMAT_STR);
271ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mDateTimeStampFormat = new SimpleDateFormat(DATETIME_FORMAT_STR);
272ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        TimeZone tzUTC = TimeZone.getTimeZone("UTC");
273ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mGPSDateStampFormat.setTimeZone(tzUTC);
274ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mGPSTimeStampFormat.setTimeZone(tzUTC);
275ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
276ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        PowerManager pm = (PowerManager) mActivity.getSystemService(Context.POWER_SERVICE);
277ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Panorama");
278ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
279ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mOrientationEventListener = new PanoOrientationEventListener(mActivity);
280ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
281ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMosaicFrameProcessor = MosaicFrameProcessor.getInstance();
282ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
283ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        Resources appRes = mActivity.getResources();
284ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPreparePreviewString = appRes.getString(R.string.pano_dialog_prepare_preview);
285ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mDialogTitle = appRes.getString(R.string.pano_dialog_title);
286ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mDialogOkString = appRes.getString(R.string.dialog_ok);
287ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mDialogPanoramaFailedString = appRes.getString(R.string.pano_dialog_panorama_failed);
288ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mDialogWaitingPreviousString = appRes.getString(R.string.pano_dialog_waiting_previous);
289ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
290ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mGLRootView = (GLRootView) mActivity.getGLRoot();
291ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
292ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMainHandler = new Handler() {
293ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            @Override
294ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            public void handleMessage(Message msg) {
295ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                switch (msg.what) {
296ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    case MSG_LOW_RES_FINAL_MOSAIC_READY:
297ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        onBackgroundThreadFinished();
298ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        showFinalMosaic((Bitmap) msg.obj);
299ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        saveHighResMosaic();
300ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        break;
301ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    case MSG_RESET_TO_PREVIEW_WITH_THUMBNAIL:
302ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        onBackgroundThreadFinished();
303ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        // If the activity is paused, save the thumbnail to the file here.
304ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        // If not, it will be saved in onPause.
305ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        if (mPaused) mActivity.saveThumbnailToFile();
306ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        // Set the thumbnail bitmap here because mThumbnailView must be accessed
307ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        // from the UI thread.
308ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
309ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        resetToPreview();
310ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        clearMosaicFrameProcessorIfNeeded();
311ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        break;
312ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    case MSG_GENERATE_FINAL_MOSAIC_ERROR:
313ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        onBackgroundThreadFinished();
314ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        if (mPaused) {
315ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            resetToPreview();
316ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        } else {
317ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            mRotateDialog.showAlertDialog(
318ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                    mDialogTitle, mDialogPanoramaFailedString,
319ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                    mDialogOkString, new Runnable() {
320ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                        @Override
321ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                        public void run() {
322ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                            resetToPreview();
323ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                        }},
324ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                    null, null);
325ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        }
326ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        clearMosaicFrameProcessorIfNeeded();
327ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        break;
328ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    case MSG_RESET_TO_PREVIEW:
329ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        onBackgroundThreadFinished();
330ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        resetToPreview();
331ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        clearMosaicFrameProcessorIfNeeded();
332ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        break;
333ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    case MSG_CLEAR_SCREEN_DELAY:
334ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        mActivity.getWindow().clearFlags(WindowManager.LayoutParams.
335ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                FLAG_KEEP_SCREEN_ON);
336ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        break;
337ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                }
338ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
339ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        };
340ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
341ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
342ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
343ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public boolean dispatchTouchEvent(MotionEvent m) {
344ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Dismiss the mode selection window if the ACTION_DOWN event is out of
345ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // its view area.
346ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        return false;
347ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
348ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
349ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void setupCamera() throws CameraHardwareException, CameraDisabledException {
350ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        openCamera();
351ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        Parameters parameters = mCameraDevice.getParameters();
352ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        setupCaptureParams(parameters);
353ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        configureCamera(parameters);
354ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
355ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
356ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void releaseCamera() {
357ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mCameraDevice != null) {
358ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mCameraDevice.setPreviewCallbackWithBuffer(null);
359ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            CameraHolder.instance().release();
360ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mCameraDevice = null;
361ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mCameraState = PREVIEW_STOPPED;
362ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
363ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
364ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
365ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void openCamera() throws CameraHardwareException, CameraDisabledException {
366ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        int cameraId = CameraHolder.instance().getBackCameraId();
367ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // If there is no back camera, use the first camera. Camera id starts
368ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // from 0. Currently if a camera is not back facing, it is front facing.
369ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // This is also forward compatible if we have a new facing other than
370ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // back or front in the future.
371ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (cameraId == -1) cameraId = 0;
372ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraDevice = Util.openCamera(mActivity, cameraId);
373ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraOrientation = Util.getCameraOrientation(cameraId);
374ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (cameraId == CameraHolder.instance().getFrontCameraId()) mUsingFrontCamera = true;
375ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
376ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
377ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private boolean findBestPreviewSize(List<Size> supportedSizes, boolean need4To3,
378ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            boolean needSmaller) {
379ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        int pixelsDiff = DEFAULT_CAPTURE_PIXELS;
380ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        boolean hasFound = false;
381ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        for (Size size : supportedSizes) {
382ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            int h = size.height;
383ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            int w = size.width;
384ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // we only want 4:3 format.
385ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            int d = DEFAULT_CAPTURE_PIXELS - h * w;
386ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            if (needSmaller && d < 0) { // no bigger preview than 960x720.
387ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                continue;
388ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
389ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            if (need4To3 && (h * 4 != w * 3)) {
390ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                continue;
391ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
392ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            d = Math.abs(d);
393ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            if (d < pixelsDiff) {
394ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mPreviewWidth = w;
395ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mPreviewHeight = h;
396ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                pixelsDiff = d;
397ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                hasFound = true;
398ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
399ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
400ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        return hasFound;
401ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
402ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
403ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void setupCaptureParams(Parameters parameters) {
404ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        List<Size> supportedSizes = parameters.getSupportedPreviewSizes();
405ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (!findBestPreviewSize(supportedSizes, true, true)) {
406ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            Log.w(TAG, "No 4:3 ratio preview size supported.");
407ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            if (!findBestPreviewSize(supportedSizes, false, true)) {
408ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                Log.w(TAG, "Can't find a supported preview size smaller than 960x720.");
409ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                findBestPreviewSize(supportedSizes, false, false);
410ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
411ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
412ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        Log.v(TAG, "preview h = " + mPreviewHeight + " , w = " + mPreviewWidth);
413ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        parameters.setPreviewSize(mPreviewWidth, mPreviewHeight);
414ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
415ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        List<int[]> frameRates = parameters.getSupportedPreviewFpsRange();
416ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        int last = frameRates.size() - 1;
417ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        int minFps = (frameRates.get(last))[Parameters.PREVIEW_FPS_MIN_INDEX];
418ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        int maxFps = (frameRates.get(last))[Parameters.PREVIEW_FPS_MAX_INDEX];
419ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        parameters.setPreviewFpsRange(minFps, maxFps);
420ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        Log.v(TAG, "preview fps: " + minFps + ", " + maxFps);
421ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
422ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        List<String> supportedFocusModes = parameters.getSupportedFocusModes();
423ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (supportedFocusModes.indexOf(mTargetFocusMode) >= 0) {
424ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            parameters.setFocusMode(mTargetFocusMode);
425ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        } else {
426ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // Use the default focus mode and log a message
427ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            Log.w(TAG, "Cannot set the focus mode to " + mTargetFocusMode +
428ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                  " becuase the mode is not supported.");
429ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
430ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
431ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        parameters.set(Util.RECORDING_HINT, Util.FALSE);
432ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
433ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mHorizontalViewAngle = parameters.getHorizontalViewAngle();
434ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mVerticalViewAngle =  parameters.getVerticalViewAngle();
435ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
436ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
437ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public int getPreviewBufSize() {
438ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        PixelFormat pixelInfo = new PixelFormat();
439ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        PixelFormat.getPixelFormatInfo(mCameraDevice.getParameters().getPreviewFormat(), pixelInfo);
440ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // TODO: remove this extra 32 byte after the driver bug is fixed.
441ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        return (mPreviewWidth * mPreviewHeight * pixelInfo.bitsPerPixel / 8) + 32;
442ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
443ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
444ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void configureCamera(Parameters parameters) {
445ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraDevice.setParameters(parameters);
446ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
447ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
448ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void configMosaicPreview(int w, int h) {
449ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        stopCameraPreview();
450ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail;
451ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        screenNail.setSize(w, h);
452ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (screenNail.getSurfaceTexture() == null) {
453ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            screenNail.acquireSurfaceTexture();
454ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        } else {
455ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            screenNail.releaseSurfaceTexture();
456ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            screenNail.acquireSurfaceTexture();
457ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mActivity.notifyScreenNailChanged();
458ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
459ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        boolean isLandscape = (mActivity.getResources().getConfiguration().orientation
460ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                == Configuration.ORIENTATION_LANDSCAPE);
461ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mMosaicPreviewRenderer != null) mMosaicPreviewRenderer.release();
462ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMosaicPreviewRenderer = new MosaicPreviewRenderer(
463ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                screenNail.getSurfaceTexture(), w, h, isLandscape);
464ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
465ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraTexture = mMosaicPreviewRenderer.getInputSurfaceTexture();
466ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (!mPaused && !mThreadRunning && mWaitProcessorTask == null) {
467ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            resetToPreview();
468ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
469ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
470ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
471ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    // Receives the layout change event from the preview area. So we can set
472ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    // the camera preview screennail to the same size and initialize the mosaic
473ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    // preview renderer.
474ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
475ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onLayoutChange(View v, int l, int t, int r, int b) {
476ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        Log.i(TAG, "layout change: "+(r - l) + "/" +(b - t));
477ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.onLayoutChange(v, l, t, r, b);
478ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        configMosaicPreview(r - l, b - t);
479ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
480ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
481ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
482ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onFrameAvailable(SurfaceTexture surface) {
483ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        /* This function may be called by some random thread,
484ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb         * so let's be safe and jump back to ui thread.
485ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb         * No OpenGL calls can be done here. */
486ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.runOnUiThread(mOnFrameAvailableRunnable);
487ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
488ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
489ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void hideDirectionIndicators() {
490ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mLeftIndicator.setVisibility(View.GONE);
491ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mRightIndicator.setVisibility(View.GONE);
492ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
493ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
494ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void showDirectionIndicators(int direction) {
495ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        switch (direction) {
496ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            case PanoProgressBar.DIRECTION_NONE:
497ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mLeftIndicator.setVisibility(View.VISIBLE);
498ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mRightIndicator.setVisibility(View.VISIBLE);
499ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                break;
500ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            case PanoProgressBar.DIRECTION_LEFT:
501ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mLeftIndicator.setVisibility(View.VISIBLE);
502ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mRightIndicator.setVisibility(View.GONE);
503ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                break;
504ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            case PanoProgressBar.DIRECTION_RIGHT:
505ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mLeftIndicator.setVisibility(View.GONE);
506ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mRightIndicator.setVisibility(View.VISIBLE);
507ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                break;
508ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
509ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
510ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
511ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void startCapture() {
512ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Reset values so we can do this again.
513ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCancelComputation = false;
514ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mTimeTaken = System.currentTimeMillis();
515ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.setSwipingEnabled(false);
516ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.hideSwitcher();
517ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureState = CAPTURE_STATE_MOSAIC;
518ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mShutterButton.setImageResource(R.drawable.btn_shutter_pan_recording);
519ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureIndicator.setVisibility(View.VISIBLE);
520ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        showDirectionIndicators(PanoProgressBar.DIRECTION_NONE);
521ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
522ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMosaicFrameProcessor.setProgressListener(new MosaicFrameProcessor.ProgressListener() {
523ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            @Override
524ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            public void onProgress(boolean isFinished, float panningRateX, float panningRateY,
525ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    float progressX, float progressY) {
526ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                float accumulatedHorizontalAngle = progressX * mHorizontalViewAngle;
527ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                float accumulatedVerticalAngle = progressY * mVerticalViewAngle;
528ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                if (isFinished
529ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        || (Math.abs(accumulatedHorizontalAngle) >= DEFAULT_SWEEP_ANGLE)
530ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        || (Math.abs(accumulatedVerticalAngle) >= DEFAULT_SWEEP_ANGLE)) {
531ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    stopCapture(false);
532ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                } else {
533ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    float panningRateXInDegree = panningRateX * mHorizontalViewAngle;
534ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    float panningRateYInDegree = panningRateY * mVerticalViewAngle;
535ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    updateProgress(panningRateXInDegree, panningRateYInDegree,
536ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            accumulatedHorizontalAngle, accumulatedVerticalAngle);
537ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                }
538ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
539ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        });
540ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
541ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.reset();
542ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // TODO: calculate the indicator width according to different devices to reflect the actual
543ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // angle of view of the camera device.
544ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setIndicatorWidth(20);
545ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setMaxProgress(DEFAULT_SWEEP_ANGLE);
546ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setVisibility(View.VISIBLE);
547ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mDeviceOrientationAtCapture = mDeviceOrientation;
548ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        keepScreenOn();
549ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
550ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
551ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void stopCapture(boolean aborted) {
552ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureState = CAPTURE_STATE_VIEWFINDER;
553ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureIndicator.setVisibility(View.GONE);
554ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        hideTooFastIndication();
555ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        hideDirectionIndicators();
556ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
557ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMosaicFrameProcessor.setProgressListener(null);
558ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        stopCameraPreview();
559ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
560ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraTexture.setOnFrameAvailableListener(null);
561ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
562ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (!aborted && !mThreadRunning) {
563ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mRotateDialog.showWaitingDialog(mPreparePreviewString);
564ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            runBackgroundThread(new Thread() {
565ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                @Override
566ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                public void run() {
567ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    MosaicJpeg jpeg = generateFinalMosaic(false);
568ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
569ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    if (jpeg != null && jpeg.isValid) {
570ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        Bitmap bitmap = null;
571ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        bitmap = BitmapFactory.decodeByteArray(jpeg.data, 0, jpeg.data.length);
572ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        mMainHandler.sendMessage(mMainHandler.obtainMessage(
573ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                MSG_LOW_RES_FINAL_MOSAIC_READY, bitmap));
574ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    } else {
575ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        mMainHandler.sendMessage(mMainHandler.obtainMessage(
576ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                MSG_RESET_TO_PREVIEW));
577ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    }
578ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                }
579ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            });
580ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
581ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        keepScreenOnAwhile();
582ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
583ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
584ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void showTooFastIndication() {
585ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mTooFastPrompt.setVisibility(View.VISIBLE);
586ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // The PreviewArea also contains the border for "too fast" indication.
587ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPreviewArea.setVisibility(View.VISIBLE);
588ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setIndicatorColor(mIndicatorColorFast);
589ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mLeftIndicator.setEnabled(true);
590ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mRightIndicator.setEnabled(true);
591ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
592ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
593ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void hideTooFastIndication() {
594ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mTooFastPrompt.setVisibility(View.GONE);
595ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // We set "INVISIBLE" instead of "GONE" here because we need mPreviewArea to have layout
596ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // information so we can know the size and position for mCameraScreenNail.
597ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPreviewArea.setVisibility(View.INVISIBLE);
598ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setIndicatorColor(mIndicatorColor);
599ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mLeftIndicator.setEnabled(false);
600ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mRightIndicator.setEnabled(false);
601ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
602ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
603ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void updateProgress(float panningRateXInDegree, float panningRateYInDegree,
604ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            float progressHorizontalAngle, float progressVerticalAngle) {
605ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mGLRootView.requestRender();
606ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
607ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // TODO: Now we just display warning message by the panning speed.
608ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Since we only support horizontal panning, we should display a warning message
609ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // in UI when there're significant vertical movements.
610ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if ((Math.abs(panningRateXInDegree) > PANNING_SPEED_THRESHOLD)
611ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            || (Math.abs(panningRateYInDegree) > PANNING_SPEED_THRESHOLD)) {
612ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            showTooFastIndication();
613ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        } else {
614ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            hideTooFastIndication();
615ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
616ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        int angleInMajorDirection =
617ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                (Math.abs(progressHorizontalAngle) > Math.abs(progressVerticalAngle))
618ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                ? (int) progressHorizontalAngle
619ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                : (int) progressVerticalAngle;
620ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setProgress((angleInMajorDirection));
621ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
622ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
623ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void setViews(Resources appRes) {
624ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureState = CAPTURE_STATE_VIEWFINDER;
625ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar = (PanoProgressBar) mRootView.findViewById(R.id.pano_pan_progress_bar);
626ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setBackgroundColor(appRes.getColor(R.color.pano_progress_empty));
627ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setDoneColor(appRes.getColor(R.color.pano_progress_done));
628ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setIndicatorColor(mIndicatorColor);
629ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setOnDirectionChangeListener(
630ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                new PanoProgressBar.OnDirectionChangeListener () {
631ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    @Override
632ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    public void onDirectionChange(int direction) {
633ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        if (mCaptureState == CAPTURE_STATE_MOSAIC) {
634ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            showDirectionIndicators(direction);
635ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        }
636ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    }
637ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                });
638ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
639ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mLeftIndicator = mRootView.findViewById(R.id.pano_pan_left_indicator);
640ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mRightIndicator = mRootView.findViewById(R.id.pano_pan_right_indicator);
641ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mLeftIndicator.setEnabled(false);
642ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mRightIndicator.setEnabled(false);
643ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mTooFastPrompt = (TextView) mRootView.findViewById(R.id.pano_capture_too_fast_textview);
644ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // This mPreviewArea also shows the border for visual "too fast" indication.
645ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPreviewArea = (LayoutNotifyView) mRootView.findViewById(R.id.pano_preview_area);
646ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPreviewArea.setOnLayoutChangeListener(this);
647ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
648ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mSavingProgressBar = (PanoProgressBar) mRootView.findViewById(R.id.pano_saving_progress_bar);
649ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mSavingProgressBar.setIndicatorWidth(0);
650ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mSavingProgressBar.setMaxProgress(100);
651ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mSavingProgressBar.setBackgroundColor(appRes.getColor(R.color.pano_progress_empty));
652ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mSavingProgressBar.setDoneColor(appRes.getColor(R.color.pano_progress_indication));
653ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
654ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureIndicator = (RotateLayout) mRootView.findViewById(R.id.pano_capture_indicator);
655ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
656ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mReviewLayout = mRootView.findViewById(R.id.pano_review_layout);
657ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mReview = (ImageView) mRootView.findViewById(R.id.pano_reviewarea);
658ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        View cancelButton = mRootView.findViewById(R.id.pano_review_cancel_button);
659ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        cancelButton.setOnClickListener(new OnClickListener() {
660ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            @Override
661ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            public void onClick(View arg0) {
662ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                if (mPaused || mCameraTexture == null) return;
663ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                cancelHighResComputation();
664ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
665ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        });
666ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
667ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mShutterButton = mActivity.getShutterButton();
668ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mShutterButton.setImageResource(R.drawable.btn_shutter_pan);
669ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mShutterButton.setOnShutterButtonListener(this);
670ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
671ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mActivity.getResources().getConfiguration().orientation
672ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                == Configuration.ORIENTATION_PORTRAIT) {
673ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            Rotatable[] rotateLayout = {
674ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    (Rotatable) mRootView.findViewById(R.id.pano_pan_progress_bar_layout),
675ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    (Rotatable) mRootView.findViewById(R.id.pano_capture_too_fast_textview_layout),
676ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    (Rotatable) mRootView.findViewById(R.id.pano_review_saving_indication_layout),
677ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    (Rotatable) mRootView.findViewById(R.id.pano_saving_progress_bar_layout),
678ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    (Rotatable) mRootView.findViewById(R.id.pano_review_cancel_button_layout),
679ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    (Rotatable) mRootView.findViewById(R.id.pano_rotate_reviewarea),
680ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    mRotateDialog,
681ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    mCaptureIndicator,
682ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    };
683ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            for (Rotatable r : rotateLayout) {
684ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                r.setOrientation(270, false);
685ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
686ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        } else {
687ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // Even if the orientation is 0, we still need to set because it might be previously
688ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // set when the configuration is portrait.
689ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mRotateDialog.setOrientation(0, false);
690ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
691ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
692ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
693ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void createContentView() {
694ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.getLayoutInflater().inflate(R.layout.panorama_module, (ViewGroup) mRootView);
695ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        Resources appRes = mActivity.getResources();
696ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureLayout = (LinearLayout) mRootView.findViewById(R.id.camera_app_root);
697ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mIndicatorColor = appRes.getColor(R.color.pano_progress_indication);
698ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mIndicatorColorFast = appRes.getColor(R.color.pano_progress_indication_fast);
699ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoLayout = (ViewGroup) mRootView.findViewById(R.id.pano_layout);
700ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mRotateDialog = new RotateDialogController(mActivity, R.layout.rotate_dialog);
701ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        setViews(appRes);
702ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
703ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
704ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
705ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onShutterButtonClick() {
706ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // If mCameraTexture == null then GL setup is not finished yet.
707ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // No buttons can be pressed.
708ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mPaused || mThreadRunning || mCameraTexture == null) return;
709ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Since this button will stay on the screen when capturing, we need to check the state
710ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // right now.
711ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        switch (mCaptureState) {
712ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            case CAPTURE_STATE_VIEWFINDER:
713ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                if(mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) return;
714ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mSoundPlayer.play(SoundClips.START_VIDEO_RECORDING);
715ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                startCapture();
716ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                break;
717ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            case CAPTURE_STATE_MOSAIC:
718ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mSoundPlayer.play(SoundClips.STOP_VIDEO_RECORDING);
719ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                stopCapture(false);
720ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
721ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
722ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
723ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
724ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onShutterButtonFocus(boolean pressed) {
725ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
726ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
727ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void reportProgress() {
728ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mSavingProgressBar.reset();
729ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mSavingProgressBar.setRightIncreasing(true);
730ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        Thread t = new Thread() {
731ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            @Override
732ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            public void run() {
733ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                while (mThreadRunning) {
734ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    final int progress = mMosaicFrameProcessor.reportProgress(
735ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            true, mCancelComputation);
736ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
737ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    try {
738ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        synchronized (mWaitObject) {
739ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            mWaitObject.wait(50);
740ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        }
741ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    } catch (InterruptedException e) {
742ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        throw new RuntimeException("Panorama reportProgress failed", e);
743ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    }
744ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    // Update the progress bar
745ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    mActivity.runOnUiThread(new Runnable() {
746ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        @Override
747ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        public void run() {
748ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            mSavingProgressBar.setProgress(progress);
749ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        }
750ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    });
751ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                }
752ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
753ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        };
754ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        t.start();
755ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
756ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
757ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void saveHighResMosaic() {
758ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        runBackgroundThread(new Thread() {
759ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            @Override
760ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            public void run() {
761ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mPartialWakeLock.acquire();
762ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                MosaicJpeg jpeg;
763ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                try {
764ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    jpeg = generateFinalMosaic(true);
765ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                } finally {
766ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    mPartialWakeLock.release();
767ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                }
768ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
769ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                if (jpeg == null) {  // Cancelled by user.
770ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    mMainHandler.sendEmptyMessage(MSG_RESET_TO_PREVIEW);
771ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                } else if (!jpeg.isValid) {  // Error when generating mosaic.
772ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    mMainHandler.sendEmptyMessage(MSG_GENERATE_FINAL_MOSAIC_ERROR);
773ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                } else {
774ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    // The panorama image returned from the library is oriented based on the
775ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    // natural orientation of a camera. We need to set an orientation for the image
776ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    // in its EXIF header, so the image can be displayed correctly.
777ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    // The orientation is calculated from compensating the
778ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    // device orientation at capture and the camera orientation respective to
779ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    // the natural orientation of the device.
780ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    int orientation;
781ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    if (mUsingFrontCamera) {
782ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        // mCameraOrientation is negative with respect to the front facing camera.
783ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        // See document of android.hardware.Camera.Parameters.setRotation.
784ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        orientation = (mDeviceOrientationAtCapture - mCameraOrientation + 360) % 360;
785ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    } else {
786ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        orientation = (mDeviceOrientationAtCapture + mCameraOrientation) % 360;
787ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    }
788ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    Uri uri = savePanorama(jpeg.data, jpeg.width, jpeg.height, orientation);
789ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    if (uri != null) {
790ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        mActivity.addSecureAlbumItemIfNeeded(false, uri);
791ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        // Create a thumbnail whose width and height is equal or bigger
792ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        // than the thumbnail view's width.
793ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        int ratio = (int) Math.ceil(
794ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                (double) (jpeg.height > jpeg.width ? jpeg.width : jpeg.height)
795ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                / mActivity.mThumbnailViewWidth);
796ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        int inSampleSize = Integer.highestOneBit(ratio);
797ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        mActivity.mThumbnail = Thumbnail.createThumbnail(
798ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                                jpeg.data, orientation, inSampleSize, uri);
799ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        Util.broadcastNewPicture(mActivity, uri);
800ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    }
801ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    mMainHandler.sendMessage(
802ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            mMainHandler.obtainMessage(MSG_RESET_TO_PREVIEW_WITH_THUMBNAIL));
803ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                }
804ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
805ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        });
806ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        reportProgress();
807ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
808ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
809ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void runBackgroundThread(Thread thread) {
810ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mThreadRunning = true;
811ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        thread.start();
812ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
813ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
814ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void onBackgroundThreadFinished() {
815ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mThreadRunning = false;
816ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mRotateDialog.dismissDialog();
817ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
818ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
819ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void cancelHighResComputation() {
820ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCancelComputation = true;
821ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        synchronized (mWaitObject) {
822ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mWaitObject.notify();
823ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
824ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
825ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
826ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @OnClickAttr
827ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onThumbnailClicked(View v) {
828ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mPaused || mThreadRunning || mCameraTexture == null
829ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                || mActivity.mThumbnail == null) return;
830ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.gotoGallery();
831ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
832ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
833ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    // This function will be called upon the first camera frame is available.
834ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void reset() {
835ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureState = CAPTURE_STATE_VIEWFINDER;
836ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
837ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // We should set mGLRootView visible too. However, since there might be no
838ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // frame available yet, setting mGLRootView visible should be done right after
839ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // the first camera frame is available and therefore it is done by
840ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // mOnFirstFrameAvailableRunnable.
841ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.setSwipingEnabled(true);
842ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.showSwitcher();
843ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mReviewLayout.setVisibility(View.GONE);
844ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mShutterButton.setImageResource(R.drawable.btn_shutter_pan);
845ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoProgressBar.setVisibility(View.GONE);
846ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureLayout.setVisibility(View.VISIBLE);
847ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMosaicFrameProcessor.reset();
848ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
849ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
850ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void resetToPreview() {
851ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        reset();
852ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (!mPaused) startCameraPreview();
853ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
854ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
855ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void showFinalMosaic(Bitmap bitmap) {
856ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (bitmap != null) {
857ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mReview.setImageBitmap(bitmap);
858ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
859ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
860ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mGLRootView.setVisibility(View.GONE);
861ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureLayout.setVisibility(View.GONE);
862ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mReviewLayout.setVisibility(View.VISIBLE);
863ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
864ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
865ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private Uri savePanorama(byte[] jpegData, int width, int height, int orientation) {
866ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (jpegData != null) {
867ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            String filename = PanoUtil.createName(
868ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    mActivity.getResources().getString(R.string.pano_file_name_format), mTimeTaken);
869ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            Uri uri = Storage.addImage(mContentResolver, filename, mTimeTaken, null,
870ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    orientation, jpegData, width, height);
871ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            if (uri != null) {
872ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                String filepath = Storage.generateFilepath(filename);
873ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                try {
874ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    ExifInterface exif = new ExifInterface(filepath);
875ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
876ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    exif.setAttribute(ExifInterface.TAG_GPS_DATESTAMP,
877ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            mGPSDateStampFormat.format(mTimeTaken));
878ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    exif.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP,
879ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            mGPSTimeStampFormat.format(mTimeTaken));
880ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    exif.setAttribute(ExifInterface.TAG_DATETIME,
881ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            mDateTimeStampFormat.format(mTimeTaken));
882ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    exif.setAttribute(ExifInterface.TAG_ORIENTATION,
883ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                            getExifOrientation(orientation));
884ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
885ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    exif.saveAttributes();
886ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                } catch (IOException e) {
887ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    Log.e(TAG, "Cannot set EXIF for " + filepath, e);
888ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                }
889ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
890ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return uri;
891ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
892ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        return null;
893ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
894ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
895ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private static String getExifOrientation(int orientation) {
896ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        switch (orientation) {
897ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            case 0:
898ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                return String.valueOf(ExifInterface.ORIENTATION_NORMAL);
899ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            case 90:
900ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_90);
901ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            case 180:
902ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_180);
903ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            case 270:
904ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                return String.valueOf(ExifInterface.ORIENTATION_ROTATE_270);
905ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            default:
906ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                throw new AssertionError("invalid: " + orientation);
907ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
908ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
909ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
910ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void clearMosaicFrameProcessorIfNeeded() {
911ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (!mPaused || mThreadRunning) return;
912ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Only clear the processor if it is initialized by this activity
913ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // instance. Other activity instances may be using it.
914ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mMosaicFrameProcessorInitialized) {
915ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mMosaicFrameProcessor.clear();
916ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mMosaicFrameProcessorInitialized = false;
917ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
918ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
919ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
920ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void initMosaicFrameProcessorIfNeeded() {
921ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mPaused || mThreadRunning) return;
922ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMosaicFrameProcessor.initialize(
923ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                mPreviewWidth, mPreviewHeight, getPreviewBufSize());
924ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMosaicFrameProcessorInitialized = true;
925ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
926ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
927ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
928ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onPauseBeforeSuper() {
929ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPaused = true;
930ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
931ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
932ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
933ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onPauseAfterSuper() {
934ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mOrientationEventListener.disable();
935ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mCameraDevice == null) {
936ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // Camera open failed. Nothing should be done here.
937ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return;
938ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
939ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Stop the capturing first.
940ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mCaptureState == CAPTURE_STATE_MOSAIC) {
941ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            stopCapture(true);
942ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            reset();
943ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
944ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
945ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        releaseCamera();
946ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraTexture = null;
947ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
948ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // The preview renderer might not have a chance to be initialized before
949ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // onPause().
950ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mMosaicPreviewRenderer != null) {
951ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mMosaicPreviewRenderer.release();
952ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mMosaicPreviewRenderer = null;
953ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
954ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
955ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        clearMosaicFrameProcessorIfNeeded();
956ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mWaitProcessorTask != null) {
957ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mWaitProcessorTask.cancel(true);
958ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mWaitProcessorTask = null;
959ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
960ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        resetScreenOn();
961ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mSoundPlayer != null) {
962ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mSoundPlayer.release();
963ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mSoundPlayer = null;
964ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
965ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail;
966ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (screenNail.getSurfaceTexture() != null) {
967ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            screenNail.releaseSurfaceTexture();
968ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
969ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        System.gc();
970ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
971ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
972ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
973ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onConfigurationChanged(Configuration newConfig) {
974ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
975ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        Drawable lowResReview = null;
976ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mThreadRunning) lowResReview = mReview.getDrawable();
977ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
978ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Change layout in response to configuration change
979ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureLayout.setOrientation(
980ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
981ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
982ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureLayout.removeAllViews();
983ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        LayoutInflater inflater = mActivity.getLayoutInflater();
984ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        inflater.inflate(R.layout.preview_frame_pano, mCaptureLayout);
985ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
986ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPanoLayout.removeView(mReviewLayout);
987ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        inflater.inflate(R.layout.pano_review, mPanoLayout);
988ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
989ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        setViews(mActivity.getResources());
990ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mThreadRunning) {
991ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mReview.setImageDrawable(lowResReview);
992ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mCaptureLayout.setVisibility(View.GONE);
993ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mReviewLayout.setVisibility(View.VISIBLE);
994ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
995ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
996ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.updateThumbnailView();
997ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
998ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
999ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1000ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onResumeBeforeSuper() {
1001ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mPaused = false;
1002ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1003ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1004ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1005ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onResumeAfterSuper() {
1006ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mOrientationEventListener.enable();
1007ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1008ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCaptureState = CAPTURE_STATE_VIEWFINDER;
1009ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1010ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        SetupCameraThread setupCameraThread = new SetupCameraThread();
1011ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        setupCameraThread.start();
1012ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        try {
1013ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            setupCameraThread.join();
1014ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        } catch (InterruptedException ex) {
1015ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // ignore
1016ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1017ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1018ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mOpenCameraFail) {
1019ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
1020ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return;
1021ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        } else if (mCameraDisabled) {
1022ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
1023ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return;
1024ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1025ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1026ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Set up sound playback for shutter button
1027ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mSoundPlayer = SoundClips.getPlayer(mActivity);
1028ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1029ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Check if another panorama instance is using the mosaic frame processor.
1030ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mRotateDialog.dismissDialog();
1031ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (!mThreadRunning && mMosaicFrameProcessor.isMosaicMemoryAllocated()) {
1032ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mGLRootView.setVisibility(View.GONE);
1033ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mRotateDialog.showWaitingDialog(mDialogWaitingPreviousString);
1034ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mWaitProcessorTask = new WaitProcessorTask().execute();
1035ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        } else {
1036ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            if (!mThreadRunning) mGLRootView.setVisibility(View.VISIBLE);
1037ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // Camera must be initialized before MosaicFrameProcessor is
1038ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // initialized. The preview size has to be decided by camera device.
1039ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            initMosaicFrameProcessorIfNeeded();
1040ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            int w = mPreviewArea.getWidth();
1041ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            int h = mPreviewArea.getHeight();
1042ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            if (w != 0 && h != 0) {  // The layout has been calculated.
1043ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                configMosaicPreview(w, h);
1044ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
1045ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1046ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.getLastThumbnail();
1047ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        keepScreenOnAwhile();
1048ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1049ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Dismiss open menu if exists.
1050ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        PopupManager.getInstance(mActivity).notifyShowPopup(null);
1051ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mRootView.requestLayout();
1052ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1053ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1054ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    /**
1055ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb     * Generate the final mosaic image.
1056ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb     *
1057ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb     * @param highRes flag to indicate whether we want to get a high-res version.
1058ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb     * @return a MosaicJpeg with its isValid flag set to true if successful; null if the generation
1059ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb     *         process is cancelled; and a MosaicJpeg with its isValid flag set to false if there
1060ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb     *         is an error in generating the final mosaic.
1061ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb     */
1062ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public MosaicJpeg generateFinalMosaic(boolean highRes) {
1063ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        int mosaicReturnCode = mMosaicFrameProcessor.createMosaic(highRes);
1064ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mosaicReturnCode == Mosaic.MOSAIC_RET_CANCELLED) {
1065ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return null;
1066ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        } else if (mosaicReturnCode == Mosaic.MOSAIC_RET_ERROR) {
1067ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return new MosaicJpeg();
1068ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1069ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1070ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        byte[] imageData = mMosaicFrameProcessor.getFinalMosaicNV21();
1071ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (imageData == null) {
1072ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            Log.e(TAG, "getFinalMosaicNV21() returned null.");
1073ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return new MosaicJpeg();
1074ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1075ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1076ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        int len = imageData.length - 8;
1077ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        int width = (imageData[len + 0] << 24) + ((imageData[len + 1] & 0xFF) << 16)
1078ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                + ((imageData[len + 2] & 0xFF) << 8) + (imageData[len + 3] & 0xFF);
1079ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        int height = (imageData[len + 4] << 24) + ((imageData[len + 5] & 0xFF) << 16)
1080ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                + ((imageData[len + 6] & 0xFF) << 8) + (imageData[len + 7] & 0xFF);
1081ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        Log.v(TAG, "ImLength = " + (len) + ", W = " + width + ", H = " + height);
1082ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1083ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (width <= 0 || height <= 0) {
1084ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // TODO: pop up an error message indicating that the final result is not generated.
1085ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            Log.e(TAG, "width|height <= 0!!, len = " + (len) + ", W = " + width + ", H = " +
1086ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    height);
1087ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return new MosaicJpeg();
1088ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1089ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1090ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        YuvImage yuvimage = new YuvImage(imageData, ImageFormat.NV21, width, height, null);
1091ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        ByteArrayOutputStream out = new ByteArrayOutputStream();
1092ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        yuvimage.compressToJpeg(new Rect(0, 0, width, height), 100, out);
1093ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        try {
1094ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            out.close();
1095ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        } catch (Exception e) {
1096ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            Log.e(TAG, "Exception in storing final mosaic", e);
1097ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return new MosaicJpeg();
1098ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1099ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        return new MosaicJpeg(out.toByteArray(), width, height);
1100ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1101ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1102ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void startCameraPreview() {
1103ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mCameraDevice == null) {
1104ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            // Camera open failed. Return.
1105ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return;
1106ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1107ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1108ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // This works around a driver issue. startPreview may fail if
1109ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // stopPreview/setPreviewTexture/startPreview are called several times
1110ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // in a row. mCameraTexture can be null after pressing home during
1111ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // mosaic generation and coming back. Preview will be started later in
1112ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // onLayoutChange->configMosaicPreview. This also reduces the latency.
1113ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mCameraTexture == null) return;
1114ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1115ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // If we're previewing already, stop the preview first (this will blank
1116ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // the screen).
1117ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mCameraState != PREVIEW_STOPPED) stopCameraPreview();
1118ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1119ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // Set the display orientation to 0, so that the underlying mosaic library
1120ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // can always get undistorted mPreviewWidth x mPreviewHeight image data
1121ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // from SurfaceTexture.
1122ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraDevice.setDisplayOrientation(0);
1123ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1124ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mCameraTexture != null) mCameraTexture.setOnFrameAvailableListener(this);
1125ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraDevice.setPreviewTextureAsync(mCameraTexture);
1126ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1127ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraDevice.startPreviewAsync();
1128ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraState = PREVIEW_ACTIVE;
1129ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1130ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1131ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void stopCameraPreview() {
1132ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1133ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            Log.v(TAG, "stopPreview");
1134ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mCameraDevice.stopPreview();
1135ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1136ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mCameraState = PREVIEW_STOPPED;
1137ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1138ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1139ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1140ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onUserInteraction() {
1141ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mCaptureState != CAPTURE_STATE_MOSAIC) keepScreenOnAwhile();
1142ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1143ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1144ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1145ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public boolean onBackPressed() {
1146ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // If panorama is generating low res or high res mosaic, ignore back
1147ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        // key. So the activity will not be destroyed.
1148ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        if (mThreadRunning) return true;
1149ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        return false;
1150ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1151ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1152ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void resetScreenOn() {
1153ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMainHandler.removeMessages(MSG_CLEAR_SCREEN_DELAY);
1154ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1155ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1156ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1157ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void keepScreenOnAwhile() {
1158ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMainHandler.removeMessages(MSG_CLEAR_SCREEN_DELAY);
1159ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1160ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1161ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1162ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1163ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private void keepScreenOn() {
1164ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mMainHandler.removeMessages(MSG_CLEAR_SCREEN_DELAY);
1165ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1166ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1167ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1168ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    private class WaitProcessorTask extends AsyncTask<Void, Void, Void> {
1169ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        @Override
1170ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        protected Void doInBackground(Void... params) {
1171ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            synchronized (mMosaicFrameProcessor) {
1172ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                while (!isCancelled() && mMosaicFrameProcessor.isMosaicMemoryAllocated()) {
1173ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    try {
1174ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        mMosaicFrameProcessor.wait();
1175ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    } catch (Exception e) {
1176ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                        // ignore
1177ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                    }
1178ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                }
1179ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
1180ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            return null;
1181ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1182ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1183ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        @Override
1184ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        protected void onPostExecute(Void result) {
1185ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mWaitProcessorTask = null;
1186ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mRotateDialog.dismissDialog();
1187ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            mGLRootView.setVisibility(View.VISIBLE);
1188ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            initMosaicFrameProcessorIfNeeded();
1189ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            int w = mPreviewArea.getWidth();
1190ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            int h = mPreviewArea.getHeight();
1191ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            if (w != 0 && h != 0) {  // The layout has been calculated.
1192ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb                configMosaicPreview(w, h);
1193ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            }
1194ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb            resetToPreview();
1195ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        }
1196ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1197ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1198ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1199ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onFullScreenChanged(boolean full) {
1200ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1201ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1202ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1203ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1204ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onStop() {
1205ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1206ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1207ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1208ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void installIntentFilter() {
1209ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1210ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1211ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1212ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onActivityResult(int requestCode, int resultCode, Intent data) {
1213ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1214ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1215ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1216ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1217ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public boolean onKeyDown(int keyCode, KeyEvent event) {
1218ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        return false;
1219ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1220ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1221ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1222ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public boolean onKeyUp(int keyCode, KeyEvent event) {
1223ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        return false;
1224ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1225ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1226ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1227ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onSingleTapUp(View view, int x, int y) {
1228ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1229ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1230ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1231ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void onPreviewTextureCopied() {
1232ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1233ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1234ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1235ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public boolean updateStorageHintOnResume() {
1236ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        return false;
1237ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1238ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1239ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1240ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public void updateCameraAppView() {
1241ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1242ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb
1243ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    @Override
1244ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    public boolean collapseCameraControls() {
1245ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb        return false;
1246ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb    }
1247ce1d8396924bf4ca9be076cfdf9594c4d33ebd9bMichael Kolb}
1248