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