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