PhotoModule.java revision d3a252bb71d119cbaeff55aeaa27dd1f8adadba2
1d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb/*
2d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb * Copyright (C) 2012 The Android Open Source Project
3d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb *
4d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License");
5d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb * you may not use this file except in compliance with the License.
6d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb * You may obtain a copy of the License at
7d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb *
8d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb *      http://www.apache.org/licenses/LICENSE-2.0
9d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb *
10d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb * Unless required by applicable law or agreed to in writing, software
11d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS,
12d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb * See the License for the specific language governing permissions and
14d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb * limitations under the License.
15d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb */
16d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
17d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbpackage com.android.camera;
18d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
19d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.annotation.TargetApi;
20d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.app.Activity;
21d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.content.BroadcastReceiver;
22d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.content.ContentProviderClient;
23d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.content.ContentResolver;
24d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.content.Context;
25d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.content.Intent;
26d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.content.IntentFilter;
27d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.content.SharedPreferences.Editor;
28d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.content.res.Configuration;
29d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.graphics.Bitmap;
30d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.graphics.SurfaceTexture;
31d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.hardware.Camera.CameraInfo;
32d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.hardware.Camera.Face;
33d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.hardware.Camera.FaceDetectionListener;
34d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.hardware.Camera.Parameters;
35d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.hardware.Camera.PictureCallback;
36d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.hardware.Camera.Size;
37d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.location.Location;
38d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.media.CameraProfile;
39d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.net.Uri;
40d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.os.Bundle;
41d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.os.ConditionVariable;
42d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.os.Handler;
43d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.os.Looper;
44d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.os.Message;
45d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.os.MessageQueue;
46d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.os.SystemClock;
47d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.provider.MediaStore;
48d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.util.Log;
49d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.view.KeyEvent;
50d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.view.LayoutInflater;
51d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.view.MotionEvent;
52d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.view.OrientationEventListener;
53d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.view.SurfaceHolder;
54d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.view.View;
55d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.view.ViewGroup;
56d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.view.WindowManager;
57d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.widget.ImageView;
58d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.widget.LinearLayout;
59d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.widget.TextView;
60d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport android.widget.Toast;
61d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
62d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.CameraManager.CameraProxy;
63d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.CameraPicker;
64d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.FaceView;
65d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.IndicatorControlContainer;
66d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.PopupManager;
67d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.PreviewSurfaceView;
68d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.Rotatable;
69d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.RotateImageView;
70d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.RotateLayout;
71d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.RotateTextToast;
72d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.TwoStateImageView;
73d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.camera.ui.ZoomControl;
74d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.gallery3d.common.ApiHelper;
75d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport com.android.gallery3d.ui.ScreenNail;
76d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
77d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport java.io.File;
78d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport java.io.FileNotFoundException;
79d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport java.io.FileOutputStream;
80d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport java.io.IOException;
81d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport java.io.OutputStream;
82d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport java.util.ArrayList;
83d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport java.util.Collections;
84d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport java.util.Formatter;
85d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbimport java.util.List;
86d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
87d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolbpublic class PhotoModule
88d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    implements CameraModule,
89d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    FocusManager.Listener,
90d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    ModePicker.OnModeChangeListener,
91d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    CameraPreference.OnPreferenceChangedListener,
92d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    LocationManager.Listener,
93d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    PreviewFrameLayout.OnSizeChangedListener,
94d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    ShutterButton.OnShutterButtonListener,
95d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    SurfaceHolder.Callback {
96d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
97d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final String TAG = "CAM_PhotoModule";
98d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
99d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // We number the request code from 1000 to avoid collision with Gallery.
100d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int REQUEST_CROP = 1000;
101d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
102d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int SETUP_PREVIEW = 1;
103d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int FIRST_TIME_INIT = 2;
104d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int CLEAR_SCREEN_DELAY = 3;
105d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
106d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int CHECK_DISPLAY_ROTATION = 5;
107d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
108d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int UPDATE_THUMBNAIL = 7;
109d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int SWITCH_CAMERA = 8;
110d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int SWITCH_CAMERA_START_ANIMATION = 9;
111d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int CAMERA_OPEN_DONE = 10;
112d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int START_PREVIEW_DONE = 11;
113d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int OPEN_CAMERA_FAIL = 12;
114d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int CAMERA_DISABLED = 13;
115d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
116d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // The subset of parameters we need to update in setCameraParameters().
117d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int UPDATE_PARAM_INITIALIZE = 1;
118d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int UPDATE_PARAM_ZOOM = 2;
119d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int UPDATE_PARAM_PREFERENCE = 4;
120d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int UPDATE_PARAM_ALL = -1;
121d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
122d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // copied from Camera hierarchy
123d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private CameraActivity mActivity;
124d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private View mRootView;
125d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private CameraProxy mCameraDevice;
126d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mCameraId;
127d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private Parameters mParameters;
128d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mPaused;
129d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
130d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // these are only used by Camera
131d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
132d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // The activity is going to switch to the specified camera id. This is
133d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // needed because texture copy is done in GL thread. -1 means camera is not
134d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // switching.
135d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    protected int mPendingSwitchCameraId = -1;
136d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mOpenCameraFail;
137d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mCameraDisabled;
138d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    protected CameraPicker mCameraPicker;
139d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
140d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
141d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // When setCameraParametersWhenIdle() is called, we accumulate the subsets
142d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // needed to be updated in mUpdateSet.
143d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mUpdateSet;
144d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
145d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int SCREEN_DELAY = 2 * 60 * 1000;
146d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
147d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mZoomValue;  // The current zoom value.
148d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mZoomMax;
149d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ZoomControl mZoomControl;
150d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
151d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private Parameters mInitialParams;
152d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mFocusAreaSupported;
153d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mMeteringAreaSupported;
154d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mAeLockSupported;
155d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mAwbLockSupported;
156d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mContinousFocusSupported;
157d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
158d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private MyOrientationEventListener mOrientationListener;
159d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // The degrees of the device rotated clockwise from its natural orientation.
160d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
161d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // The orientation compensation for icons and thumbnails. Ex: if the value
162d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // is 90, the UI components should be rotated 90 degrees counter-clockwise.
163d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mOrientationCompensation = 0;
164d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ComboPreferences mPreferences;
165d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
166d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final String sTempCropFilename = "crop-temp";
167d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
168d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ContentProviderClient mMediaProviderClient;
169d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ShutterButton mShutterButton;
170d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mFaceDetectionStarted = false;
171d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
172d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private PreviewFrameLayout mPreviewFrameLayout;
173d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private Object mSurfaceTexture;
174d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
175d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // for API level 10
176d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private PreviewSurfaceView mPreviewSurfaceView;
177d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private volatile SurfaceHolder mCameraSurfaceHolder;
178d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
179d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private RotateDialogController mRotateDialog;
180d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ModePicker mModePicker;
181d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private FaceView mFaceView;
182d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private RotateLayout mFocusAreaIndicator;
183d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private Rotatable mReviewCancelButton;
184d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private Rotatable mReviewDoneButton;
185d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private View mReviewRetakeButton;
186d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
187d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
188d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private String mCropValue;
189d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private Uri mSaveUri;
190d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
191d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // Small indicators which show the camera settings in the viewfinder.
192d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private TextView mExposureIndicator;
193d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ImageView mGpsIndicator;
194d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ImageView mFlashIndicator;
195d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ImageView mSceneIndicator;
196d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ImageView mWhiteBalanceIndicator;
197d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ImageView mFocusIndicator;
198d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // A view group that contains all the small indicators.
199d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private Rotatable mOnScreenIndicators;
200d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
201d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // We use a thread in ImageSaver to do the work of saving images and
202d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // generating thumbnails. This reduces the shot-to-shot time.
203d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ImageSaver mImageSaver;
204d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // Similarly, we use a thread to generate the name of the picture and insert
205d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // it into MediaStore while picture taking is still in progress.
206d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ImageNamer mImageNamer;
207d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
208d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private SoundClips.Player mSoundPlayer;
209d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
210d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private Runnable mDoSnapRunnable = new Runnable() {
211d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
212d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void run() {
213d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            onShutterButtonClick();
214d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
215d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    };
216d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
217d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final StringBuilder mBuilder = new StringBuilder();
218d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final Formatter mFormatter = new Formatter(mBuilder);
219d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final Object[] mFormatterArgs = new Object[1];
220d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
221d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    /**
222d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb     * An unpublished intent flag requesting to return as soon as capturing
223d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb     * is completed.
224d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb     *
225d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb     * TODO: consider publishing by moving into MediaStore.
226d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb     */
227d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final String EXTRA_QUICK_CAPTURE =
228d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            "android.intent.extra.quickCapture";
229d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
230d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // The display rotation in degrees. This is only valid when mCameraState is
231d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // not PREVIEW_STOPPED.
232d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mDisplayRotation;
233d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // The value for android.hardware.Camera.setDisplayOrientation.
234d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mCameraDisplayOrientation;
235d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // The value for UI components like indicators.
236d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mDisplayOrientation;
237d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // The value for android.hardware.Camera.Parameters.setRotation.
238d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mJpegRotation;
239d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mFirstTimeInitialized;
240d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mIsImageCaptureIntent;
241d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
242d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int PREVIEW_STOPPED = 0;
243d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int IDLE = 1;  // preview is active
244d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // Focus is in progress. The exact focus state is in Focus.java.
245d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int FOCUSING = 2;
246d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int SNAPSHOT_IN_PROGRESS = 3;
247d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // Switching between cameras.
248d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static final int SWITCHING_CAMERA = 4;
249d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int mCameraState = PREVIEW_STOPPED;
250d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mSnapshotOnIdle = false;
251d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
252d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private ContentResolver mContentResolver;
253d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mDidRegister = false;
254d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
255d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private LocationManager mLocationManager;
256d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
257d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final ShutterCallback mShutterCallback = new ShutterCallback();
258d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final PostViewPictureCallback mPostViewPictureCallback =
259d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            new PostViewPictureCallback();
260d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final RawPictureCallback mRawPictureCallback =
261d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            new RawPictureCallback();
262d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final AutoFocusCallback mAutoFocusCallback =
263d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            new AutoFocusCallback();
264d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final Object mAutoFocusMoveCallback =
265d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
266d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            ? new AutoFocusMoveCallback()
267d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            : null;
268d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
269d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
270d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
271d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private long mFocusStartTime;
272d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private long mShutterCallbackTime;
273d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private long mPostViewPictureCallbackTime;
274d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private long mRawPictureCallbackTime;
275d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private long mJpegPictureCallbackTime;
276d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private long mOnResumeTime;
277d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private byte[] mJpegImageData;
278d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
279d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // These latency time are for the CameraLatency test.
280d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public long mAutoFocusTime;
281d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public long mShutterLag;
282d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public long mShutterToPictureDisplayedTime;
283d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public long mPictureDisplayedToJpegCallbackTime;
284d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public long mJpegCallbackFinishTime;
285d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public long mCaptureStartTime;
286d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
287d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // This handles everything about focus.
288d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private FocusManager mFocusManager;
289d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private String mSceneMode;
290d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private Toast mNotSelectableToast;
291d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
292d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final Handler mHandler = new MainHandler();
293d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private IndicatorControlContainer mIndicatorControlContainer;
294d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private PreferenceGroup mPreferenceGroup;
295d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
296d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean mQuickCapture;
297d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
298d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    CameraStartUpThread mCameraStartUpThread;
299d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
300d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
301d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    protected class CameraOpenThread extends Thread {
302d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
303d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void run() {
304d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            try {
305d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mCameraDevice = Util.openCamera(mActivity, mCameraId);
306d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mParameters = mCameraDevice.getParameters();
307d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } catch (CameraHardwareException e) {
308d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mOpenCameraFail = true;
309d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } catch (CameraDisabledException e) {
310d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mCameraDisabled = true;
311d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
312d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
313d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
314d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
315d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // The purpose is not to block the main thread in onCreate and onResume.
316d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private class CameraStartUpThread extends Thread {
317d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private volatile boolean mCancelled;
318d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
319d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void cancel() {
320d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCancelled = true;
321d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
322d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
323d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
324d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void run() {
325d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            try {
326d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // We need to check whether the activity is paused before long
327d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // operations to ensure that onPause() can be done ASAP.
328d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mCancelled) return;
329d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mCameraDevice = Util.openCamera(mActivity, mCameraId);
330d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mParameters = mCameraDevice.getParameters();
331d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // Wait until all the initialization needed by startPreview are
332d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // done.
333d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mStartPreviewPrerequisiteReady.block();
334d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
335d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                initializeCapabilities();
336d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mFocusManager == null) initializeFocusManager();
337d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mCancelled) return;
338d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                setCameraParameters(UPDATE_PARAM_ALL);
339d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
340d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mCancelled) return;
341d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                startPreview();
342d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mHandler.sendEmptyMessage(START_PREVIEW_DONE);
343d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mOnResumeTime = SystemClock.uptimeMillis();
344d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
345d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } catch (CameraHardwareException e) {
346d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
347d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } catch (CameraDisabledException e) {
348d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mHandler.sendEmptyMessage(CAMERA_DISABLED);
349d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
350d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
351d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
352d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
353d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    /**
354d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb     * This Handler is used to post message back onto the main thread of the
355d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb     * application
356d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb     */
357d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private class MainHandler extends Handler {
358d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
359d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void handleMessage(Message msg) {
360d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            switch (msg.what) {
361d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case SETUP_PREVIEW: {
362d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    setupPreview();
363d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
364d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
365d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
366d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case CLEAR_SCREEN_DELAY: {
367d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mActivity.getWindow().clearFlags(
368d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
369d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
370d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
371d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
372d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case FIRST_TIME_INIT: {
373d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    initializeFirstTime();
374d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
375d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
376d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
377d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
378d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    setCameraParametersWhenIdle(0);
379d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
380d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
381d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
382d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case CHECK_DISPLAY_ROTATION: {
383d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // Set the display orientation if display rotation has changed.
384d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // Sometimes this happens when the device is held upside
385d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // down and camera app is opened. Rotation animation will
386d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // take some time and the rotation value we have got may be
387d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // wrong. Framework does not have a callback for this now.
388d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
389d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        setDisplayOrientation();
390d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
391d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
392d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
393d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
394d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
395d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
396d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
397d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case SHOW_TAP_TO_FOCUS_TOAST: {
398d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    showTapToFocusToast();
399d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
400d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
401d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
402d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case UPDATE_THUMBNAIL: {
403d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mImageSaver.updateThumbnail();
404d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
405d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
406d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
407d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case SWITCH_CAMERA: {
408d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    switchCamera();
409d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
410d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
411d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
412d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case SWITCH_CAMERA_START_ANIMATION: {
413d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    ((CameraScreenNail) PhotoModule.this.getScreenNail()).animateSwitchCamera();
414d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
415d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
416d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
417d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case CAMERA_OPEN_DONE: {
418d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    initializeAfterCameraOpen();
419d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
420d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
421d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
422d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case START_PREVIEW_DONE: {
423d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mCameraStartUpThread = null;
424d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    setCameraState(IDLE);
425d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    if (!ApiHelper.HAS_SURFACE_TEXTURE) {
426d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        // This may happen if surfaceCreated has arrived.
427d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder);
428d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
429d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    startFaceDetection();
430d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
431d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
432d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
433d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case OPEN_CAMERA_FAIL: {
434d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mCameraStartUpThread = null;
435d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mOpenCameraFail = true;
436d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    Util.showErrorAndFinish(mActivity,
437d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                            R.string.cannot_connect_camera);
438d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
439d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
440d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
441d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                case CAMERA_DISABLED: {
442d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mCameraStartUpThread = null;
443d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mCameraDisabled = true;
444d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    Util.showErrorAndFinish(mActivity,
445d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                            R.string.camera_disabled);
446d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    break;
447d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
448d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
449d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
450d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
451d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
452d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
453d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void init(CameraActivity activity, View parent) {
454d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity = activity;
455d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mRootView = parent;
456d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPreferences = new ComboPreferences(mActivity);
457d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
458d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraId = getPreferredCameraId(mPreferences);
459d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
460d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mContentResolver = mActivity.getContentResolver();
461d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
462d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // To reduce startup time, open the camera and start the preview in
463d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // another thread.
464d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraStartUpThread = new CameraStartUpThread();
465d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraStartUpThread.start();
466d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
467d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity.getLayoutInflater().inflate(R.layout.photo_module, (ViewGroup) mRootView);
468d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
469d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Surface texture is from camera screen nail and startPreview needs it.
470d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // This must be done before startPreview.
471d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mIsImageCaptureIntent = isImageCaptureIntent();
472d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity.reuseCameraScreenNail(!mIsImageCaptureIntent);
473d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
474d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPreferences.setLocalId(mActivity, mCameraId);
475d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
476d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusAreaIndicator = (RotateLayout) mRootView.findViewById(
477d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                R.id.focus_indicator_rotate_layout);
478d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // we need to reset exposure for the preview
479d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        resetExposureCompensation();
480d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Starting the preview needs preferences, camera screen nail, and
481d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // focus area indicator.
482d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mStartPreviewPrerequisiteReady.open();
483d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
484d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeControlByIntent();
485d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mRotateDialog = new RotateDialogController(mActivity, R.layout.rotate_dialog);
486d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
487d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeMiscControls();
488d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mLocationManager = new LocationManager(mActivity, this);
489d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initOnScreenIndicator();
490d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Make sure all views are disabled before camera is open.
491d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        enableCameraControls(false);
492d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
493d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
494d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
495d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void initializeAfterCameraOpen() {
496d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // These depend on camera parameters.
497d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setPreviewFrameLayoutAspectRatio();
498d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusManager.setPreviewSize(mPreviewFrameLayout.getWidth(),
499d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mPreviewFrameLayout.getHeight());
500d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mIndicatorControlContainer == null) {
501d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            initializeIndicatorControl();
502d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
503d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // This should be enabled after preview is started.
504d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mIndicatorControlContainer.setEnabled(false);
505d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeZoom();
506d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateOnScreenIndicators();
507d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        showTapToFocusToastIfNeeded();
508d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
509d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
510d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void resetExposureCompensation() {
511d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
512d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraSettings.EXPOSURE_DEFAULT_VALUE);
513d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
514d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Editor editor = mPreferences.edit();
515d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            editor.putString(CameraSettings.KEY_EXPOSURE, "0");
516d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            editor.apply();
517d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
518d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
519d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
520d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void keepMediaProviderInstance() {
521d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // We want to keep a reference to MediaProvider in camera's lifecycle.
522d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // TODO: Utilize mMediaProviderClient instance to replace
523d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // ContentResolver calls.
524d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mMediaProviderClient == null) {
525d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mMediaProviderClient = mContentResolver
526d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    .acquireContentProviderClient(MediaStore.AUTHORITY);
527d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
528d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
529d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
530d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // Snapshots can only be taken after this is called. It should be called
531d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // once only. We could have done these things in onCreate() but we want to
532d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // make preview screen appear as soon as possible.
533d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void initializeFirstTime() {
534d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFirstTimeInitialized) return;
535d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
536d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Create orientation listener. This should be done first because it
537d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // takes some time to get first orientation.
538d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mOrientationListener = new MyOrientationEventListener(mActivity);
539d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mOrientationListener.enable();
540d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
541d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Initialize location service.
542d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        boolean recordLocation = RecordLocationPreference.get(
543d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mPreferences, mContentResolver);
544d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mLocationManager.recordLocation(recordLocation);
545d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
546d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        keepMediaProviderInstance();
547d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
548d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Initialize shutter button.
549d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mShutterButton = mActivity.getShutterButton();
550d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mShutterButton.setImageResource(R.drawable.btn_shutter);
551d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mShutterButton.setOnShutterButtonListener(this);
552d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mShutterButton.setVisibility(View.VISIBLE);
553d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
554d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mImageSaver = new ImageSaver();
555d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mImageNamer = new ImageNamer();
556d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
557d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFirstTimeInitialized = true;
558d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        addIdleHandler();
559d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
560d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity.updateStorageSpaceAndHint();
561d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
562d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
563d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void showTapToFocusToastIfNeeded() {
564d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Show the tap to focus toast if this is the first start.
565d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFocusAreaSupported &&
566d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
567d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Delay the toast for one second to wait for orientation.
568d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
569d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
570d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
571d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
572d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void addIdleHandler() {
573d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        MessageQueue queue = Looper.myQueue();
574d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        queue.addIdleHandler(new MessageQueue.IdleHandler() {
575d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            @Override
576d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            public boolean queueIdle() {
577d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Storage.ensureOSXCompatible();
578d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                return false;
579d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
580d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        });
581d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
582d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
583d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // If the activity is paused and resumed, this method will be called in
584d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // onResume.
585d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void initializeSecondTime() {
586d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Start orientation listener as soon as possible because it takes
587d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // some time to get first orientation.
588d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mOrientationListener.enable();
589d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
590d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Start location update if needed.
591d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        boolean recordLocation = RecordLocationPreference.get(
592d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mPreferences, mContentResolver);
593d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mLocationManager.recordLocation(recordLocation);
594d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
595d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mImageSaver = new ImageSaver();
596d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mImageNamer = new ImageNamer();
597d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeZoom();
598d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        keepMediaProviderInstance();
599d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        hidePostCaptureAlert();
600d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
601d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!mIsImageCaptureIntent) {
602d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mModePicker != null) {
603d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mModePicker.setCurrentMode(ModePicker.MODE_CAMERA);
604d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
605d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
606d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mIndicatorControlContainer != null) {
607d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mIndicatorControlContainer.reloadPreferences();
608d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
609d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
610d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
611d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private class ZoomChangeListener implements ZoomControl.OnZoomChangedListener {
612d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
613d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void onZoomValueChanged(int index) {
614d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Not useful to change zoom value when the activity is paused.
615d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mPaused) return;
616d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mZoomValue = index;
617d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
618d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Set zoom parameters asynchronously
619d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.setZoom(mZoomValue);
620d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setParametersAsync(mParameters);
621d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
622d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
623d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
624d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void initializeZoom() {
625d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!mParameters.isZoomSupported()) return;
626d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mZoomMax = mParameters.getMaxZoom();
627d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Currently we use immediate zoom for fast zooming to get better UX and
628d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // there is no plan to take advantage of the smooth zoom.
629d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mZoomControl.setZoomMax(mZoomMax);
630d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mZoomControl.setZoomIndex(mParameters.getZoom());
631d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mZoomControl.setOnZoomChangeListener(new ZoomChangeListener());
632d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
633d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
634d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
635d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
636d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void startFaceDetection() {
637d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!ApiHelper.HAS_FACE_DETECTION) return;
638d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFaceDetectionStarted) return;
639d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mParameters.getMaxNumDetectedFaces() > 0) {
640d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceDetectionStarted = true;
641d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.clear();
642d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.setVisibility(View.VISIBLE);
643d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.setDisplayOrientation(mDisplayOrientation);
644d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
645d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT);
646d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.resume();
647d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusManager.setFaceView(mFaceView);
648d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setFaceDetectionListener(new FaceDetectionListener() {
649d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                @Override
650d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                public void onFaceDetection(Face[] faces, android.hardware.Camera camera) {
651d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mFaceView.setFaces(faces);
652d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
653d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            });
654d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.startFaceDetection();
655d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
656d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
657d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
658d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
659d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
660d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
661d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void stopFaceDetection() {
662d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!ApiHelper.HAS_FACE_DETECTION) return;
663d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!mFaceDetectionStarted) return;
664d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mParameters.getMaxNumDetectedFaces() > 0) {
665d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceDetectionStarted = false;
666d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setFaceDetectionListener(null);
667d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.stopFaceDetection();
668d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mFaceView != null) mFaceView.clear();
669d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
670d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
671d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
672d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
673d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public boolean dispatchTouchEvent(MotionEvent m) {
674d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mCameraState == SWITCHING_CAMERA) return true;
675d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
676d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Check if the popup window should be dismissed first.
677d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (m.getAction() == MotionEvent.ACTION_DOWN) {
678d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            float x = m.getX();
679d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            float y = m.getY();
680d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Dismiss the mode selection window if the ACTION_DOWN event is out
681d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // of its view area.
682d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if ((mModePicker != null) && !Util.pointInView(x, y, mModePicker)) {
683d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mModePicker.dismissModeSelection();
684d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
685d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Check if the popup window is visible. Indicator control can be
686d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // null if camera is not opened yet.
687d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mIndicatorControlContainer != null) {
688d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                View popup = mIndicatorControlContainer.getActiveSettingPopup();
689d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (popup != null) {
690d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // Let popup window, indicator control or preview frame
691d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // handle the event by themselves. Dismiss the popup window
692d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // if users touch on other areas.
693d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    if (!Util.pointInView(x, y, popup)
694d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                            && !Util.pointInView(x, y, mIndicatorControlContainer)
695d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                            && !Util.pointInView(x, y, mPreviewFrameLayout)) {
696d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mIndicatorControlContainer.dismissSettingPopup();
697d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
698d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
699d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
700d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
701d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return false;
702d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
703d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
704d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
705d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
706d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void onReceive(Context context, Intent intent) {
707d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            String action = intent.getAction();
708d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (!mIsImageCaptureIntent && action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
709d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.getLastThumbnail();
710d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
711d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
712d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    };
713d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
714d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void initOnScreenIndicator() {
715d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mGpsIndicator = (ImageView) mRootView.findViewById(R.id.onscreen_gps_indicator);
716d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mExposureIndicator = (TextView) mRootView.findViewById(R.id.onscreen_exposure_indicator);
717d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFlashIndicator = (ImageView) mRootView.findViewById(R.id.onscreen_flash_indicator);
718d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mSceneIndicator = (ImageView) mRootView.findViewById(R.id.onscreen_scene_indicator);
719d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mWhiteBalanceIndicator =
720d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                (ImageView) mRootView.findViewById(R.id.onscreen_white_balance_indicator);
721d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusIndicator = (ImageView) mRootView.findViewById(R.id.onscreen_focus_indicator);
722d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
723d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
724d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
725d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void showGpsOnScreenIndicator(boolean hasSignal) {
726d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mGpsIndicator == null) {
727d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
728d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
729d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (hasSignal) {
730d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mGpsIndicator.setImageResource(R.drawable.ic_viewfinder_gps_on);
731d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
732d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mGpsIndicator.setImageResource(R.drawable.ic_viewfinder_gps_no_signal);
733d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
734d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mGpsIndicator.setVisibility(View.VISIBLE);
735d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
736d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
737d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
738d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void hideGpsOnScreenIndicator() {
739d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mGpsIndicator == null) {
740d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
741d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
742d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mGpsIndicator.setVisibility(View.GONE);
743d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
744d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
745d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateExposureOnScreenIndicator(int value) {
746d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mExposureIndicator == null) {
747d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
748d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
749d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (value == 0) {
750d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mExposureIndicator.setText("");
751d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mExposureIndicator.setVisibility(View.GONE);
752d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
753d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            float step = mParameters.getExposureCompensationStep();
754d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFormatterArgs[0] = value * step;
755d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mBuilder.delete(0, mBuilder.length());
756d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFormatter.format("%+1.1f", mFormatterArgs);
757d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            String exposure = mFormatter.toString();
758d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mExposureIndicator.setText(exposure);
759d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mExposureIndicator.setVisibility(View.VISIBLE);
760d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
761d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
762d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
763d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateFlashOnScreenIndicator(String value) {
764d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFlashIndicator == null) {
765d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
766d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
767d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (value == null || Parameters.FLASH_MODE_OFF.equals(value)) {
768d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFlashIndicator.setVisibility(View.GONE);
769d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
770d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFlashIndicator.setVisibility(View.VISIBLE);
771d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (Parameters.FLASH_MODE_AUTO.equals(value)) {
772d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mFlashIndicator.setImageResource(R.drawable.ic_indicators_landscape_flash_auto);
773d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else if (Parameters.FLASH_MODE_ON.equals(value)) {
774d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mFlashIndicator.setImageResource(R.drawable.ic_indicators_landscape_flash_on);
775d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else {
776d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // Should not happen.
777d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mFlashIndicator.setVisibility(View.GONE);
778d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
779d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
780d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
781d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
782d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateSceneOnScreenIndicator(String value) {
783d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mSceneIndicator == null) {
784d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
785d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
786d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        boolean isGone = (value == null) || (Parameters.SCENE_MODE_AUTO.equals(value));
787d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mSceneIndicator.setVisibility(isGone ? View.GONE : View.VISIBLE);
788d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
789d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
790d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateWhiteBalanceOnScreenIndicator(String value) {
791d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mWhiteBalanceIndicator == null) {
792d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
793d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
794d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (value == null || Parameters.WHITE_BALANCE_AUTO.equals(value)) {
795d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mWhiteBalanceIndicator.setVisibility(View.GONE);
796d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
797d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mWhiteBalanceIndicator.setVisibility(View.VISIBLE);
798d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (Parameters.WHITE_BALANCE_FLUORESCENT.equals(value)) {
799d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mWhiteBalanceIndicator.setImageResource(R.drawable.ic_indicators_fluorescent);
800d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else if (Parameters.WHITE_BALANCE_INCANDESCENT.equals(value)) {
801d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mWhiteBalanceIndicator.setImageResource(R.drawable.ic_indicators_incandescent);
802d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else if (Parameters.WHITE_BALANCE_DAYLIGHT.equals(value)) {
803d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mWhiteBalanceIndicator.setImageResource(R.drawable.ic_indicators_sunlight);
804d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else if (Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT.equals(value)) {
805d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mWhiteBalanceIndicator.setImageResource(R.drawable.ic_indicators_cloudy);
806d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else {
807d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // Should not happen.
808d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mWhiteBalanceIndicator.setVisibility(View.GONE);
809d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
810d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
811d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
812d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
813d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateFocusOnScreenIndicator(String value) {
814d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFocusIndicator == null) {
815d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
816d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
817d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Do not show the indicator if users cannot choose.
818d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPreferenceGroup.findPreference(CameraSettings.KEY_FOCUS_MODE) == null) {
819d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusIndicator.setVisibility(View.GONE);
820d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
821d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusIndicator.setVisibility(View.VISIBLE);
822d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (Parameters.FOCUS_MODE_INFINITY.equals(value)) {
823d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mFocusIndicator.setImageResource(R.drawable.ic_indicators_landscape);
824d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else if (Parameters.FOCUS_MODE_MACRO.equals(value)) {
825d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mFocusIndicator.setImageResource(R.drawable.ic_indicators_macro);
826d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else {
827d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // Should not happen.
828d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mFocusIndicator.setVisibility(View.GONE);
829d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
830d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
831d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
832d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
833d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateOnScreenIndicators() {
834d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateSceneOnScreenIndicator(mParameters.getSceneMode());
835d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateExposureOnScreenIndicator(CameraSettings.readExposure(mPreferences));
836d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateFlashOnScreenIndicator(mParameters.getFlashMode());
837d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateWhiteBalanceOnScreenIndicator(mParameters.getWhiteBalance());
838d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateFocusOnScreenIndicator(mParameters.getFocusMode());
839d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
840d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
841d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final class ShutterCallback
842d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            implements android.hardware.Camera.ShutterCallback {
843d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
844d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void onShutter() {
845d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mShutterCallbackTime = System.currentTimeMillis();
846d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mShutterLag = mShutterCallbackTime - mCaptureStartTime;
847d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
848d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
849d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
850d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
851d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final class PostViewPictureCallback implements PictureCallback {
852d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
853d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void onPictureTaken(
854d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                byte [] data, android.hardware.Camera camera) {
855d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mPostViewPictureCallbackTime = System.currentTimeMillis();
856d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Log.v(TAG, "mShutterToPostViewCallbackTime = "
857d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    + (mPostViewPictureCallbackTime - mShutterCallbackTime)
858d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    + "ms");
859d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
860d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
861d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
862d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final class RawPictureCallback implements PictureCallback {
863d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
864d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void onPictureTaken(
865d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                byte [] rawData, android.hardware.Camera camera) {
866d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mRawPictureCallbackTime = System.currentTimeMillis();
867d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Log.v(TAG, "mShutterToRawCallbackTime = "
868d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
869d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
870d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
871d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
872d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final class JpegPictureCallback implements PictureCallback {
873d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Location mLocation;
874d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
875d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public JpegPictureCallback(Location loc) {
876d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mLocation = loc;
877d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
878d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
879d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
880d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void onPictureTaken(
881d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                final byte [] jpegData, final android.hardware.Camera camera) {
882d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mPaused) {
883d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                return;
884d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
885d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
886d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mJpegPictureCallbackTime = System.currentTimeMillis();
887d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // If postview callback has arrived, the captured image is displayed
888d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // in postview callback. If not, the captured image is displayed in
889d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // raw picture callback.
890d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mPostViewPictureCallbackTime != 0) {
891d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mShutterToPictureDisplayedTime =
892d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mPostViewPictureCallbackTime - mShutterCallbackTime;
893d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mPictureDisplayedToJpegCallbackTime =
894d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
895d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else {
896d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mShutterToPictureDisplayedTime =
897d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mRawPictureCallbackTime - mShutterCallbackTime;
898d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mPictureDisplayedToJpegCallbackTime =
899d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mJpegPictureCallbackTime - mRawPictureCallbackTime;
900d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
901d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
902d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    + mPictureDisplayedToJpegCallbackTime + "ms");
903d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
904d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
905d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (!mIsImageCaptureIntent) {
906d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
907d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    setupPreview();
908d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                } else {
909d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // Camera HAL of some devices have a bug. Starting preview
910d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // immediately after taking a picture will fail. Wait some
911d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // time before starting the preview.
912d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
913d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
914d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
915d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
916d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (!mIsImageCaptureIntent) {
917d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // Calculate the width and the height of the jpeg.
918d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Size s = mParameters.getPictureSize();
919d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                int orientation = Exif.getOrientation(jpegData);
920d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                int width, height;
921d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if ((mJpegRotation + orientation) % 180 == 0) {
922d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    width = s.width;
923d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    height = s.height;
924d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                } else {
925d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    width = s.height;
926d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    height = s.width;
927d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
928d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Uri uri = mImageNamer.getUri();
929d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.addSecureAlbumItemIfNeeded(false, uri);
930d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                String title = mImageNamer.getTitle();
931d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mImageSaver.addImage(jpegData, uri, title, mLocation,
932d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        width, height, mActivity.mThumbnailViewWidth, orientation);
933d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else {
934d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mJpegImageData = jpegData;
935d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (!mQuickCapture) {
936d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    showPostCaptureAlert();
937d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                } else {
938d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    doAttach();
939d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
940d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
941d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
942d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Check this in advance of each shot so we don't add to shutter
943d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // latency. It's true that someone else could write to the SD card in
944d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // the mean time and fill it, but that could have happened between the
945d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // shutter press and saving the JPEG too.
946d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mActivity.updateStorageSpaceAndHint();
947d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
948d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            long now = System.currentTimeMillis();
949d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
950d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Log.v(TAG, "mJpegCallbackFinishTime = "
951d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    + mJpegCallbackFinishTime + "ms");
952d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mJpegPictureCallbackTime = 0;
953d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
954d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
955d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
956d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final class AutoFocusCallback
957d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            implements android.hardware.Camera.AutoFocusCallback {
958d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
959d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void onAutoFocus(
960d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                boolean focused, android.hardware.Camera camera) {
961d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mPaused) return;
962d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
963d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
964d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
965d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            setCameraState(IDLE);
966d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusManager.onAutoFocus(focused);
967d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
968d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
969d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
970d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
971d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private final class AutoFocusMoveCallback
972d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            implements android.hardware.Camera.AutoFocusMoveCallback {
973d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
974d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void onAutoFocusMoving(
975d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            boolean moving, android.hardware.Camera camera) {
976d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mFocusManager.onAutoFocusMoving(moving);
977d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
978d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
979d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
980d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // Each SaveRequest remembers the data needed to save an image.
981d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static class SaveRequest {
982d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        byte[] data;
983d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Uri uri;
984d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        String title;
985d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Location loc;
986d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        int width, height;
987d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        int thumbnailWidth;
988d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        int orientation;
989d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
990d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
991d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // We use a queue to store the SaveRequests that have not been completed
992d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // yet. The main thread puts the request into the queue. The saver thread
993d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // gets it from the queue, does the work, and removes it from the queue.
994d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    //
995d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // The main thread needs to wait for the saver thread to finish all the work
996d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // in the queue, when the activity's onPause() is called, we need to finish
997d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // all the work, so other programs (like Gallery) can see all the images.
998d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    //
999d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // If the queue becomes too long, adding a new request will block the main
1000d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // thread until the queue length drops below the threshold (QUEUE_LIMIT).
1001d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // If we don't do this, we may face several problems: (1) We may OOM
1002d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // because we are holding all the jpeg data in memory. (2) We may ANR
1003d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // when we need to wait for saver thread finishing all the work (in
1004d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // onPause() or gotoGallery()) because the time to finishing a long queue
1005d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // of work may be too long.
1006d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private class ImageSaver extends Thread {
1007d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private static final int QUEUE_LIMIT = 3;
1008d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1009d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private ArrayList<SaveRequest> mQueue;
1010d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private Thumbnail mPendingThumbnail;
1011d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private Object mUpdateThumbnailLock = new Object();
1012d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private boolean mStop;
1013d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1014d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in main thread
1015d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public ImageSaver() {
1016d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mQueue = new ArrayList<SaveRequest>();
1017d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            start();
1018d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1019d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1020d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in main thread
1021d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void addImage(final byte[] data, Uri uri, String title,
1022d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Location loc, int width, int height, int thumbnailWidth,
1023d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                int orientation) {
1024d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            SaveRequest r = new SaveRequest();
1025d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            r.data = data;
1026d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            r.uri = uri;
1027d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            r.title = title;
1028d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            r.loc = (loc == null) ? null : new Location(loc);  // make a copy
1029d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            r.width = width;
1030d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            r.height = height;
1031d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            r.thumbnailWidth = thumbnailWidth;
1032d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            r.orientation = orientation;
1033d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            synchronized (this) {
1034d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                while (mQueue.size() >= QUEUE_LIMIT) {
1035d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    try {
1036d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        wait();
1037d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    } catch (InterruptedException ex) {
1038d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        // ignore.
1039d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
1040d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1041d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mQueue.add(r);
1042d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                notifyAll();  // Tell saver thread there is new work to do.
1043d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1044d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1045d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1046d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in saver thread
1047d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
1048d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void run() {
1049d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            while (true) {
1050d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                SaveRequest r;
1051d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                synchronized (this) {
1052d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    if (mQueue.isEmpty()) {
1053d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        notifyAll();  // notify main thread in waitDone
1054d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1055d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        // Note that we can only stop after we saved all images
1056d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        // in the queue.
1057d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        if (mStop) break;
1058d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1059d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        try {
1060d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                            wait();
1061d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        } catch (InterruptedException ex) {
1062d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                            // ignore.
1063d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        }
1064d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        continue;
1065d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
1066d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    r = mQueue.get(0);
1067d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1068d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                storeImage(r.data, r.uri, r.title, r.loc, r.width, r.height,
1069d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        r.thumbnailWidth, r.orientation);
1070d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                synchronized (this) {
1071d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mQueue.remove(0);
1072d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    notifyAll();  // the main thread may wait in addImage
1073d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1074d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1075d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1076d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1077d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in main thread
1078d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void waitDone() {
1079d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            synchronized (this) {
1080d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                while (!mQueue.isEmpty()) {
1081d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    try {
1082d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        wait();
1083d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    } catch (InterruptedException ex) {
1084d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        // ignore.
1085d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
1086d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1087d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1088d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            updateThumbnail();
1089d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1090d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1091d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in main thread
1092d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void finish() {
1093d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            waitDone();
1094d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            synchronized (this) {
1095d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mStop = true;
1096d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                notifyAll();
1097d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1098d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            try {
1099d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                join();
1100d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } catch (InterruptedException ex) {
1101d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // ignore.
1102d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1103d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1104d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1105d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in main thread (because we need to update mThumbnailView in the
1106d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // main thread)
1107d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void updateThumbnail() {
1108d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Thumbnail t;
1109d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            synchronized (mUpdateThumbnailLock) {
1110d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mHandler.removeMessages(UPDATE_THUMBNAIL);
1111d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                t = mPendingThumbnail;
1112d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mPendingThumbnail = null;
1113d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1114d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1115d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (t != null) {
1116d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.mThumbnail = t;
1117d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mActivity.mThumbnailView != null) {
1118d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mActivity.mThumbnailView.setBitmap(mActivity.mThumbnail.getBitmap());
1119d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1120d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1121d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1122d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1123d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in saver thread
1124d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private void storeImage(final byte[] data, Uri uri, String title,
1125d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Location loc, int width, int height, int thumbnailWidth,
1126d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                int orientation) {
1127d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            boolean ok = Storage.updateImage(mContentResolver, uri, title, loc,
1128d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    orientation, data, width, height);
1129d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (ok) {
1130d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                boolean needThumbnail;
1131d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                synchronized (this) {
1132d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // If the number of requests in the queue (include the
1133d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // current one) is greater than 1, we don't need to generate
1134d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // thumbnail for this image. Because we'll soon replace it
1135d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // with the thumbnail for some image later in the queue.
1136d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    needThumbnail = (mQueue.size() <= 1);
1137d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1138d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (needThumbnail) {
1139d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // Create a thumbnail whose width is equal or bigger than
1140d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // that of the thumbnail view.
1141d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    int ratio = (int) Math.ceil((double) width / thumbnailWidth);
1142d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    int inSampleSize = Integer.highestOneBit(ratio);
1143d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    Thumbnail t = Thumbnail.createThumbnail(
1144d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                                data, orientation, inSampleSize, uri);
1145d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    synchronized (mUpdateThumbnailLock) {
1146d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        // We need to update the thumbnail in the main thread,
1147d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        // so send a message to run updateThumbnail().
1148d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mPendingThumbnail = t;
1149d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mHandler.sendEmptyMessage(UPDATE_THUMBNAIL);
1150d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
1151d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1152d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Util.broadcastNewPicture(mActivity, uri);
1153d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1154d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1155d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1156d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1157d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private static class ImageNamer extends Thread {
1158d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private boolean mRequestPending;
1159d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private ContentResolver mResolver;
1160d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private long mDateTaken;
1161d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private int mWidth, mHeight;
1162d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private boolean mStop;
1163d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private Uri mUri;
1164d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private String mTitle;
1165d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1166d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in main thread
1167d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public ImageNamer() {
1168d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            start();
1169d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1170d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1171d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in main thread
1172d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public synchronized void prepareUri(ContentResolver resolver,
1173d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                long dateTaken, int width, int height, int rotation) {
1174d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (rotation % 180 != 0) {
1175d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                int tmp = width;
1176d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                width = height;
1177d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                height = tmp;
1178d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1179d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mRequestPending = true;
1180d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mResolver = resolver;
1181d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mDateTaken = dateTaken;
1182d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mWidth = width;
1183d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mHeight = height;
1184d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            notifyAll();
1185d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1186d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1187d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in main thread
1188d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public synchronized Uri getUri() {
1189d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // wait until the request is done.
1190d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            while (mRequestPending) {
1191d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                try {
1192d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    wait();
1193d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                } catch (InterruptedException ex) {
1194d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // ignore.
1195d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1196d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1197d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1198d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // return the uri generated
1199d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Uri uri = mUri;
1200d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mUri = null;
1201d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return uri;
1202d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1203d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1204d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in main thread, should be called after getUri().
1205d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public synchronized String getTitle() {
1206d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return mTitle;
1207d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1208d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1209d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in namer thread
1210d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
1211d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public synchronized void run() {
1212d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            while (true) {
1213d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mStop) break;
1214d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (!mRequestPending) {
1215d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    try {
1216d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        wait();
1217d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    } catch (InterruptedException ex) {
1218d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        // ignore.
1219d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
1220d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    continue;
1221d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1222d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                cleanOldUri();
1223d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                generateUri();
1224d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mRequestPending = false;
1225d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                notifyAll();
1226d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1227d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            cleanOldUri();
1228d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1229d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1230d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in main thread
1231d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public synchronized void finish() {
1232d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mStop = true;
1233d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            notifyAll();
1234d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1235d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1236d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in namer thread
1237d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private void generateUri() {
1238d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mTitle = Util.createJpegName(mDateTaken);
1239d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mUri = Storage.newImage(mResolver, mTitle, mDateTaken, mWidth, mHeight);
1240d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1241d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1242d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Runs in namer thread
1243d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        private void cleanOldUri() {
1244d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mUri == null) return;
1245d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Storage.deleteImage(mResolver, mUri);
1246d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mUri = null;
1247d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1248d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1249d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1250d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setCameraState(int state) {
1251d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraState = state;
1252d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        switch (state) {
1253d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            case PREVIEW_STOPPED:
1254d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            case SNAPSHOT_IN_PROGRESS:
1255d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            case FOCUSING:
1256d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            case SWITCHING_CAMERA:
1257d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                enableCameraControls(false);
1258d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                break;
1259d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            case IDLE:
1260d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                enableCameraControls(true);
1261d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                break;
1262d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1263d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1264d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1265d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1266d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public boolean capture() {
1267d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // If we are already in the middle of taking a snapshot then ignore.
1268d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
1269d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                || mCameraState == SWITCHING_CAMERA) {
1270d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return false;
1271d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1272d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCaptureStartTime = System.currentTimeMillis();
1273d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPostViewPictureCallbackTime = 0;
1274d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mJpegImageData = null;
1275d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1276d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Set rotation and gps data.
1277d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mJpegRotation = Util.getJpegRotation(mCameraId, mOrientation);
1278d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mParameters.setRotation(mJpegRotation);
1279d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Location loc = mLocationManager.getCurrentLocation();
1280d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Util.setGpsParameters(mParameters, loc);
1281d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraDevice.setParameters(mParameters);
1282d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1283d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback,
1284d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mPostViewPictureCallback, new JpegPictureCallback(loc));
1285d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1286d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Size size = mParameters.getPictureSize();
1287d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mImageNamer.prepareUri(mContentResolver, mCaptureStartTime,
1288d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                size.width, size.height, mJpegRotation);
1289d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1290d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent) {
1291d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Start capture animation.
1292d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            ((CameraScreenNail) getScreenNail()).animateCapture(getCameraRotation());
1293d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1294d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFaceDetectionStarted = false;
1295d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setCameraState(SNAPSHOT_IN_PROGRESS);
1296d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return true;
1297d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1298d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1299d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int getCameraRotation() {
1300d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return (mOrientationCompensation - mDisplayRotation + 360) % 360;
1301d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1302d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1303d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1304d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void setFocusParameters() {
1305d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setCameraParameters(UPDATE_PARAM_PREFERENCE);
1306d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1307d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1308d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1309d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void playSound(int soundId) {
1310d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mSoundPlayer.play(soundId);
1311d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1312d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1313d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private int getPreferredCameraId(ComboPreferences preferences) {
1314d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
1315d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (intentCameraId != -1) {
1316d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Testing purpose. Launch a specific camera through the intent
1317d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // extras.
1318d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return intentCameraId;
1319d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
1320d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return CameraSettings.readPreferredCameraId(preferences);
1321d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1322d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1323d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1324d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1325d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1326d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onFullScreenChanged(boolean full) {
1327d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (ApiHelper.HAS_SURFACE_TEXTURE) return;
1328d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1329d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (full) {
1330d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mPreviewSurfaceView.expand();
1331d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
1332d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mPreviewSurfaceView.shrink();
1333d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1334d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1335d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1336d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1337d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
1338d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Log.v(TAG, "surfaceChanged:" + holder + " width=" + width + ". height="
1339d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                + height);
1340d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1341d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1342d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1343d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void surfaceCreated(SurfaceHolder holder) {
1344d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Log.v(TAG, "surfaceCreated: " + holder);
1345d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraSurfaceHolder = holder;
1346d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Do not access the camera if camera start up thread is not finished.
1347d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mCameraDevice == null || mCameraStartUpThread != null) return;
1348d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1349d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraDevice.setPreviewDisplayAsync(holder);
1350d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // This happens when onConfigurationChanged arrives, surface has been
1351d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // destroyed, and there is no onFullScreenChanged.
1352d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mCameraState == PREVIEW_STOPPED) {
1353d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            setupPreview();
1354d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1355d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1356d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1357d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1358d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void surfaceDestroyed(SurfaceHolder holder) {
1359d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Log.v(TAG, "surfaceDestroyed: " + holder);
1360d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraSurfaceHolder = null;
1361d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        stopPreview();
1362d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1363d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1364d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void overrideCameraSettings(final String flashMode,
1365d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            final String whiteBalance, final String focusMode) {
1366d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mIndicatorControlContainer != null) {
1367d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mIndicatorControlContainer.enableFilter(true);
1368d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mIndicatorControlContainer.overrideSettings(
1369d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    CameraSettings.KEY_FLASH_MODE, flashMode,
1370d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
1371d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    CameraSettings.KEY_FOCUS_MODE, focusMode);
1372d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mIndicatorControlContainer.enableFilter(false);
1373d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1374d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1375d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1376d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateSceneModeUI() {
1377d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // If scene mode is set, we cannot set flash mode, white balance, and
1378d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // focus mode, instead, we read it from driver
1379d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1380d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            overrideCameraSettings(mParameters.getFlashMode(),
1381d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mParameters.getWhiteBalance(), mParameters.getFocusMode());
1382d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
1383d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            overrideCameraSettings(null, null, null);
1384d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1385d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1386d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1387d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void loadCameraPreferences() {
1388d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
1389d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mCameraId, CameraHolder.instance().getCameraInfo());
1390d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
1391d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1392d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1393d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void initializeIndicatorControl() {
1394d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // setting the indicator buttons.
1395d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mIndicatorControlContainer =
1396d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                (IndicatorControlContainer) mRootView.findViewById(R.id.indicator_control);
1397d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        loadCameraPreferences();
1398d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        final String[] SETTING_KEYS = {
1399d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraSettings.KEY_FLASH_MODE,
1400d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraSettings.KEY_WHITE_BALANCE,
1401d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraSettings.KEY_EXPOSURE,
1402d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraSettings.KEY_SCENE_MODE};
1403d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        final String[] OTHER_SETTING_KEYS = {
1404d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraSettings.KEY_RECORD_LOCATION,
1405d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraSettings.KEY_PICTURE_SIZE,
1406d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraSettings.KEY_FOCUS_MODE};
1407d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1408d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        CameraPicker.setImageResourceId(R.drawable.ic_switch_photo_facing_holo_light);
1409d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mIndicatorControlContainer.initialize(mActivity, mPreferenceGroup,
1410d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mParameters.isZoomSupported(),
1411d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                SETTING_KEYS, OTHER_SETTING_KEYS);
1412d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraPicker = (CameraPicker) mIndicatorControlContainer.findViewById(
1413d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                R.id.camera_picker);
1414d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateSceneModeUI();
1415d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mIndicatorControlContainer.setListener(this);
1416d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1417d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1418d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1419d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public boolean collapseCameraControls() {
1420d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if ((mIndicatorControlContainer != null)
1421d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                && mIndicatorControlContainer.dismissSettingPopup()) {
1422d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return true;
1423d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1424d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mModePicker != null && mModePicker.dismissModeSelection()) return true;
1425d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return false;
1426d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1427d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1428d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void enableCameraControls(boolean enable) {
1429d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mIndicatorControlContainer != null) {
1430d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mIndicatorControlContainer.setEnabled(enable);
1431d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1432d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mModePicker != null) mModePicker.setEnabled(enable);
1433d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mZoomControl != null) mZoomControl.setEnabled(enable);
1434d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mActivity.mThumbnailView != null) mActivity.mThumbnailView.setEnabled(enable);
1435d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1436d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1437d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private class MyOrientationEventListener
1438d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            extends OrientationEventListener {
1439d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public MyOrientationEventListener(Context context) {
1440d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            super(context);
1441d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1442d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1443d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        @Override
1444d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        public void onOrientationChanged(int orientation) {
1445d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // We keep the last known orientation. So if the user first orient
1446d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // the camera then point the camera to floor or sky, we still have
1447d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // the correct orientation.
1448d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (orientation == ORIENTATION_UNKNOWN) return;
1449d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mOrientation = Util.roundOrientation(orientation, mOrientation);
1450d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // When the screen is unlocked, display rotation may change. Always
1451d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // calculate the up-to-date orientationCompensation.
1452d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            int orientationCompensation =
1453d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    (mOrientation + Util.getDisplayRotation(mActivity)) % 360;
1454d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mOrientationCompensation != orientationCompensation) {
1455d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mOrientationCompensation = orientationCompensation;
1456d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                setOrientationIndicator(mOrientationCompensation, true);
1457d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1458d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1459d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Show the toast after getting the first orientation changed.
1460d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1461d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1462d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                showTapToFocusToast();
1463d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1464d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1465d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1466d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1467d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setOrientationIndicator(int orientation, boolean animation) {
1468d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Rotatable[] indicators = {
1469d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mIndicatorControlContainer, mZoomControl, mFocusAreaIndicator, mFaceView,
1470d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mReviewDoneButton, mRotateDialog, mOnScreenIndicators};
1471d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        for (Rotatable indicator : indicators) {
1472d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (indicator != null) indicator.setOrientation(orientation, animation);
1473d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1474d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1475d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // We change the orientation of the review cancel button only for tablet
1476d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // UI because there's a label along with the X icon. For phone UI, we
1477d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // don't change the orientation because there's only a symmetrical X
1478d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // icon.
1479d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mReviewCancelButton instanceof RotateLayout) {
1480d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mReviewCancelButton.setOrientation(orientation, animation);
1481d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1482d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1483d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1484d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1485d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onStop() {
1486d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mMediaProviderClient != null) {
1487d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mMediaProviderClient.release();
1488d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mMediaProviderClient = null;
1489d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1490d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1491d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1492d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @OnClickAttr
1493d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onThumbnailClicked(View v) {
1494d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (isCameraIdle() && mActivity.mThumbnail != null) {
1495d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mImageSaver != null) mImageSaver.waitDone();
1496d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mActivity.gotoGallery();
1497d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1498d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1499d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1500d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // onClick handler for R.id.btn_retake
1501d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @OnClickAttr
1502d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onReviewRetakeClicked(View v) {
1503d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPaused) return;
1504d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1505d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        hidePostCaptureAlert();
1506d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setupPreview();
1507d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1508d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1509d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // onClick handler for R.id.btn_done
1510d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @OnClickAttr
1511d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onReviewDoneClicked(View v) {
1512d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        doAttach();
1513d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1514d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1515d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // onClick handler for R.id.btn_cancel
1516d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @OnClickAttr
1517d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onReviewCancelClicked(View v) {
1518d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        doCancel();
1519d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1520d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1521d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void doAttach() {
1522d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPaused) {
1523d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
1524d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1525d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1526d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        byte[] data = mJpegImageData;
1527d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1528d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mCropValue == null) {
1529d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // First handle the no crop case -- just return the value.  If the
1530d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // caller specifies a "save uri" then write the data to its
1531d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // stream. Otherwise, pass back a scaled down version of the bitmap
1532d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // directly in the extras.
1533d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mSaveUri != null) {
1534d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                OutputStream outputStream = null;
1535d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                try {
1536d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    outputStream = mContentResolver.openOutputStream(mSaveUri);
1537d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    outputStream.write(data);
1538d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    outputStream.close();
1539d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1540d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mActivity.setResultEx(Activity.RESULT_OK);
1541d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mActivity.finish();
1542d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                } catch (IOException ex) {
1543d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // ignore exception
1544d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                } finally {
1545d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    Util.closeSilently(outputStream);
1546d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1547d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else {
1548d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                int orientation = Exif.getOrientation(data);
1549d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
1550d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                bitmap = Util.rotate(bitmap, orientation);
1551d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.setResultEx(Activity.RESULT_OK,
1552d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        new Intent("inline-data").putExtra("data", bitmap));
1553d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.finish();
1554d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1555d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
1556d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Save the image to a temp file and invoke the cropper
1557d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Uri tempUri = null;
1558d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            FileOutputStream tempStream = null;
1559d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            try {
1560d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                File path = mActivity.getFileStreamPath(sTempCropFilename);
1561d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                path.delete();
1562d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1563d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                tempStream.write(data);
1564d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                tempStream.close();
1565d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                tempUri = Uri.fromFile(path);
1566d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } catch (FileNotFoundException ex) {
1567d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.setResultEx(Activity.RESULT_CANCELED);
1568d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.finish();
1569d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                return;
1570d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } catch (IOException ex) {
1571d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.setResultEx(Activity.RESULT_CANCELED);
1572d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.finish();
1573d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                return;
1574d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } finally {
1575d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Util.closeSilently(tempStream);
1576d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1577d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1578d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Bundle newExtras = new Bundle();
1579d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mCropValue.equals("circle")) {
1580d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                newExtras.putString("circleCrop", "true");
1581d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1582d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mSaveUri != null) {
1583d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1584d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else {
1585d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                newExtras.putBoolean("return-data", true);
1586d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1587d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1588d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Intent cropIntent = new Intent("com.android.camera.action.CROP");
1589d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1590d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            cropIntent.setData(tempUri);
1591d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            cropIntent.putExtras(newExtras);
1592d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1593d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1594d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1595d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1596d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1597d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void doCancel() {
1598d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1599d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity.finish();
1600d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1601d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1602d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1603d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onShutterButtonFocus(boolean pressed) {
1604d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPaused || collapseCameraControls()
1605d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                || (mCameraState == SNAPSHOT_IN_PROGRESS)
1606d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                || (mCameraState == PREVIEW_STOPPED)) return;
1607d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1608d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Do not do focus if there is not enough storage.
1609d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (pressed && !canTakePicture()) return;
1610d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1611d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (pressed) {
1612d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusManager.onShutterDown();
1613d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
1614d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusManager.onShutterUp();
1615d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1616d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1617d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1618d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1619d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onShutterButtonClick() {
1620d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPaused || collapseCameraControls()
1621d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                || (mCameraState == SWITCHING_CAMERA)
1622d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                || (mCameraState == PREVIEW_STOPPED)) return;
1623d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1624d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Do not take the picture if there is not enough storage.
1625d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
1626d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Log.i(TAG, "Not enough space or storage not ready. remaining="
1627d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    + mActivity.getStorageSpace());
1628d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
1629d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1630d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1631d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1632d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // If the user wants to do a snapshot while the previous one is still
1633d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // in progress, remember the fact and do it after we finish the previous
1634d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // one and re-start the preview. Snapshot in progress also includes the
1635d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // state that autofocus is focusing and a picture will be taken when
1636d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // focus callback arrives.
1637d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1638d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                && !mIsImageCaptureIntent) {
1639d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mSnapshotOnIdle = true;
1640d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
1641d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1642d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1643d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mSnapshotOnIdle = false;
1644d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusManager.doSnap();
1645d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1646d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1647d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1648d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void installIntentFilter() {
1649d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // install an intent filter to receive SD card related events.
1650d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        IntentFilter intentFilter =
1651d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                new IntentFilter(Intent.ACTION_MEDIA_SCANNER_FINISHED);
1652d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        intentFilter.addDataScheme("file");
1653d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity.registerReceiver(mReceiver, intentFilter);
1654d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mDidRegister = true;
1655d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1656d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1657d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1658d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public boolean updateStorageHintOnResume() {
1659d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return mFirstTimeInitialized;
1660d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1661d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1662d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1663d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void updateCameraAppView() {
1664d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1665d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1666d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1667d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onResumeBeforeSuper() {
1668d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPaused = false;
1669d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1670d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1671d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1672d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onResumeAfterSuper() {
1673d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mOpenCameraFail || mCameraDisabled) return;
1674d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1675d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mJpegPictureCallbackTime = 0;
1676d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mZoomValue = 0;
1677d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1678d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Start the preview if it is not started.
1679d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
1680d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            resetExposureCompensation();
1681d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraStartUpThread = new CameraStartUpThread();
1682d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraStartUpThread.start();
1683d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1684d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1685d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!mIsImageCaptureIntent) mActivity.getLastThumbnail();
1686d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1687d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // If first time initialization is not finished, put it in the
1688d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // message queue.
1689d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!mFirstTimeInitialized) {
1690d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1691d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
1692d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            initializeSecondTime();
1693d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1694d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        keepScreenOnAwhile();
1695d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1696d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Dismiss open menu if exists.
1697d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        PopupManager.getInstance(mActivity).notifyShowPopup(null);
1698d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1699d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mSoundPlayer = SoundClips.getPlayer(mActivity);
1700d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1701d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1702d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    void waitCameraStartUpThread() {
1703d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        try {
1704d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mCameraStartUpThread != null) {
1705d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mCameraStartUpThread.cancel();
1706d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mCameraStartUpThread.join();
1707d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mCameraStartUpThread = null;
1708d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                setCameraState(IDLE);
1709d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1710d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } catch (InterruptedException e) {
1711d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // ignore
1712d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1713d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1714d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1715d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1716d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onPauseBeforeSuper() {
1717d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPaused = true;
1718d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1719d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1720d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1721d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onPauseAfterSuper() {
1722d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Wait the camera start up thread to finish.
1723d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        waitCameraStartUpThread();
1724d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1725d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        stopPreview();
1726d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Close the camera now because other activities may need to use it.
1727d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        closeCamera();
1728d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mSurfaceTexture != null) {
1729d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            ((CameraScreenNail) getScreenNail()).releaseSurfaceTexture();
1730d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mSurfaceTexture = null;
1731d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1732d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mSoundPlayer != null) {
1733d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mSoundPlayer.release();
1734d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mSoundPlayer = null;
1735d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1736d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        resetScreenOn();
1737d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1738d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Clear UI.
1739d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        collapseCameraControls();
1740d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFaceView != null) mFaceView.clear();
1741d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1742d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFirstTimeInitialized) {
1743d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mOrientationListener.disable();
1744d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mImageSaver != null) {
1745d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mImageSaver.finish();
1746d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mImageSaver = null;
1747d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mImageNamer.finish();
1748d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mImageNamer = null;
1749d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1750d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1751d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1752d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mDidRegister) {
1753d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mActivity.unregisterReceiver(mReceiver);
1754d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mDidRegister = false;
1755d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1756d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mLocationManager != null) mLocationManager.recordLocation(false);
1757d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateExposureOnScreenIndicator(0);
1758d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1759d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // If we are in an image capture intent and has taken
1760d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // a picture, we just clear it in onPause.
1761d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mJpegImageData = null;
1762d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1763d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Remove the messages in the event queue.
1764d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(SETUP_PREVIEW);
1765d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(FIRST_TIME_INIT);
1766d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
1767d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(SWITCH_CAMERA);
1768d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
1769d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(CAMERA_OPEN_DONE);
1770d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(START_PREVIEW_DONE);
1771d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(OPEN_CAMERA_FAIL);
1772d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(CAMERA_DISABLED);
1773d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1774d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPendingSwitchCameraId = -1;
1775d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFocusManager != null) mFocusManager.removeMessages();
1776d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1777d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1778d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void initializeControlByIntent() {
1779d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mIsImageCaptureIntent) {
1780d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Cannot use RotateImageView for "done" and "cancel" button because
1781d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // the tablet layout uses RotateLayout, which cannot be cast to
1782d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // RotateImageView.
1783d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mReviewDoneButton = (Rotatable) mRootView.findViewById(R.id.btn_done);
1784d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mReviewCancelButton = (Rotatable) mRootView.findViewById(R.id.btn_cancel);
1785d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mReviewRetakeButton = mRootView.findViewById(R.id.btn_retake);
1786d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mRootView.findViewById(R.id.btn_cancel).setVisibility(View.VISIBLE);
1787d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1788d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Not grayed out upon disabled, to make the follow-up fade-out
1789d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // effect look smooth. Note that the review done button in tablet
1790d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // layout is not a TwoStateImageView.
1791d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mReviewDoneButton instanceof TwoStateImageView) {
1792d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                ((TwoStateImageView) mReviewDoneButton).enableFilter(false);
1793d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1794d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1795d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            setupCaptureParams();
1796d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
1797d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mActivity.mThumbnailView = ((RotateImageView) mRootView.findViewById(R.id.thumbnail));
1798d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mActivity.mThumbnailView != null) {
1799d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.mThumbnailView.enableFilter(false);
1800d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.mThumbnailView.setVisibility(View.VISIBLE);
1801d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.mThumbnailViewWidth = mActivity.mThumbnailView.getLayoutParams().width;
1802d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1803d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mModePicker = (ModePicker) mRootView.findViewById(R.id.mode_picker);
1804d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mModePicker.setVisibility(View.VISIBLE);
1805d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mModePicker.setOnModeChangeListener(this);
1806d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mModePicker.setCurrentMode(ModePicker.MODE_CAMERA);
1807d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1808d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1809d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1810d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1811d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void initializeFocusManager() {
1812d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Create FocusManager object. startPreview needs it.
1813d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1814d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
1815d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        String[] defaultFocusModes = mActivity.getResources().getStringArray(
1816d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                R.array.pref_camera_focusmode_default_array);
1817d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusManager = new FocusManager(mPreferences, defaultFocusModes,
1818d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mFocusAreaIndicator, mInitialParams, this, mirror,
1819d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.getMainLooper());
1820d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1821d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1822d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void initializeMiscControls() {
1823d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // startPreview needs this.
1824d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPreviewFrameLayout = (PreviewFrameLayout) mRootView.findViewById(R.id.frame);
1825d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Set touch focus listener.
1826d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity.setSingleTapUpListener(mPreviewFrameLayout);
1827d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1828d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mZoomControl = (ZoomControl) mRootView.findViewById(R.id.zoom_control);
1829d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mOnScreenIndicators = (Rotatable) mRootView.findViewById(R.id.on_screen_indicators);
1830d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
1831d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPreviewFrameLayout.setOnSizeChangedListener(this);
1832d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPreviewFrameLayout.setOnLayoutChangeListener(mActivity);
1833d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!ApiHelper.HAS_SURFACE_TEXTURE) {
1834d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mPreviewSurfaceView =
1835d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    (PreviewSurfaceView) mRootView.findViewById(R.id.preview_surface_view);
1836d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mPreviewSurfaceView.setVisibility(View.VISIBLE);
1837d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mPreviewSurfaceView.getHolder().addCallback(this);
1838d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1839d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1840d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1841d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1842d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onConfigurationChanged(Configuration newConfig) {
1843d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Log.v(TAG, "onConfigurationChanged");
1844d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setDisplayOrientation();
1845d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1846d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        ((ViewGroup) mRootView).removeAllViews();
1847d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        LayoutInflater inflater = mActivity.getLayoutInflater();
1848d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        inflater.inflate(R.layout.photo_module, (ViewGroup) mRootView);
1849d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1850d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // from onCreate()
1851d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeControlByIntent();
1852d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFocusManager != null) mFocusManager.removeMessages();
1853d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeFocusManager();
1854d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeMiscControls();
1855d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeIndicatorControl();
1856d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusAreaIndicator = (RotateLayout) mRootView.findViewById(
1857d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                R.id.focus_indicator_rotate_layout);
1858d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusManager.setFocusAreaIndicator(mFocusAreaIndicator);
1859d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1860d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // from onResume()
1861d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!mIsImageCaptureIntent) mActivity.updateThumbnailView();
1862d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1863d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // from initializeFirstTime()
1864d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mShutterButton = mActivity.getShutterButton();
1865d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mShutterButton.setOnShutterButtonListener(this);
1866d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mShutterButton.setVisibility(View.VISIBLE);
1867d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeZoom();
1868d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initOnScreenIndicator();
1869d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateOnScreenIndicators();
1870d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFaceView != null) {
1871d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.clear();
1872d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.setVisibility(View.VISIBLE);
1873d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.setDisplayOrientation(mDisplayOrientation);
1874d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1875d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT);
1876d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.resume();
1877d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusManager.setFaceView(mFaceView);
1878d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1879d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1880d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1881d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1882d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onActivityResult(
1883d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            int requestCode, int resultCode, Intent data) {
1884d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        switch (requestCode) {
1885d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            case REQUEST_CROP: {
1886d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Intent intent = new Intent();
1887d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (data != null) {
1888d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    Bundle extras = data.getExtras();
1889d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    if (extras != null) {
1890d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        intent.putExtras(extras);
1891d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
1892d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1893d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.setResultEx(resultCode, intent);
1894d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.finish();
1895d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1896d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                File path = mActivity.getFileStreamPath(sTempCropFilename);
1897d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                path.delete();
1898d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1899d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                break;
1900d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
1901d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1902d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1903d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1904d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean canTakePicture() {
1905d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
1906d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1907d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1908d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1909d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void autoFocus() {
1910d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusStartTime = System.currentTimeMillis();
1911d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraDevice.autoFocus(mAutoFocusCallback);
1912d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setCameraState(FOCUSING);
1913d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1914d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1915d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1916d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void cancelAutoFocus() {
1917d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraDevice.cancelAutoFocus();
1918d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setCameraState(IDLE);
1919d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setCameraParameters(UPDATE_PARAM_PREFERENCE);
1920d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1921d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1922d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // Preview area is touched. Handle touch focus.
1923d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1924d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onSingleTapUp(View view, int x, int y) {
1925d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1926d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                || mCameraState == SNAPSHOT_IN_PROGRESS
1927d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                || mCameraState == SWITCHING_CAMERA
1928d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                || mCameraState == PREVIEW_STOPPED) {
1929d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
1930d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1931d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1932d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Do not trigger touch focus if popup window is opened.
1933d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (collapseCameraControls()) return;
1934d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1935d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Check if metering area or focus area is supported.
1936d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1937d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1938d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusManager.onSingleTapUp(x, y);
1939d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1940d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1941d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1942d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public boolean onBackPressed() {
1943d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!isCameraIdle()) {
1944d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // ignore backs while we're taking a picture
1945d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return true;
1946d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
1947d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return collapseCameraControls();
1948d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1949d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1950d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1951d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1952d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public boolean onKeyDown(int keyCode, KeyEvent event) {
1953d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        switch (keyCode) {
1954d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            case KeyEvent.KEYCODE_FOCUS:
1955d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1956d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    onShutterButtonFocus(true);
1957d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1958d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                return true;
1959d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            case KeyEvent.KEYCODE_CAMERA:
1960d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1961d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    onShutterButtonClick();
1962d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1963d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                return true;
1964d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            case KeyEvent.KEYCODE_DPAD_CENTER:
1965d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // If we get a dpad center event without any focused view, move
1966d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // the focus to the shutter button and press it.
1967d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1968d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // Start auto-focus immediately to reduce shutter lag. After
1969d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // the shutter button gets the focus, onShutterButtonFocus()
1970d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    // will be called again but it is fine.
1971d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    if (collapseCameraControls()) return true;
1972d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    onShutterButtonFocus(true);
1973d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    if (mShutterButton.isInTouchMode()) {
1974d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mShutterButton.requestFocusFromTouch();
1975d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    } else {
1976d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        mShutterButton.requestFocus();
1977d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    }
1978d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mShutterButton.setPressed(true);
1979d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1980d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                return true;
1981d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1982d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return false;
1983d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1984d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1985d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
1986d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public boolean onKeyUp(int keyCode, KeyEvent event) {
1987d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        switch (keyCode) {
1988d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            case KeyEvent.KEYCODE_FOCUS:
1989d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mFirstTimeInitialized) {
1990d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    onShutterButtonFocus(false);
1991d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
1992d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                return true;
1993d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
1994d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return false;
1995d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
1996d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
1997d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
1998d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void closeCamera() {
1999d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mCameraDevice != null) {
2000d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setZoomChangeListener(null);
2001d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if(ApiHelper.HAS_FACE_DETECTION) {
2002d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mCameraDevice.setFaceDetectionListener(null);
2003d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
2004d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setErrorCallback(null);
2005d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            CameraHolder.instance().release();
2006d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceDetectionStarted = false;
2007d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice = null;
2008d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            setCameraState(PREVIEW_STOPPED);
2009d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusManager.onCameraReleased();
2010d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2011d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2012d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2013d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setDisplayOrientation() {
2014d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mDisplayRotation = Util.getDisplayRotation(mActivity);
2015d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
2016d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId);
2017d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFaceView != null) {
2018d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceView.setDisplayOrientation(mDisplayOrientation);
2019d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2020d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusManager.setDisplayOrientation(mDisplayOrientation);
2021d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2022d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2023d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // Only called by UI thread.
2024d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setupPreview() {
2025d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusManager.resetTouchFocus();
2026d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        startPreview();
2027d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setCameraState(IDLE);
2028d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        startFaceDetection();
2029d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2030d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2031d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // This can be called by UI Thread or CameraStartUpThread. So this should
2032d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // not modify the views.
2033d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void startPreview() {
2034d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraDevice.setErrorCallback(mErrorCallback);
2035d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2036d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // If we're previewing already, stop the preview first (this will blank
2037d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // the screen).
2038d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mCameraState != PREVIEW_STOPPED) stopPreview();
2039d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2040d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setDisplayOrientation();
2041d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2042d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!mSnapshotOnIdle) {
2043d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // If the focus mode is continuous autofocus, call cancelAutoFocus to
2044d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // resume it because it may have been paused by autoFocus call.
2045d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
2046d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mCameraDevice.cancelAutoFocus();
2047d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
2048d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
2049d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2050d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setCameraParameters(UPDATE_PARAM_ALL);
2051d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2052d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (ApiHelper.HAS_SURFACE_TEXTURE) {
2053d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            CameraScreenNail screenNail = (CameraScreenNail) getScreenNail();
2054d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mSurfaceTexture == null) {
2055d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Size size = mParameters.getPreviewSize();
2056d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (mCameraDisplayOrientation % 180 == 0) {
2057d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    screenNail.setSize(size.width, size.height);
2058d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                } else {
2059d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    screenNail.setSize(size.height, size.width);
2060d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
2061d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.notifyScreenNailChanged();
2062d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                screenNail.acquireSurfaceTexture();
2063d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mSurfaceTexture = screenNail.getSurfaceTexture();
2064d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
2065d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
2066d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setPreviewTextureAsync((SurfaceTexture) mSurfaceTexture);
2067d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
2068d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setDisplayOrientation(mDisplayOrientation);
2069d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder);
2070d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2071d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2072d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Log.v(TAG, "startPreview");
2073d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraDevice.startPreviewAsync();
2074d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2075d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusManager.onPreviewStarted();
2076d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2077d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mSnapshotOnIdle) {
2078d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mHandler.post(mDoSnapRunnable);
2079d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2080d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2081d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2082d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void stopPreview() {
2083d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
2084d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Log.v(TAG, "stopPreview");
2085d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.cancelAutoFocus(); // Reset the focus.
2086d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.stopPreview();
2087d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFaceDetectionStarted = false;
2088d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2089d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setCameraState(PREVIEW_STOPPED);
2090d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFocusManager != null) mFocusManager.onPreviewStopped();
2091d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2092d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2093d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @SuppressWarnings("deprecation")
2094d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateCameraParametersInitialize() {
2095d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Reset preview frame rate to the maximum because it may be lowered by
2096d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // video camera application.
2097d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates();
2098d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (frameRates != null) {
2099d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Integer max = Collections.max(frameRates);
2100d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.setPreviewFrameRate(max);
2101d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2102d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2103d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mParameters.set(Util.RECORDING_HINT, Util.FALSE);
2104d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2105d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Disable video stabilization. Convenience methods not available in API
2106d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // level <= 14
2107d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        String vstabSupported = mParameters.get("video-stabilization-supported");
2108d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if ("true".equals(vstabSupported)) {
2109d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.set("video-stabilization", "false");
2110d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2111d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2112d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2113d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateCameraParametersZoom() {
2114d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Set zoom.
2115d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mParameters.isZoomSupported()) {
2116d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.setZoom(mZoomValue);
2117d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2118d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2119d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2120d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2121d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
2122d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setAutoExposureLockIfSupported() {
2123d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mAeLockSupported) {
2124d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
2125d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2126d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2127d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2128d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
2129d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setAutoWhiteBalanceLockIfSupported() {
2130d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mAwbLockSupported) {
2131d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
2132d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2133d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2134d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2135d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
2136d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setFocusAreasIfSupported() {
2137d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFocusAreaSupported) {
2138d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.setFocusAreas(mFocusManager.getFocusAreas());
2139d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2140d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2141d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2142d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
2143d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setMeteringAreasIfSupported() {
2144d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mMeteringAreaSupported) {
2145d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Use the same area for focus and metering.
2146d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
2147d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2148d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2149d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2150d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateCameraParametersPreference() {
2151d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setAutoExposureLockIfSupported();
2152d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setAutoWhiteBalanceLockIfSupported();
2153d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setFocusAreasIfSupported();
2154d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setMeteringAreasIfSupported();
2155d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2156d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Set picture size.
2157d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        String pictureSize = mPreferences.getString(
2158d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraSettings.KEY_PICTURE_SIZE, null);
2159d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (pictureSize == null) {
2160d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            CameraSettings.initialCameraPictureSize(mActivity, mParameters);
2161d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
2162d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            List<Size> supported = mParameters.getSupportedPictureSizes();
2163d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            CameraSettings.setCameraPictureSize(
2164d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    pictureSize, supported, mParameters);
2165d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2166d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Size size = mParameters.getPictureSize();
2167d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2168d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Set a preview size that is closest to the viewfinder height and has
2169d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // the right aspect ratio.
2170d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        List<Size> sizes = mParameters.getSupportedPreviewSizes();
2171d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes,
2172d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                (double) size.width / size.height);
2173d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Size original = mParameters.getPreviewSize();
2174d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (!original.equals(optimalSize)) {
2175d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
2176d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2177d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Zoom related settings will be changed for different preview
2178d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // sizes, so set and read the parameters to get latest values
2179d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setParameters(mParameters);
2180d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters = mCameraDevice.getParameters();
2181d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2182d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
2183d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2184d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Since change scene mode may change supported values,
2185d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Set scene mode first,
2186d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mSceneMode = mPreferences.getString(
2187d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraSettings.KEY_SCENE_MODE,
2188d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.getString(R.string.pref_camera_scenemode_default));
2189d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
2190d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (!mParameters.getSceneMode().equals(mSceneMode)) {
2191d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mParameters.setSceneMode(mSceneMode);
2192d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2193d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // Setting scene mode will change the settings of flash mode,
2194d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // white balance, and focus mode. Here we read back the
2195d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                // parameters, so we can know those settings.
2196d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mCameraDevice.setParameters(mParameters);
2197d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mParameters = mCameraDevice.getParameters();
2198d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
2199d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
2200d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mSceneMode = mParameters.getSceneMode();
2201d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mSceneMode == null) {
2202d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mSceneMode = Parameters.SCENE_MODE_AUTO;
2203d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
2204d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2205d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2206d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Set JPEG quality.
2207d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2208d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                CameraProfile.QUALITY_HIGH);
2209d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mParameters.setJpegQuality(jpegQuality);
2210d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2211d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // For the following settings, we need to check if the settings are
2212d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // still supported by latest driver, if not, ignore the settings.
2213d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2214d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Set exposure compensation
2215d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        int value = CameraSettings.readExposure(mPreferences);
2216d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        int max = mParameters.getMaxExposureCompensation();
2217d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        int min = mParameters.getMinExposureCompensation();
2218d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (value >= min && value <= max) {
2219d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.setExposureCompensation(value);
2220d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
2221d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Log.w(TAG, "invalid exposure range: " + value);
2222d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2223d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2224d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
2225d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Set flash mode.
2226d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            String flashMode = mPreferences.getString(
2227d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    CameraSettings.KEY_FLASH_MODE,
2228d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mActivity.getString(R.string.pref_camera_flashmode_default));
2229d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            List<String> supportedFlash = mParameters.getSupportedFlashModes();
2230d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (Util.isSupported(flashMode, supportedFlash)) {
2231d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mParameters.setFlashMode(flashMode);
2232d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else {
2233d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                flashMode = mParameters.getFlashMode();
2234d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (flashMode == null) {
2235d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    flashMode = mActivity.getString(
2236d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                            R.string.pref_camera_flashmode_no_flash);
2237d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
2238d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
2239d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2240d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Set white balance parameter.
2241d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            String whiteBalance = mPreferences.getString(
2242d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    CameraSettings.KEY_WHITE_BALANCE,
2243d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mActivity.getString(R.string.pref_camera_whitebalance_default));
2244d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (Util.isSupported(whiteBalance,
2245d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mParameters.getSupportedWhiteBalance())) {
2246d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mParameters.setWhiteBalance(whiteBalance);
2247d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            } else {
2248d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                whiteBalance = mParameters.getWhiteBalance();
2249d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                if (whiteBalance == null) {
2250d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    whiteBalance = Parameters.WHITE_BALANCE_AUTO;
2251d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                }
2252d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
2253d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2254d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Set focus mode.
2255d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusManager.overrideFocusMode(null);
2256d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mParameters.setFocusMode(mFocusManager.getFocusMode());
2257d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
2258d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mFocusManager.overrideFocusMode(mParameters.getFocusMode());
2259d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2260d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2261d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2262d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            updateAutoFocusMoveCallback();
2263d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2264d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2265d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2266d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
2267d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void updateAutoFocusMoveCallback() {
2268d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
2269d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setAutoFocusMoveCallback(
2270d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                (AutoFocusMoveCallback) mAutoFocusMoveCallback);
2271d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
2272d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCameraDevice.setAutoFocusMoveCallback(null);
2273d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2274d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2275d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2276d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // We separate the parameters into several subsets, so we can update only
2277d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // the subsets actually need updating. The PREFERENCE set needs extra
2278d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // locking because the preference can be changed from GLThread as well.
2279d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setCameraParameters(int updateSet) {
2280d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2281d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            updateCameraParametersInitialize();
2282d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2283d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2284d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2285d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            updateCameraParametersZoom();
2286d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2287d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2288d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
2289d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            updateCameraParametersPreference();
2290d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2291d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2292d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraDevice.setParameters(mParameters);
2293d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2294d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2295d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // If the Camera is idle, update the parameters immediately, otherwise
2296d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // accumulate them in mUpdateSet and update later.
2297d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2298d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mUpdateSet |= additionalUpdateSet;
2299d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mCameraDevice == null) {
2300d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // We will update all the parameters when we open the device, so
2301d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // we don't need to do anything now.
2302d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mUpdateSet = 0;
2303d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            return;
2304d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else if (isCameraIdle()) {
2305d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            setCameraParameters(mUpdateSet);
2306d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            updateSceneModeUI();
2307d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mUpdateSet = 0;
2308d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
2309d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2310d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mHandler.sendEmptyMessageDelayed(
2311d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
2312d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
2313d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2314d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2315d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2316d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean isCameraIdle() {
2317d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return (mCameraState == IDLE) ||
2318d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                ((mFocusManager != null) && mFocusManager.isFocusCompleted()
2319d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                        && (mCameraState != SWITCHING_CAMERA));
2320d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2321d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2322d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private boolean isImageCaptureIntent() {
2323d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        String action = mActivity.getIntent().getAction();
2324d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action));
2325d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2326d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2327d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void setupCaptureParams() {
2328d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Bundle myExtras = mActivity.getIntent().getExtras();
2329d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (myExtras != null) {
2330d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2331d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mCropValue = myExtras.getString("crop");
2332d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2333d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2334d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2335d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void showPostCaptureAlert() {
2336d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mIsImageCaptureIntent) {
2337d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Util.fadeOut(mIndicatorControlContainer);
2338d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Util.fadeOut(mShutterButton);
2339d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2340d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Util.fadeIn(mReviewRetakeButton);
2341d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Util.fadeIn((View) mReviewDoneButton);
2342d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2343d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2344d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2345d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void hidePostCaptureAlert() {
2346d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mIsImageCaptureIntent) {
2347d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Util.fadeOut(mReviewRetakeButton);
2348d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Util.fadeOut((View) mReviewDoneButton);
2349d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2350d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Util.fadeIn(mShutterButton);
2351d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            if (mIndicatorControlContainer != null) {
2352d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Util.fadeIn(mIndicatorControlContainer);
2353d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
2354d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2355d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2356d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2357d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void switchToOtherMode(int mode) {
2358d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mActivity.isFinishing()) return;
2359d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mImageSaver != null) mImageSaver.waitDone();
2360d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mActivity.mThumbnail != null) ThumbnailHolder.keep(mActivity.mThumbnail);
2361d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        MenuHelper.gotoMode(mode, mActivity, false);
2362d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(FIRST_TIME_INIT);
2363d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity.finish();
2364d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2365d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2366d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
2367d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onModeChanged(int mode) {
2368d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mode != ModePicker.MODE_CAMERA) switchToOtherMode(mode);
2369d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2370d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2371d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
2372d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onSharedPreferenceChanged() {
2373d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // ignore the events after "onPause()"
2374d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPaused) return;
2375d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2376d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        boolean recordLocation = RecordLocationPreference.get(
2377d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mPreferences, mContentResolver);
2378d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mLocationManager.recordLocation(recordLocation);
2379d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2380d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
2381d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setPreviewFrameLayoutAspectRatio();
2382d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateOnScreenIndicators();
2383d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2384d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2385d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
2386d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onCameraPickerClicked(int cameraId) {
2387d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPaused || mPendingSwitchCameraId != -1) return;
2388d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2389d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPendingSwitchCameraId = cameraId;
2390d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (ApiHelper.HAS_SURFACE_TEXTURE) {
2391d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            Log.v(TAG, "Start to copy texture. cameraId=" + cameraId);
2392d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // We need to keep a preview frame for the animation before
2393d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // releasing the camera. This will trigger onPreviewTextureCopied.
2394d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            ((CameraScreenNail) getScreenNail()).copyTexture();
2395d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Disable all camera controls.
2396d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            setCameraState(SWITCHING_CAMERA);
2397d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } else {
2398d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            switchCamera();
2399d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2400d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2401d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2402d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void switchCamera() {
2403d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPaused) return;
2404d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2405d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
2406d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraId = mPendingSwitchCameraId;
2407d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPendingSwitchCameraId = -1;
2408d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mCameraPicker.setCameraId(mCameraId);
2409d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2410d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // from onPause
2411d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        closeCamera();
2412d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        collapseCameraControls();
2413d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFaceView != null) mFaceView.clear();
2414d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFocusManager != null) mFocusManager.removeMessages();
2415d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2416d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Restart the camera and initialize the UI. From onCreate.
2417d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPreferences.setLocalId(mActivity, mCameraId);
2418d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
2419d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        CameraOpenThread cameraOpenThread = new CameraOpenThread();
2420d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        cameraOpenThread.start();
2421d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        try {
2422d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            cameraOpenThread.join();
2423d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        } catch (InterruptedException ex) {
2424d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // ignore
2425d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2426d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeCapabilities();
2427d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
2428d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
2429d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusManager.setMirror(mirror);
2430d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusManager.setParameters(mInitialParams);
2431d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setupPreview();
2432d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeIndicatorControl();
2433d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2434d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // from onResume
2435d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        setOrientationIndicator(mOrientationCompensation, false);
2436d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // from initializeFirstTime
2437d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        initializeZoom();
2438d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        updateOnScreenIndicators();
2439d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        showTapToFocusToastIfNeeded();
2440d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2441d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (ApiHelper.HAS_SURFACE_TEXTURE) {
2442d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // Start switch camera animation. Post a message because
2443d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            // onFrameAvailable from the old camera may already exist.
2444d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
2445d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2446d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2447d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2448d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // Preview texture has been copied. Now camera can be released and the
2449d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // animation can be started.
2450d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
2451d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onPreviewTextureCopied() {
2452d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.sendEmptyMessage(SWITCH_CAMERA);
2453d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2454d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2455d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
2456d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onUserInteraction() {
2457d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        keepScreenOnAwhile();
2458d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2459d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2460d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void resetScreenOn() {
2461d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(CLEAR_SCREEN_DELAY);
2462d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2463d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2464d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2465d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void keepScreenOnAwhile() {
2466d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.removeMessages(CLEAR_SCREEN_DELAY);
2467d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
2468d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
2469d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2470d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2471d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
2472d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onRestorePreferencesClicked() {
2473d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPaused) return;
2474d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Runnable runnable = new Runnable() {
2475d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            @Override
2476d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            public void run() {
2477d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                restorePreferences();
2478d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            }
2479d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        };
2480d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mRotateDialog.showAlertDialog(
2481d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                null,
2482d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.getString(R.string.confirm_restore_message),
2483d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.getString(android.R.string.ok), runnable,
2484d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                mActivity.getString(android.R.string.cancel), null);
2485d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2486d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2487d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void restorePreferences() {
2488d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Reset the zoom. Zoom value is not stored in preference.
2489d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mParameters.isZoomSupported()) {
2490d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mZoomValue = 0;
2491d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            setCameraParametersWhenIdle(UPDATE_PARAM_ZOOM);
2492d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mZoomControl.setZoomIndex(0);
2493d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2494d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mIndicatorControlContainer != null) {
2495d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mIndicatorControlContainer.dismissSettingPopup();
2496d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            CameraSettings.restorePreferences(mActivity, mPreferences,
2497d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                    mParameters);
2498d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mIndicatorControlContainer.reloadPreferences();
2499d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            onSharedPreferenceChanged();
2500d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2501d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2502d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2503d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
2504d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onOverriddenPreferencesClicked() {
2505d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mPaused) return;
2506d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mNotSelectableToast == null) {
2507d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            String str = mActivity.getResources().getString(R.string.not_selectable_in_scene_mode);
2508d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb            mNotSelectableToast = Toast.makeText(mActivity, str, Toast.LENGTH_SHORT);
2509d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        }
2510d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mNotSelectableToast.show();
2511d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2512d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2513d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void showTapToFocusToast() {
2514d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        new RotateTextToast(mActivity, R.string.tap_to_focus, mOrientationCompensation).show();
2515d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Clear the preference.
2516d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Editor editor = mPreferences.edit();
2517d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
2518d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        editor.apply();
2519d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2520d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2521d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    private void initializeCapabilities() {
2522d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mInitialParams = mCameraDevice.getParameters();
2523d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
2524d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
2525d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
2526d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams);
2527d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
2528d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb                Util.FOCUS_MODE_CONTINUOUS_PICTURE);
2529d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2530d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2531d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // PreviewFrameLayout size has changed.
2532d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    @Override
2533d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    public void onSizeChanged(int width, int height) {
2534d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
2535d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2536d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2537d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    void setPreviewFrameLayoutAspectRatio() {
2538d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        // Set the preview frame aspect ratio according to the picture size.
2539d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        Size size = mParameters.getPictureSize();
2540d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        mPreviewFrameLayout.setAspectRatio((double) size.width / size.height);
2541d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2542d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2543d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    // state access methods
2544d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    ScreenNail getScreenNail() {
2545d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb        return mActivity.mCameraScreenNail;
2546d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb    }
2547d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb
2548d3a252bb71d119cbaeff55aeaa27dd1f8adadba2Michael Kolb}
2549