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