blob: c78eb7152298d630e8f03aa4927687af5bd844e4 [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
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
17package com.android.camera;
18
19import android.annotation.TargetApi;
20import android.graphics.Matrix;
21import android.graphics.Rect;
22import android.graphics.RectF;
23import android.hardware.Camera.Area;
24import android.hardware.Camera.Parameters;
25import android.os.Handler;
26import android.os.Looper;
27import android.os.Message;
28import android.util.Log;
29
30import com.android.camera.ui.FaceView;
31import com.android.camera.ui.FocusIndicator;
32import com.android.camera.ui.PieRenderer;
33import com.android.gallery3d.common.ApiHelper;
34
35import java.util.ArrayList;
36import java.util.List;
37
38/* A class that handles everything about focus in still picture mode.
39 * This also handles the metering area because it is the same as focus area.
40 *
41 * The test cases:
42 * (1) The camera has continuous autofocus. Move the camera. Take a picture when
43 * CAF is not in progress.
44 * (2) The camera has continuous autofocus. Move the camera. Take a picture when
45 * CAF is in progress.
46 * (3) The camera has face detection. Point the camera at some faces. Hold the
47 * shutter. Release to take a picture.
48 * (4) The camera has face detection. Point the camera at some faces. Single tap
49 * the shutter to take a picture.
50 * (5) The camera has autofocus. Single tap the shutter to take a picture.
51 * (6) The camera has autofocus. Hold the shutter. Release to take a picture.
52 * (7) The camera has no autofocus. Single tap the shutter and take a picture.
53 * (8) The camera has autofocus and supports focus area. Touch the screen to
54 * trigger autofocus. Take a picture.
55 * (9) The camera has autofocus and supports focus area. Touch the screen to
56 * trigger autofocus. Wait until it times out.
57 * (10) The camera has no autofocus and supports metering area. Touch the screen
58 * to change metering area.
59 */
60public class FocusOverlayManager {
61 private static final String TAG = "CAM_FocusManager";
62
63 private static final int RESET_TOUCH_FOCUS = 0;
64 private static final int RESET_TOUCH_FOCUS_DELAY = 3000;
65
66 private int mState = STATE_IDLE;
67 private static final int STATE_IDLE = 0; // Focus is not active.
68 private static final int STATE_FOCUSING = 1; // Focus is in progress.
69 // Focus is in progress and the camera should take a picture after focus finishes.
70 private static final int STATE_FOCUSING_SNAP_ON_FINISH = 2;
71 private static final int STATE_SUCCESS = 3; // Focus finishes and succeeds.
72 private static final int STATE_FAIL = 4; // Focus finishes and fails.
73
74 private boolean mInitialized;
75 private boolean mFocusAreaSupported;
76 private boolean mMeteringAreaSupported;
77 private boolean mLockAeAwbNeeded;
78 private boolean mAeAwbLock;
79 private Matrix mMatrix;
80
81 private PieRenderer mPieRenderer;
82
83 private int mPreviewWidth; // The width of the preview frame layout.
84 private int mPreviewHeight; // The height of the preview frame layout.
85 private boolean mMirror; // true if the camera is front-facing.
86 private int mDisplayOrientation;
87 private FaceView mFaceView;
88 private List<Object> mFocusArea; // focus area in driver format
89 private List<Object> mMeteringArea; // metering area in driver format
90 private String mFocusMode;
91 private String[] mDefaultFocusModes;
92 private String mOverrideFocusMode;
93 private Parameters mParameters;
94 private ComboPreferences mPreferences;
95 private Handler mHandler;
96 Listener mListener;
Michael Kolbe3de7222013-02-18 15:16:44 -080097 private boolean mPreviousMoving;
Michael Kolb8872c232013-01-29 10:33:22 -080098
99 public interface Listener {
100 public void autoFocus();
101 public void cancelAutoFocus();
102 public boolean capture();
103 public void startFaceDetection();
104 public void stopFaceDetection();
105 public void setFocusParameters();
106 }
107
108 private class MainHandler extends Handler {
109 public MainHandler(Looper looper) {
110 super(looper);
111 }
112
113 @Override
114 public void handleMessage(Message msg) {
115 switch (msg.what) {
116 case RESET_TOUCH_FOCUS: {
117 cancelAutoFocus();
118 mListener.startFaceDetection();
119 break;
120 }
121 }
122 }
123 }
124
125 public FocusOverlayManager(ComboPreferences preferences, String[] defaultFocusModes,
126 Parameters parameters, Listener listener,
127 boolean mirror, Looper looper) {
128 mHandler = new MainHandler(looper);
129 mMatrix = new Matrix();
130 mPreferences = preferences;
131 mDefaultFocusModes = defaultFocusModes;
132 setParameters(parameters);
133 mListener = listener;
134 setMirror(mirror);
135 }
136
137 public void setFocusRenderer(PieRenderer renderer) {
138 mPieRenderer = renderer;
139 mInitialized = (mMatrix != null);
140 }
141
142 public void setParameters(Parameters parameters) {
143 // parameters can only be null when onConfigurationChanged is called
144 // before camera is open. We will just return in this case, because
145 // parameters will be set again later with the right parameters after
146 // camera is open.
147 if (parameters == null) return;
148 mParameters = parameters;
149 mFocusAreaSupported = Util.isFocusAreaSupported(parameters);
150 mMeteringAreaSupported = Util.isMeteringAreaSupported(parameters);
151 mLockAeAwbNeeded = (Util.isAutoExposureLockSupported(mParameters) ||
152 Util.isAutoWhiteBalanceLockSupported(mParameters));
153 }
154
155 public void setPreviewSize(int previewWidth, int previewHeight) {
156 if (mPreviewWidth != previewWidth || mPreviewHeight != previewHeight) {
157 mPreviewWidth = previewWidth;
158 mPreviewHeight = previewHeight;
159 setMatrix();
160 }
161 }
162
163 public void setMirror(boolean mirror) {
164 mMirror = mirror;
165 setMatrix();
166 }
167
168 public void setDisplayOrientation(int displayOrientation) {
169 mDisplayOrientation = displayOrientation;
170 setMatrix();
171 }
172
173 public void setFaceView(FaceView faceView) {
174 mFaceView = faceView;
175 }
176
177 private void setMatrix() {
178 if (mPreviewWidth != 0 && mPreviewHeight != 0) {
179 Matrix matrix = new Matrix();
180 Util.prepareMatrix(matrix, mMirror, mDisplayOrientation,
181 mPreviewWidth, mPreviewHeight);
182 // In face detection, the matrix converts the driver coordinates to UI
183 // coordinates. In tap focus, the inverted matrix converts the UI
184 // coordinates to driver coordinates.
185 matrix.invert(mMatrix);
186 mInitialized = (mPieRenderer != null);
187 }
188 }
189
190 private void lockAeAwbIfNeeded() {
191 if (mLockAeAwbNeeded && !mAeAwbLock) {
192 mAeAwbLock = true;
193 mListener.setFocusParameters();
194 }
195 }
196
197 private void unlockAeAwbIfNeeded() {
198 if (mLockAeAwbNeeded && mAeAwbLock && (mState != STATE_FOCUSING_SNAP_ON_FINISH)) {
199 mAeAwbLock = false;
200 mListener.setFocusParameters();
201 }
202 }
203
204 public void onShutterDown() {
205 if (!mInitialized) return;
206
207 boolean autoFocusCalled = false;
208 if (needAutoFocusCall()) {
209 // Do not focus if touch focus has been triggered.
210 if (mState != STATE_SUCCESS && mState != STATE_FAIL) {
211 autoFocus();
212 autoFocusCalled = true;
213 }
214 }
215
216 if (!autoFocusCalled) lockAeAwbIfNeeded();
217 }
218
219 public void onShutterUp() {
220 if (!mInitialized) return;
221
222 if (needAutoFocusCall()) {
223 // User releases half-pressed focus key.
224 if (mState == STATE_FOCUSING || mState == STATE_SUCCESS
225 || mState == STATE_FAIL) {
226 cancelAutoFocus();
227 }
228 }
229
230 // Unlock AE and AWB after cancelAutoFocus. Camera API does not
231 // guarantee setParameters can be called during autofocus.
232 unlockAeAwbIfNeeded();
233 }
234
235 public void doSnap() {
236 if (!mInitialized) return;
237
238 // If the user has half-pressed the shutter and focus is completed, we
239 // can take the photo right away. If the focus mode is infinity, we can
240 // also take the photo.
241 if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) {
242 capture();
243 } else if (mState == STATE_FOCUSING) {
244 // Half pressing the shutter (i.e. the focus button event) will
245 // already have requested AF for us, so just request capture on
246 // focus here.
247 mState = STATE_FOCUSING_SNAP_ON_FINISH;
248 } else if (mState == STATE_IDLE) {
249 // We didn't do focus. This can happen if the user press focus key
250 // while the snapshot is still in progress. The user probably wants
251 // the next snapshot as soon as possible, so we just do a snapshot
252 // without focusing again.
253 capture();
254 }
255 }
256
257 public void onAutoFocus(boolean focused, boolean shutterButtonPressed) {
258 if (mState == STATE_FOCUSING_SNAP_ON_FINISH) {
259 // Take the picture no matter focus succeeds or fails. No need
260 // to play the AF sound if we're about to play the shutter
261 // sound.
262 if (focused) {
263 mState = STATE_SUCCESS;
264 } else {
265 mState = STATE_FAIL;
266 }
267 updateFocusUI();
268 capture();
269 } else if (mState == STATE_FOCUSING) {
270 // This happens when (1) user is half-pressing the focus key or
271 // (2) touch focus is triggered. Play the focus tone. Do not
272 // take the picture now.
273 if (focused) {
274 mState = STATE_SUCCESS;
275 } else {
276 mState = STATE_FAIL;
277 }
278 updateFocusUI();
279 // If this is triggered by touch focus, cancel focus after a
280 // while.
281 if (mFocusArea != null) {
282 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
283 }
284 if (shutterButtonPressed) {
285 // Lock AE & AWB so users can half-press shutter and recompose.
286 lockAeAwbIfNeeded();
287 }
288 } else if (mState == STATE_IDLE) {
289 // User has released the focus key before focus completes.
290 // Do nothing.
291 }
292 }
293
294 public void onAutoFocusMoving(boolean moving) {
295 if (!mInitialized) return;
296 // Ignore if the camera has detected some faces.
297 if (mFaceView != null && mFaceView.faceExists()) {
298 mPieRenderer.clear();
299 return;
300 }
301
302 // Ignore if we have requested autofocus. This method only handles
303 // continuous autofocus.
304 if (mState != STATE_IDLE) return;
305
Michael Kolbe3de7222013-02-18 15:16:44 -0800306 // animate on false->true trasition only b/8219520
307 if (moving && !mPreviousMoving) {
Michael Kolb8872c232013-01-29 10:33:22 -0800308 mPieRenderer.showStart();
Michael Kolbe3de7222013-02-18 15:16:44 -0800309 } else if (!moving) {
Michael Kolb8872c232013-01-29 10:33:22 -0800310 mPieRenderer.showSuccess(true);
311 }
Michael Kolbe3de7222013-02-18 15:16:44 -0800312 mPreviousMoving = moving;
Michael Kolb8872c232013-01-29 10:33:22 -0800313 }
314
315 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
316 private void initializeFocusAreas(int focusWidth, int focusHeight,
317 int x, int y, int previewWidth, int previewHeight) {
318 if (mFocusArea == null) {
319 mFocusArea = new ArrayList<Object>();
320 mFocusArea.add(new Area(new Rect(), 1));
321 }
322
323 // Convert the coordinates to driver format.
324 calculateTapArea(focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight,
325 ((Area) mFocusArea.get(0)).rect);
326 }
327
328 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
329 private void initializeMeteringAreas(int focusWidth, int focusHeight,
330 int x, int y, int previewWidth, int previewHeight) {
331 if (mMeteringArea == null) {
332 mMeteringArea = new ArrayList<Object>();
333 mMeteringArea.add(new Area(new Rect(), 1));
334 }
335
336 // Convert the coordinates to driver format.
337 // AE area is bigger because exposure is sensitive and
338 // easy to over- or underexposure if area is too small.
339 calculateTapArea(focusWidth, focusHeight, 1.5f, x, y, previewWidth, previewHeight,
340 ((Area) mMeteringArea.get(0)).rect);
341 }
342
343 public void onSingleTapUp(int x, int y) {
344 if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) return;
345
346 // Let users be able to cancel previous touch focus.
347 if ((mFocusArea != null) && (mState == STATE_FOCUSING ||
348 mState == STATE_SUCCESS || mState == STATE_FAIL)) {
349 cancelAutoFocus();
350 }
351 // Initialize variables.
352 int focusWidth = mPieRenderer.getSize();
353 int focusHeight = mPieRenderer.getSize();
354 if (focusWidth == 0 || mPieRenderer.getWidth() == 0
355 || mPieRenderer.getHeight() == 0) return;
356 int previewWidth = mPreviewWidth;
357 int previewHeight = mPreviewHeight;
358 // Initialize mFocusArea.
359 if (mFocusAreaSupported) {
360 initializeFocusAreas(
361 focusWidth, focusHeight, x, y, previewWidth, previewHeight);
362 }
363 // Initialize mMeteringArea.
364 if (mMeteringAreaSupported) {
365 initializeMeteringAreas(
366 focusWidth, focusHeight, x, y, previewWidth, previewHeight);
367 }
368
369 // Use margin to set the focus indicator to the touched area.
370 mPieRenderer.setFocus(x, y);
371
372 // Stop face detection because we want to specify focus and metering area.
373 mListener.stopFaceDetection();
374
375 // Set the focus area and metering area.
376 mListener.setFocusParameters();
377 if (mFocusAreaSupported) {
378 autoFocus();
379 } else { // Just show the indicator in all other cases.
380 updateFocusUI();
381 // Reset the metering area in 3 seconds.
382 mHandler.removeMessages(RESET_TOUCH_FOCUS);
383 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
384 }
385 }
386
387 public void onPreviewStarted() {
388 mState = STATE_IDLE;
389 }
390
391 public void onPreviewStopped() {
392 // If auto focus was in progress, it would have been stopped.
393 mState = STATE_IDLE;
394 resetTouchFocus();
395 updateFocusUI();
396 }
397
398 public void onCameraReleased() {
399 onPreviewStopped();
400 }
401
402 private void autoFocus() {
403 Log.v(TAG, "Start autofocus.");
404 mListener.autoFocus();
405 mState = STATE_FOCUSING;
406 // Pause the face view because the driver will keep sending face
407 // callbacks after the focus completes.
408 if (mFaceView != null) mFaceView.pause();
409 updateFocusUI();
410 mHandler.removeMessages(RESET_TOUCH_FOCUS);
411 }
412
413 private void cancelAutoFocus() {
414 Log.v(TAG, "Cancel autofocus.");
415
416 // Reset the tap area before calling mListener.cancelAutofocus.
417 // Otherwise, focus mode stays at auto and the tap area passed to the
418 // driver is not reset.
419 resetTouchFocus();
420 mListener.cancelAutoFocus();
421 if (mFaceView != null) mFaceView.resume();
422 mState = STATE_IDLE;
423 updateFocusUI();
424 mHandler.removeMessages(RESET_TOUCH_FOCUS);
425 }
426
427 private void capture() {
428 if (mListener.capture()) {
429 mState = STATE_IDLE;
430 mHandler.removeMessages(RESET_TOUCH_FOCUS);
431 }
432 }
433
434 public String getFocusMode() {
435 if (mOverrideFocusMode != null) return mOverrideFocusMode;
Michael Kolb4a40e122013-02-14 08:30:59 -0800436 if (mParameters == null) return Parameters.FOCUS_MODE_AUTO;
Michael Kolb8872c232013-01-29 10:33:22 -0800437 List<String> supportedFocusModes = mParameters.getSupportedFocusModes();
438
439 if (mFocusAreaSupported && mFocusArea != null) {
440 // Always use autofocus in tap-to-focus.
441 mFocusMode = Parameters.FOCUS_MODE_AUTO;
442 } else {
443 // The default is continuous autofocus.
444 mFocusMode = mPreferences.getString(
445 CameraSettings.KEY_FOCUS_MODE, null);
446
447 // Try to find a supported focus mode from the default list.
448 if (mFocusMode == null) {
449 for (int i = 0; i < mDefaultFocusModes.length; i++) {
450 String mode = mDefaultFocusModes[i];
451 if (Util.isSupported(mode, supportedFocusModes)) {
452 mFocusMode = mode;
453 break;
454 }
455 }
456 }
457 }
458 if (!Util.isSupported(mFocusMode, supportedFocusModes)) {
459 // For some reasons, the driver does not support the current
460 // focus mode. Fall back to auto.
461 if (Util.isSupported(Parameters.FOCUS_MODE_AUTO,
462 mParameters.getSupportedFocusModes())) {
463 mFocusMode = Parameters.FOCUS_MODE_AUTO;
464 } else {
465 mFocusMode = mParameters.getFocusMode();
466 }
467 }
468 return mFocusMode;
469 }
470
471 public List getFocusAreas() {
472 return mFocusArea;
473 }
474
475 public List getMeteringAreas() {
476 return mMeteringArea;
477 }
478
479 public void updateFocusUI() {
480 if (!mInitialized) return;
481 // Show only focus indicator or face indicator.
482 boolean faceExists = (mFaceView != null && mFaceView.faceExists());
483 FocusIndicator focusIndicator = (faceExists) ? mFaceView : mPieRenderer;
484
485 if (mState == STATE_IDLE) {
486 if (mFocusArea == null) {
487 focusIndicator.clear();
488 } else {
489 // Users touch on the preview and the indicator represents the
490 // metering area. Either focus area is not supported or
491 // autoFocus call is not required.
492 focusIndicator.showStart();
493 }
494 } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
495 focusIndicator.showStart();
496 } else {
497 if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) {
498 // TODO: check HAL behavior and decide if this can be removed.
499 focusIndicator.showSuccess(false);
500 } else if (mState == STATE_SUCCESS) {
501 focusIndicator.showSuccess(false);
502 } else if (mState == STATE_FAIL) {
503 focusIndicator.showFail(false);
504 }
505 }
506 }
507
508 public void resetTouchFocus() {
509 if (!mInitialized) return;
510
511 // Put focus indicator to the center. clear reset position
512 mPieRenderer.clear();
513
514 mFocusArea = null;
515 mMeteringArea = null;
516 }
517
518 private void calculateTapArea(int focusWidth, int focusHeight, float areaMultiple,
519 int x, int y, int previewWidth, int previewHeight, Rect rect) {
520 int areaWidth = (int) (focusWidth * areaMultiple);
521 int areaHeight = (int) (focusHeight * areaMultiple);
522 int left = Util.clamp(x - areaWidth / 2, 0, previewWidth - areaWidth);
523 int top = Util.clamp(y - areaHeight / 2, 0, previewHeight - areaHeight);
524
525 RectF rectF = new RectF(left, top, left + areaWidth, top + areaHeight);
526 mMatrix.mapRect(rectF);
527 Util.rectFToRect(rectF, rect);
528 }
529
530 /* package */ int getFocusState() {
531 return mState;
532 }
533
534 public boolean isFocusCompleted() {
535 return mState == STATE_SUCCESS || mState == STATE_FAIL;
536 }
537
538 public boolean isFocusingSnapOnFinish() {
539 return mState == STATE_FOCUSING_SNAP_ON_FINISH;
540 }
541
542 public void removeMessages() {
543 mHandler.removeMessages(RESET_TOUCH_FOCUS);
544 }
545
546 public void overrideFocusMode(String focusMode) {
547 mOverrideFocusMode = focusMode;
548 }
549
550 public void setAeAwbLock(boolean lock) {
551 mAeAwbLock = lock;
552 }
553
554 public boolean getAeAwbLock() {
555 return mAeAwbLock;
556 }
557
558 private boolean needAutoFocusCall() {
559 String focusMode = getFocusMode();
560 return !(focusMode.equals(Parameters.FOCUS_MODE_INFINITY)
561 || focusMode.equals(Parameters.FOCUS_MODE_FIXED)
562 || focusMode.equals(Parameters.FOCUS_MODE_EDOF));
563 }
564}