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