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