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