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.os.Handler;
     30 import android.os.Message;
     31 import android.util.Log;
     32 import android.view.MotionEvent;
     33 import android.view.View;
     34 import android.view.ViewGroup;
     35 import android.widget.RelativeLayout;
     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 public class FocusManager {
     43     private static final String TAG = "FocusManager";
     44 
     45     private static final int RESET_TOUCH_FOCUS = 0;
     46     private static final int FOCUS_BEEP_VOLUME = 100;
     47     private static final int RESET_TOUCH_FOCUS_DELAY = 3000;
     48 
     49     private int mState = STATE_IDLE;
     50     private static final int STATE_IDLE = 0; // Focus is not active.
     51     private static final int STATE_FOCUSING = 1; // Focus is in progress.
     52     // Focus is in progress and the camera should take a picture after focus finishes.
     53     private static final int STATE_FOCUSING_SNAP_ON_FINISH = 2;
     54     private static final int STATE_SUCCESS = 3; // Focus finishes and succeeds.
     55     private static final int STATE_FAIL = 4; // Focus finishes and fails.
     56 
     57     private boolean mInitialized;
     58     private boolean mFocusAreaSupported;
     59     private boolean mInLongPress;
     60     private boolean mLockAeAwbNeeded;
     61     private boolean mAeAwbLock;
     62     private Matrix mMatrix;
     63     private SoundPlayer mSoundPlayer;
     64     private View mFocusIndicatorRotateLayout;
     65     private FocusIndicatorView mFocusIndicator;
     66     private View mPreviewFrame;
     67     private FaceView mFaceView;
     68     private List<Area> mFocusArea; // focus area in driver format
     69     private List<Area> mMeteringArea; // metering area in driver format
     70     private String mFocusMode;
     71     private String mDefaultFocusMode;
     72     private String mOverrideFocusMode;
     73     private Parameters mParameters;
     74     private ComboPreferences mPreferences;
     75     private Handler mHandler;
     76     Listener mListener;
     77 
     78     public interface Listener {
     79         public void autoFocus();
     80         public void cancelAutoFocus();
     81         public boolean capture();
     82         public void startFaceDetection();
     83         public void stopFaceDetection();
     84         public void setFocusParameters();
     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 defaultFocusMode) {
    101         mPreferences = preferences;
    102         mDefaultFocusMode = defaultFocusMode;
    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 shutterLongPressed() {
    178         if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)
    179                 && isSupported(Parameters.FOCUS_MODE_AUTO, mParameters.getSupportedFocusModes())) {
    180             if (mState == STATE_IDLE || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
    181                 Log.e(TAG, "Invalid focus state=" + mState);
    182             }
    183             mInLongPress = true;
    184             // Cancel any outstanding Auto focus requests. The auto focus mode
    185             // will be changed from CAF to auto in cancelAutoFocus.
    186             onShutterUp();
    187             // Call Autofocus
    188             onShutterDown();
    189             mInLongPress = false;
    190         }
    191     }
    192 
    193     public void doSnap() {
    194         if (!mInitialized) return;
    195 
    196         // If the user has half-pressed the shutter and focus is completed, we
    197         // can take the photo right away. If the focus mode is infinity, we can
    198         // also take the photo.
    199         if (!needAutoFocusCall() || (mState == STATE_SUCCESS || mState == STATE_FAIL)) {
    200             capture();
    201         } else if (mState == STATE_FOCUSING) {
    202             // Half pressing the shutter (i.e. the focus button event) will
    203             // already have requested AF for us, so just request capture on
    204             // focus here.
    205             mState = STATE_FOCUSING_SNAP_ON_FINISH;
    206         } else if (mState == STATE_IDLE) {
    207             // We didn't do focus. This can happen if the user press focus key
    208             // while the snapshot is still in progress. The user probably wants
    209             // the next snapshot as soon as possible, so we just do a snapshot
    210             // without focusing again.
    211             capture();
    212         }
    213     }
    214 
    215     public void onShutter() {
    216         resetTouchFocus();
    217         updateFocusUI();
    218     }
    219 
    220     public void onAutoFocus(boolean focused) {
    221         if (mState == STATE_FOCUSING_SNAP_ON_FINISH) {
    222             // Take the picture no matter focus succeeds or fails. No need
    223             // to play the AF sound if we're about to play the shutter
    224             // sound.
    225             if (focused) {
    226                 mState = STATE_SUCCESS;
    227             } else {
    228                 mState = STATE_FAIL;
    229             }
    230             updateFocusUI();
    231             capture();
    232         } else if (mState == STATE_FOCUSING) {
    233             // This happens when (1) user is half-pressing the focus key or
    234             // (2) touch focus is triggered. Play the focus tone. Do not
    235             // take the picture now.
    236             if (focused) {
    237                 mState = STATE_SUCCESS;
    238                 // Do not play the sound in continuous autofocus mode. It does
    239                 // not do a full scan. The focus callback arrives before doSnap
    240                 // so the state is always STATE_FOCUSING.
    241                 if (!Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)
    242                         && mSoundPlayer != null) {
    243                     mSoundPlayer.play();
    244                 }
    245             } else {
    246                 mState = STATE_FAIL;
    247             }
    248             updateFocusUI();
    249             // If this is triggered by touch focus, cancel focus after a
    250             // while.
    251             if (mFocusArea != null) {
    252                 mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
    253             }
    254         } else if (mState == STATE_IDLE) {
    255             // User has released the focus key before focus completes.
    256             // Do nothing.
    257         }
    258     }
    259 
    260     public boolean onTouch(MotionEvent e) {
    261         if (!mInitialized || mState == STATE_FOCUSING_SNAP_ON_FINISH) return false;
    262 
    263         // Let users be able to cancel previous touch focus.
    264         if ((mFocusArea != null) && (mState == STATE_FOCUSING ||
    265                     mState == STATE_SUCCESS || mState == STATE_FAIL)) {
    266             cancelAutoFocus();
    267         }
    268 
    269         // Initialize variables.
    270         int x = Math.round(e.getX());
    271         int y = Math.round(e.getY());
    272         int focusWidth = mFocusIndicatorRotateLayout.getWidth();
    273         int focusHeight = mFocusIndicatorRotateLayout.getHeight();
    274         int previewWidth = mPreviewFrame.getWidth();
    275         int previewHeight = mPreviewFrame.getHeight();
    276         if (mFocusArea == null) {
    277             mFocusArea = new ArrayList<Area>();
    278             mFocusArea.add(new Area(new Rect(), 1));
    279             mMeteringArea = new ArrayList<Area>();
    280             mMeteringArea.add(new Area(new Rect(), 1));
    281         }
    282 
    283         // Convert the coordinates to driver format.
    284         // AE area is bigger because exposure is sensitive and
    285         // easy to over- or underexposure if area is too small.
    286         calculateTapArea(focusWidth, focusHeight, 1f, x, y, previewWidth, previewHeight,
    287                 mFocusArea.get(0).rect);
    288         calculateTapArea(focusWidth, focusHeight, 1.5f, x, y, previewWidth, previewHeight,
    289                 mMeteringArea.get(0).rect);
    290 
    291         // Use margin to set the focus indicator to the touched area.
    292         RelativeLayout.LayoutParams p =
    293                 (RelativeLayout.LayoutParams) mFocusIndicatorRotateLayout.getLayoutParams();
    294         int left = Util.clamp(x - focusWidth / 2, 0, previewWidth - focusWidth);
    295         int top = Util.clamp(y - focusHeight / 2, 0, previewHeight - focusHeight);
    296         p.setMargins(left, top, 0, 0);
    297         // Disable "center" rule because we no longer want to put it in the center.
    298         int[] rules = p.getRules();
    299         rules[RelativeLayout.CENTER_IN_PARENT] = 0;
    300         mFocusIndicatorRotateLayout.requestLayout();
    301 
    302         // Stop face detection because we want to specify focus and metering area.
    303         mListener.stopFaceDetection();
    304 
    305         // Set the focus area and metering area.
    306         mListener.setFocusParameters();
    307         if (mFocusAreaSupported && (e.getAction() == MotionEvent.ACTION_UP)) {
    308             autoFocus();
    309         } else {  // Just show the indicator in all other cases.
    310             updateFocusUI();
    311             // Reset the metering area in 3 seconds.
    312             mHandler.removeMessages(RESET_TOUCH_FOCUS);
    313             mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY);
    314         }
    315 
    316         return true;
    317     }
    318 
    319     public void onPreviewStarted() {
    320         mState = STATE_IDLE;
    321     }
    322 
    323     public void onPreviewStopped() {
    324         mState = STATE_IDLE;
    325         resetTouchFocus();
    326         // If auto focus was in progress, it would have been canceled.
    327         updateFocusUI();
    328     }
    329 
    330     public void onCameraReleased() {
    331         onPreviewStopped();
    332     }
    333 
    334     private void autoFocus() {
    335         Log.v(TAG, "Start autofocus.");
    336         mListener.autoFocus();
    337         mState = STATE_FOCUSING;
    338         // Pause the face view because the driver will keep sending face
    339         // callbacks after the focus completes.
    340         if (mFaceView != null) mFaceView.pause();
    341         updateFocusUI();
    342         mHandler.removeMessages(RESET_TOUCH_FOCUS);
    343     }
    344 
    345     private void cancelAutoFocus() {
    346         Log.v(TAG, "Cancel autofocus.");
    347 
    348         // Reset the tap area before calling mListener.cancelAutofocus.
    349         // Otherwise, focus mode stays at auto and the tap area passed to the
    350         // driver is not reset.
    351         resetTouchFocus();
    352         mListener.cancelAutoFocus();
    353         if (mFaceView != null) mFaceView.resume();
    354         mState = STATE_IDLE;
    355         updateFocusUI();
    356         mHandler.removeMessages(RESET_TOUCH_FOCUS);
    357     }
    358 
    359     private void capture() {
    360         if (mListener.capture()) {
    361             mState = STATE_IDLE;
    362             mHandler.removeMessages(RESET_TOUCH_FOCUS);
    363         }
    364     }
    365 
    366     public void initializeSoundPlayer(AssetFileDescriptor fd) {
    367         mSoundPlayer = new SoundPlayer(fd);
    368     }
    369 
    370     public void releaseSoundPlayer() {
    371         if (mSoundPlayer != null) {
    372             mSoundPlayer.release();
    373             mSoundPlayer = null;
    374         }
    375     }
    376 
    377 
    378     // This can only be called after mParameters is initialized.
    379     public String getFocusMode() {
    380         if (mOverrideFocusMode != null) return mOverrideFocusMode;
    381 
    382         if (mInLongPress) {
    383             // Users long-press the shutter button in CAF. Change it to auto
    384             // mode, so it will do a full scan.
    385             mFocusMode = Parameters.FOCUS_MODE_AUTO;
    386         } else if (mFocusAreaSupported && mFocusArea != null) {
    387             // Always use autofocus in tap-to-focus.
    388             mFocusMode = Parameters.FOCUS_MODE_AUTO;
    389         } else {
    390             // The default is continuous autofocus.
    391             mFocusMode = mPreferences.getString(
    392                     CameraSettings.KEY_FOCUS_MODE, mDefaultFocusMode);
    393         }
    394         if (!isSupported(mFocusMode, mParameters.getSupportedFocusModes())) {
    395             // For some reasons, the driver does not support the current
    396             // focus mode. Fall back to auto.
    397             if (isSupported(Parameters.FOCUS_MODE_AUTO,
    398                     mParameters.getSupportedFocusModes())) {
    399                 mFocusMode = Parameters.FOCUS_MODE_AUTO;
    400             } else {
    401                 mFocusMode = mParameters.getFocusMode();
    402             }
    403         }
    404         return mFocusMode;
    405     }
    406 
    407     public List<Area> getFocusAreas() {
    408         return mFocusArea;
    409     }
    410 
    411     public List<Area> getMeteringAreas() {
    412         return mMeteringArea;
    413     }
    414 
    415     public void updateFocusUI() {
    416         if (!mInitialized) return;
    417 
    418         // Set the length of focus indicator according to preview frame size.
    419         int len = Math.min(mPreviewFrame.getWidth(), mPreviewFrame.getHeight()) / 4;
    420         ViewGroup.LayoutParams layout = mFocusIndicator.getLayoutParams();
    421         layout.width = len;
    422         layout.height = len;
    423 
    424         // Show only focus indicator or face indicator.
    425         boolean faceExists = (mFaceView != null && mFaceView.faceExists());
    426         FocusIndicator focusIndicator = (faceExists) ? mFaceView : mFocusIndicator;
    427 
    428         if (mState == STATE_IDLE) {
    429             if (mFocusArea == null) {
    430                 focusIndicator.clear();
    431             } else {
    432                 // Users touch on the preview and the indicator represents the
    433                 // metering area. Either focus area is not supported or
    434                 // autoFocus call is not required.
    435                 focusIndicator.showStart();
    436             }
    437         } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) {
    438             focusIndicator.showStart();
    439         } else {
    440             // In CAF, do not show success or failure because it only returns
    441             // the focus status. It does not do a full scan. So the result is
    442             // failure most of the time.
    443             if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) {
    444                 focusIndicator.showStart();
    445             } else if (mState == STATE_SUCCESS) {
    446                 focusIndicator.showSuccess();
    447             } else if (mState == STATE_FAIL) {
    448                 focusIndicator.showFail();
    449             }
    450         }
    451     }
    452 
    453     public void resetTouchFocus() {
    454         if (!mInitialized) return;
    455 
    456         // Put focus indicator to the center.
    457         RelativeLayout.LayoutParams p =
    458                 (RelativeLayout.LayoutParams) mFocusIndicatorRotateLayout.getLayoutParams();
    459         int[] rules = p.getRules();
    460         rules[RelativeLayout.CENTER_IN_PARENT] = RelativeLayout.TRUE;
    461         p.setMargins(0, 0, 0, 0);
    462 
    463         mFocusArea = null;
    464         mMeteringArea = null;
    465     }
    466 
    467     public void calculateTapArea(int focusWidth, int focusHeight, float areaMultiple,
    468             int x, int y, int previewWidth, int previewHeight, Rect rect) {
    469         int areaWidth = (int)(focusWidth * areaMultiple);
    470         int areaHeight = (int)(focusHeight * areaMultiple);
    471         int left = Util.clamp(x - areaWidth / 2, 0, previewWidth - areaWidth);
    472         int top = Util.clamp(y - areaHeight / 2, 0, previewHeight - areaHeight);
    473 
    474         RectF rectF = new RectF(left, top, left + areaWidth, top + areaHeight);
    475         mMatrix.mapRect(rectF);
    476         Util.rectFToRect(rectF, rect);
    477     }
    478 
    479     public boolean isFocusCompleted() {
    480         return mState == STATE_SUCCESS || mState == STATE_FAIL;
    481     }
    482 
    483     public void removeMessages() {
    484         mHandler.removeMessages(RESET_TOUCH_FOCUS);
    485     }
    486 
    487     public void overrideFocusMode(String focusMode) {
    488         mOverrideFocusMode = focusMode;
    489     }
    490 
    491     public void setAeAwbLock(boolean lock) {
    492         mAeAwbLock = lock;
    493     }
    494 
    495     public boolean getAeAwbLock() {
    496         return mAeAwbLock;
    497     }
    498 
    499     private static boolean isSupported(String value, List<String> supported) {
    500         return supported == null ? false : supported.indexOf(value) >= 0;
    501     }
    502 
    503     private boolean needAutoFocusCall() {
    504         String focusMode = getFocusMode();
    505         return !(focusMode.equals(Parameters.FOCUS_MODE_INFINITY)
    506                 || focusMode.equals(Parameters.FOCUS_MODE_FIXED)
    507                 || focusMode.equals(Parameters.FOCUS_MODE_EDOF));
    508     }
    509 }
    510