PanoramaActivity.java revision 6108e0ed3a6b03deed2a8586d98607a6756637b1
1bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen/*
2bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen * Copyright (C) 2011 The Android Open Source Project
3bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen *
4bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen * Licensed under the Apache License, Version 2.0 (the "License");
5bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen * you may not use this file except in compliance with the License.
6bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen * You may obtain a copy of the License at
7bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen *
8bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen *      http://www.apache.org/licenses/LICENSE-2.0
9bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen *
10bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen * Unless required by applicable law or agreed to in writing, software
11bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen * distributed under the License is distributed on an "AS IS" BASIS,
12bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen * See the License for the specific language governing permissions and
14bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen * limitations under the License.
15bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen */
16bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
17bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenpackage com.android.camera.panorama;
18bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
19d1cff41cd8ae0fc6e9df1050730bb5e47bd364afWu-cheng Liimport com.android.camera.CameraDisabledException;
20d1cff41cd8ae0fc6e9df1050730bb5e47bd364afWu-cheng Liimport com.android.camera.CameraHardwareException;
21d1cff41cd8ae0fc6e9df1050730bb5e47bd364afWu-cheng Liimport com.android.camera.CameraHolder;
22a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kongimport com.android.camera.Exif;
23d1cff41cd8ae0fc6e9df1050730bb5e47bd364afWu-cheng Liimport com.android.camera.MenuHelper;
24d1cff41cd8ae0fc6e9df1050730bb5e47bd364afWu-cheng Liimport com.android.camera.ModePicker;
25a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kongimport com.android.camera.OnClickAttr;
26d1cff41cd8ae0fc6e9df1050730bb5e47bd364afWu-cheng Liimport com.android.camera.R;
27fcdf619d92701052621a96572d956d047e07cf24Angus Kongimport com.android.camera.ShutterButton;
28d1cff41cd8ae0fc6e9df1050730bb5e47bd364afWu-cheng Liimport com.android.camera.Storage;
29a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kongimport com.android.camera.Thumbnail;
30d1cff41cd8ae0fc6e9df1050730bb5e47bd364afWu-cheng Liimport com.android.camera.Util;
31a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kongimport com.android.camera.ui.RotateImageView;
32a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kongimport com.android.camera.ui.SharePopup;
33d1cff41cd8ae0fc6e9df1050730bb5e47bd364afWu-cheng Li
342f9ed6f52580b8fecb77ec3ca123a804eea89f8cAngus Kongimport android.animation.Animator;
352f9ed6f52580b8fecb77ec3ca123a804eea89f8cAngus Kongimport android.animation.AnimatorInflater;
36b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kongimport android.animation.AnimatorSet;
372f9ed6f52580b8fecb77ec3ca123a804eea89f8cAngus Kongimport android.animation.ObjectAnimator;
38b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kongimport android.animation.ValueAnimator;
39bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.app.Activity;
4022d4b7fbace56c03092eb088a4df98e04a6681afAngus Kongimport android.app.AlertDialog;
415fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kongimport android.app.ProgressDialog;
42bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.content.Context;
4322d4b7fbace56c03092eb088a4df98e04a6681afAngus Kongimport android.content.DialogInterface;
44057040c0e19834d98719143c2627a9f0df3e4c35Angus Kongimport android.content.res.Resources;
45142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kongimport android.graphics.Bitmap;
46142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kongimport android.graphics.BitmapFactory;
478a2c41754655a3733175fce81fb7506ff7022959Angus Kongimport android.graphics.ImageFormat;
48bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.graphics.PixelFormat;
498a2c41754655a3733175fce81fb7506ff7022959Angus Kongimport android.graphics.Rect;
5041a2e9735136f372de95652d0828600282c8e967mbansalimport android.graphics.SurfaceTexture;
518a2c41754655a3733175fce81fb7506ff7022959Angus Kongimport android.graphics.YuvImage;
528a2c41754655a3733175fce81fb7506ff7022959Angus Kongimport android.hardware.Camera;
536f6ea6ab6dad370d2cc378be91a2649ae44b2c94Angus Kongimport android.hardware.Camera.Parameters;
546f6ea6ab6dad370d2cc378be91a2649ae44b2c94Angus Kongimport android.hardware.Camera.Size;
55bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.hardware.Sensor;
56bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.hardware.SensorEvent;
57bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.hardware.SensorEventListener;
58bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.hardware.SensorManager;
59a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kongimport android.net.Uri;
60bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.os.Bundle;
618a2c41754655a3733175fce81fb7506ff7022959Angus Kongimport android.os.Handler;
62ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kongimport android.os.Message;
63bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.util.Log;
64a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kongimport android.view.Gravity;
65c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kongimport android.view.OrientationEventListener;
66bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.view.View;
67bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.view.WindowManager;
68b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kongimport android.view.animation.LinearInterpolator;
69bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport android.widget.ImageView;
702f9ed6f52580b8fecb77ec3ca123a804eea89f8cAngus Kongimport android.widget.RelativeLayout;
71fbc5751416caa3a0d065ae15f56d1fb0253552c5Angus Kongimport android.widget.TextView;
72bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
73ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kongimport java.io.ByteArrayOutputStream;
74bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chenimport java.util.List;
75bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
76a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong/**
77a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong * Activity to handle panorama capturing.
78a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong */
7998f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chenpublic class PanoramaActivity extends Activity implements
80fcdf619d92701052621a96572d956d047e07cf24Angus Kong        ModePicker.OnModeChangeListener, SurfaceTexture.OnFrameAvailableListener,
81fcdf619d92701052621a96572d956d047e07cf24Angus Kong        ShutterButton.OnShutterButtonListener,
8236839102e03107e3fb121197036365a58ec2686eAngus Kong        MosaicRendererSurfaceViewRenderer.MosaicSurfaceCreateListener {
83ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong    public static final int DEFAULT_SWEEP_ANGLE = 160;
84bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    public static final int DEFAULT_BLEND_MODE = Mosaic.BLENDTYPE_HORIZONTAL;
85bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    public static final int DEFAULT_CAPTURE_PIXELS = 960 * 720;
86bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
872f9ed6f52580b8fecb77ec3ca123a804eea89f8cAngus Kong    private static final int MSG_LOW_RES_FINAL_MOSAIC_READY = 1;
88a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong    private static final int MSG_RESET_TO_PREVIEW_WITH_THUMBNAIL = 2;
8922d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong    private static final int MSG_GENERATE_FINAL_MOSAIC_ERROR = 3;
9022d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong    private static final int MSG_DISMISS_ALERT_DIALOG_AND_RESET_TO_PREVIEW = 4;
9113e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong    private static final int MSG_RESET_TO_PREVIEW = 5;
9294f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen
93ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong    private static final String TAG = "PanoramaActivity";
9498f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen    private static final int PREVIEW_STOPPED = 0;
9598f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen    private static final int PREVIEW_ACTIVE = 1;
96a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong    private static final int CAPTURE_STATE_VIEWFINDER = 0;
97a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong    private static final int CAPTURE_STATE_MOSAIC = 1;
9841a2e9735136f372de95652d0828600282c8e967mbansal
9943b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen    // Speed is in unit of deg/sec
100e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen    private static final float PANNING_SPEED_THRESHOLD = 20f;
10143b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen
102a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong    // Ratio of nanosecond to second
103a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong    private static final float NS2S = 1.0f / 1000000000.0f;
104142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
105142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    private boolean mPausing;
106142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
1077648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li    private View mPanoLayout;
108a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong    private View mCaptureLayout;
109a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong    private View mReviewLayout;
110bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private ImageView mReview;
1118df38bbe67338a1f5dc58996bd8866f4c1fe264dAngus Kong    private TextView mCaptureIndicator;
112057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong    private PanoProgressBar mPanoProgressBar;
113215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong    private PanoProgressBar mSavingProgressBar;
1146108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong    private View mLeftIndicator;
1156108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong    private View mRightIndicator;
1162faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong    private MosaicRendererSurfaceView mMosaicView;
117fbc5751416caa3a0d065ae15f56d1fb0253552c5Angus Kong    private TextView mTooFastPrompt;
118fcdf619d92701052621a96572d956d047e07cf24Angus Kong    private ShutterButton mShutterButton;
119215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong    private Object mWaitObject = new Object();
120142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
1215fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong    private String mPreparePreviewString;
1222f9ed6f52580b8fecb77ec3ca123a804eea89f8cAngus Kong    private AlertDialog mAlertDialog;
123215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong    private ProgressDialog mProgressDialog;
12422d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong    private String mDialogTitle;
12522d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong    private String mDialogOk;
1262f9ed6f52580b8fecb77ec3ca123a804eea89f8cAngus Kong
127c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private float mCompassValueX;
128c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private float mCompassValueY;
129c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private float mCompassValueXStart;
130c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private float mCompassValueYStart;
131c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private float mCompassValueXStartBuffer;
132c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private float mCompassValueYStartBuffer;
133c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private int mCompassThreshold;
134c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private int mTraversedAngleX;
135c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private int mTraversedAngleY;
136c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private long mTimestamp;
137c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    // Control variables for the terminate condition.
138c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private int mMinAngleX;
139c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private int mMaxAngleX;
140c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private int mMinAngleY;
141c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private int mMaxAngleY;
142c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong
143a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong    private RotateImageView mThumbnailView;
144a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong    private Thumbnail mThumbnail;
145a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong    private SharePopup mSharePopup;
146a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong
147b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong    private AnimatorSet mThumbnailViewAndModePickerOut;
148b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong    private AnimatorSet mThumbnailViewAndModePickerIn;
149b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong
150bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private int mPreviewWidth;
151bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private int mPreviewHeight;
1528a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private Camera mCameraDevice;
15398f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen    private int mCameraState;
15441a2e9735136f372de95652d0828600282c8e967mbansal    private int mCaptureState;
155bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private SensorManager mSensorManager;
156bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private Sensor mSensor;
157a72d73cbac59db43d413291e4db66763be08143arepo sync    private ModePicker mModePicker;
1588a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private MosaicFrameProcessor mMosaicFrameProcessor;
1598a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private long mTimeTaken;
160ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong    private Handler mMainHandler;
1612faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong    private SurfaceTexture mSurfaceTexture;
162142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    private boolean mThreadRunning;
163e1178a73fd5756771d25d0b8375452450f509e99mbansal    private boolean mCancelComputation;
1642faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong    private float[] mTransformMatrix;
16543b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen    private float mHorizontalViewAngle;
166c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong
167c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private PanoOrientationEventListener mOrientationEventListener;
168c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    // The value could be 0, 1, 2, 3 for the 4 different orientations measured in clockwise
169c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    // respectively.
170c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private int mDeviceOrientation;
171142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
1727648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li    private class MosaicJpeg {
1737648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li        public MosaicJpeg(byte[] data, int width, int height) {
1747648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li            this.data = data;
1757648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li            this.width = width;
1767648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li            this.height = height;
17713e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong            this.isValid = true;
17813e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong        }
17913e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong
18013e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong        public MosaicJpeg() {
18113e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong            this.data = null;
18213e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong            this.width = 0;
18313e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong            this.height = 0;
18413e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong            this.isValid = false;
1857648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li        }
1867648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li
1877648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li        public final byte[] data;
1887648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li        public final int width;
1897648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li        public final int height;
19013e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong        public final boolean isValid;
1917648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li    }
1927648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li
193c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private class PanoOrientationEventListener extends OrientationEventListener {
194c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        public PanoOrientationEventListener(Context context) {
195c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            super(context);
196c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        }
197c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong
198c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        @Override
199c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        public void onOrientationChanged(int orientation) {
200c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            // Default to the last known orientation.
201c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            if (orientation == ORIENTATION_UNKNOWN) return;
202c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            mDeviceOrientation = ((orientation + 45) / 90) % 4;
203c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        }
204c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    }
205c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong
206bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    @Override
207bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    public void onCreate(Bundle icicle) {
208bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        super.onCreate(icicle);
209bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
210bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
211bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
212e0861e7473d042bdd9c935f3bdf50ae220891b6cWu-cheng Li        Util.enterLightsOutMode(getWindow());
213bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
214bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        createContentView();
215bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
216bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
217bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
218bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        if (mSensor == null) {
219bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
220bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        }
2218a2c41754655a3733175fce81fb7506ff7022959Angus Kong
222c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mOrientationEventListener = new PanoOrientationEventListener(this);
223c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong
2242faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mTransformMatrix = new float[16];
2252faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong
2265fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong        mPreparePreviewString =
2275fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong                getResources().getString(R.string.pano_dialog_prepare_preview);
22822d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong        mDialogTitle = getResources().getString(R.string.pano_dialog_title);
22922d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong        mDialogOk = getResources().getString(R.string.dialog_ok);
2305fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong
231ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong        mMainHandler = new Handler() {
232ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong            @Override
233ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong            public void handleMessage(Message msg) {
234ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong                switch (msg.what) {
2352f9ed6f52580b8fecb77ec3ca123a804eea89f8cAngus Kong                    case MSG_LOW_RES_FINAL_MOSAIC_READY:
2365fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong                        onBackgroundThreadFinished();
237142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong                        showFinalMosaic((Bitmap) msg.obj);
238215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong                        saveHighResMosaic();
239142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong                        break;
240a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong                    case MSG_RESET_TO_PREVIEW_WITH_THUMBNAIL:
2415fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong                        onBackgroundThreadFinished();
242a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong                        // Set the thumbnail bitmap here because mThumbnailView must be accessed
243a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong                        // from the UI thread.
244a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong                        if (mThumbnail != null) {
245a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong                            mThumbnailView.setBitmap(mThumbnail.getBitmap());
246a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong                        }
247142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong                        resetToPreview();
248142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong                        break;
24922d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                    case MSG_GENERATE_FINAL_MOSAIC_ERROR:
25022d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                        onBackgroundThreadFinished();
25122d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                        mAlertDialog = (new AlertDialog.Builder(PanoramaActivity.this))
25222d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                                .setTitle(mDialogTitle)
25322d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                                .setMessage(R.string.pano_dialog_panorama_failed)
25422d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                                .create();
25522d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                        mAlertDialog.setButton(DialogInterface.BUTTON_POSITIVE, mDialogOk,
25622d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                                obtainMessage(MSG_DISMISS_ALERT_DIALOG_AND_RESET_TO_PREVIEW));
25722d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                        mAlertDialog.show();
25822d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                        break;
25922d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                    case MSG_DISMISS_ALERT_DIALOG_AND_RESET_TO_PREVIEW:
26022d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                        mAlertDialog.dismiss();
26122d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                        mAlertDialog = null;
26222d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                        resetToPreview();
26322d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                        break;
26413e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                    case MSG_RESET_TO_PREVIEW:
265e1178a73fd5756771d25d0b8375452450f509e99mbansal                        onBackgroundThreadFinished();
266e1178a73fd5756771d25d0b8375452450f509e99mbansal                        resetToPreview();
267ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong                }
268142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong                clearMosaicFrameProcessorIfNeeded();
269ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong            }
270ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong        };
271bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
272bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
273bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private void setupCamera() {
274bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        openCamera();
275bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        Parameters parameters = mCameraDevice.getParameters();
276bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        setupCaptureParams(parameters);
277bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        configureCamera(parameters);
278bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
279bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
280ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong    private void releaseCamera() {
281ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong        if (mCameraDevice != null) {
282142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong            mCameraDevice.setPreviewCallbackWithBuffer(null);
283ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong            CameraHolder.instance().release();
284ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong            mCameraDevice = null;
28598f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen            mCameraState = PREVIEW_STOPPED;
286ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong        }
287ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong    }
288ac86e91982dbb64eac089aa8ccf23bddb51f27f1Angus Kong
289bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private void openCamera() {
290bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        try {
291bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            mCameraDevice = Util.openCamera(this, CameraHolder.instance().getBackCameraId());
292bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        } catch (CameraHardwareException e) {
293bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            Util.showErrorAndFinish(this, R.string.cannot_connect_camera);
294bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            return;
295bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        } catch (CameraDisabledException e) {
296bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            Util.showErrorAndFinish(this, R.string.camera_disabled);
297bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            return;
298bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        }
299bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
300bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
301bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private boolean findBestPreviewSize(List<Size> supportedSizes, boolean need4To3,
302bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            boolean needSmaller) {
303bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        int pixelsDiff = DEFAULT_CAPTURE_PIXELS;
304bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        boolean hasFound = false;
3058a2c41754655a3733175fce81fb7506ff7022959Angus Kong        for (Size size : supportedSizes) {
306bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            int h = size.height;
307bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            int w = size.width;
308bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            // we only want 4:3 format.
309bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            int d = DEFAULT_CAPTURE_PIXELS - h * w;
31094f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen            if (needSmaller && d < 0) { // no bigger preview than 960x720.
311bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                continue;
312bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            }
313bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            if (need4To3 && (h * 4 != w * 3)) {
314bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                continue;
315bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            }
316bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            d = Math.abs(d);
317bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            if (d < pixelsDiff) {
318bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                mPreviewWidth = w;
319bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                mPreviewHeight = h;
320bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                pixelsDiff = d;
321bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                hasFound = true;
322bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            }
323bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        }
324bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        return hasFound;
325bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
326bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
327bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private void setupCaptureParams(Parameters parameters) {
328bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        List<Size> supportedSizes = parameters.getSupportedPreviewSizes();
329bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        if (!findBestPreviewSize(supportedSizes, true, true)) {
330bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            Log.w(TAG, "No 4:3 ratio preview size supported.");
331bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            if (!findBestPreviewSize(supportedSizes, false, true)) {
332bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                Log.w(TAG, "Can't find a supported preview size smaller than 960x720.");
333bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                findBestPreviewSize(supportedSizes, false, false);
334bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            }
335bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        }
336bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        Log.v(TAG, "preview h = " + mPreviewHeight + " , w = " + mPreviewWidth);
337bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        parameters.setPreviewSize(mPreviewWidth, mPreviewHeight);
338bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
339bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        List<int[]> frameRates = parameters.getSupportedPreviewFpsRange();
340bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        int last = frameRates.size() - 1;
341bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        int minFps = (frameRates.get(last))[Parameters.PREVIEW_FPS_MIN_INDEX];
342bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        int maxFps = (frameRates.get(last))[Parameters.PREVIEW_FPS_MAX_INDEX];
343bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        parameters.setPreviewFpsRange(minFps, maxFps);
344bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        Log.v(TAG, "preview fps: " + minFps + ", " + maxFps);
3453ab4f0912da83f7f5f8266a57a606d521c1649e3Wu-cheng Li
34693ad273a53e7d2975a67d7d1f8a157f845e41cf2Wu-cheng Li        parameters.setRecordingHint(false);
34743b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen
348c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mHorizontalViewAngle = ((mDeviceOrientation % 2) == 0) ?
349c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                parameters.getHorizontalViewAngle() : parameters.getVerticalViewAngle();
350bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
351bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
352bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    public int getPreviewBufSize() {
353bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        PixelFormat pixelInfo = new PixelFormat();
354bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        PixelFormat.getPixelFormatInfo(mCameraDevice.getParameters().getPreviewFormat(), pixelInfo);
355bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        // TODO: remove this extra 32 byte after the driver bug is fixed.
356bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        return (mPreviewWidth * mPreviewHeight * pixelInfo.bitsPerPixel / 8) + 32;
357bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
358bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
359bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private void configureCamera(Parameters parameters) {
360bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        mCameraDevice.setParameters(parameters);
361bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
3624b602592e0d189499b22d107d997b83e798b5bd9Wu-cheng Li        int orientation = Util.getDisplayOrientation(Util.getDisplayRotation(this),
3634b602592e0d189499b22d107d997b83e798b5bd9Wu-cheng Li                CameraHolder.instance().getBackCameraId());
3644b602592e0d189499b22d107d997b83e798b5bd9Wu-cheng Li        mCameraDevice.setDisplayOrientation(orientation);
365bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
366bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
367a72d73cbac59db43d413291e4db66763be08143arepo sync    private boolean switchToOtherMode(int mode) {
368a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong        if (isFinishing()) {
36994f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen            return false;
370a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong        }
371a72d73cbac59db43d413291e4db66763be08143arepo sync        MenuHelper.gotoMode(mode, this);
372a72d73cbac59db43d413291e4db66763be08143arepo sync        finish();
373a72d73cbac59db43d413291e4db66763be08143arepo sync        return true;
374a72d73cbac59db43d413291e4db66763be08143arepo sync    }
375a72d73cbac59db43d413291e4db66763be08143arepo sync
376a72d73cbac59db43d413291e4db66763be08143arepo sync    public boolean onModeChanged(int mode) {
377a72d73cbac59db43d413291e4db66763be08143arepo sync        if (mode != ModePicker.MODE_PANORAMA) {
378a72d73cbac59db43d413291e4db66763be08143arepo sync            return switchToOtherMode(mode);
379a72d73cbac59db43d413291e4db66763be08143arepo sync        } else {
380a72d73cbac59db43d413291e4db66763be08143arepo sync            return true;
381a72d73cbac59db43d413291e4db66763be08143arepo sync        }
382a72d73cbac59db43d413291e4db66763be08143arepo sync    }
383a72d73cbac59db43d413291e4db66763be08143arepo sync
3842faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong    @Override
3852faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong    public void onMosaicSurfaceCreated(final int textureID) {
38636839102e03107e3fb121197036365a58ec2686eAngus Kong        runOnUiThread(new Runnable() {
38736839102e03107e3fb121197036365a58ec2686eAngus Kong            @Override
38836839102e03107e3fb121197036365a58ec2686eAngus Kong            public void run() {
3892faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong                if (mSurfaceTexture != null) {
3902faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong                    mSurfaceTexture.release();
3912faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong                }
3922faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong                mSurfaceTexture = new SurfaceTexture(textureID);
3932faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong                if (!mPausing) {
3942faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong                    mSurfaceTexture.setOnFrameAvailableListener(PanoramaActivity.this);
3952faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong                    startCameraPreview();
3962faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong                }
39736839102e03107e3fb121197036365a58ec2686eAngus Kong            }
39836839102e03107e3fb121197036365a58ec2686eAngus Kong        });
39936839102e03107e3fb121197036365a58ec2686eAngus Kong    }
40036839102e03107e3fb121197036365a58ec2686eAngus Kong
40141a2e9735136f372de95652d0828600282c8e967mbansal    public void runViewFinder() {
4022faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.setWarping(false);
4032faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        // Call preprocess to render it to low-res and high-res RGB textures.
4042faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.preprocess(mTransformMatrix);
4052faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.setReady();
4062faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.requestRender();
40741a2e9735136f372de95652d0828600282c8e967mbansal    }
40841a2e9735136f372de95652d0828600282c8e967mbansal
40941a2e9735136f372de95652d0828600282c8e967mbansal    public void runMosaicCapture() {
4102faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.setWarping(true);
4112faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        // Call preprocess to render it to low-res and high-res RGB textures.
4122faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.preprocess(mTransformMatrix);
4132faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        // Lock the conditional variable to ensure the order of transferGPUtoCPU and
4142faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        // mMosaicFrame.processFrame().
4152faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.lockPreviewReadyFlag();
41641a2e9735136f372de95652d0828600282c8e967mbansal        // Now, transfer the textures from GPU to CPU memory for processing
4172faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.transferGPUtoCPU();
41841a2e9735136f372de95652d0828600282c8e967mbansal        // Wait on the condition variable (will be opened when GPU->CPU transfer is done).
4192faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.waitUntilPreviewReady();
4200adf2489520d3a98a56e081aeacb1ab9336a012fWei-Ta Chen        mMosaicFrameProcessor.processFrame();
42141a2e9735136f372de95652d0828600282c8e967mbansal    }
42241a2e9735136f372de95652d0828600282c8e967mbansal
423739f9e1541c30735e45d5cb90255dd36a32d3604Angus Kong    public synchronized void onFrameAvailable(SurfaceTexture surface) {
4244d1c2846e24a7f7f6c8ff59e01ca36a000344944Angus Kong        /* This function may be called by some random thread,
42541a2e9735136f372de95652d0828600282c8e967mbansal         * so let's be safe and use synchronize. No OpenGL calls can be done here.
42641a2e9735136f372de95652d0828600282c8e967mbansal         */
4272faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        // Updating the texture should be done in the GL thread which mMosaicView is attached.
4282faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.queueEvent(new Runnable() {
4292faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong            @Override
4302faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong            public void run() {
4312faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong                mSurfaceTexture.updateTexImage();
4322faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong                mSurfaceTexture.getTransformMatrix(mTransformMatrix);
4332faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong            }
4342faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        });
4352faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        // Update the transformation matrix for mosaic pre-process.
436a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong        if (mCaptureState == CAPTURE_STATE_VIEWFINDER) {
43741a2e9735136f372de95652d0828600282c8e967mbansal            runViewFinder();
43841a2e9735136f372de95652d0828600282c8e967mbansal        } else {
43941a2e9735136f372de95652d0828600282c8e967mbansal            runMosaicCapture();
44041a2e9735136f372de95652d0828600282c8e967mbansal        }
44141a2e9735136f372de95652d0828600282c8e967mbansal    }
44241a2e9735136f372de95652d0828600282c8e967mbansal
4436108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong    private void hideDirectionIndicators() {
4446108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong        mLeftIndicator.setVisibility(View.GONE);
4456108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong        mRightIndicator.setVisibility(View.GONE);
4466108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong    }
4476108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong
4486108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong    private void showDirectionIndicators(int direction) {
4496108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong        switch (direction) {
4506108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong            case PanoProgressBar.DIRECTION_NONE:
4516108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                mLeftIndicator.setVisibility(View.VISIBLE);
4526108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                mRightIndicator.setVisibility(View.VISIBLE);
4536108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                break;
4546108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong            case PanoProgressBar.DIRECTION_LEFT:
4556108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                mLeftIndicator.setVisibility(View.VISIBLE);
4566108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                mRightIndicator.setVisibility(View.GONE);
4576108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                break;
4586108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong            case PanoProgressBar.DIRECTION_RIGHT:
4596108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                mLeftIndicator.setVisibility(View.GONE);
4606108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                mRightIndicator.setVisibility(View.VISIBLE);
4616108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                break;
4626108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong        }
4636108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong    }
4646108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong
465a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong    public void startCapture() {
4668a2c41754655a3733175fce81fb7506ff7022959Angus Kong        // Reset values so we can do this again.
46713e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong        mCancelComputation = false;
4688a2c41754655a3733175fce81fb7506ff7022959Angus Kong        mTimeTaken = System.currentTimeMillis();
469a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong        mCaptureState = CAPTURE_STATE_MOSAIC;
470fcdf619d92701052621a96572d956d047e07cf24Angus Kong        mShutterButton.setBackgroundResource(R.drawable.btn_shutter_pan_recording);
4718df38bbe67338a1f5dc58996bd8866f4c1fe264dAngus Kong        mCaptureIndicator.setVisibility(View.VISIBLE);
4726108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong        showDirectionIndicators(PanoProgressBar.DIRECTION_NONE);
4738a2c41754655a3733175fce81fb7506ff7022959Angus Kong
474b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        // XML-style animations can not be used here. The Y position has to be calculated runtime.
475b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        float ystart = mThumbnailView.getY();
476b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        ValueAnimator va1 = ObjectAnimator.ofFloat(
477b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong                mThumbnailView, "y", ystart, -mThumbnailView.getHeight());
478b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        ValueAnimator va1Reverse = ObjectAnimator.ofFloat(
479b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong                mThumbnailView, "y", -mThumbnailView.getHeight(), ystart);
480b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        ystart = mModePicker.getY();
481b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        float height = mCaptureLayout.getHeight();
482b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        ValueAnimator va2 = ObjectAnimator.ofFloat(
483b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong                mModePicker, "y", ystart, height + 1);
484b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        ValueAnimator va2Reverse = ObjectAnimator.ofFloat(
485b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong                mModePicker, "y", height + 1, ystart);
486b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        LinearInterpolator li = new LinearInterpolator();
487b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        mThumbnailViewAndModePickerOut = new AnimatorSet();
488b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        mThumbnailViewAndModePickerOut.play(va1).with(va2);
489b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        mThumbnailViewAndModePickerOut.setDuration(500);
490b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        mThumbnailViewAndModePickerOut.setInterpolator(li);
491b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        mThumbnailViewAndModePickerIn = new AnimatorSet();
492b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        mThumbnailViewAndModePickerIn.play(va1Reverse).with(va2Reverse);
493b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        mThumbnailViewAndModePickerIn.setDuration(500);
494b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        mThumbnailViewAndModePickerIn.setInterpolator(li);
495b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong
496b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        mThumbnailViewAndModePickerOut.start();
497b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong
498c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mCompassValueXStart = mCompassValueXStartBuffer;
499c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mCompassValueYStart = mCompassValueYStartBuffer;
500c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mMinAngleX = 0;
501c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mMaxAngleX = 0;
502c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mMinAngleY = 0;
503c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mMaxAngleY = 0;
504c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mTimestamp = 0;
505c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong
5068a2c41754655a3733175fce81fb7506ff7022959Angus Kong        mMosaicFrameProcessor.setProgressListener(new MosaicFrameProcessor.ProgressListener() {
5078a2c41754655a3733175fce81fb7506ff7022959Angus Kong            @Override
508c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            public void onProgress(boolean isFinished, float panningRateX, float panningRateY) {
509c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                if (isFinished
510c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                        || (mMaxAngleX - mMinAngleX >= DEFAULT_SWEEP_ANGLE)
511c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                        || (mMaxAngleY - mMinAngleY >= DEFAULT_SWEEP_ANGLE)) {
512a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong                    stopCapture();
5138a2c41754655a3733175fce81fb7506ff7022959Angus Kong                } else {
514c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                    updateProgress(panningRateX);
5158a2c41754655a3733175fce81fb7506ff7022959Angus Kong                }
5168a2c41754655a3733175fce81fb7506ff7022959Angus Kong            }
5178a2c41754655a3733175fce81fb7506ff7022959Angus Kong        });
5188a2c41754655a3733175fce81fb7506ff7022959Angus Kong
519057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        mPanoProgressBar.reset();
520057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        // TODO: calculate the indicator width according to different devices to reflect the actual
521057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        // angle of view of the camera device.
522057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        mPanoProgressBar.setIndicatorWidth(20);
523057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        mPanoProgressBar.setMaxProgress(DEFAULT_SWEEP_ANGLE);
524057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        mPanoProgressBar.setVisibility(View.VISIBLE);
5258a2c41754655a3733175fce81fb7506ff7022959Angus Kong    }
5268a2c41754655a3733175fce81fb7506ff7022959Angus Kong
527a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong    private void stopCapture() {
528a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong        mCaptureState = CAPTURE_STATE_VIEWFINDER;
5290c9caf77ab90b173fbe2719ec6a78e588964ac33Wei-Ta Chen        mTooFastPrompt.setVisibility(View.GONE);
5308df38bbe67338a1f5dc58996bd8866f4c1fe264dAngus Kong        mCaptureIndicator.setVisibility(View.GONE);
5316108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong        hideDirectionIndicators();
53241a2e9735136f372de95652d0828600282c8e967mbansal
5338a2c41754655a3733175fce81fb7506ff7022959Angus Kong        mMosaicFrameProcessor.setProgressListener(null);
53436839102e03107e3fb121197036365a58ec2686eAngus Kong        stopCameraPreview();
53541a2e9735136f372de95652d0828600282c8e967mbansal
5362faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mSurfaceTexture.setOnFrameAvailableListener(null);
53741a2e9735136f372de95652d0828600282c8e967mbansal
538142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        if (!mThreadRunning) {
539215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong            showDialog(mPreparePreviewString);
540215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong            runBackgroundThread(new Thread() {
541142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong                @Override
542142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong                public void run() {
5437648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li                    MosaicJpeg jpeg = generateFinalMosaic(false);
544e1178a73fd5756771d25d0b8375452450f509e99mbansal
54513e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                    if (jpeg != null && jpeg.isValid) {
546e1178a73fd5756771d25d0b8375452450f509e99mbansal                        Bitmap bitmap = null;
54713e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                        bitmap = BitmapFactory.decodeByteArray(jpeg.data, 0, jpeg.data.length);
548e1178a73fd5756771d25d0b8375452450f509e99mbansal                        mMainHandler.sendMessage(mMainHandler.obtainMessage(
549e1178a73fd5756771d25d0b8375452450f509e99mbansal                                MSG_LOW_RES_FINAL_MOSAIC_READY, bitmap));
55013e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                    } else {
55113e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                        mMainHandler.sendMessage(mMainHandler.obtainMessage(
55213e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                                MSG_RESET_TO_PREVIEW));
553ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong                    }
554142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong                }
5555fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong            });
556142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        }
557b3b80f6550f6a827e15b23abd780457cb26eebdfAngus Kong        mThumbnailViewAndModePickerIn.start();
5588a2c41754655a3733175fce81fb7506ff7022959Angus Kong    }
5598a2c41754655a3733175fce81fb7506ff7022959Angus Kong
560c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private void updateProgress(float panningRate) {
5612faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.setReady();
5622faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.requestRender();
563eeb94d4de94bfd4e01f3a716803f77a530f5b92cmbansal
56443b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen        // TODO: Now we just display warning message by the panning speed.
56543b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen        // Since we only support horizontal panning, we should display a warning message
56643b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen        // in UI when there're significant vertical movements.
567c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        if (Math.abs(panningRate * mHorizontalViewAngle) > PANNING_SPEED_THRESHOLD) {
568fbc5751416caa3a0d065ae15f56d1fb0253552c5Angus Kong            // TODO: draw speed indication according to the UI spec.
569fbc5751416caa3a0d065ae15f56d1fb0253552c5Angus Kong            mTooFastPrompt.setVisibility(View.VISIBLE);
570e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen            mTooFastPrompt.invalidate();
5718a2c41754655a3733175fce81fb7506ff7022959Angus Kong        } else {
572fbc5751416caa3a0d065ae15f56d1fb0253552c5Angus Kong            mTooFastPrompt.setVisibility(View.GONE);
573e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen            mTooFastPrompt.invalidate();
5748a2c41754655a3733175fce81fb7506ff7022959Angus Kong        }
5758a2c41754655a3733175fce81fb7506ff7022959Angus Kong    }
5768a2c41754655a3733175fce81fb7506ff7022959Angus Kong
577bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    private void createContentView() {
578bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        setContentView(R.layout.panorama);
579bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
580a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong        mCaptureState = CAPTURE_STATE_VIEWFINDER;
58141a2e9735136f372de95652d0828600282c8e967mbansal
582057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        Resources appRes = getResources();
583057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong
584a955261dd734524eb7d8e1af76c695122efc8aa7Angus Kong        mCaptureLayout = (View) findViewById(R.id.pano_capture_layout);
585215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        mPanoProgressBar = (PanoProgressBar) findViewById(R.id.pano_pan_progress_bar);
586057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        mPanoProgressBar.setBackgroundColor(appRes.getColor(R.color.pano_progress_empty));
587057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        mPanoProgressBar.setDoneColor(appRes.getColor(R.color.pano_progress_done));
588057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        mPanoProgressBar.setIndicatorColor(appRes.getColor(R.color.pano_progress_indication));
5896108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong        mPanoProgressBar.setOnDirectionChangeListener(
5906108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                new PanoProgressBar.OnDirectionChangeListener () {
5916108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                    @Override
5926108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                    public void onDirectionChange(int direction) {
5936108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                        if (mCaptureState == CAPTURE_STATE_MOSAIC) {
5946108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                            showDirectionIndicators(direction);
5956108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                        }
5966108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                    }
5976108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong                });
5986108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong
5996108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong        mLeftIndicator = (ImageView) findViewById(R.id.pano_pan_left_indicator);
6006108e0ed3a6b03deed2a8586d98607a6756637b1Angus Kong        mRightIndicator = (ImageView) findViewById(R.id.pano_pan_right_indicator);
601fbc5751416caa3a0d065ae15f56d1fb0253552c5Angus Kong        mTooFastPrompt = (TextView) findViewById(R.id.pano_capture_too_fast_textview);
602bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
603215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        mSavingProgressBar = (PanoProgressBar) findViewById(R.id.pano_saving_progress_bar);
604215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        mSavingProgressBar.setIndicatorWidth(0);
605215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        mSavingProgressBar.setMaxProgress(100);
606215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        mSavingProgressBar.setBackgroundColor(appRes.getColor(R.color.pano_progress_empty));
607215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        mSavingProgressBar.setDoneColor(appRes.getColor(R.color.pano_progress_indication));
608215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong
6098df38bbe67338a1f5dc58996bd8866f4c1fe264dAngus Kong        mCaptureIndicator = (TextView) findViewById(R.id.pano_capture_indicator);
6108df38bbe67338a1f5dc58996bd8866f4c1fe264dAngus Kong
611a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong        mThumbnailView = (RotateImageView) findViewById(R.id.thumbnail);
612a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong
6135af9472f1c260c1a3b54d2e1bd448c27b5e5968fWei-Ta Chen        mReviewLayout = (View) findViewById(R.id.pano_review_layout);
6145af9472f1c260c1a3b54d2e1bd448c27b5e5968fWei-Ta Chen        mReview = (ImageView) findViewById(R.id.pano_reviewarea);
6152faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView = (MosaicRendererSurfaceView) findViewById(R.id.pano_renderer);
6162faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.getRenderer().setMosaicSurfaceCreateListener(this);
617eeb94d4de94bfd4e01f3a716803f77a530f5b92cmbansal
618a72d73cbac59db43d413291e4db66763be08143arepo sync        mModePicker = (ModePicker) findViewById(R.id.mode_picker);
619a72d73cbac59db43d413291e4db66763be08143arepo sync        mModePicker.setVisibility(View.VISIBLE);
620a72d73cbac59db43d413291e4db66763be08143arepo sync        mModePicker.setOnModeChangeListener(this);
621a72d73cbac59db43d413291e4db66763be08143arepo sync        mModePicker.setCurrentMode(ModePicker.MODE_PANORAMA);
6227648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li
623fcdf619d92701052621a96572d956d047e07cf24Angus Kong        mShutterButton = (ShutterButton) findViewById(R.id.shutter_button);
624fcdf619d92701052621a96572d956d047e07cf24Angus Kong        mShutterButton.setBackgroundResource(R.drawable.btn_shutter_pan);
625fcdf619d92701052621a96572d956d047e07cf24Angus Kong        mShutterButton.setOnShutterButtonListener(this);
626fcdf619d92701052621a96572d956d047e07cf24Angus Kong
6277648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li        mPanoLayout = findViewById(R.id.pano_layout);
628bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
629bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
630fcdf619d92701052621a96572d956d047e07cf24Angus Kong    @Override
631fcdf619d92701052621a96572d956d047e07cf24Angus Kong    public void onShutterButtonClick(ShutterButton b) {
6322faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        // If mSurfaceTexture == null then GL setup is not finished yet.
6332faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        // No buttons can be pressed.
6342faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        if (mPausing || mThreadRunning || mSurfaceTexture == null) return;
635a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong        // Since this button will stay on the screen when capturing, we need to check the state
636a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong        // right now.
637fcdf619d92701052621a96572d956d047e07cf24Angus Kong        switch (mCaptureState) {
638fcdf619d92701052621a96572d956d047e07cf24Angus Kong            case CAPTURE_STATE_VIEWFINDER:
639fcdf619d92701052621a96572d956d047e07cf24Angus Kong                startCapture();
640fcdf619d92701052621a96572d956d047e07cf24Angus Kong                break;
641fcdf619d92701052621a96572d956d047e07cf24Angus Kong            case CAPTURE_STATE_MOSAIC:
642fcdf619d92701052621a96572d956d047e07cf24Angus Kong                stopCapture();
643a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong        }
644739f9e1541c30735e45d5cb90255dd36a32d3604Angus Kong    }
645739f9e1541c30735e45d5cb90255dd36a32d3604Angus Kong
646fcdf619d92701052621a96572d956d047e07cf24Angus Kong    @Override
647fcdf619d92701052621a96572d956d047e07cf24Angus Kong    public void onShutterButtonFocus(ShutterButton b, boolean pressed) {
648142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    }
649142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
650215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong    public void reportProgress() {
651215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        mSavingProgressBar.reset();
652215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        mSavingProgressBar.setRightIncreasing(true);
65350b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal        Thread t = new Thread() {
65450b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal            @Override
65550b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal            public void run() {
6566f6ea6ab6dad370d2cc378be91a2649ae44b2c94Angus Kong                while (mThreadRunning) {
657e1178a73fd5756771d25d0b8375452450f509e99mbansal                    final int progress = mMosaicFrameProcessor.reportProgress(
658215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong                            true, mCancelComputation);
65950b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal
6606f6ea6ab6dad370d2cc378be91a2649ae44b2c94Angus Kong                    try {
661215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong                        synchronized (mWaitObject) {
662215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong                            mWaitObject.wait(50);
663215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong                        }
664215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong                    } catch (InterruptedException e) {
6656f6ea6ab6dad370d2cc378be91a2649ae44b2c94Angus Kong                        throw new RuntimeException("Panorama reportProgress failed", e);
6666f6ea6ab6dad370d2cc378be91a2649ae44b2c94Angus Kong                    }
6676f6ea6ab6dad370d2cc378be91a2649ae44b2c94Angus Kong                    // Update the progress bar
66850b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal                    runOnUiThread(new Runnable() {
66950b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal                        public void run() {
670215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong                            mSavingProgressBar.setProgress(progress);
67150b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal                        }
67250b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal                    });
67350b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal                }
67450b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal            }
67550b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal        };
67650b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal        t.start();
67750b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal    }
67850b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal
679215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong    public void saveHighResMosaic() {
680215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        runBackgroundThread(new Thread() {
681142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong            @Override
682142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong            public void run() {
6837648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li                MosaicJpeg jpeg = generateFinalMosaic(true);
684e1178a73fd5756771d25d0b8375452450f509e99mbansal
68513e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                if (jpeg == null) {  // Cancelled by user.
68613e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                    mMainHandler.sendEmptyMessage(MSG_RESET_TO_PREVIEW);
68713e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                } else if (!jpeg.isValid) {  // Error when generating mosaic.
68813e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                    mMainHandler.sendEmptyMessage(MSG_GENERATE_FINAL_MOSAIC_ERROR);
68922d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                } else {
69013e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                    int orientation = Exif.getOrientation(jpeg.data);
69113e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                    Uri uri = savePanorama(jpeg.data, orientation);
69213e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                    if (uri != null) {
69313e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                        // Create a thumbnail whose width is equal or bigger
69413e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                        // than the entire screen.
69513e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                        int ratio = (int) Math.ceil((double) jpeg.width /
69613e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                                mPanoLayout.getWidth());
69713e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                        int inSampleSize = Integer.highestOneBit(ratio);
69813e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                        mThumbnail = Thumbnail.createThumbnail(
69913e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                                jpeg.data, orientation, inSampleSize, uri);
70022d4b7fbace56c03092eb088a4df98e04a6681afAngus Kong                    }
70113e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                    mMainHandler.sendMessage(
70213e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong                            mMainHandler.obtainMessage(MSG_RESET_TO_PREVIEW_WITH_THUMBNAIL));
703a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong                }
704142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong            }
7055fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong        });
706215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        reportProgress();
707215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong    }
708215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong
709215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong    private void showDialog(String str) {
710215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong          mProgressDialog = new ProgressDialog(this);
711215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong          mProgressDialog.setMessage(str);
712215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong          mProgressDialog.show();
7135fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong    }
7145fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong
715215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong    private void runBackgroundThread(Thread thread) {
7165fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong        mThreadRunning = true;
7175fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong        thread.start();
7185fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong    }
7195fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong
7205fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong    private void onBackgroundThreadFinished() {
7215fde6d5259fccaed4bc517697a4930d3b69ae860Angus Kong        mThreadRunning = false;
722215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        if (mProgressDialog != null ) {
723215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong            mProgressDialog.dismiss();
724215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong            mProgressDialog = null;
725215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        }
726142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    }
727142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
728142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    @OnClickAttr
729215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong    public void onCancelButtonClicked(View v) {
730215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        if (mPausing || mSurfaceTexture == null) return;
731215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        mCancelComputation = true;
732215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        synchronized (mWaitObject) {
733215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong            mWaitObject.notify();
734215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        }
735142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    }
736142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
737a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong    @OnClickAttr
738a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong    public void onThumbnailClicked(View v) {
739a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong        if (mPausing || mThreadRunning || mSurfaceTexture == null) return;
740a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong        showSharePopup();
741a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong    }
742a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong
743a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong    private void showSharePopup() {
744a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong        if (mThumbnail == null) return;
745a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong        Uri uri = mThumbnail.getUri();
746a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong        if (mSharePopup == null || !uri.equals(mSharePopup.getUri())) {
747a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong            // The orientation compensation is set to 0 here because we only support landscape.
7484591c5ecbaade585c3094be353d27b14f0592494Wu-cheng Li            // Panorama picture is long. Use pano_layout so the share popup can be full-screen.
749757b67951f40850b1ac3102dc0b6fe8977615a4aWu-cheng Li            mSharePopup = new SharePopup(this, uri, mThumbnail.getBitmap(), 0,
750757b67951f40850b1ac3102dc0b6fe8977615a4aWu-cheng Li                    findViewById(R.id.pano_layout));
751a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong        }
752a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong        mSharePopup.showAtLocation(mThumbnailView, Gravity.NO_GRAVITY, 0, 0);
753a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong    }
754a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong
755142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    private void resetToPreview() {
756a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong        mCaptureState = CAPTURE_STATE_VIEWFINDER;
75741a2e9735136f372de95652d0828600282c8e967mbansal
75841a2e9735136f372de95652d0828600282c8e967mbansal        mReviewLayout.setVisibility(View.GONE);
759fcdf619d92701052621a96572d956d047e07cf24Angus Kong        mShutterButton.setBackgroundResource(R.drawable.btn_shutter_pan);
760057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        mPanoProgressBar.setVisibility(View.GONE);
76160fc534ec8f68d95a486984cb396a2da0e200518Angus Kong        mCaptureLayout.setVisibility(View.VISIBLE);
762142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        mMosaicFrameProcessor.reset();
76341a2e9735136f372de95652d0828600282c8e967mbansal
7642faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mSurfaceTexture.setOnFrameAvailableListener(this);
76541a2e9735136f372de95652d0828600282c8e967mbansal
76636839102e03107e3fb121197036365a58ec2686eAngus Kong        if (!mPausing) startCameraPreview();
767142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    }
768142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
769142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    private void showFinalMosaic(Bitmap bitmap) {
770142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        if (bitmap != null) {
771142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong            mReview.setImageBitmap(bitmap);
772142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        }
773ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        mCaptureLayout.setVisibility(View.GONE);
774ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        mReviewLayout.setVisibility(View.VISIBLE);
775142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    }
776142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
777a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong    private Uri savePanorama(byte[] jpegData, int orientation) {
778ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        if (jpegData != null) {
779ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong            String imagePath = PanoUtil.createName(
780ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong                    getResources().getString(R.string.pano_file_name_format), mTimeTaken);
781a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong            return Storage.addImage(getContentResolver(), imagePath, mTimeTaken, null,
782a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong                    orientation, jpegData);
783142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        }
784a907c122ccec649c6cedf3a45b1c426a5fca932eAngus Kong        return null;
785142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    }
786142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
787142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    private void clearMosaicFrameProcessorIfNeeded() {
788142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        if (!mPausing || mThreadRunning) return;
789142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        mMosaicFrameProcessor.clear();
790142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    }
791142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong
792142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    private void initMosaicFrameProcessorIfNeeded() {
793142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        if (mPausing || mThreadRunning) return;
794142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        if (mMosaicFrameProcessor == null) {
795142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong            // Start the activity for the first time.
796c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            mMosaicFrameProcessor = new MosaicFrameProcessor(
7970adf2489520d3a98a56e081aeacb1ab9336a012fWei-Ta Chen                    mPreviewWidth, mPreviewHeight, getPreviewBufSize());
798142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        }
799142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        mMosaicFrameProcessor.initialize();
800bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
801bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
802bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    @Override
803bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    protected void onPause() {
804bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        super.onPause();
8054d1c2846e24a7f7f6c8ff59e01ca36a000344944Angus Kong
806bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        releaseCamera();
807142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        mPausing = true;
8082faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.onPause();
809142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        clearMosaicFrameProcessorIfNeeded();
810215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong        mSensorManager.unregisterListener(mListener);
811c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mOrientationEventListener.disable();
81294f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen        System.gc();
813bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
814bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
815bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    @Override
816bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    protected void onResume() {
817bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        super.onResume();
818bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
819142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        mPausing = false;
820c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mOrientationEventListener.enable();
821bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        /*
82294f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen         * It is not necessary to get accelerometer events at a very high rate,
823c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong         * by using a game rate (SENSOR_DELAY_UI), we get an automatic
82494f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen         * low-pass filter, which "extracts" the gravity component of the
82594f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen         * acceleration. As an added benefit, we use less power and CPU
82694f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen         * resources.
827bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen         */
828bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        mSensorManager.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_UI);
829215f7ecf1fcc861986397fdf6724602d4775ab2cAngus Kong
830a466abbd54caa8100a1ef6584a5b0db78224a125Angus Kong        mCaptureState = CAPTURE_STATE_VIEWFINDER;
831bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        setupCamera();
8324d1c2846e24a7f7f6c8ff59e01ca36a000344944Angus Kong        if (mSurfaceTexture != null) {
8334d1c2846e24a7f7f6c8ff59e01ca36a000344944Angus Kong            mSurfaceTexture.setOnFrameAvailableListener(this);
8344d1c2846e24a7f7f6c8ff59e01ca36a000344944Angus Kong            startCameraPreview();
8354d1c2846e24a7f7f6c8ff59e01ca36a000344944Angus Kong        }
836142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        // Camera must be initialized before MosaicFrameProcessor is initialized. The preview size
837142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        // has to be decided by camera device.
838142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        initMosaicFrameProcessorIfNeeded();
8392faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        mMosaicView.onResume();
840bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
841bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
842c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private void updateCompassValue() {
843917cd82d626aaa5ea48b301fe9c7bc7452a0177fAngus Kong        if (mCaptureState == CAPTURE_STATE_VIEWFINDER) return;
844c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        // By what angle has the camera moved since start of capture?
845c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mTraversedAngleX = (int) (mCompassValueX - mCompassValueXStart);
846c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mTraversedAngleY = (int) (mCompassValueY - mCompassValueYStart);
847c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mMinAngleX = Math.min(mMinAngleX, mTraversedAngleX);
848c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mMaxAngleX = Math.max(mMaxAngleX, mTraversedAngleX);
849c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mMinAngleY = Math.min(mMinAngleY, mTraversedAngleY);
850c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        mMaxAngleY = Math.max(mMaxAngleY, mTraversedAngleY);
851c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong
852c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        // Use orientation to identify if the user is panning to the right or the left.
853c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        switch (mDeviceOrientation) {
854c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            case 0:
855057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong                mPanoProgressBar.setProgress(-mTraversedAngleX);
856c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                break;
857c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            case 1:
858057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong                mPanoProgressBar.setProgress(mTraversedAngleY);
859c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                break;
860c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            case 2:
861057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong                mPanoProgressBar.setProgress(mTraversedAngleX);
862c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                break;
863c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            case 3:
864057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong                mPanoProgressBar.setProgress(-mTraversedAngleY);
865c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                break;
866c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong        }
867057040c0e19834d98719143c2627a9f0df3e4c35Angus Kong        mPanoProgressBar.invalidate();
868c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    }
869bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
870c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong    private final SensorEventListener mListener = new SensorEventListener() {
871bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        public void onSensorChanged(SensorEvent event) {
872bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
873bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                if (mTimestamp != 0) {
874bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                    final float dT = (event.timestamp - mTimestamp) * NS2S;
875c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                    mCompassValueX += event.values[1] * dT * 180.0f / Math.PI;
876c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                    mCompassValueY += event.values[0] * dT * 180.0f / Math.PI;
877c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                    mCompassValueXStartBuffer = mCompassValueX;
878c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                    mCompassValueYStartBuffer = mCompassValueY;
879c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong                    updateCompassValue();
880bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                }
881bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen                mTimestamp = event.timestamp;
882bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
883bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen            }
884bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        }
885bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
886bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        @Override
887bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        public void onAccuracyChanged(Sensor sensor, int accuracy) {
888bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen        }
889bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    };
890bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
8917648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li    public MosaicJpeg generateFinalMosaic(boolean highRes) {
89213e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong        if (mMosaicFrameProcessor.createMosaic(highRes) == Mosaic.MOSAIC_RET_CANCELLED) {
893e1178a73fd5756771d25d0b8375452450f509e99mbansal            return null;
89413e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong        }
895bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen
896ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        byte[] imageData = mMosaicFrameProcessor.getFinalMosaicNV21();
897ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        if (imageData == null) {
898ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong            Log.e(TAG, "getFinalMosaicNV21() returned null.");
89913e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong            return new MosaicJpeg();
9008a2c41754655a3733175fce81fb7506ff7022959Angus Kong        }
9018a2c41754655a3733175fce81fb7506ff7022959Angus Kong
902ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        int len = imageData.length - 8;
903ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        int width = (imageData[len + 0] << 24) + ((imageData[len + 1] & 0xFF) << 16)
904ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong                + ((imageData[len + 2] & 0xFF) << 8) + (imageData[len + 3] & 0xFF);
905ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        int height = (imageData[len + 4] << 24) + ((imageData[len + 5] & 0xFF) << 16)
906ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong                + ((imageData[len + 6] & 0xFF) << 8) + (imageData[len + 7] & 0xFF);
907ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        Log.v(TAG, "ImLength = " + (len) + ", W = " + width + ", H = " + height);
9088a2c41754655a3733175fce81fb7506ff7022959Angus Kong
909e46486104523760a4433f03e09ed615987d83b6cAngus Kong        if (width <= 0 || height <= 0) {
910e46486104523760a4433f03e09ed615987d83b6cAngus Kong            // TODO: pop up a error meesage indicating that the final result is not generated.
911e46486104523760a4433f03e09ed615987d83b6cAngus Kong            Log.e(TAG, "width|height <= 0!!, len = " + (len) + ", W = " + width + ", H = " +
912e46486104523760a4433f03e09ed615987d83b6cAngus Kong                    height);
91313e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong            return new MosaicJpeg();
914e46486104523760a4433f03e09ed615987d83b6cAngus Kong        }
915e46486104523760a4433f03e09ed615987d83b6cAngus Kong
916ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        YuvImage yuvimage = new YuvImage(imageData, ImageFormat.NV21, width, height, null);
917ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        ByteArrayOutputStream out = new ByteArrayOutputStream();
918ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        yuvimage.compressToJpeg(new Rect(0, 0, width, height), 100, out);
919ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        try {
920ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong            out.close();
921ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        } catch (Exception e) {
922ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong            Log.e(TAG, "Exception in storing final mosaic", e);
92313e2b964e821f6ee40cf5715b6baec19c7fd09b8Angus Kong            return new MosaicJpeg();
924ef91f8514bf3c21091dde6b292f9616464dc32e8Angus Kong        }
9257648319f0416b86ae13b97eeba0466581ace1a48Wu-cheng Li        return new MosaicJpeg(out.toByteArray(), width, height);
926bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen    }
9278a2c41754655a3733175fce81fb7506ff7022959Angus Kong
92841a2e9735136f372de95652d0828600282c8e967mbansal    private void setPreviewTexture(SurfaceTexture surface) {
92998f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        try {
93041a2e9735136f372de95652d0828600282c8e967mbansal            mCameraDevice.setPreviewTexture(surface);
93198f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        } catch (Throwable ex) {
93298f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen            releaseCamera();
93341a2e9735136f372de95652d0828600282c8e967mbansal            throw new RuntimeException("setPreviewTexture failed", ex);
93498f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        }
93598f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen    }
93698f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen
93736839102e03107e3fb121197036365a58ec2686eAngus Kong    private void startCameraPreview() {
93898f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        // If we're previewing already, stop the preview first (this will blank
93998f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        // the screen).
94036839102e03107e3fb121197036365a58ec2686eAngus Kong        if (mCameraState != PREVIEW_STOPPED) stopCameraPreview();
94198f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen
9422faf16a90a6a6d18e40faf45ced217b913b214b6Angus Kong        setPreviewTexture(mSurfaceTexture);
94398f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen
94498f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        try {
94598f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen            Log.v(TAG, "startPreview");
94698f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen            mCameraDevice.startPreview();
94798f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        } catch (Throwable ex) {
94898f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen            releaseCamera();
94998f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen            throw new RuntimeException("startPreview failed", ex);
95098f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        }
95198f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        mCameraState = PREVIEW_ACTIVE;
95298f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen    }
95398f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen
95436839102e03107e3fb121197036365a58ec2686eAngus Kong    private void stopCameraPreview() {
95598f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
95698f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen            Log.v(TAG, "stopPreview");
95798f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen            mCameraDevice.stopPreview();
95898f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        }
95998f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen        mCameraState = PREVIEW_STOPPED;
96098f41918daf64318a7da4ec94712eeb8d6697f31Wei-Ta Chen    }
961bdc7e2b461064b25b7f17b0941077fc97653093dWei-Ta Chen}
962