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