blob: 4686d60d47e764550b69a7ced6b54ef7d138529e [file] [log] [blame]
Michael Kolbd6954f32013-03-08 20:43:01 -08001/*
Doris Liu6432cd62013-06-13 17:20:31 -07002 * Copyright (C) 2012 The Android Open Source Project
Michael Kolbd6954f32013-03-08 20:43:01 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package com.android.camera;
19
Doris Liu6a83d522013-07-02 12:03:32 -070020import android.app.AlertDialog;
21import android.content.DialogInterface;
Sascha Haeberling37f36112013-08-06 14:31:52 -070022import android.graphics.Bitmap;
Doris Liuc3679c02013-08-08 18:08:43 -070023import android.graphics.Color;
Doris Liu6432cd62013-06-13 17:20:31 -070024import android.graphics.Matrix;
Doris Liu36ebcb12013-10-28 14:44:24 -070025import android.graphics.RectF;
Doris Liu6432cd62013-06-13 17:20:31 -070026import android.graphics.SurfaceTexture;
Doris Liuc3679c02013-08-08 18:08:43 -070027import android.graphics.drawable.ColorDrawable;
Michael Kolbd6954f32013-03-08 20:43:01 -080028import android.hardware.Camera;
29import android.hardware.Camera.Face;
Sascha Haeberling37f36112013-08-06 14:31:52 -070030import android.os.AsyncTask;
Michael Kolbd6954f32013-03-08 20:43:01 -080031import android.util.Log;
32import android.view.Gravity;
Doris Liu6432cd62013-06-13 17:20:31 -070033import android.view.TextureView;
Michael Kolbd6954f32013-03-08 20:43:01 -080034import android.view.View;
35import android.view.View.OnClickListener;
36import android.view.View.OnLayoutChangeListener;
37import android.view.ViewGroup;
38import android.view.ViewStub;
Michael Kolbd6954f32013-03-08 20:43:01 -080039import android.widget.FrameLayout.LayoutParams;
Sascha Haeberling37f36112013-08-06 14:31:52 -070040import android.widget.ImageView;
Doris Liuc3679c02013-08-08 18:08:43 -070041import android.widget.PopupWindow;
Michael Kolbd6954f32013-03-08 20:43:01 -080042import android.widget.Toast;
43
44import com.android.camera.CameraPreference.OnPreferenceChangedListener;
45import com.android.camera.FocusOverlayManager.FocusUI;
Angus Kong20fad242013-11-11 18:23:46 -080046import com.android.camera.app.CameraManager;
Michael Kolbd6954f32013-03-08 20:43:01 -080047import com.android.camera.ui.AbstractSettingPopup;
Doris Liu6432cd62013-06-13 17:20:31 -070048import com.android.camera.ui.CameraControls;
Angus Kong9e765522013-07-31 14:05:20 -070049import com.android.camera.ui.CameraRootView;
Michael Kolbd6954f32013-03-08 20:43:01 -080050import com.android.camera.ui.CountDownView;
51import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
52import com.android.camera.ui.FaceView;
53import com.android.camera.ui.FocusIndicator;
54import com.android.camera.ui.PieRenderer;
55import com.android.camera.ui.PieRenderer.PieListener;
56import com.android.camera.ui.RenderOverlay;
57import com.android.camera.ui.ZoomRenderer;
Angus Kongb50b5cb2013-08-09 14:55:20 -070058import com.android.camera.util.CameraUtil;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070059import com.android.camera2.R;
Angus Kongb50b5cb2013-08-09 14:55:20 -070060
61import java.util.List;
Michael Kolbd6954f32013-03-08 20:43:01 -080062
Michael Kolbd6954f32013-03-08 20:43:01 -080063public class PhotoUI implements PieListener,
Michael Kolbd6954f32013-03-08 20:43:01 -080064 PreviewGestures.SingleTapListener,
Doris Liu6432cd62013-06-13 17:20:31 -070065 FocusUI, TextureView.SurfaceTextureListener,
66 LocationManager.Listener, CameraRootView.MyDisplayListener,
Angus Kong9e765522013-07-31 14:05:20 -070067 CameraManager.CameraFaceDetectionCallback {
Michael Kolbd6954f32013-03-08 20:43:01 -080068
69 private static final String TAG = "CAM_UI";
Sascha Haeberling37f36112013-08-06 14:31:52 -070070 private static final int DOWN_SAMPLE_FACTOR = 4;
71 private final AnimationManager mAnimationManager;
Michael Kolbd6954f32013-03-08 20:43:01 -080072 private CameraActivity mActivity;
73 private PhotoController mController;
74 private PreviewGestures mGestures;
75
76 private View mRootView;
Angus Kongdcccc512013-08-08 17:06:03 -070077 private SurfaceTexture mSurfaceTexture;
Michael Kolbd6954f32013-03-08 20:43:01 -080078
Doris Liuc3679c02013-08-08 18:08:43 -070079 private PopupWindow mPopup;
Michael Kolbd6954f32013-03-08 20:43:01 -080080 private ShutterButton mShutterButton;
81 private CountDownView mCountDownView;
82
83 private FaceView mFaceView;
84 private RenderOverlay mRenderOverlay;
85 private View mReviewCancelButton;
86 private View mReviewDoneButton;
87 private View mReviewRetakeButton;
Doris Liu36e56fb2013-09-11 17:38:08 -070088 private ImageView mReviewImage;
89 private DecodeImageForReview mDecodeTaskForReview = null;
Michael Kolbd6954f32013-03-08 20:43:01 -080090
91 private View mMenuButton;
Michael Kolbd6954f32013-03-08 20:43:01 -080092 private PhotoMenu mMenu;
Doris Liu6432cd62013-06-13 17:20:31 -070093 private CameraControls mCameraControls;
Doris Liu6a83d522013-07-02 12:03:32 -070094 private AlertDialog mLocationDialog;
Michael Kolbd6954f32013-03-08 20:43:01 -080095
Doris Liu6432cd62013-06-13 17:20:31 -070096 // Small indicators which show the camera settings in the viewfinder.
Sascha Haeberlingc7965e22013-04-22 14:27:36 -070097 private OnScreenIndicators mOnScreenIndicators;
Michael Kolbd6954f32013-03-08 20:43:01 -080098
99 private PieRenderer mPieRenderer;
100 private ZoomRenderer mZoomRenderer;
101 private Toast mNotSelectableToast;
102
103 private int mZoomMax;
104 private List<Integer> mZoomRatios;
105
106 private int mPreviewWidth = 0;
107 private int mPreviewHeight = 0;
Doris Liu6432cd62013-06-13 17:20:31 -0700108 private float mSurfaceTextureUncroppedWidth;
109 private float mSurfaceTextureUncroppedHeight;
110
Sascha Haeberling37f36112013-08-06 14:31:52 -0700111 private ImageView mPreviewThumb;
Doris Liuc2e9abd2013-06-19 14:20:51 -0700112 private View mFlashOverlay;
Michael Kolbd6954f32013-03-08 20:43:01 -0800113
Doris Liu6432cd62013-06-13 17:20:31 -0700114 private SurfaceTextureSizeChangedListener mSurfaceTextureSizeListener;
115 private TextureView mTextureView;
116 private Matrix mMatrix = null;
117 private float mAspectRatio = 4f / 3f;
Doris Liu3a45c332013-10-15 19:10:28 -0700118 private View mPreviewCover;
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800119 private final Object mSurfaceTextureLock = new Object();
Doris Liu6432cd62013-06-13 17:20:31 -0700120
121 public interface SurfaceTextureSizeChangedListener {
122 public void onSurfaceTextureSizeChanged(int uncroppedWidth, int uncroppedHeight);
123 }
124
Michael Kolbd6954f32013-03-08 20:43:01 -0800125 private OnLayoutChangeListener mLayoutListener = new OnLayoutChangeListener() {
126 @Override
127 public void onLayoutChange(View v, int left, int top, int right,
128 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
129 int width = right - left;
130 int height = bottom - top;
Doris Liu6432cd62013-06-13 17:20:31 -0700131 if (mPreviewWidth != width || mPreviewHeight != height) {
132 mPreviewWidth = width;
133 mPreviewHeight = height;
Doris Liu13ca71e2013-08-20 11:14:07 -0700134 setTransformMatrix(width, height);
Michael Kolbd6954f32013-03-08 20:43:01 -0800135 }
136 }
137 };
138
Doris Liu36e56fb2013-09-11 17:38:08 -0700139 private class DecodeTask extends AsyncTask<Void, Void, Bitmap> {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700140 private final byte [] mData;
Doris Liuce2acbc2013-08-21 18:45:29 -0700141 private int mOrientation;
Doris Liu29da2db2013-08-28 14:28:45 -0700142 private boolean mMirror;
Doris Liuc2e9abd2013-06-19 14:20:51 -0700143
Doris Liu29da2db2013-08-28 14:28:45 -0700144 public DecodeTask(byte[] data, int orientation, boolean mirror) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700145 mData = data;
Doris Liuce2acbc2013-08-21 18:45:29 -0700146 mOrientation = orientation;
Doris Liu29da2db2013-08-28 14:28:45 -0700147 mMirror = mirror;
Doris Liuc2e9abd2013-06-19 14:20:51 -0700148 }
149
150 @Override
Doris Liu36e56fb2013-09-11 17:38:08 -0700151 protected Bitmap doInBackground(Void... params) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700152 // Decode image in background.
Doris Liuce2acbc2013-08-21 18:45:29 -0700153 Bitmap bitmap = CameraUtil.downSample(mData, DOWN_SAMPLE_FACTOR);
Doris Liu29da2db2013-08-28 14:28:45 -0700154 if (mOrientation != 0 || mMirror) {
Doris Liuce2acbc2013-08-21 18:45:29 -0700155 Matrix m = new Matrix();
Doris Liu29da2db2013-08-28 14:28:45 -0700156 if (mMirror) {
157 // Flip horizontally
158 m.setScale(-1f, 1f);
159 }
Andy Huiberscec75e42013-10-29 16:03:58 -0700160 m.preRotate(mOrientation);
Doris Liuce2acbc2013-08-21 18:45:29 -0700161 return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m,
162 false);
163 }
164 return bitmap;
Sascha Haeberling37f36112013-08-06 14:31:52 -0700165 }
Doris Liuc2e9abd2013-06-19 14:20:51 -0700166
167 @Override
Sascha Haeberling37f36112013-08-06 14:31:52 -0700168 protected void onPostExecute(Bitmap bitmap) {
169 mPreviewThumb.setImageBitmap(bitmap);
170 mAnimationManager.startCaptureAnimation(mPreviewThumb);
171 }
172 }
Doris Liuc2e9abd2013-06-19 14:20:51 -0700173
Doris Liu36e56fb2013-09-11 17:38:08 -0700174 private class DecodeImageForReview extends DecodeTask {
175 public DecodeImageForReview(byte[] data, int orientation, boolean mirror) {
176 super(data, orientation, mirror);
177 }
178
179 @Override
180 protected void onPostExecute(Bitmap bitmap) {
181 if (isCancelled()) {
182 return;
183 }
184 mReviewImage.setImageBitmap(bitmap);
185 mReviewImage.setVisibility(View.VISIBLE);
186 mDecodeTaskForReview = null;
187 }
188 }
189
Michael Kolbd6954f32013-03-08 20:43:01 -0800190 public PhotoUI(CameraActivity activity, PhotoController controller, View parent) {
191 mActivity = activity;
192 mController = controller;
193 mRootView = parent;
194
195 mActivity.getLayoutInflater().inflate(R.layout.photo_module,
196 (ViewGroup) mRootView, true);
197 mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
Doris Liuc2e9abd2013-06-19 14:20:51 -0700198 mFlashOverlay = mRootView.findViewById(R.id.flash_overlay);
Doris Liu3a45c332013-10-15 19:10:28 -0700199 mPreviewCover = mRootView.findViewById(R.id.preview_cover);
Doris Liu6432cd62013-06-13 17:20:31 -0700200 // display the view
201 mTextureView = (TextureView) mRootView.findViewById(R.id.preview_content);
202 mTextureView.setSurfaceTextureListener(this);
203 mTextureView.addOnLayoutChangeListener(mLayoutListener);
Michael Kolbd6954f32013-03-08 20:43:01 -0800204 initIndicators();
Michael Kolbd6954f32013-03-08 20:43:01 -0800205
Doris Liu6432cd62013-06-13 17:20:31 -0700206 mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button);
Doris Liu6432cd62013-06-13 17:20:31 -0700207 mMenuButton = mRootView.findViewById(R.id.menu);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700208 ViewStub faceViewStub = (ViewStub) mRootView
209 .findViewById(R.id.face_view_stub);
210 if (faceViewStub != null) {
211 faceViewStub.inflate();
212 mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
213 setSurfaceTextureSizeChangedListener(mFaceView);
Michael Kolbd6954f32013-03-08 20:43:01 -0800214 }
Doris Liu6432cd62013-06-13 17:20:31 -0700215 mCameraControls = (CameraControls) mRootView.findViewById(R.id.camera_controls);
Sascha Haeberling37f36112013-08-06 14:31:52 -0700216 mAnimationManager = new AnimationManager();
Doris Liu6432cd62013-06-13 17:20:31 -0700217 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800218
Doris Liu6432cd62013-06-13 17:20:31 -0700219 public void setSurfaceTextureSizeChangedListener(SurfaceTextureSizeChangedListener listener) {
220 mSurfaceTextureSizeListener = listener;
221 }
222
Doris Liud053a5b2013-10-25 20:20:43 -0700223 public void updatePreviewAspectRatio(float aspectRatio) {
224 if (aspectRatio <= 0) {
225 Log.e(TAG, "Invalid aspect ratio: " + aspectRatio);
226 return;
227 }
228 if (aspectRatio < 1f) {
229 aspectRatio = 1f / aspectRatio;
230 }
231
232 if (mAspectRatio != aspectRatio) {
233 mAspectRatio = aspectRatio;
234 // Update transform matrix with the new aspect ratio.
235 if (mPreviewWidth != 0 && mPreviewHeight != 0) {
236 setTransformMatrix(mPreviewWidth, mPreviewHeight);
237 }
238 }
239 }
240
Doris Liu6432cd62013-06-13 17:20:31 -0700241 private void setTransformMatrix(int width, int height) {
242 mMatrix = mTextureView.getTransform(mMatrix);
Doris Liu6432cd62013-06-13 17:20:31 -0700243 float scaleX = 1f, scaleY = 1f;
244 float scaledTextureWidth, scaledTextureHeight;
245 if (width > height) {
246 scaledTextureWidth = Math.max(width,
247 (int) (height * mAspectRatio));
248 scaledTextureHeight = Math.max(height,
249 (int)(width / mAspectRatio));
250 } else {
251 scaledTextureWidth = Math.max(width,
252 (int) (height / mAspectRatio));
253 scaledTextureHeight = Math.max(height,
254 (int) (width * mAspectRatio));
255 }
256
257 if (mSurfaceTextureUncroppedWidth != scaledTextureWidth ||
258 mSurfaceTextureUncroppedHeight != scaledTextureHeight) {
259 mSurfaceTextureUncroppedWidth = scaledTextureWidth;
260 mSurfaceTextureUncroppedHeight = scaledTextureHeight;
261 if (mSurfaceTextureSizeListener != null) {
262 mSurfaceTextureSizeListener.onSurfaceTextureSizeChanged(
263 (int) mSurfaceTextureUncroppedWidth, (int) mSurfaceTextureUncroppedHeight);
264 }
265 }
266 scaleX = scaledTextureWidth / width;
267 scaleY = scaledTextureHeight / height;
268 mMatrix.setScale(scaleX, scaleY, (float) width / 2, (float) height / 2);
269 mTextureView.setTransform(mMatrix);
Doris Liu36ebcb12013-10-28 14:44:24 -0700270
271 // Calculate the new preview rectangle.
272 RectF previewRect = new RectF(0, 0, width, height);
273 mMatrix.mapRect(previewRect);
274 mController.onPreviewRectChanged(CameraUtil.rectFToRect(previewRect));
Doris Liu6432cd62013-06-13 17:20:31 -0700275 }
276
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800277 protected Object getSurfaceTextureLock() {
278 return mSurfaceTextureLock;
279 }
280
Angus Kongdcccc512013-08-08 17:06:03 -0700281 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700282 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800283 synchronized (mSurfaceTextureLock) {
284 Log.v(TAG, "SurfaceTexture ready.");
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800285 mSurfaceTexture = surface;
286 mController.onPreviewUIReady();
287 // Workaround for b/11168275, see b/10981460 for more details
288 if (mPreviewWidth != 0 && mPreviewHeight != 0) {
289 // Re-apply transform matrix for new surface texture
290 setTransformMatrix(mPreviewWidth, mPreviewHeight);
291 }
Doris Liu18f20b92013-10-09 20:51:23 -0700292 }
Doris Liu6432cd62013-06-13 17:20:31 -0700293 }
294
Angus Kongdcccc512013-08-08 17:06:03 -0700295 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700296 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
297 // Ignored, Camera does all the work for us
298 }
299
Angus Kongdcccc512013-08-08 17:06:03 -0700300 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700301 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800302 synchronized (mSurfaceTextureLock) {
303 mSurfaceTexture = null;
304 mController.onPreviewUIDestroyed();
305 Log.w(TAG, "SurfaceTexture destroyed");
306 return true;
307 }
Doris Liu6432cd62013-06-13 17:20:31 -0700308 }
309
ztenghui7b265a62013-09-09 14:58:44 -0700310 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700311 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Andy Huibersfccf83b2013-11-06 14:37:26 -0800312 // Make sure preview cover is hidden if preview data is available.
313 if (mPreviewCover.getVisibility() != View.GONE) {
314 mPreviewCover.setVisibility(View.GONE);
315 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800316 }
317
318 public View getRootView() {
319 return mRootView;
320 }
321
322 private void initIndicators() {
Michael Kolbeb8adc12013-04-26 11:09:29 -0700323 mOnScreenIndicators = new OnScreenIndicators(mActivity,
Doris Liu6432cd62013-06-13 17:20:31 -0700324 mRootView.findViewById(R.id.on_screen_indicators));
Michael Kolbd6954f32013-03-08 20:43:01 -0800325 }
326
327 public void onCameraOpened(PreferenceGroup prefGroup, ComboPreferences prefs,
328 Camera.Parameters params, OnPreferenceChangedListener listener) {
329 if (mPieRenderer == null) {
330 mPieRenderer = new PieRenderer(mActivity);
331 mPieRenderer.setPieListener(this);
Michael Kolbe3974bb2013-03-22 10:56:12 -0700332 mRenderOverlay.addRenderer(mPieRenderer);
Michael Kolbd6954f32013-03-08 20:43:01 -0800333 }
Doris Liu6432cd62013-06-13 17:20:31 -0700334
Michael Kolbd6954f32013-03-08 20:43:01 -0800335 if (mMenu == null) {
336 mMenu = new PhotoMenu(mActivity, this, mPieRenderer);
337 mMenu.setListener(listener);
338 }
339 mMenu.initialize(prefGroup);
340
341 if (mZoomRenderer == null) {
342 mZoomRenderer = new ZoomRenderer(mActivity);
Michael Kolbe3974bb2013-03-22 10:56:12 -0700343 mRenderOverlay.addRenderer(mZoomRenderer);
Michael Kolbd6954f32013-03-08 20:43:01 -0800344 }
Doris Liu6432cd62013-06-13 17:20:31 -0700345
Michael Kolbd6954f32013-03-08 20:43:01 -0800346 if (mGestures == null) {
347 // this will handle gesture disambiguation and dispatching
Doris Liu6432cd62013-06-13 17:20:31 -0700348 mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer);
349 mRenderOverlay.setGestures(mGestures);
Michael Kolbd6954f32013-03-08 20:43:01 -0800350 }
Doris Liu6432cd62013-06-13 17:20:31 -0700351 mGestures.setZoomEnabled(params.isZoomSupported());
Michael Kolbd6954f32013-03-08 20:43:01 -0800352 mGestures.setRenderOverlay(mRenderOverlay);
Michael Kolbd6954f32013-03-08 20:43:01 -0800353 mRenderOverlay.requestLayout();
354
355 initializeZoom(params);
Michael Kolbeb8adc12013-04-26 11:09:29 -0700356 updateOnScreenIndicators(params, prefGroup, prefs);
Michael Kolbd6954f32013-03-08 20:43:01 -0800357 }
358
Doris Liu29da2db2013-08-28 14:28:45 -0700359 public void animateCapture(final byte[] jpegData, int orientation, boolean mirror) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700360 // Decode jpeg byte array and then animate the jpeg
Doris Liu29da2db2013-08-28 14:28:45 -0700361 DecodeTask task = new DecodeTask(jpegData, orientation, mirror);
Sascha Haeberling37f36112013-08-06 14:31:52 -0700362 task.execute();
363 }
364
Michael Kolb3bc96b22013-03-12 10:24:42 -0700365 private void openMenu() {
366 if (mPieRenderer != null) {
367 // If autofocus is not finished, cancel autofocus so that the
368 // subsequent touch can be handled by PreviewGestures
369 if (mController.getCameraState() == PhotoController.FOCUSING) {
370 mController.cancelAutoFocus();
371 }
372 mPieRenderer.showInCenter();
373 }
374 }
375
Michael Kolbd6954f32013-03-08 20:43:01 -0800376 public void initializeControlByIntent() {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700377 mPreviewThumb = (ImageView) mRootView.findViewById(R.id.preview_thumb);
Michael Kolb78a5b222013-04-30 15:34:19 -0700378 mPreviewThumb.setOnClickListener(new OnClickListener() {
379 @Override
380 public void onClick(View v) {
Angus Kong734598c2013-08-21 15:25:05 -0700381 mActivity.gotoGallery();
Michael Kolb78a5b222013-04-30 15:34:19 -0700382 }
383 });
Doris Liu6432cd62013-06-13 17:20:31 -0700384 mMenuButton = mRootView.findViewById(R.id.menu);
Michael Kolbd6954f32013-03-08 20:43:01 -0800385 mMenuButton.setOnClickListener(new OnClickListener() {
386 @Override
387 public void onClick(View v) {
Michael Kolb3bc96b22013-03-12 10:24:42 -0700388 openMenu();
Michael Kolbd6954f32013-03-08 20:43:01 -0800389 }
390 });
391 if (mController.isImageCaptureIntent()) {
Doris Liu6432cd62013-06-13 17:20:31 -0700392 ViewGroup cameraControls = (ViewGroup) mRootView.findViewById(R.id.camera_controls);
Michael Kolbd6954f32013-03-08 20:43:01 -0800393 mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls);
394
Doris Liu6432cd62013-06-13 17:20:31 -0700395 mReviewDoneButton = mRootView.findViewById(R.id.btn_done);
396 mReviewCancelButton = mRootView.findViewById(R.id.btn_cancel);
397 mReviewRetakeButton = mRootView.findViewById(R.id.btn_retake);
Doris Liu36e56fb2013-09-11 17:38:08 -0700398 mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image);
Michael Kolbd6954f32013-03-08 20:43:01 -0800399 mReviewCancelButton.setVisibility(View.VISIBLE);
400
401 mReviewDoneButton.setOnClickListener(new OnClickListener() {
402 @Override
403 public void onClick(View v) {
404 mController.onCaptureDone();
405 }
406 });
407 mReviewCancelButton.setOnClickListener(new OnClickListener() {
408 @Override
409 public void onClick(View v) {
410 mController.onCaptureCancelled();
411 }
412 });
413
414 mReviewRetakeButton.setOnClickListener(new OnClickListener() {
415 @Override
416 public void onClick(View v) {
417 mController.onCaptureRetake();
418 }
419 });
420 }
421 }
422
Doris Liu6432cd62013-06-13 17:20:31 -0700423 public void hideUI() {
424 mCameraControls.setVisibility(View.INVISIBLE);
Doris Liu6432cd62013-06-13 17:20:31 -0700425 }
426
427 public void showUI() {
428 mCameraControls.setVisibility(View.VISIBLE);
429 }
430
Erin Dahlgren3044d8c2013-10-10 18:23:45 -0700431 public boolean arePreviewControlsVisible() {
432 return (mCameraControls.getVisibility() == View.VISIBLE);
433 }
434
Michael Kolbd6954f32013-03-08 20:43:01 -0800435 // called from onResume but only the first time
436 public void initializeFirstTime() {
437 // Initialize shutter button.
Michael Kolbd6954f32013-03-08 20:43:01 -0800438 mShutterButton.setImageResource(R.drawable.btn_new_shutter);
439 mShutterButton.setOnShutterButtonListener(mController);
440 mShutterButton.setVisibility(View.VISIBLE);
441 }
442
443 // called from onResume every other time
444 public void initializeSecondTime(Camera.Parameters params) {
445 initializeZoom(params);
446 if (mController.isImageCaptureIntent()) {
447 hidePostCaptureAlert();
448 }
449 if (mMenu != null) {
450 mMenu.reloadPreferences();
451 }
452 }
453
Doris Liu6a83d522013-07-02 12:03:32 -0700454 public void showLocationDialog() {
455 mLocationDialog = new AlertDialog.Builder(mActivity)
456 .setTitle(R.string.remember_location_title)
457 .setMessage(R.string.remember_location_prompt)
458 .setPositiveButton(R.string.remember_location_yes,
459 new DialogInterface.OnClickListener() {
460 @Override
461 public void onClick(DialogInterface dialog, int arg1) {
462 mController.enableRecordingLocation(true);
463 mLocationDialog = null;
464 }
465 })
466 .setNegativeButton(R.string.remember_location_no,
467 new DialogInterface.OnClickListener() {
468 @Override
469 public void onClick(DialogInterface dialog, int arg1) {
470 dialog.cancel();
471 }
472 })
473 .setOnCancelListener(new DialogInterface.OnCancelListener() {
474 @Override
475 public void onCancel(DialogInterface dialog) {
476 mController.enableRecordingLocation(false);
477 mLocationDialog = null;
478 }
479 })
480 .show();
481 }
482
Michael Kolbd6954f32013-03-08 20:43:01 -0800483 public void initializeZoom(Camera.Parameters params) {
484 if ((params == null) || !params.isZoomSupported()
485 || (mZoomRenderer == null)) return;
486 mZoomMax = params.getMaxZoom();
487 mZoomRatios = params.getZoomRatios();
488 // Currently we use immediate zoom for fast zooming to get better UX and
489 // there is no plan to take advantage of the smooth zoom.
490 if (mZoomRenderer != null) {
491 mZoomRenderer.setZoomMax(mZoomMax);
492 mZoomRenderer.setZoom(params.getZoom());
493 mZoomRenderer.setZoomValue(mZoomRatios.get(params.getZoom()));
494 mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener());
495 }
496 }
497
ztenghui7b265a62013-09-09 14:58:44 -0700498 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -0800499 public void showGpsOnScreenIndicator(boolean hasSignal) { }
500
ztenghui7b265a62013-09-09 14:58:44 -0700501 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -0800502 public void hideGpsOnScreenIndicator() { }
503
504 public void overrideSettings(final String ... keyvalues) {
505 mMenu.overrideSettings(keyvalues);
506 }
507
508 public void updateOnScreenIndicators(Camera.Parameters params,
Michael Kolbeb8adc12013-04-26 11:09:29 -0700509 PreferenceGroup group, ComboPreferences prefs) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800510 if (params == null) return;
Sascha Haeberlingc7965e22013-04-22 14:27:36 -0700511 mOnScreenIndicators.updateSceneOnScreenIndicator(params.getSceneMode());
512 mOnScreenIndicators.updateExposureOnScreenIndicator(params,
Michael Kolbd6954f32013-03-08 20:43:01 -0800513 CameraSettings.readExposure(prefs));
Sascha Haeberlingc7965e22013-04-22 14:27:36 -0700514 mOnScreenIndicators.updateFlashOnScreenIndicator(params.getFlashMode());
Michael Kolbeb8adc12013-04-26 11:09:29 -0700515 int wbIndex = 2;
516 ListPreference pref = group.findPreference(CameraSettings.KEY_WHITE_BALANCE);
517 if (pref != null) {
518 wbIndex = pref.getCurrentIndex();
519 }
520 mOnScreenIndicators.updateWBIndicator(wbIndex);
Michael Kolb87880792013-04-30 15:38:49 -0700521 boolean location = RecordLocationPreference.get(
522 prefs, mActivity.getContentResolver());
Michael Kolbeb8adc12013-04-26 11:09:29 -0700523 mOnScreenIndicators.updateLocationIndicator(location);
Michael Kolbd6954f32013-03-08 20:43:01 -0800524 }
525
Michael Kolbd6954f32013-03-08 20:43:01 -0800526 public void setCameraState(int state) {
527 }
528
Doris Liuc2e9abd2013-06-19 14:20:51 -0700529 public void animateFlash() {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700530 mAnimationManager.startFlashAnimation(mFlashOverlay);
Doris Liuc2e9abd2013-06-19 14:20:51 -0700531 }
532
Doris Liu6432cd62013-06-13 17:20:31 -0700533 public void enableGestures(boolean enable) {
534 if (mGestures != null) {
535 mGestures.setEnabled(enable);
Michael Kolbd6954f32013-03-08 20:43:01 -0800536 }
Doris Liu6432cd62013-06-13 17:20:31 -0700537 }
538
539 // forward from preview gestures to controller
540 @Override
541 public void onSingleTapUp(View view, int x, int y) {
542 mController.onSingleTapUp(view, x, y);
Michael Kolbd6954f32013-03-08 20:43:01 -0800543 }
544
545 public boolean onBackPressed() {
546 if (mPieRenderer != null && mPieRenderer.showsItems()) {
547 mPieRenderer.hide();
548 return true;
549 }
550 // In image capture mode, back button should:
551 // 1) if there is any popup, dismiss them, 2) otherwise, get out of
552 // image capture
553 if (mController.isImageCaptureIntent()) {
Doris Liuc3679c02013-08-08 18:08:43 -0700554 mController.onCaptureCancelled();
Michael Kolbd6954f32013-03-08 20:43:01 -0800555 return true;
556 } else if (!mController.isCameraIdle()) {
557 // ignore backs while we're taking a picture
558 return true;
559 } else {
Doris Liuc3679c02013-08-08 18:08:43 -0700560 return false;
Michael Kolbd6954f32013-03-08 20:43:01 -0800561 }
562 }
563
ztenghui7b265a62013-09-09 14:58:44 -0700564 public void onPreviewFocusChanged(boolean previewFocused) {
565 if (previewFocused) {
Doris Liu6432cd62013-06-13 17:20:31 -0700566 showUI();
567 } else {
568 hideUI();
569 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800570 if (mFaceView != null) {
ztenghui7b265a62013-09-09 14:58:44 -0700571 mFaceView.setBlockDraw(!previewFocused);
Michael Kolbd6954f32013-03-08 20:43:01 -0800572 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800573 if (mGestures != null) {
ztenghui7b265a62013-09-09 14:58:44 -0700574 mGestures.setEnabled(previewFocused);
Michael Kolbd6954f32013-03-08 20:43:01 -0800575 }
576 if (mRenderOverlay != null) {
577 // this can not happen in capture mode
ztenghui7b265a62013-09-09 14:58:44 -0700578 mRenderOverlay.setVisibility(previewFocused ? View.VISIBLE : View.GONE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800579 }
580 if (mPieRenderer != null) {
ztenghui7b265a62013-09-09 14:58:44 -0700581 mPieRenderer.setBlockFocus(!previewFocused);
Michael Kolbd6954f32013-03-08 20:43:01 -0800582 }
ztenghui7b265a62013-09-09 14:58:44 -0700583 setShowMenu(previewFocused);
584 if (!previewFocused && mCountDownView != null) mCountDownView.cancelCountDown();
Michael Kolbd6954f32013-03-08 20:43:01 -0800585 }
586
Michael Kolbd6954f32013-03-08 20:43:01 -0800587 public void showPopup(AbstractSettingPopup popup) {
Doris Liu6432cd62013-06-13 17:20:31 -0700588 hideUI();
Doris Liuc3679c02013-08-08 18:08:43 -0700589
590 if (mPopup == null) {
591 mPopup = new PopupWindow(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
592 mPopup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
593 mPopup.setOutsideTouchable(true);
594 mPopup.setFocusable(true);
595 mPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
596 @Override
597 public void onDismiss() {
598 mPopup = null;
599 mMenu.popupDismissed();
600 showUI();
Sascha Haeberling4ec139d2013-10-09 23:46:20 -0700601
602 // Switch back into fullscreen/lights-out mode after popup
603 // is dimissed.
604 mActivity.setSystemBarsVisibility(false);
Doris Liuc3679c02013-08-08 18:08:43 -0700605 }
606 });
607 }
608 popup.setVisibility(View.VISIBLE);
609 mPopup.setContentView(popup);
610 mPopup.showAtLocation(mRootView, Gravity.CENTER, 0, 0);
Michael Kolbd6954f32013-03-08 20:43:01 -0800611 }
612
Michael Kolb70489722013-05-08 11:14:35 -0700613 public void dismissPopup() {
Doris Liuc3679c02013-08-08 18:08:43 -0700614 if (mPopup != null && mPopup.isShowing()) {
615 mPopup.dismiss();
Michael Kolbd6954f32013-03-08 20:43:01 -0800616 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800617 }
618
619 public void onShowSwitcherPopup() {
620 if (mPieRenderer != null && mPieRenderer.showsItems()) {
621 mPieRenderer.hide();
622 }
623 }
624
625 private void setShowMenu(boolean show) {
626 if (mOnScreenIndicators != null) {
627 mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE);
628 }
629 if (mMenuButton != null) {
630 mMenuButton.setVisibility(show ? View.VISIBLE : View.GONE);
631 }
632 }
633
634 public boolean collapseCameraControls() {
635 // Remove all the popups/dialog boxes
636 boolean ret = false;
637 if (mPopup != null) {
Michael Kolb70489722013-05-08 11:14:35 -0700638 dismissPopup();
Michael Kolbd6954f32013-03-08 20:43:01 -0800639 ret = true;
640 }
Michael Kolbb16bae92013-05-08 16:05:04 -0700641 onShowSwitcherPopup();
Michael Kolbd6954f32013-03-08 20:43:01 -0800642 return ret;
643 }
644
Doris Liu36e56fb2013-09-11 17:38:08 -0700645 protected void showCapturedImageForReview(byte[] jpegData, int orientation, boolean mirror) {
646 mDecodeTaskForReview = new DecodeImageForReview(jpegData, orientation, mirror);
647 mDecodeTaskForReview.execute();
Michael Kolbd6954f32013-03-08 20:43:01 -0800648 mOnScreenIndicators.setVisibility(View.GONE);
649 mMenuButton.setVisibility(View.GONE);
Angus Kongb50b5cb2013-08-09 14:55:20 -0700650 CameraUtil.fadeIn(mReviewDoneButton);
Michael Kolbd6954f32013-03-08 20:43:01 -0800651 mShutterButton.setVisibility(View.INVISIBLE);
Angus Kongb50b5cb2013-08-09 14:55:20 -0700652 CameraUtil.fadeIn(mReviewRetakeButton);
Doris Liud31cdfe2013-05-14 11:31:19 -0700653 pauseFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800654 }
655
656 protected void hidePostCaptureAlert() {
Doris Liu36e56fb2013-09-11 17:38:08 -0700657 if (mDecodeTaskForReview != null) {
658 mDecodeTaskForReview.cancel(true);
659 }
660 mReviewImage.setVisibility(View.GONE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800661 mOnScreenIndicators.setVisibility(View.VISIBLE);
662 mMenuButton.setVisibility(View.VISIBLE);
Angus Kongb50b5cb2013-08-09 14:55:20 -0700663 CameraUtil.fadeOut(mReviewDoneButton);
Michael Kolbd6954f32013-03-08 20:43:01 -0800664 mShutterButton.setVisibility(View.VISIBLE);
Angus Kongb50b5cb2013-08-09 14:55:20 -0700665 CameraUtil.fadeOut(mReviewRetakeButton);
Doris Liud31cdfe2013-05-14 11:31:19 -0700666 resumeFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800667 }
668
669 public void setDisplayOrientation(int orientation) {
670 if (mFaceView != null) {
671 mFaceView.setDisplayOrientation(orientation);
672 }
673 }
674
675 // shutter button handling
676
677 public boolean isShutterPressed() {
678 return mShutterButton.isPressed();
679 }
680
Sascha Haeberling88901942013-08-28 17:49:00 -0700681 /**
682 * Enables or disables the shutter button.
683 */
Doris Liu6432cd62013-06-13 17:20:31 -0700684 public void enableShutter(boolean enabled) {
685 if (mShutterButton != null) {
686 mShutterButton.setEnabled(enabled);
687 }
688 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800689
Doris Liu6432cd62013-06-13 17:20:31 -0700690 public void pressShutterButton() {
691 if (mShutterButton.isInTouchMode()) {
692 mShutterButton.requestFocusFromTouch();
693 } else {
694 mShutterButton.requestFocus();
695 }
696 mShutterButton.setPressed(true);
697 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800698
699 private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
700 @Override
701 public void onZoomValueChanged(int index) {
702 int newZoom = mController.onZoomChanged(index);
703 if (mZoomRenderer != null) {
704 mZoomRenderer.setZoomValue(mZoomRatios.get(newZoom));
705 }
706 }
707
708 @Override
709 public void onZoomStart() {
710 if (mPieRenderer != null) {
711 mPieRenderer.setBlockFocus(true);
712 }
713 }
714
715 @Override
716 public void onZoomEnd() {
717 if (mPieRenderer != null) {
718 mPieRenderer.setBlockFocus(false);
719 }
720 }
721 }
722
723 @Override
724 public void onPieOpened(int centerX, int centerY) {
Doris Liu6432cd62013-06-13 17:20:31 -0700725 setSwipingEnabled(false);
Michael Kolbd6954f32013-03-08 20:43:01 -0800726 if (mFaceView != null) {
727 mFaceView.setBlockDraw(true);
728 }
729 }
730
731 @Override
732 public void onPieClosed() {
Doris Liu6432cd62013-06-13 17:20:31 -0700733 setSwipingEnabled(true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800734 if (mFaceView != null) {
735 mFaceView.setBlockDraw(false);
736 }
737 }
738
Doris Liu6432cd62013-06-13 17:20:31 -0700739 public void setSwipingEnabled(boolean enable) {
740 mActivity.setSwipingEnabled(enable);
Michael Kolbd6954f32013-03-08 20:43:01 -0800741 }
742
Angus Kongdcccc512013-08-08 17:06:03 -0700743 public SurfaceTexture getSurfaceTexture() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800744 return mSurfaceTexture;
745 }
746
Doris Liu6432cd62013-06-13 17:20:31 -0700747 // Countdown timer
Michael Kolbd6954f32013-03-08 20:43:01 -0800748
Doris Liu6432cd62013-06-13 17:20:31 -0700749 private void initializeCountDown() {
750 mActivity.getLayoutInflater().inflate(R.layout.count_down_to_capture,
751 (ViewGroup) mRootView, true);
752 mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture));
753 mCountDownView.setCountDownFinishedListener((OnCountDownFinishedListener) mController);
Michael Kolbd6954f32013-03-08 20:43:01 -0800754 }
755
756 public boolean isCountingDown() {
Doris Liu6432cd62013-06-13 17:20:31 -0700757 return mCountDownView != null && mCountDownView.isCountingDown();
Michael Kolbd6954f32013-03-08 20:43:01 -0800758 }
759
760 public void cancelCountDown() {
Doris Liu6432cd62013-06-13 17:20:31 -0700761 if (mCountDownView == null) return;
Michael Kolbd6954f32013-03-08 20:43:01 -0800762 mCountDownView.cancelCountDown();
763 }
764
765 public void startCountDown(int sec, boolean playSound) {
Doris Liu6432cd62013-06-13 17:20:31 -0700766 if (mCountDownView == null) initializeCountDown();
Michael Kolbd6954f32013-03-08 20:43:01 -0800767 mCountDownView.startCountDown(sec, playSound);
768 }
769
770 public void showPreferencesToast() {
771 if (mNotSelectableToast == null) {
772 String str = mActivity.getResources().getString(R.string.not_selectable_in_scene_mode);
773 mNotSelectableToast = Toast.makeText(mActivity, str, Toast.LENGTH_SHORT);
774 }
775 mNotSelectableToast.show();
776 }
777
Doris Liu3a45c332013-10-15 19:10:28 -0700778 public void showPreviewCover() {
779 mPreviewCover.setVisibility(View.VISIBLE);
780 }
781
Michael Kolbd6954f32013-03-08 20:43:01 -0800782 public void onPause() {
Doris Liu6432cd62013-06-13 17:20:31 -0700783 cancelCountDown();
Michael Kolbd6954f32013-03-08 20:43:01 -0800784
785 // Clear UI.
786 collapseCameraControls();
787 if (mFaceView != null) mFaceView.clear();
788
Doris Liu6a83d522013-07-02 12:03:32 -0700789 if (mLocationDialog != null && mLocationDialog.isShowing()) {
790 mLocationDialog.dismiss();
791 }
792 mLocationDialog = null;
Michael Kolbd6954f32013-03-08 20:43:01 -0800793 }
794
Doris Liu59390062013-10-10 17:20:56 -0700795 public void initDisplayChangeListener() {
796 ((CameraRootView) mRootView).setDisplayChangeListener(this);
797 }
798
799 public void removeDisplayChangeListener() {
800 ((CameraRootView) mRootView).removeDisplayChangeListener();
801 }
802
Michael Kolbd6954f32013-03-08 20:43:01 -0800803 // focus UI implementation
804
805 private FocusIndicator getFocusIndicator() {
806 return (mFaceView != null && mFaceView.faceExists()) ? mFaceView : mPieRenderer;
807 }
808
809 @Override
810 public boolean hasFaces() {
811 return (mFaceView != null && mFaceView.faceExists());
812 }
813
814 public void clearFaces() {
815 if (mFaceView != null) mFaceView.clear();
816 }
817
818 @Override
819 public void clearFocus() {
Michael Kolb2441ada2013-03-13 10:47:06 -0700820 FocusIndicator indicator = getFocusIndicator();
821 if (indicator != null) indicator.clear();
Michael Kolbd6954f32013-03-08 20:43:01 -0800822 }
823
824 @Override
825 public void setFocusPosition(int x, int y) {
826 mPieRenderer.setFocus(x, y);
827 }
828
829 @Override
830 public void onFocusStarted() {
831 getFocusIndicator().showStart();
832 }
833
834 @Override
835 public void onFocusSucceeded(boolean timeout) {
836 getFocusIndicator().showSuccess(timeout);
837 }
838
839 @Override
840 public void onFocusFailed(boolean timeout) {
841 getFocusIndicator().showFail(timeout);
842 }
843
844 @Override
845 public void pauseFaceDetection() {
846 if (mFaceView != null) mFaceView.pause();
847 }
848
849 @Override
850 public void resumeFaceDetection() {
851 if (mFaceView != null) mFaceView.resume();
852 }
853
854 public void onStartFaceDetection(int orientation, boolean mirror) {
855 mFaceView.clear();
856 mFaceView.setVisibility(View.VISIBLE);
857 mFaceView.setDisplayOrientation(orientation);
858 mFaceView.setMirror(mirror);
859 mFaceView.resume();
860 }
861
862 @Override
Angus Kong9e765522013-07-31 14:05:20 -0700863 public void onFaceDetection(Face[] faces, CameraManager.CameraProxy camera) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800864 mFaceView.setFaces(faces);
865 }
866
ztenghui7b265a62013-09-09 14:58:44 -0700867 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700868 public void onDisplayChanged() {
Doris Liu0ba8eaa2013-10-16 12:51:02 -0700869 Log.d(TAG, "Device flip detected.");
Doris Liu6432cd62013-06-13 17:20:31 -0700870 mCameraControls.checkLayoutFlip();
871 mController.updateCameraOrientation();
Michael Kolb3bc96b22013-03-12 10:24:42 -0700872 }
873
Michael Kolbd6954f32013-03-08 20:43:01 -0800874}