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