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.graphics.Rect;
20import android.graphics.RectF;
21import android.hardware.Camera.Area;
22import android.os.Handler;
23import android.os.Looper;
24import android.os.Message;
25
26import com.android.camera.app.AppController;
27import com.android.camera.app.MotionManager;
28import com.android.camera.debug.Log;
29import com.android.camera.one.Settings3A;
30import com.android.camera.settings.Keys;
31import com.android.camera.settings.SettingsManager;
32import com.android.camera.ui.PreviewStatusListener;
33import com.android.camera.ui.TouchCoordinate;
34import com.android.camera.util.ApiHelper;
35import com.android.camera.ui.focus.CameraCoordinateTransformer;
36import com.android.camera.ui.focus.FocusRing;
37import com.android.camera.util.CameraUtil;
38import com.android.camera.stats.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 CameraCoordinateTransformer mCoordinateTransformer;
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 FocusRing mFocusRing;
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 Listener {
115        public void autoFocus();
116        public void cancelAutoFocus();
117        public boolean capture();
118        public void startFaceDetection();
119        public void stopFaceDetection();
120        public void setFocusParameters();
121    }
122
123    /**
124     * TODO: Refactor this so that we either don't need a handler or make
125     * mListener not be the activity.
126     */
127    private static class MainHandler extends Handler {
128        /**
129         * The outer mListener at the moment is actually the CameraActivity,
130         * which we would leak if we didn't break the GC path here using a
131         * WeakReference.
132         */
133        final WeakReference<FocusOverlayManager> mManager;
134        public MainHandler(FocusOverlayManager manager, Looper looper) {
135            super(looper);
136            mManager = new WeakReference<FocusOverlayManager>(manager);
137        }
138
139        @Override
140        public void handleMessage(Message msg) {
141            FocusOverlayManager manager = mManager.get();
142            if (manager == null) {
143                return;
144            }
145
146            switch (msg.what) {
147                case RESET_TOUCH_FOCUS: {
148                    manager.cancelAutoFocus();
149                    manager.mListener.startFaceDetection();
150                    break;
151                }
152            }
153        }
154    }
155
156    public FocusOverlayManager(AppController appController,
157            List<CameraCapabilities.FocusMode> defaultFocusModes, CameraCapabilities capabilities,
158            Listener listener, boolean mirror, Looper looper, FocusRing focusRing) {
159        mAppController = appController;
160        mSettingsManager = appController.getSettingsManager();
161        mHandler = new MainHandler(this, looper);
162        mDefaultFocusModes = new ArrayList<CameraCapabilities.FocusMode>(defaultFocusModes);
163        updateCapabilities(capabilities);
164        mListener = listener;
165        setMirror(mirror);
166        mFocusRing = focusRing;
167        mFocusLocked = false;
168    }
169
170    public void updateCapabilities(CameraCapabilities capabilities) {
171        // capabilities can only be null when onConfigurationChanged is called
172        // before camera is open. We will just return in this case, because
173        // capabilities will be set again later with the right capabilities after
174        // camera is open.
175        if (capabilities == null) {
176            return;
177        }
178        mCapabilities = capabilities;
179        mFocusAreaSupported = mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
180        mMeteringAreaSupported = mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
181        mLockAeAwbNeeded = (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)
182                || mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK));
183    }
184
185    /** This setter should be the only way to mutate mPreviewRect. */
186    public void setPreviewRect(Rect previewRect) {
187        if (!mPreviewRect.equals(previewRect)) {
188            mPreviewRect.set(previewRect);
189            mFocusRing.configurePreviewDimensions(CameraUtil.rectToRectF(mPreviewRect));
190            resetCoordinateTransformer();
191            mInitialized = true;
192        }
193    }
194
195    @Override
196    public void onPreviewAreaChanged(RectF previewArea) {
197        setPreviewRect(CameraUtil.rectFToRect(previewArea));
198    }
199
200    public void setMirror(boolean mirror) {
201        mMirror = mirror;
202        resetCoordinateTransformer();
203    }
204
205    public void setDisplayOrientation(int displayOrientation) {
206        mDisplayOrientation = displayOrientation;
207        resetCoordinateTransformer();
208    }
209
210    private void resetCoordinateTransformer() {
211        if (mPreviewRect.width() > 0 && mPreviewRect.height() > 0) {
212            mCoordinateTransformer = new CameraCoordinateTransformer(mMirror, mDisplayOrientation,
213                  CameraUtil.rectToRectF(mPreviewRect));
214        } else {
215            Log.w(TAG, "The coordinate transformer could not be built because the preview rect"
216                  + "did not have a width and height");
217        }
218    }
219
220
221    private void lockAeAwbIfNeeded() {
222        if (mLockAeAwbNeeded && !mAeAwbLock) {
223            mAeAwbLock = true;
224            mListener.setFocusParameters();
225        }
226    }
227
228    private void unlockAeAwbIfNeeded() {
229        if (mLockAeAwbNeeded && mAeAwbLock && (mState != STATE_FOCUSING_SNAP_ON_FINISH)) {
230            mAeAwbLock = false;
231            mListener.setFocusParameters();
232        }
233    }
234
235    public void onShutterUp(CameraCapabilities.FocusMode currentFocusMode) {
236        if (!mInitialized) {
237            return;
238        }
239
240        if (needAutoFocusCall(currentFocusMode)) {
241            // User releases half-pressed focus key.
242            if (mState == STATE_FOCUSING || mState == STATE_SUCCESS
243                    || mState == STATE_FAIL) {
244                cancelAutoFocus();
245            }
246        }
247
248        // Unlock AE and AWB after cancelAutoFocus. Camera API does not
249        // guarantee setParameters can be called during autofocus.
250        unlockAeAwbIfNeeded();
251    }
252
253    public void focusAndCapture(CameraCapabilities.FocusMode currentFocusMode) {
254        if (!mInitialized) {
255            return;
256        }
257
258        if (!needAutoFocusCall(currentFocusMode)) {
259            // Focus is not needed.
260            capture();
261        } else if (mState == STATE_SUCCESS || mState == STATE_FAIL) {
262            // Focus is done already.
263            capture();
264        } else if (mState == STATE_FOCUSING) {
265            // Still focusing and will not trigger snap upon finish.
266            mState = STATE_FOCUSING_SNAP_ON_FINISH;
267        } else if (mState == STATE_IDLE) {
268            autoFocusAndCapture();
269        }
270    }
271
272    public void onAutoFocus(boolean focused, boolean shutterButtonPressed) {
273        if (mState == STATE_FOCUSING_SNAP_ON_FINISH) {
274            // Take the picture no matter focus succeeds or fails. No need
275            // to play the AF sound if we're about to play the shutter
276            // sound.
277            if (focused) {
278                mState = STATE_SUCCESS;
279            } else {
280                mState = STATE_FAIL;
281            }
282            capture();
283        } else if (mState == STATE_FOCUSING) {
284            // This happens when (1) user is half-pressing the focus key or
285            // (2) touch focus is triggered. Play the focus tone. Do not
286            // take the picture now.
287            if (focused) {
288                mState = STATE_SUCCESS;
289            } else {
290                mState = STATE_FAIL;
291            }
292            // If this is triggered by touch focus, cancel focus after a
293            // while.
294            if (mFocusArea != null) {
295                mFocusLocked = true;
296                mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY_MILLIS);
297            }
298            if (shutterButtonPressed) {
299                // Lock AE & AWB so users can half-press shutter and recompose.
300                lockAeAwbIfNeeded();
301            }
302        } else if (mState == STATE_IDLE) {
303            // User has released the focus key before focus completes.
304            // Do nothing.
305        }
306    }
307
308    public void onAutoFocusMoving(boolean moving) {
309        if (!mInitialized) {
310            return;
311        }
312
313        // Ignore if we have requested autofocus. This method only handles
314        // continuous autofocus.
315        if (mState != STATE_IDLE) {
316            return;
317        }
318
319        // animate on false->true trasition only b/8219520
320        if (moving && !mPreviousMoving) {
321            // Auto focus at the center of the preview.
322            mFocusRing.startPassiveFocus();
323            mFocusRing.centerFocusLocation();
324        } else if (!moving && mFocusRing.isPassiveFocusRunning()) {
325            mFocusRing.stopFocusAnimations();
326        }
327        mPreviousMoving = moving;
328    }
329
330    /** Returns width of auto focus region in pixels. */
331    private int getAFRegionSizePx() {
332        return (int) (Math.min(mPreviewRect.width(), mPreviewRect.height()) * AF_REGION_BOX);
333    }
334
335    /** Returns width of metering region in pixels. */
336    private int getAERegionSizePx() {
337        return (int) (Math.min(mPreviewRect.width(), mPreviewRect.height()) * AE_REGION_BOX);
338    }
339
340    private void initializeFocusAreas(int x, int y) {
341        if (mFocusArea == null) {
342            mFocusArea = new ArrayList<Area>();
343            mFocusArea.add(new Area(new Rect(), 1));
344        }
345
346        // Convert the coordinates to driver format.
347        mFocusArea.get(0).rect = computeCameraRectFromPreviewCoordinates(x, y, getAFRegionSizePx());
348    }
349
350    private void initializeMeteringAreas(int x, int y) {
351        if (mMeteringArea == null) {
352            mMeteringArea = new ArrayList<Area>();
353            mMeteringArea.add(new Area(new Rect(), 1));
354        }
355
356        // Convert the coordinates to driver format.
357        mMeteringArea.get(0).rect = computeCameraRectFromPreviewCoordinates(x, y, getAERegionSizePx());
358    }
359
360    public void onSingleTapUp(int x, int y) {
361        if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
362            return;
363        }
364
365        // Let users be able to cancel previous touch focus.
366        if ((mFocusArea != null) && (mState == STATE_FOCUSING ||
367                    mState == STATE_SUCCESS || mState == STATE_FAIL)) {
368            cancelAutoFocus();
369        }
370        if (mPreviewRect.width() == 0 || mPreviewRect.height() == 0) {
371            return;
372        }
373        // Initialize variables.
374        // Initialize mFocusArea.
375        if (mFocusAreaSupported) {
376            initializeFocusAreas(x, y);
377        }
378        // Initialize mMeteringArea.
379        if (mMeteringAreaSupported) {
380            initializeMeteringAreas(x, y);
381        }
382
383        mFocusRing.startActiveFocus();
384        mFocusRing.setFocusLocation(x, y);
385
386        // Log manual tap to focus.
387        mTouchCoordinate = new TouchCoordinate(x, y, mPreviewRect.width(), mPreviewRect.height());
388        mTouchTime = System.currentTimeMillis();
389
390        // Stop face detection because we want to specify focus and metering area.
391        mListener.stopFaceDetection();
392
393        // Set the focus area and metering area.
394        mListener.setFocusParameters();
395        if (mFocusAreaSupported) {
396            autoFocus();
397        } else {  // Just show the indicator in all other cases.
398            // Reset the metering area in 4 seconds.
399            mHandler.removeMessages(RESET_TOUCH_FOCUS);
400            mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY_MILLIS);
401        }
402    }
403
404    public void onPreviewStarted() {
405        mState = STATE_IDLE;
406        // Avoid resetting touch focus if N4, b/18681082.
407        if (!ApiHelper.IS_NEXUS_4) {
408            resetTouchFocus();
409        }
410    }
411
412    public void onPreviewStopped() {
413        // If auto focus was in progress, it would have been stopped.
414        mState = STATE_IDLE;
415    }
416
417    public void onCameraReleased() {
418        onPreviewStopped();
419    }
420
421    @Override
422    public void onMoving() {
423        if (mFocusLocked) {
424            Log.d(TAG, "onMoving: Early focus unlock.");
425            cancelAutoFocus();
426        }
427    }
428
429    /**
430     * Triggers the autofocus and sets the specified state.
431     *
432     * @param focusingState The state to use when focus is in progress.
433     */
434    private void autoFocus(int focusingState) {
435        mListener.autoFocus();
436        mState = focusingState;
437        mHandler.removeMessages(RESET_TOUCH_FOCUS);
438    }
439
440    /**
441     * Triggers the autofocus and set the state to indicate the focus is in
442     * progress.
443     */
444    private void autoFocus() {
445        autoFocus(STATE_FOCUSING);
446    }
447
448    /**
449     * Triggers the autofocus and set the state to which a capture will happen
450     * in the following autofocus callback.
451     */
452    private void autoFocusAndCapture() {
453        autoFocus(STATE_FOCUSING_SNAP_ON_FINISH);
454    }
455
456    private void cancelAutoFocus() {
457        Log.v(TAG, "Cancel autofocus.");
458        // Reset the tap area before calling mListener.cancelAutofocus.
459        // Otherwise, focus mode stays at auto and the tap area passed to the
460        // driver is not reset.
461        resetTouchFocus();
462        mListener.cancelAutoFocus();
463        mState = STATE_IDLE;
464        mFocusLocked = false;
465        mHandler.removeMessages(RESET_TOUCH_FOCUS);
466    }
467
468    private void capture() {
469        if (mListener.capture()) {
470            mState = STATE_IDLE;
471            mHandler.removeMessages(RESET_TOUCH_FOCUS);
472        }
473    }
474
475    public CameraCapabilities.FocusMode getFocusMode(
476            final CameraCapabilities.FocusMode currentFocusMode) {
477        if (mOverrideFocusMode != null) {
478            Log.v(TAG, "returning override focus: " + mOverrideFocusMode);
479            return mOverrideFocusMode;
480        }
481        if (mCapabilities == null) {
482            Log.v(TAG, "no capabilities, returning default AUTO focus mode");
483            return CameraCapabilities.FocusMode.AUTO;
484        }
485
486        if (mFocusAreaSupported && mFocusArea != null) {
487            Log.v(TAG, "in tap to focus, returning AUTO focus mode");
488            // Always use autofocus in tap-to-focus.
489            mFocusMode = CameraCapabilities.FocusMode.AUTO;
490        } else {
491            String focusSetting = mSettingsManager.getString(mAppController.getCameraScope(),
492                    Keys.KEY_FOCUS_MODE);
493            Log.v(TAG, "stored focus setting for camera: " + focusSetting);
494            // The default is continuous autofocus.
495            mFocusMode = mCapabilities.getStringifier().focusModeFromString(focusSetting);
496            Log.v(TAG, "focus mode resolved from setting: " + mFocusMode);
497            // Try to find a supported focus mode from the default list.
498            if (mFocusMode == null) {
499                for (CameraCapabilities.FocusMode mode : mDefaultFocusModes) {
500                    if (mCapabilities.supports(mode)) {
501                        mFocusMode = mode;
502                        Log.v(TAG, "selected supported focus mode from default list" + mode);
503                        break;
504                    }
505                }
506            }
507        }
508        if (!mCapabilities.supports(mFocusMode)) {
509            // For some reasons, the driver does not support the current
510            // focus mode. Fall back to auto.
511            if (mCapabilities.supports(CameraCapabilities.FocusMode.AUTO)) {
512                Log.v(TAG, "no supported focus mode, falling back to AUTO");
513                mFocusMode = CameraCapabilities.FocusMode.AUTO;
514            } else {
515                Log.v(TAG, "no supported focus mode, falling back to current: " + currentFocusMode);
516                mFocusMode = currentFocusMode;
517            }
518        }
519        return mFocusMode;
520    }
521
522    public List<Area> getFocusAreas() {
523        return mFocusArea;
524    }
525
526    public List<Area> getMeteringAreas() {
527        return mMeteringArea;
528    }
529
530    public void resetTouchFocus() {
531        if (!mInitialized) {
532            return;
533        }
534
535        mFocusArea = null;
536        mMeteringArea = null;
537        // This will cause current module to call getFocusAreas() and
538        // getMeteringAreas() and send updated regions to camera.
539        mListener.setFocusParameters();
540
541        if (mTouchCoordinate != null) {
542            UsageStatistics.instance().tapToFocus(mTouchCoordinate,
543                    0.001f * (System.currentTimeMillis() - mTouchTime));
544            mTouchCoordinate = null;
545        }
546    }
547
548    private Rect computeCameraRectFromPreviewCoordinates(int x, int y, int size) {
549        int left = CameraUtil.clamp(x - size / 2, mPreviewRect.left,
550                mPreviewRect.right - size);
551        int top = CameraUtil.clamp(y - size / 2, mPreviewRect.top,
552                mPreviewRect.bottom - size);
553
554        RectF rectF = new RectF(left, top, left + size, top + size);
555        return CameraUtil.rectFToRect(mCoordinateTransformer.toCameraSpace(rectF));
556    }
557
558    /* package */ int getFocusState() {
559        return mState;
560    }
561
562    public boolean isFocusCompleted() {
563        return mState == STATE_SUCCESS || mState == STATE_FAIL;
564    }
565
566    public boolean isFocusingSnapOnFinish() {
567        return mState == STATE_FOCUSING_SNAP_ON_FINISH;
568    }
569
570    public void removeMessages() {
571        mHandler.removeMessages(RESET_TOUCH_FOCUS);
572    }
573
574    public void overrideFocusMode(CameraCapabilities.FocusMode focusMode) {
575        mOverrideFocusMode = focusMode;
576    }
577
578    public void setAeAwbLock(boolean lock) {
579        mAeAwbLock = lock;
580    }
581
582    public boolean getAeAwbLock() {
583        return mAeAwbLock;
584    }
585
586    private boolean needAutoFocusCall(CameraCapabilities.FocusMode focusMode) {
587        return !(focusMode == CameraCapabilities.FocusMode.INFINITY
588                || focusMode == CameraCapabilities.FocusMode.FIXED
589                || focusMode == CameraCapabilities.FocusMode.EXTENDED_DOF);
590    }
591}
592