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