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.os.Build;
25import android.os.Handler;
26import android.os.Looper;
27import android.os.Message;
28
29import com.android.camera.app.AppController;
30import com.android.camera.app.MotionManager;
31import com.android.camera.debug.Log;
32import com.android.camera.one.Settings3A;
33import com.android.camera.settings.Keys;
34import com.android.camera.settings.SettingsManager;
35import com.android.camera.ui.PreviewStatusListener;
36import com.android.camera.ui.TouchCoordinate;
37import com.android.camera.util.CameraUtil;
38import com.android.camera.util.UsageStatistics;
39import com.android.ex.camera2.portability.CameraCapabilities;
40
41import java.lang.ref.WeakReference;
42import java.util.ArrayList;
43import java.util.List;
44
45/* A class that handles everything about focus in still picture mode.
46 * This also handles the metering area because it is the same as focus area.
47 *
48 * The test cases:
49 * (1) The camera has continuous autofocus. Move the camera. Take a picture when
50 *     CAF is not in progress.
51 * (2) The camera has continuous autofocus. Move the camera. Take a picture when
52 *     CAF is in progress.
53 * (3) The camera has face detection. Point the camera at some faces. Hold the
54 *     shutter. Release to take a picture.
55 * (4) The camera has face detection. Point the camera at some faces. Single tap
56 *     the shutter to take a picture.
57 * (5) The camera has autofocus. Single tap the shutter to take a picture.
58 * (6) The camera has autofocus. Hold the shutter. Release to take a picture.
59 * (7) The camera has no autofocus. Single tap the shutter and take a picture.
60 * (8) The camera has autofocus and supports focus area. Touch the screen to
61 *     trigger autofocus. Take a picture.
62 * (9) The camera has autofocus and supports focus area. Touch the screen to
63 *     trigger autofocus. Wait until it times out.
64 * (10) The camera has no autofocus and supports metering area. Touch the screen
65 *     to change metering area.
66 */
67public class FocusOverlayManager implements PreviewStatusListener.PreviewAreaChangedListener,
68        MotionManager.MotionListener {
69    private static final Log.Tag TAG = new Log.Tag("FocusOverlayMgr");
70
71    private static final int RESET_TOUCH_FOCUS = 0;
72
73    private static final int RESET_TOUCH_FOCUS_DELAY_MILLIS = Settings3A.getFocusHoldMillis();
74
75    public static final float AF_REGION_BOX = Settings3A.getAutoFocusRegionWidth();
76    public static final float AE_REGION_BOX = Settings3A.getMeteringRegionWidth();
77
78    private int mState = STATE_IDLE;
79    private static final int STATE_IDLE = 0; // Focus is not active.
80    private static final int STATE_FOCUSING = 1; // Focus is in progress.
81    // Focus is in progress and the camera should take a picture after focus finishes.
82    private static final int STATE_FOCUSING_SNAP_ON_FINISH = 2;
83    private static final int STATE_SUCCESS = 3; // Focus finishes and succeeds.
84    private static final int STATE_FAIL = 4; // Focus finishes and fails.
85
86    private boolean mInitialized;
87    private boolean mFocusAreaSupported;
88    private boolean mMeteringAreaSupported;
89    private boolean mLockAeAwbNeeded;
90    private boolean mAeAwbLock;
91    private final Matrix mMatrix;
92
93    private boolean mMirror; // true if the camera is front-facing.
94    private int mDisplayOrientation;
95    private List<Area> mFocusArea; // focus area in driver format
96    private List<Area> mMeteringArea; // metering area in driver format
97    private CameraCapabilities.FocusMode mFocusMode;
98    private final List<CameraCapabilities.FocusMode> mDefaultFocusModes;
99    private CameraCapabilities.FocusMode mOverrideFocusMode;
100    private CameraCapabilities mCapabilities;
101    private final AppController mAppController;
102    private final SettingsManager mSettingsManager;
103    private final Handler mHandler;
104    Listener mListener;
105    private boolean mPreviousMoving;
106    private final FocusUI mUI;
107    private final Rect mPreviewRect = new Rect(0, 0, 0, 0);
108    private boolean mFocusLocked;
109
110    /** Manual tap to focus parameters */
111    private TouchCoordinate mTouchCoordinate;
112    private long mTouchTime;
113
114    public  interface FocusUI {
115        public boolean hasFaces();
116        public void clearFocus();
117        public void setFocusPosition(int x, int y, boolean isPassiveScan, int aFsize, int aEsize);
118        public void setFocusPosition(int x, int y, boolean isPassiveScan);
119        public void onFocusStarted();
120        public void onFocusSucceeded();
121        public void onFocusFailed();
122        public void setPassiveFocusSuccess(boolean success);
123        public void showDebugMessage(String message);
124        public void pauseFaceDetection();
125        public void resumeFaceDetection();
126    }
127
128    public interface Listener {
129        public void autoFocus();
130        public void cancelAutoFocus();
131        public boolean capture();
132        public void startFaceDetection();
133        public void stopFaceDetection();
134        public void setFocusParameters();
135    }
136
137    /**
138     * TODO: Refactor this so that we either don't need a handler or make
139     * mListener not be the activity.
140     */
141    private static class MainHandler extends Handler {
142        /**
143         * The outer mListener at the moment is actually the CameraActivity,
144         * which we would leak if we didn't break the GC path here using a
145         * WeakReference.
146         */
147        final WeakReference<FocusOverlayManager> mManager;
148        public MainHandler(FocusOverlayManager manager, Looper looper) {
149            super(looper);
150            mManager = new WeakReference<FocusOverlayManager>(manager);
151        }
152
153        @Override
154        public void handleMessage(Message msg) {
155            FocusOverlayManager manager = mManager.get();
156            if (manager == null) {
157                return;
158            }
159
160            switch (msg.what) {
161                case RESET_TOUCH_FOCUS: {
162                    manager.cancelAutoFocus();
163                    manager.mListener.startFaceDetection();
164                    break;
165                }
166            }
167        }
168    }
169
170    public FocusOverlayManager(AppController appController,
171            List<CameraCapabilities.FocusMode> defaultFocusModes, CameraCapabilities capabilities,
172            Listener listener, boolean mirror, Looper looper, FocusUI ui) {
173        mAppController = appController;
174        mSettingsManager = appController.getSettingsManager();
175        mHandler = new MainHandler(this, looper);
176        mMatrix = new Matrix();
177        mDefaultFocusModes = new ArrayList<CameraCapabilities.FocusMode>(defaultFocusModes);
178        updateCapabilities(capabilities);
179        mListener = listener;
180        setMirror(mirror);
181        mUI = ui;
182        mFocusLocked = false;
183    }
184
185    public void updateCapabilities(CameraCapabilities capabilities) {
186        // capabilities can only be null when onConfigurationChanged is called
187        // before camera is open. We will just return in this case, because
188        // capabilities will be set again later with the right capabilities after
189        // camera is open.
190        if (capabilities == null) {
191            return;
192        }
193        mCapabilities = capabilities;
194        mFocusAreaSupported = mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
195        mMeteringAreaSupported = mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
196        mLockAeAwbNeeded = (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)
197                || mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK));
198    }
199
200    /** This setter should be the only way to mutate mPreviewRect. */
201    public void setPreviewRect(Rect previewRect) {
202        if (!mPreviewRect.equals(previewRect)) {
203            mPreviewRect.set(previewRect);
204            setMatrix();
205        }
206    }
207
208    @Override
209    public void onPreviewAreaChanged(RectF previewArea) {
210        setPreviewRect(CameraUtil.rectFToRect(previewArea));
211    }
212
213    /** Returns a copy of mPreviewRect so that outside class cannot modify preview
214     *  rect except deliberately doing so through the setter. */
215    public Rect getPreviewRect() {
216        return new Rect(mPreviewRect);
217    }
218
219    public void setMirror(boolean mirror) {
220        mMirror = mirror;
221        setMatrix();
222    }
223
224    public void setDisplayOrientation(int displayOrientation) {
225        mDisplayOrientation = displayOrientation;
226        setMatrix();
227    }
228
229    private void setMatrix() {
230        if (mPreviewRect.width() != 0 && mPreviewRect.height() != 0) {
231            Matrix matrix = new Matrix();
232            CameraUtil.prepareMatrix(matrix, mMirror, mDisplayOrientation, getPreviewRect());
233            // In face detection, the matrix converts the driver coordinates to UI
234            // coordinates. In tap focus, the inverted matrix converts the UI
235            // coordinates to driver coordinates.
236            matrix.invert(mMatrix);
237            mInitialized = true;
238        }
239    }
240
241    private void lockAeAwbIfNeeded() {
242        if (mLockAeAwbNeeded && !mAeAwbLock) {
243            mAeAwbLock = true;
244            mListener.setFocusParameters();
245        }
246    }
247
248    private void unlockAeAwbIfNeeded() {
249        if (mLockAeAwbNeeded && mAeAwbLock && (mState != STATE_FOCUSING_SNAP_ON_FINISH)) {
250            mAeAwbLock = false;
251            mListener.setFocusParameters();
252        }
253    }
254
255    public void onShutterUp(CameraCapabilities.FocusMode currentFocusMode) {
256        if (!mInitialized) {
257            return;
258        }
259
260        if (needAutoFocusCall(currentFocusMode)) {
261            // User releases half-pressed focus key.
262            if (mState == STATE_FOCUSING || mState == STATE_SUCCESS
263                    || mState == STATE_FAIL) {
264                cancelAutoFocus();
265            }
266        }
267
268        // Unlock AE and AWB after cancelAutoFocus. Camera API does not
269        // guarantee setParameters can be called during autofocus.
270        unlockAeAwbIfNeeded();
271    }
272
273    public void focusAndCapture(CameraCapabilities.FocusMode currentFocusMode) {
274        if (!mInitialized) {
275            return;
276        }
277
278        if (!needAutoFocusCall(currentFocusMode)) {
279            // Focus is not needed.
280            capture();
281        } else if (mState == STATE_SUCCESS || mState == STATE_FAIL) {
282            // Focus is done already.
283            capture();
284        } else if (mState == STATE_FOCUSING) {
285            // Still focusing and will not trigger snap upon finish.
286            mState = STATE_FOCUSING_SNAP_ON_FINISH;
287        } else if (mState == STATE_IDLE) {
288            autoFocusAndCapture();
289        }
290    }
291
292    public void onAutoFocus(boolean focused, boolean shutterButtonPressed) {
293        if (mState == STATE_FOCUSING_SNAP_ON_FINISH) {
294            // Take the picture no matter focus succeeds or fails. No need
295            // to play the AF sound if we're about to play the shutter
296            // sound.
297            if (focused) {
298                mState = STATE_SUCCESS;
299            } else {
300                mState = STATE_FAIL;
301            }
302            updateFocusUI();
303            capture();
304        } else if (mState == STATE_FOCUSING) {
305            // This happens when (1) user is half-pressing the focus key or
306            // (2) touch focus is triggered. Play the focus tone. Do not
307            // take the picture now.
308            if (focused) {
309                mState = STATE_SUCCESS;
310            } else {
311                mState = STATE_FAIL;
312            }
313            updateFocusUI();
314            // If this is triggered by touch focus, cancel focus after a
315            // while.
316            if (mFocusArea != null) {
317                mFocusLocked = true;
318                mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY_MILLIS);
319            }
320            if (shutterButtonPressed) {
321                // Lock AE & AWB so users can half-press shutter and recompose.
322                lockAeAwbIfNeeded();
323            }
324        } else if (mState == STATE_IDLE) {
325            // User has released the focus key before focus completes.
326            // Do nothing.
327        }
328    }
329
330    public void onAutoFocusMoving(boolean moving) {
331        if (!mInitialized) {
332            return;
333        }
334
335
336        // Ignore if the camera has detected some faces.
337        if (mUI.hasFaces()) {
338            mUI.clearFocus();
339            return;
340        }
341
342        // Ignore if we have requested autofocus. This method only handles
343        // continuous autofocus.
344        if (mState != STATE_IDLE) {
345            return;
346        }
347
348        // animate on false->true trasition only b/8219520
349        if (moving && !mPreviousMoving) {
350            // Auto focus at the center of the preview.
351            mUI.setFocusPosition(mPreviewRect.centerX(), mPreviewRect.centerY(), true,
352                    getAFRegionEdge(), getAERegionEdge());
353            mUI.onFocusStarted();
354        } else if (!moving) {
355            mUI.onFocusSucceeded();
356        }
357        mPreviousMoving = moving;
358    }
359
360    /** Returns width of auto focus region in pixels. */
361    private int getAFRegionEdge() {
362        return (int) (Math.min(mPreviewRect.width(), mPreviewRect.height()) * AF_REGION_BOX);
363    }
364
365    /** Returns width of metering region in pixels. */
366    private int getAERegionEdge() {
367        return (int) (Math.min(mPreviewRect.width(), mPreviewRect.height()) * AE_REGION_BOX);
368    }
369
370    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
371    private void initializeFocusAreas(int x, int y) {
372        if (mFocusArea == null) {
373            mFocusArea = new ArrayList<Area>();
374            mFocusArea.add(new Area(new Rect(), 1));
375        }
376
377        // Convert the coordinates to driver format.
378        calculateTapArea(x, y, getAFRegionEdge(), mFocusArea.get(0).rect);
379    }
380
381    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
382    private void initializeMeteringAreas(int x, int y) {
383        if (mMeteringArea == null) {
384            mMeteringArea = new ArrayList<Area>();
385            mMeteringArea.add(new Area(new Rect(), 1));
386        }
387
388        // Convert the coordinates to driver format.
389        calculateTapArea(x, y, getAERegionEdge(), mMeteringArea.get(0).rect);
390    }
391
392    public void onSingleTapUp(int x, int y) {
393        if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
394            return;
395        }
396
397        // Let users be able to cancel previous touch focus.
398        if ((mFocusArea != null) && (mState == STATE_FOCUSING ||
399                    mState == STATE_SUCCESS || mState == STATE_FAIL)) {
400            cancelAutoFocus();
401        }
402        if (mPreviewRect.width() == 0 || mPreviewRect.height() == 0) {
403            return;
404        }
405        // Initialize variables.
406        // Initialize mFocusArea.
407        if (mFocusAreaSupported) {
408            initializeFocusAreas(x, y);
409        }
410        // Initialize mMeteringArea.
411        if (mMeteringAreaSupported) {
412            initializeMeteringAreas(x, y);
413        }
414
415        // Use margin to set the focus indicator to the touched area.
416        mUI.setFocusPosition(x, y, false, getAFRegionEdge(), getAERegionEdge());
417        // Log manual tap to focus.
418        mTouchCoordinate = new TouchCoordinate(x, y, mPreviewRect.width(), mPreviewRect.height());
419        mTouchTime = System.currentTimeMillis();
420
421        // Stop face detection because we want to specify focus and metering area.
422        mListener.stopFaceDetection();
423
424        // Set the focus area and metering area.
425        mListener.setFocusParameters();
426        if (mFocusAreaSupported) {
427            autoFocus();
428        } else {  // Just show the indicator in all other cases.
429            updateFocusUI();
430            // Reset the metering area in 4 seconds.
431            mHandler.removeMessages(RESET_TOUCH_FOCUS);
432            mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY_MILLIS);
433        }
434    }
435
436    public void onPreviewStarted() {
437        mState = STATE_IDLE;
438        resetTouchFocus();
439    }
440
441    public void onPreviewStopped() {
442        // If auto focus was in progress, it would have been stopped.
443        mState = STATE_IDLE;
444        updateFocusUI();
445    }
446
447    public void onCameraReleased() {
448        onPreviewStopped();
449    }
450
451    @Override
452    public void onMoving() {
453        if (mFocusLocked) {
454            Log.d(TAG, "onMoving: Early focus unlock.");
455            cancelAutoFocus();
456        }
457    }
458
459    /**
460     * Triggers the autofocus and sets the specified state.
461     *
462     * @param focusingState The state to use when focus is in progress.
463     */
464    private void autoFocus(int focusingState) {
465        mListener.autoFocus();
466        mState = focusingState;
467        // Pause the face view because the driver will keep sending face
468        // callbacks after the focus completes.
469        mUI.pauseFaceDetection();
470        updateFocusUI();
471        mHandler.removeMessages(RESET_TOUCH_FOCUS);
472    }
473
474    /**
475     * Triggers the autofocus and set the state to indicate the focus is in
476     * progress.
477     */
478    private void autoFocus() {
479        autoFocus(STATE_FOCUSING);
480    }
481
482    /**
483     * Triggers the autofocus and set the state to which a capture will happen
484     * in the following autofocus callback.
485     */
486    private void autoFocusAndCapture() {
487        autoFocus(STATE_FOCUSING_SNAP_ON_FINISH);
488    }
489
490    private void cancelAutoFocus() {
491        Log.v(TAG, "Cancel autofocus.");
492        // Reset the tap area before calling mListener.cancelAutofocus.
493        // Otherwise, focus mode stays at auto and the tap area passed to the
494        // driver is not reset.
495        resetTouchFocus();
496        mListener.cancelAutoFocus();
497        mUI.resumeFaceDetection();
498        mState = STATE_IDLE;
499        mFocusLocked = false;
500        updateFocusUI();
501        mHandler.removeMessages(RESET_TOUCH_FOCUS);
502    }
503
504    private void capture() {
505        if (mListener.capture()) {
506            mState = STATE_IDLE;
507            mHandler.removeMessages(RESET_TOUCH_FOCUS);
508        }
509    }
510
511    public CameraCapabilities.FocusMode getFocusMode(
512            final CameraCapabilities.FocusMode currentFocusMode) {
513        if (mOverrideFocusMode != null) {
514            Log.v(TAG, "returning override focus: " + mOverrideFocusMode);
515            return mOverrideFocusMode;
516        }
517        if (mCapabilities == null) {
518            Log.v(TAG, "no capabilities, returning default AUTO focus mode");
519            return CameraCapabilities.FocusMode.AUTO;
520        }
521
522        if (mFocusAreaSupported && mFocusArea != null) {
523            Log.v(TAG, "in tap to focus, returning AUTO focus mode");
524            // Always use autofocus in tap-to-focus.
525            mFocusMode = CameraCapabilities.FocusMode.AUTO;
526        } else {
527            String focusSetting = mSettingsManager.getString(mAppController.getCameraScope(),
528                    Keys.KEY_FOCUS_MODE);
529            Log.v(TAG, "stored focus setting for camera: " + focusSetting);
530            // The default is continuous autofocus.
531            mFocusMode = mCapabilities.getStringifier().focusModeFromString(focusSetting);
532            Log.v(TAG, "focus mode resolved from setting: " + mFocusMode);
533            // Try to find a supported focus mode from the default list.
534            if (mFocusMode == null) {
535                for (CameraCapabilities.FocusMode mode : mDefaultFocusModes) {
536                    if (mCapabilities.supports(mode)) {
537                        mFocusMode = mode;
538                        Log.v(TAG, "selected supported focus mode from default list" + mode);
539                        break;
540                    }
541                }
542            }
543        }
544        if (!mCapabilities.supports(mFocusMode)) {
545            // For some reasons, the driver does not support the current
546            // focus mode. Fall back to auto.
547            if (mCapabilities.supports(CameraCapabilities.FocusMode.AUTO)) {
548                Log.v(TAG, "no supported focus mode, falling back to AUTO");
549                mFocusMode = CameraCapabilities.FocusMode.AUTO;
550            } else {
551                Log.v(TAG, "no supported focus mode, falling back to current: " + currentFocusMode);
552                mFocusMode = currentFocusMode;
553            }
554        }
555        return mFocusMode;
556    }
557
558    public List<Area> getFocusAreas() {
559        return mFocusArea;
560    }
561
562    public List<Area> getMeteringAreas() {
563        return mMeteringArea;
564    }
565
566    public void updateFocusUI() {
567        if (!mInitialized) {
568            // Show only focus indicator or face indicator.
569            return;
570        }
571        if (mState == STATE_IDLE) {
572            if (mFocusArea == null) {
573                mUI.clearFocus();
574            } else {
575                // Users touch on the preview and the indicator represents the
576                // metering area. Either focus area is not supported or
577                // autoFocus call is not required.
578                mUI.onFocusStarted();
579            }
580        } else if (mState == STATE_FOCUSING) {
581            mUI.onFocusStarted();
582        } else {
583            if (mFocusMode == CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
584                // TODO: check HAL behavior and decide if this can be removed.
585                mUI.onFocusSucceeded();
586            } else if (mState == STATE_SUCCESS) {
587                mUI.onFocusSucceeded();
588            } else if (mState == STATE_FAIL) {
589                mUI.onFocusFailed();
590            }
591        }
592    }
593
594    public void resetTouchFocus() {
595        if (!mInitialized) {
596            return;
597        }
598
599        // Put focus indicator to the center. clear reset position
600        mUI.clearFocus();
601        // Initialize mFocusArea.
602        mFocusArea = null;
603        mMeteringArea = null;
604        // This will cause current module to call getFocusAreas() and
605        // getMeteringAreas() and send updated regions to camera.
606        mListener.setFocusParameters();
607
608        if (mTouchCoordinate != null) {
609            UsageStatistics.instance().tapToFocus(mTouchCoordinate,
610                    0.001f * (System.currentTimeMillis() - mTouchTime));
611            mTouchCoordinate = null;
612        }
613    }
614
615    private void calculateTapArea(int x, int y, int size, Rect rect) {
616        int left = CameraUtil.clamp(x - size / 2, mPreviewRect.left,
617                mPreviewRect.right - size);
618        int top = CameraUtil.clamp(y - size / 2, mPreviewRect.top,
619                mPreviewRect.bottom - size);
620
621        RectF rectF = new RectF(left, top, left + size, top + size);
622        mMatrix.mapRect(rectF);
623        CameraUtil.rectFToRect(rectF, rect);
624    }
625
626    /* package */ int getFocusState() {
627        return mState;
628    }
629
630    public boolean isFocusCompleted() {
631        return mState == STATE_SUCCESS || mState == STATE_FAIL;
632    }
633
634    public boolean isFocusingSnapOnFinish() {
635        return mState == STATE_FOCUSING_SNAP_ON_FINISH;
636    }
637
638    public void removeMessages() {
639        mHandler.removeMessages(RESET_TOUCH_FOCUS);
640    }
641
642    public void overrideFocusMode(CameraCapabilities.FocusMode focusMode) {
643        mOverrideFocusMode = focusMode;
644    }
645
646    public void setAeAwbLock(boolean lock) {
647        mAeAwbLock = lock;
648    }
649
650    public boolean getAeAwbLock() {
651        return mAeAwbLock;
652    }
653
654    private boolean needAutoFocusCall(CameraCapabilities.FocusMode focusMode) {
655        return !(focusMode == CameraCapabilities.FocusMode.INFINITY
656                || focusMode == CameraCapabilities.FocusMode.FIXED
657                || focusMode == CameraCapabilities.FocusMode.EXTENDED_DOF);
658    }
659}
660