blob: 9329ca16fed1f11ce82a5ec65bc24567a22e7cc5 [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
Michael Kolbd6954f32013-03-08 20:43:01 -080017package com.android.camera;
18
Doris Liu6a83d522013-07-02 12:03:32 -070019import android.app.AlertDialog;
Sascha Haeberling37f36112013-08-06 14:31:52 -070020import android.graphics.Bitmap;
Doris Liu6432cd62013-06-13 17:20:31 -070021import android.graphics.Matrix;
22import android.graphics.SurfaceTexture;
Michael Kolbd6954f32013-03-08 20:43:01 -080023import android.hardware.Camera;
24import android.hardware.Camera.Face;
Sascha Haeberling37f36112013-08-06 14:31:52 -070025import android.os.AsyncTask;
Doris Liuf9e4f8f2013-12-04 18:04:22 -080026import android.view.GestureDetector;
27import android.view.MotionEvent;
Michael Kolbd6954f32013-03-08 20:43:01 -080028import android.view.View;
Michael Kolbd6954f32013-03-08 20:43:01 -080029import android.view.ViewGroup;
Michael Kolbd6954f32013-03-08 20:43:01 -080030
Michael Kolbd6954f32013-03-08 20:43:01 -080031import com.android.camera.FocusOverlayManager.FocusUI;
Angus Kong20fad242013-11-11 18:23:46 -080032import com.android.camera.app.CameraManager;
Angus Kong2bca2102014-03-11 16:27:30 -070033import com.android.camera.debug.Log;
Michael Kolbd6954f32013-03-08 20:43:01 -080034import com.android.camera.ui.FaceView;
Doris Liuf9e4f8f2013-12-04 18:04:22 -080035import com.android.camera.ui.PreviewOverlay;
Doris Liu06db7422013-12-09 19:36:25 -080036import com.android.camera.ui.PreviewStatusListener;
Angus Kongb50b5cb2013-08-09 14:55:20 -070037import com.android.camera.util.CameraUtil;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070038import com.android.camera2.R;
Angus Kongb50b5cb2013-08-09 14:55:20 -070039
40import java.util.List;
Michael Kolbd6954f32013-03-08 20:43:01 -080041
Doris Liu482de022013-12-18 19:18:16 -080042public class PhotoUI implements PreviewStatusListener,
Angus Kong9e765522013-07-31 14:05:20 -070043 CameraManager.CameraFaceDetectionCallback {
Michael Kolbd6954f32013-03-08 20:43:01 -080044
Angus Kong2bca2102014-03-11 16:27:30 -070045 private static final Log.Tag TAG = new Log.Tag("PhotoUI");
Sascha Haeberling37f36112013-08-06 14:31:52 -070046 private static final int DOWN_SAMPLE_FACTOR = 4;
Doris Liu70da9182013-12-17 18:41:15 -080047 private static final float UNSET = 0f;
Spike Sprague0f3c4b42013-12-10 19:50:17 -080048
Doris Liuf9e4f8f2013-12-04 18:04:22 -080049 private final PreviewOverlay mPreviewOverlay;
Doris Liu482de022013-12-18 19:18:16 -080050 private final FocusUI mFocusUI;
Sascha Haeberlinge2914fd2014-01-17 19:03:52 -080051 private final CameraActivity mActivity;
52 private final PhotoController mController;
Michael Kolbd6954f32013-03-08 20:43:01 -080053
Sascha Haeberlinge2914fd2014-01-17 19:03:52 -080054 private final View mRootView;
Michael Kolbd6954f32013-03-08 20:43:01 -080055
Doris Liud27ea7b2014-02-25 11:46:11 -080056 // TODO: Remove face view logic if UX does not bring it back within a month.
57 private FaceView mFaceView = null;
Doris Liu36e56fb2013-09-11 17:38:08 -070058 private DecodeImageForReview mDecodeTaskForReview = null;
Michael Kolbd6954f32013-03-08 20:43:01 -080059
60 private int mZoomMax;
61 private List<Integer> mZoomRatios;
62
63 private int mPreviewWidth = 0;
64 private int mPreviewHeight = 0;
Doris Liu70da9182013-12-17 18:41:15 -080065 private float mAspectRatio = UNSET;
Marco Nelissen0744e4a2013-11-22 01:47:37 +000066
Doris Liu06db7422013-12-09 19:36:25 -080067 private final GestureDetector.OnGestureListener mPreviewGestureListener
68 = new GestureDetector.SimpleOnGestureListener() {
69 @Override
70 public boolean onSingleTapUp(MotionEvent ev) {
71 mController.onSingleTapUp(null, (int) ev.getX(), (int) ev.getY());
72 return true;
73 }
74 };
Marco Nelissen0744e4a2013-11-22 01:47:37 +000075
Doris Liu06db7422013-12-09 19:36:25 -080076 @Override
77 public GestureDetector.OnGestureListener getGestureListener() {
78 return mPreviewGestureListener;
79 }
80
Doris Liu4a010db2013-12-16 18:44:49 -080081 @Override
Erin Dahlgrend8de0772014-02-03 10:12:27 -080082 public View.OnTouchListener getTouchListener() {
83 return null;
84 }
85
86 @Override
Doris Liu4a010db2013-12-16 18:44:49 -080087 public void onPreviewLayoutChanged(View v, int left, int top, int right,
88 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
89 int width = right - left;
90 int height = bottom - top;
91 if (mPreviewWidth != width || mPreviewHeight != height) {
92 mPreviewWidth = width;
93 mPreviewHeight = height;
Michael Kolbd6954f32013-03-08 20:43:01 -080094 }
Doris Liu4a010db2013-12-16 18:44:49 -080095 }
Michael Kolbd6954f32013-03-08 20:43:01 -080096
Doris Liu70da9182013-12-17 18:41:15 -080097 @Override
98 public boolean shouldAutoAdjustTransformMatrixOnLayout() {
99 return true;
100 }
101
102 @Override
103 public boolean shouldAutoAdjustBottomBar() {
104 return true;
105 }
106
Doris Liu11ddd732014-01-07 10:52:11 -0800107 @Override
108 public void onPreviewFlipped() {
109 mController.updateCameraOrientation();
110 }
111
Doris Liu36e56fb2013-09-11 17:38:08 -0700112 private class DecodeTask extends AsyncTask<Void, Void, Bitmap> {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700113 private final byte [] mData;
Sascha Haeberlinge2914fd2014-01-17 19:03:52 -0800114 private final int mOrientation;
115 private final boolean mMirror;
Doris Liuc2e9abd2013-06-19 14:20:51 -0700116
Doris Liu29da2db2013-08-28 14:28:45 -0700117 public DecodeTask(byte[] data, int orientation, boolean mirror) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700118 mData = data;
Doris Liuce2acbc2013-08-21 18:45:29 -0700119 mOrientation = orientation;
Doris Liu29da2db2013-08-28 14:28:45 -0700120 mMirror = mirror;
Doris Liuc2e9abd2013-06-19 14:20:51 -0700121 }
122
123 @Override
Doris Liu36e56fb2013-09-11 17:38:08 -0700124 protected Bitmap doInBackground(Void... params) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700125 // Decode image in background.
Doris Liuce2acbc2013-08-21 18:45:29 -0700126 Bitmap bitmap = CameraUtil.downSample(mData, DOWN_SAMPLE_FACTOR);
Doris Liu29da2db2013-08-28 14:28:45 -0700127 if (mOrientation != 0 || mMirror) {
Doris Liuce2acbc2013-08-21 18:45:29 -0700128 Matrix m = new Matrix();
Doris Liu29da2db2013-08-28 14:28:45 -0700129 if (mMirror) {
130 // Flip horizontally
131 m.setScale(-1f, 1f);
132 }
Andy Huiberscec75e42013-10-29 16:03:58 -0700133 m.preRotate(mOrientation);
Doris Liuce2acbc2013-08-21 18:45:29 -0700134 return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m,
135 false);
136 }
137 return bitmap;
Sascha Haeberling37f36112013-08-06 14:31:52 -0700138 }
Sascha Haeberling37f36112013-08-06 14:31:52 -0700139 }
Doris Liuc2e9abd2013-06-19 14:20:51 -0700140
Doris Liu36e56fb2013-09-11 17:38:08 -0700141 private class DecodeImageForReview extends DecodeTask {
142 public DecodeImageForReview(byte[] data, int orientation, boolean mirror) {
143 super(data, orientation, mirror);
144 }
145
146 @Override
147 protected void onPostExecute(Bitmap bitmap) {
148 if (isCancelled()) {
149 return;
150 }
Doris Liu36e56fb2013-09-11 17:38:08 -0700151 mDecodeTaskForReview = null;
152 }
153 }
154
Michael Kolbd6954f32013-03-08 20:43:01 -0800155 public PhotoUI(CameraActivity activity, PhotoController controller, View parent) {
156 mActivity = activity;
157 mController = controller;
158 mRootView = parent;
159
Doris Liuf55f3c42013-11-20 00:24:46 -0800160 ViewGroup moduleRoot = (ViewGroup) mRootView.findViewById(R.id.module_layout);
Michael Kolbd6954f32013-03-08 20:43:01 -0800161 mActivity.getLayoutInflater().inflate(R.layout.photo_module,
Sascha Haeberlinge2914fd2014-01-17 19:03:52 -0800162 moduleRoot, true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800163 initIndicators();
Doris Liu482de022013-12-18 19:18:16 -0800164 mFocusUI = (FocusUI) mRootView.findViewById(R.id.focus_overlay);
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800165 mPreviewOverlay = (PreviewOverlay) mRootView.findViewById(R.id.preview_overlay);
Doris Liu6432cd62013-06-13 17:20:31 -0700166 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800167
Doris Liu482de022013-12-18 19:18:16 -0800168 public FocusUI getFocusUI() {
169 return mFocusUI;
170 }
171
Doris Liud053a5b2013-10-25 20:20:43 -0700172 public void updatePreviewAspectRatio(float aspectRatio) {
173 if (aspectRatio <= 0) {
174 Log.e(TAG, "Invalid aspect ratio: " + aspectRatio);
175 return;
176 }
177 if (aspectRatio < 1f) {
178 aspectRatio = 1f / aspectRatio;
179 }
180
181 if (mAspectRatio != aspectRatio) {
182 mAspectRatio = aspectRatio;
183 // Update transform matrix with the new aspect ratio.
Doris Liu70da9182013-12-17 18:41:15 -0800184 mController.updatePreviewAspectRatio(mAspectRatio);
Doris Liud053a5b2013-10-25 20:20:43 -0700185 }
186 }
187
Angus Kongdcccc512013-08-08 17:06:03 -0700188 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700189 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800190 mController.onPreviewUIReady();
Doris Liu6432cd62013-06-13 17:20:31 -0700191 }
192
Angus Kongdcccc512013-08-08 17:06:03 -0700193 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700194 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
195 // Ignored, Camera does all the work for us
196 }
197
Angus Kongdcccc512013-08-08 17:06:03 -0700198 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700199 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800200 mController.onPreviewUIDestroyed();
201 return true;
Doris Liu6432cd62013-06-13 17:20:31 -0700202 }
203
ztenghui7b265a62013-09-09 14:58:44 -0700204 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700205 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800206 }
207
208 public View getRootView() {
209 return mRootView;
210 }
211
212 private void initIndicators() {
Marco Nelissen0744e4a2013-11-22 01:47:37 +0000213 // TODO init toggle buttons on bottom bar here
Michael Kolbd6954f32013-03-08 20:43:01 -0800214 }
215
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800216 public void onCameraOpened(Camera.Parameters params) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800217 initializeZoom(params);
Michael Kolbd6954f32013-03-08 20:43:01 -0800218 }
219
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800220 public void animateCapture(final byte[] jpegData, int orientation, boolean mirror) {
221 // Decode jpeg byte array and then animate the jpeg
222 DecodeTask task = new DecodeTask(jpegData, orientation, mirror);
223 task.execute();
Spike Spragueeb3134f2013-12-12 11:14:27 -0800224 }
225
Michael Kolbd6954f32013-03-08 20:43:01 -0800226 // called from onResume but only the first time
Kevin Gabayanffbc43c2013-12-09 11:41:50 -0800227 public void initializeFirstTime() {
Marco Nelissen0744e4a2013-11-22 01:47:37 +0000228
Michael Kolbd6954f32013-03-08 20:43:01 -0800229 }
230
231 // called from onResume every other time
232 public void initializeSecondTime(Camera.Parameters params) {
233 initializeZoom(params);
234 if (mController.isImageCaptureIntent()) {
235 hidePostCaptureAlert();
236 }
Erin Dahlgren357b7672013-11-20 17:38:14 -0800237 // Removes pie menu.
Michael Kolbd6954f32013-03-08 20:43:01 -0800238 }
239
Doris Liu6a83d522013-07-02 12:03:32 -0700240 public void showLocationDialog() {
Erin Dahlgren491c6282013-11-25 13:22:07 -0800241 AlertDialog alert = mActivity.getFirstTimeLocationAlert();
242 alert.show();
Doris Liu6a83d522013-07-02 12:03:32 -0700243 }
244
Michael Kolbd6954f32013-03-08 20:43:01 -0800245 public void initializeZoom(Camera.Parameters params) {
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800246 if ((params == null) || !params.isZoomSupported()) return;
Michael Kolbd6954f32013-03-08 20:43:01 -0800247 mZoomMax = params.getMaxZoom();
248 mZoomRatios = params.getZoomRatios();
249 // Currently we use immediate zoom for fast zooming to get better UX and
250 // there is no plan to take advantage of the smooth zoom.
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800251 // TODO: Need to setup a path to AppUI to do this
252 mPreviewOverlay.setupZoom(mZoomMax, params.getZoom(), mZoomRatios, new ZoomChangeListener());
Michael Kolbd6954f32013-03-08 20:43:01 -0800253 }
254
Doris Liuc2e9abd2013-06-19 14:20:51 -0700255 public void animateFlash() {
Doris Liu1dfe7822013-12-12 00:02:08 -0800256 mController.startPreCaptureAnimation();
Doris Liuc2e9abd2013-06-19 14:20:51 -0700257 }
258
Michael Kolbd6954f32013-03-08 20:43:01 -0800259 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800260 // In image capture mode, back button should:
261 // 1) if there is any popup, dismiss them, 2) otherwise, get out of
262 // image capture
263 if (mController.isImageCaptureIntent()) {
Doris Liuc3679c02013-08-08 18:08:43 -0700264 mController.onCaptureCancelled();
Michael Kolbd6954f32013-03-08 20:43:01 -0800265 return true;
266 } else if (!mController.isCameraIdle()) {
267 // ignore backs while we're taking a picture
268 return true;
269 } else {
Doris Liuc3679c02013-08-08 18:08:43 -0700270 return false;
Michael Kolbd6954f32013-03-08 20:43:01 -0800271 }
272 }
273
Doris Liu36e56fb2013-09-11 17:38:08 -0700274 protected void showCapturedImageForReview(byte[] jpegData, int orientation, boolean mirror) {
275 mDecodeTaskForReview = new DecodeImageForReview(jpegData, orientation, mirror);
276 mDecodeTaskForReview.execute();
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800277
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800278 mActivity.getCameraAppUI().transitionToIntentReviewLayout();
Doris Liud31cdfe2013-05-14 11:31:19 -0700279 pauseFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800280 }
281
282 protected void hidePostCaptureAlert() {
Doris Liu36e56fb2013-09-11 17:38:08 -0700283 if (mDecodeTaskForReview != null) {
284 mDecodeTaskForReview.cancel(true);
285 }
Doris Liud31cdfe2013-05-14 11:31:19 -0700286 resumeFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800287 }
288
289 public void setDisplayOrientation(int orientation) {
290 if (mFaceView != null) {
291 mFaceView.setDisplayOrientation(orientation);
292 }
293 }
294
295 // shutter button handling
296
297 public boolean isShutterPressed() {
Marco Nelissen0744e4a2013-11-22 01:47:37 +0000298 return false;
Michael Kolbd6954f32013-03-08 20:43:01 -0800299 }
300
Sascha Haeberling88901942013-08-28 17:49:00 -0700301 /**
302 * Enables or disables the shutter button.
303 */
Doris Liu6432cd62013-06-13 17:20:31 -0700304 public void enableShutter(boolean enabled) {
Marco Nelissen0744e4a2013-11-22 01:47:37 +0000305
Doris Liu6432cd62013-06-13 17:20:31 -0700306 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800307
Doris Liu6432cd62013-06-13 17:20:31 -0700308 public void pressShutterButton() {
Marco Nelissen0744e4a2013-11-22 01:47:37 +0000309
Doris Liu6432cd62013-06-13 17:20:31 -0700310 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800311
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800312 private class ZoomChangeListener implements PreviewOverlay.OnZoomChangedListener {
Michael Kolbd6954f32013-03-08 20:43:01 -0800313 @Override
314 public void onZoomValueChanged(int index) {
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800315 mController.onZoomChanged(index);
Michael Kolbd6954f32013-03-08 20:43:01 -0800316 }
317
318 @Override
319 public void onZoomStart() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800320 }
321
322 @Override
323 public void onZoomEnd() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800324 }
325 }
326
Doris Liu6432cd62013-06-13 17:20:31 -0700327 public void setSwipingEnabled(boolean enable) {
328 mActivity.setSwipingEnabled(enable);
Michael Kolbd6954f32013-03-08 20:43:01 -0800329 }
330
Michael Kolbd6954f32013-03-08 20:43:01 -0800331 public void onPause() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800332 if (mFaceView != null) mFaceView.clear();
Michael Kolbd6954f32013-03-08 20:43:01 -0800333 }
334
Michael Kolbd6954f32013-03-08 20:43:01 -0800335 public void clearFaces() {
Marco Nelissen0744e4a2013-11-22 01:47:37 +0000336 if (mFaceView != null) {
337 mFaceView.clear();
338 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800339 }
340
Michael Kolbd6954f32013-03-08 20:43:01 -0800341 public void pauseFaceDetection() {
342 if (mFaceView != null) mFaceView.pause();
343 }
344
Michael Kolbd6954f32013-03-08 20:43:01 -0800345 public void resumeFaceDetection() {
346 if (mFaceView != null) mFaceView.resume();
347 }
348
349 public void onStartFaceDetection(int orientation, boolean mirror) {
Doris Liud27ea7b2014-02-25 11:46:11 -0800350 if (mFaceView != null) {
351 mFaceView.clear();
352 mFaceView.setVisibility(View.VISIBLE);
353 mFaceView.setDisplayOrientation(orientation);
354 mFaceView.setMirror(mirror);
355 mFaceView.resume();
356 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800357 }
358
359 @Override
Angus Kong9e765522013-07-31 14:05:20 -0700360 public void onFaceDetection(Face[] faces, CameraManager.CameraProxy camera) {
Doris Liud27ea7b2014-02-25 11:46:11 -0800361 if (mFaceView != null) {
362 mFaceView.setFaces(faces);
363 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800364 }
365
Doris Liu59401042014-01-14 17:51:32 -0800366 /**
Angus Kong2bacca72014-03-06 12:57:29 -0800367 * Returns a {@link com.android.camera.ui.PreviewStatusListener.PreviewAreaChangedListener}
Doris Liu59401042014-01-14 17:51:32 -0800368 * that should be registered to listen to preview area change.
369 */
Angus Kong2bacca72014-03-06 12:57:29 -0800370 public PreviewAreaChangedListener getPreviewAreaSizeChangedListener() {
Doris Liu59401042014-01-14 17:51:32 -0800371 return mFaceView;
372 }
373
Michael Kolbd6954f32013-03-08 20:43:01 -0800374}