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     private boolean mFocusDefault;
     93 
     94     private FocusUI mUI;
     95     private final Rect mPreviewRect = new Rect(0, 0, 0, 0);
     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 (mPreviewRect.width() != previewWidth || mPreviewRect.height() != previewHeight) {
    163             setPreviewRect(new Rect(0, 0, previewWidth, previewHeight));
    164         }
    165     }
    166 
    167     /** This setter should be the only way to mutate mPreviewRect. */
    168     public void setPreviewRect(Rect previewRect) {
    169         if (!mPreviewRect.equals(previewRect)) {
    170             mPreviewRect.set(previewRect);
    171             setMatrix();
    172         }
    173     }
    174 
    175     /** Returns a copy of mPreviewRect so that outside class cannot modify preview
    176      *  rect except deliberately doing so through the setter. */
    177     public Rect getPreviewRect() {
    178         return new Rect(mPreviewRect);
    179     }
    180 
    181     public void setMirror(boolean mirror) {
    182         mMirror = mirror;
    183         setMatrix();
    184     }
    185 
    186     public void setDisplayOrientation(int displayOrientation) {
    187         mDisplayOrientation = displayOrientation;
    188         setMatrix();
    189     }
    190 
    191     private void setMatrix() {
    192         if (mPreviewRect.width() != 0 && mPreviewRect.height() != 0) {
    193             Matrix matrix = new Matrix();
    194             CameraUtil.prepareMatrix(matrix, mMirror, mDisplayOrientation, getPreviewRect());
    195             // In face detection, the matrix converts the driver coordinates to UI
    196             // coordinates. In tap focus, the inverted matrix converts the UI
    197             // coordinates to driver coordinates.
    198             matrix.invert(mMatrix);
    199             mInitialized = true;
    200         }
    201     }
    202 
    203     private void lockAeAwbIfNeeded() {
    204         if (mLockAeAwbNeeded && !mAeAwbLock) {
    205             mAeAwbLock = true;
    206             mListener.setFocusParameters();
    207         }
    208     }
    209 
    210     private void unlockAeAwbIfNeeded() {
    211         if (mLockAeAwbNeeded && mAeAwbLock && (mState != STATE_FOCUSING_SNAP_ON_FINISH)) {
    212             mAeAwbLock = false;
    213             mListener.setFocusParameters();
    214         }
    215     }
    216 
    217     public void onShutterDown() {
    218         if (!mInitialized) return;
    219 
    220         boolean autoFocusCalled = false;
    221         if (needAutoFocusCall()) {
    222             // Do not focus if touch focus has been triggered.
    223             if (mState != STATE_SUCCESS && mState != STATE_FAIL) {
    224                 autoFocus();
    225                 autoFocusCalled = true;
    226             }
    227         }
    228 
    229         if (!autoFocusCalled) lockAeAwbIfNeeded();
    230     }
    231 
    232     public void onShutterUp() {
    233         if (!mInitialized) return;
    234 
    235         if (needAutoFocusCall()) {
    236             // User releases half-pressed focus key.
    237             if (mState == STATE_FOCUSING || mState == STATE_SUCCESS
    238                     || mState == STATE_FAIL) {
    239                 cancelAutoFocus();
    240             }
    241         }
    242 
    243         // Unlock AE and AWB after cancelAutoFocus. Camera API does not
    244         // guarantee setParameters can be called during autofocus.
    245         unlockAeAwbIfNeeded();
    246     }
    247 
    248     public void doSnap() {
    249         if (!mInitialized) return;
    250 
    251         // If the user has half-pressed the shutter and focus is completed, we
    252         // can take the photo right away. If the focus mode is infinity, we can
    253         // also take the photo.
    254         if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) {
    255             capture();
    256         } else if (mState == STATE_FOCUSING) {
    257             // Half pressing the shutter (i.e. the focus button event) will
    258             // already have requested AF for us, so just request capture on
    259             // focus here.
    260             mState = STATE_FOCUSING_SNAP_ON_FINISH;
    261         } else if (mState == STATE_IDLE) {
    262             // We didn't do focus. This can happen if the user press focus key
    263             // while the snapshot is still in progress. The user probably wants
    264             // the next snapshot as soon as possible, so we just do a snapshot
    265             // without focusing again.
    266             capture();
    267         }
    268     }
    269 
    270     public void onAutoFocus(boolean focused, boolean shutterButtonPressed) {
    271         if (mState == STATE_FOCUSING_SNAP_ON_FINISH) {
    272             // Take the picture no matter focus succeeds or fails. No need
    273             // to play the AF sound if we're about to play the shutter
    274             // sound.
    275             if (focused) {
    276                 mState = STATE_SUCCESS;
    277             } else {
    278                 mState = STATE_FAIL;
    279             }
    280             updateFocusUI();
    281             capture();
    282         } else if (mState == STATE_FOCUSING) {
    283             // This happens when (1) user is half-pressing the focus key or
    284             // (2) touch focus is triggered. Play the focus tone. Do not
    285             // take the picture now.
    286             if (focused) {
    287                 mState = STATE_SUCCESS;
    288             } else {
    289                 mState = STATE_FAIL;
    290             }
    291             updateFocusUI();
    292             // If this is triggered by touch focus, cancel focus after a
    293             // while.
    294             if (!mFocusDefault) {
    295                 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
    296             }
    297             if (shutterButtonPressed) {
    298                 // Lock AE & AWB so users can half-press shutter and recompose.
    299                 lockAeAwbIfNeeded();
    300             }
    301         } else if (mState == STATE_IDLE) {
    302             // User has released the focus key before focus completes.
    303             // Do nothing.
    304         }
    305     }
    306 
    307     public void onAutoFocusMoving(boolean moving) {
    308         if (!mInitialized) return;
    309 
    310 
    311         // Ignore if the camera has detected some faces.
    312         if (mUI.hasFaces()) {
    313             mUI.clearFocus();
    314             return;
    315         }
    316 
    317         // Ignore if we have requested autofocus. This method only handles
    318         // continuous autofocus.
    319         if (mState != STATE_IDLE) return;
    320 
    321         // animate on false->true trasition only b/8219520
    322         if (moving && !mPreviousMoving) {
    323             mUI.onFocusStarted();
    324         } else if (!moving) {
    325             mUI.onFocusSucceeded(true);
    326         }
    327         mPreviousMoving = moving;
    328     }
    329 
    330     @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    331     private void initializeFocusAreas(int x, int y) {
    332         if (mFocusArea == null) {
    333             mFocusArea = new ArrayList<Object>();
    334             mFocusArea.add(new Area(new Rect(), 1));
    335         }
    336 
    337         // Convert the coordinates to driver format.
    338         calculateTapArea(x, y, 1f, ((Area) mFocusArea.get(0)).rect);
    339     }
    340 
    341     @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    342     private void initializeMeteringAreas(int x, int y) {
    343         if (mMeteringArea == null) {
    344             mMeteringArea = new ArrayList<Object>();
    345             mMeteringArea.add(new Area(new Rect(), 1));
    346         }
    347 
    348         // Convert the coordinates to driver format.
    349         // AE area is bigger because exposure is sensitive and
    350         // easy to over- or underexposure if area is too small.
    351         calculateTapArea(x, y, 1.5f, ((Area) mMeteringArea.get(0)).rect);
    352     }
    353 
    354     private void resetMeteringAreas() {
    355         mMeteringArea = null;
    356     }
    357 
    358     public void onSingleTapUp(int x, int y) {
    359         if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) return;
    360 
    361         UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
    362                 UsageStatistics.ACTION_TOUCH_FOCUS, x + "," + y);
    363 
    364         // Let users be able to cancel previous touch focus.
    365         if ((!mFocusDefault) && (mState == STATE_FOCUSING ||
    366                     mState == STATE_SUCCESS || mState == STATE_FAIL)) {
    367             cancelAutoFocus();
    368         }
    369         if (mPreviewRect.width() == 0 || mPreviewRect.height() == 0) return;
    370         mFocusDefault = false;
    371         // Initialize mFocusArea.
    372         if (mFocusAreaSupported) {
    373             initializeFocusAreas(x, y);
    374         }
    375         // Initialize mMeteringArea.
    376         if (mMeteringAreaSupported) {
    377             initializeMeteringAreas(x, y);
    378         }
    379 
    380         // Use margin to set the focus indicator to the touched area.
    381         mUI.setFocusPosition(x, y);
    382 
    383         // Stop face detection because we want to specify focus and metering area.
    384         mListener.stopFaceDetection();
    385 
    386         // Set the focus area and metering area.
    387         mListener.setFocusParameters();
    388         if (mFocusAreaSupported) {
    389             autoFocus();
    390         } else {  // Just show the indicator in all other cases.
    391             updateFocusUI();
    392             // Reset the metering area in 3 seconds.
    393             mHandler.removeMessages(RESET_TOUCH_FOCUS);
    394             mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
    395         }
    396     }
    397 
    398     public void onPreviewStarted() {
    399         mState = STATE_IDLE;
    400     }
    401 
    402     public void onPreviewStopped() {
    403         // If auto focus was in progress, it would have been stopped.
    404         mState = STATE_IDLE;
    405         resetTouchFocus();
    406         updateFocusUI();
    407     }
    408 
    409     public void onCameraReleased() {
    410         onPreviewStopped();
    411     }
    412 
    413     private void autoFocus() {
    414         Log.v(TAG, "Start autofocus.");
    415         mListener.autoFocus();
    416         mState = STATE_FOCUSING;
    417         // Pause the face view because the driver will keep sending face
    418         // callbacks after the focus completes.
    419         mUI.pauseFaceDetection();
    420         updateFocusUI();
    421         mHandler.removeMessages(RESET_TOUCH_FOCUS);
    422     }
    423 
    424     private void cancelAutoFocus() {
    425         Log.v(TAG, "Cancel autofocus.");
    426 
    427         // Reset the tap area before calling mListener.cancelAutofocus.
    428         // Otherwise, focus mode stays at auto and the tap area passed to the
    429         // driver is not reset.
    430         resetTouchFocus();
    431         mListener.cancelAutoFocus();
    432         mUI.resumeFaceDetection();
    433         mState = STATE_IDLE;
    434         updateFocusUI();
    435         mHandler.removeMessages(RESET_TOUCH_FOCUS);
    436     }
    437 
    438     private void capture() {
    439         if (mListener.capture()) {
    440             mState = STATE_IDLE;
    441             mHandler.removeMessages(RESET_TOUCH_FOCUS);
    442         }
    443     }
    444 
    445     public String getFocusMode() {
    446         if (mOverrideFocusMode != null) return mOverrideFocusMode;
    447         if (mParameters == null) return Parameters.FOCUS_MODE_AUTO;
    448         List<String> supportedFocusModes = mParameters.getSupportedFocusModes();
    449 
    450         if (mFocusAreaSupported && !mFocusDefault) {
    451             // Always use autofocus in tap-to-focus.
    452             mFocusMode = Parameters.FOCUS_MODE_AUTO;
    453         } else {
    454             // The default is continuous autofocus.
    455             mFocusMode = mPreferences.getString(
    456                     CameraSettings.KEY_FOCUS_MODE, null);
    457 
    458             // Try to find a supported focus mode from the default list.
    459             if (mFocusMode == null) {
    460                 for (int i = 0; i < mDefaultFocusModes.length; i++) {
    461                     String mode = mDefaultFocusModes[i];
    462                     if (CameraUtil.isSupported(mode, supportedFocusModes)) {
    463                         mFocusMode = mode;
    464                         break;
    465                     }
    466                 }
    467             }
    468         }
    469         if (!CameraUtil.isSupported(mFocusMode, supportedFocusModes)) {
    470             // For some reasons, the driver does not support the current
    471             // focus mode. Fall back to auto.
    472             if (CameraUtil.isSupported(Parameters.FOCUS_MODE_AUTO,
    473                     mParameters.getSupportedFocusModes())) {
    474                 mFocusMode = Parameters.FOCUS_MODE_AUTO;
    475             } else {
    476                 mFocusMode = mParameters.getFocusMode();
    477             }
    478         }
    479         return mFocusMode;
    480     }
    481 
    482     public List getFocusAreas() {
    483         return mFocusArea;
    484     }
    485 
    486     public List getMeteringAreas() {
    487         return mMeteringArea;
    488     }
    489 
    490     public void updateFocusUI() {
    491         if (!mInitialized) return;
    492         // Show only focus indicator or face indicator.
    493 
    494         if (mState == STATE_IDLE) {
    495             if (mFocusDefault) {
    496                 mUI.clearFocus();
    497             } else {
    498                 // Users touch on the preview and the indicator represents the
    499                 // metering area. Either focus area is not supported or
    500                 // autoFocus call is not required.
    501                 mUI.onFocusStarted();
    502             }
    503         } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
    504             mUI.onFocusStarted();
    505         } else {
    506             if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) {
    507                 // TODO: check HAL behavior and decide if this can be removed.
    508                 mUI.onFocusSucceeded(false);
    509             } else if (mState == STATE_SUCCESS) {
    510                 mUI.onFocusSucceeded(false);
    511             } else if (mState == STATE_FAIL) {
    512                 mUI.onFocusFailed(false);
    513             }
    514         }
    515     }
    516 
    517     public void resetTouchFocus() {
    518         if (!mInitialized) return;
    519 
    520         // Put focus indicator to the center. clear reset position
    521         mUI.clearFocus();
    522         // Initialize mFocusArea.
    523         if (mFocusAreaSupported) {
    524             initializeFocusAreas(mPreviewRect.centerX(), mPreviewRect.centerY());
    525         }
    526         // Reset metering area when no specific region is selected.
    527         if (mMeteringAreaSupported) {
    528             resetMeteringAreas();
    529         }
    530         mFocusDefault = true;
    531     }
    532 
    533     private void calculateTapArea(int x, int y, float areaMultiple, Rect rect) {
    534         int areaSize = (int) (getAreaSize() * areaMultiple);
    535         int left = CameraUtil.clamp(x - areaSize / 2, mPreviewRect.left,
    536                 mPreviewRect.right - areaSize);
    537         int top = CameraUtil.clamp(y - areaSize / 2, mPreviewRect.top,
    538                 mPreviewRect.bottom - areaSize);
    539 
    540         RectF rectF = new RectF(left, top, left + areaSize, top + areaSize);
    541         mMatrix.mapRect(rectF);
    542         CameraUtil.rectFToRect(rectF, rect);
    543     }
    544 
    545     private int getAreaSize() {
    546         // Recommended focus area size from the manufacture is 1/8 of the image
    547         // width (i.e. longer edge of the image)
    548         return Math.max(mPreviewRect.width(), mPreviewRect.height()) / 8;
    549     }
    550 
    551     /* package */ int getFocusState() {
    552         return mState;
    553     }
    554 
    555     public boolean isFocusCompleted() {
    556         return mState == STATE_SUCCESS || mState == STATE_FAIL;
    557     }
    558 
    559     public boolean isFocusingSnapOnFinish() {
    560         return mState == STATE_FOCUSING_SNAP_ON_FINISH;
    561     }
    562 
    563     public void removeMessages() {
    564         mHandler.removeMessages(RESET_TOUCH_FOCUS);
    565     }
    566 
    567     public void overrideFocusMode(String focusMode) {
    568         mOverrideFocusMode = focusMode;
    569     }
    570 
    571     public void setAeAwbLock(boolean lock) {
    572         mAeAwbLock = lock;
    573     }
    574 
    575     public boolean getAeAwbLock() {
    576         return mAeAwbLock;
    577     }
    578 
    579     private boolean needAutoFocusCall() {
    580         String focusMode = getFocusMode();
    581         return !(focusMode.equals(Parameters.FOCUS_MODE_INFINITY)
    582                 || focusMode.equals(Parameters.FOCUS_MODE_FIXED)
    583                 || focusMode.equals(Parameters.FOCUS_MODE_EDOF));
    584     }
    585 }
    586