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