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