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