Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2013 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 static com.android.camera.util.CameraUtil.Assert;
     20 
     21 import java.io.IOException;
     22 
     23 import android.annotation.TargetApi;
     24 import android.graphics.SurfaceTexture;
     25 import android.hardware.Camera;
     26 import android.hardware.Camera.AutoFocusCallback;
     27 import android.hardware.Camera.AutoFocusMoveCallback;
     28 import android.hardware.Camera.ErrorCallback;
     29 import android.hardware.Camera.FaceDetectionListener;
     30 import android.hardware.Camera.OnZoomChangeListener;
     31 import android.hardware.Camera.Parameters;
     32 import android.hardware.Camera.PictureCallback;
     33 import android.hardware.Camera.PreviewCallback;
     34 import android.hardware.Camera.ShutterCallback;
     35 import android.os.Build;
     36 import android.os.Handler;
     37 import android.os.HandlerThread;
     38 import android.os.Looper;
     39 import android.os.Message;
     40 import android.util.Log;
     41 import android.view.SurfaceHolder;
     42 
     43 /**
     44  * A class to implement {@link CameraManager} of the Android camera framework.
     45  */
     46 class AndroidCameraManagerImpl implements CameraManager {
     47     private static final String TAG = "CAM_" +
     48             AndroidCameraManagerImpl.class.getSimpleName();
     49 
     50     private Parameters mParameters;
     51     private boolean mParametersIsDirty;
     52     private IOException mReconnectIOException;
     53 
     54     /* Messages used in CameraHandler. */
     55     // Camera initialization/finalization
     56     private static final int OPEN_CAMERA = 1;
     57     private static final int RELEASE =     2;
     58     private static final int RECONNECT =   3;
     59     private static final int UNLOCK =      4;
     60     private static final int LOCK =        5;
     61     // Preview
     62     private static final int SET_PREVIEW_TEXTURE_ASYNC =        101;
     63     private static final int START_PREVIEW_ASYNC =              102;
     64     private static final int STOP_PREVIEW =                     103;
     65     private static final int SET_PREVIEW_CALLBACK_WITH_BUFFER = 104;
     66     private static final int ADD_CALLBACK_BUFFER =              105;
     67     private static final int SET_PREVIEW_DISPLAY_ASYNC =        106;
     68     private static final int SET_PREVIEW_CALLBACK =             107;
     69     // Parameters
     70     private static final int SET_PARAMETERS =     201;
     71     private static final int GET_PARAMETERS =     202;
     72     private static final int REFRESH_PARAMETERS = 203;
     73     // Focus, Zoom
     74     private static final int AUTO_FOCUS =                   301;
     75     private static final int CANCEL_AUTO_FOCUS =            302;
     76     private static final int SET_AUTO_FOCUS_MOVE_CALLBACK = 303;
     77     private static final int SET_ZOOM_CHANGE_LISTENER =     304;
     78     // Face detection
     79     private static final int SET_FACE_DETECTION_LISTENER = 461;
     80     private static final int START_FACE_DETECTION =        462;
     81     private static final int STOP_FACE_DETECTION =         463;
     82     private static final int SET_ERROR_CALLBACK =          464;
     83     // Presentation
     84     private static final int ENABLE_SHUTTER_SOUND =    501;
     85     private static final int SET_DISPLAY_ORIENTATION = 502;
     86 
     87     private CameraHandler mCameraHandler;
     88     private android.hardware.Camera mCamera;
     89 
     90     // Used to retain a copy of Parameters for setting parameters.
     91     private Parameters mParamsToSet;
     92 
     93     AndroidCameraManagerImpl() {
     94         HandlerThread ht = new HandlerThread("Camera Handler Thread");
     95         ht.start();
     96         mCameraHandler = new CameraHandler(ht.getLooper());
     97     }
     98 
     99     private class CameraHandler extends Handler {
    100         CameraHandler(Looper looper) {
    101             super(looper);
    102         }
    103 
    104         private void startFaceDetection() {
    105             mCamera.startFaceDetection();
    106         }
    107 
    108         private void stopFaceDetection() {
    109             mCamera.stopFaceDetection();
    110         }
    111 
    112         private void setFaceDetectionListener(FaceDetectionListener listener) {
    113             mCamera.setFaceDetectionListener(listener);
    114         }
    115 
    116         private void setPreviewTexture(Object surfaceTexture) {
    117             try {
    118                 mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture);
    119             } catch (IOException e) {
    120                 Log.e(TAG, "Could not set preview texture", e);
    121             }
    122         }
    123 
    124         @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    125         private void enableShutterSound(boolean enable) {
    126             mCamera.enableShutterSound(enable);
    127         }
    128 
    129         @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    130         private void setAutoFocusMoveCallback(
    131                 android.hardware.Camera camera, Object cb) {
    132             camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb);
    133         }
    134 
    135         public void requestTakePicture(
    136                 final ShutterCallback shutter,
    137                 final PictureCallback raw,
    138                 final PictureCallback postView,
    139                 final PictureCallback jpeg) {
    140             post(new Runnable() {
    141                 @Override
    142                 public void run() {
    143                     try {
    144                         mCamera.takePicture(shutter, raw, postView, jpeg);
    145                     } catch (RuntimeException e) {
    146                         // TODO: output camera state and focus state for debugging.
    147                         Log.e(TAG, "take picture failed.");
    148                         throw e;
    149                     }
    150                 }
    151             });
    152         }
    153 
    154         /**
    155          * Waits for all the {@code Message} and {@code Runnable} currently in the queue
    156          * are processed.
    157          *
    158          * @return {@code false} if the wait was interrupted, {@code true} otherwise.
    159          */
    160         public boolean waitDone() {
    161             final Object waitDoneLock = new Object();
    162             final Runnable unlockRunnable = new Runnable() {
    163                 @Override
    164                 public void run() {
    165                     synchronized (waitDoneLock) {
    166                         waitDoneLock.notifyAll();
    167                     }
    168                 }
    169             };
    170 
    171             synchronized (waitDoneLock) {
    172                 mCameraHandler.post(unlockRunnable);
    173                 try {
    174                     waitDoneLock.wait();
    175                 } catch (InterruptedException ex) {
    176                     Log.v(TAG, "waitDone interrupted");
    177                     return false;
    178                 }
    179             }
    180             return true;
    181         }
    182 
    183         /**
    184          * This method does not deal with the API level check.  Everyone should
    185          * check first for supported operations before sending message to this handler.
    186          */
    187         @Override
    188         public void handleMessage(final Message msg) {
    189             try {
    190                 switch (msg.what) {
    191                     case OPEN_CAMERA:
    192                         mCamera = android.hardware.Camera.open(msg.arg1);
    193                         if (mCamera != null) {
    194                             mParametersIsDirty = true;
    195 
    196                             // Get a instance of Camera.Parameters for later use.
    197                             if (mParamsToSet == null) {
    198                                 mParamsToSet = mCamera.getParameters();
    199                             }
    200                         } else {
    201                             if (msg.obj != null) {
    202                                 ((CameraOpenErrorCallback) msg.obj).onDeviceOpenFailure(msg.arg1);
    203                             }
    204                         }
    205                         return;
    206 
    207                     case RELEASE:
    208                         mCamera.release();
    209                         mCamera = null;
    210                         return;
    211 
    212                     case RECONNECT:
    213                         mReconnectIOException = null;
    214                         try {
    215                             mCamera.reconnect();
    216                         } catch (IOException ex) {
    217                             mReconnectIOException = ex;
    218                         }
    219                         return;
    220 
    221                     case UNLOCK:
    222                         mCamera.unlock();
    223                         return;
    224 
    225                     case LOCK:
    226                         mCamera.lock();
    227                         return;
    228 
    229                     case SET_PREVIEW_TEXTURE_ASYNC:
    230                         setPreviewTexture(msg.obj);
    231                         return;
    232 
    233                     case SET_PREVIEW_DISPLAY_ASYNC:
    234                         try {
    235                             mCamera.setPreviewDisplay((SurfaceHolder) msg.obj);
    236                         } catch (IOException e) {
    237                             throw new RuntimeException(e);
    238                         }
    239                         return;
    240 
    241                     case START_PREVIEW_ASYNC:
    242                         mCamera.startPreview();
    243                         return;
    244 
    245                     case STOP_PREVIEW:
    246                         mCamera.stopPreview();
    247                         return;
    248 
    249                     case SET_PREVIEW_CALLBACK_WITH_BUFFER:
    250                         mCamera.setPreviewCallbackWithBuffer(
    251                             (PreviewCallback) msg.obj);
    252                         return;
    253 
    254                     case ADD_CALLBACK_BUFFER:
    255                         mCamera.addCallbackBuffer((byte[]) msg.obj);
    256                         return;
    257 
    258                     case AUTO_FOCUS:
    259                         mCamera.autoFocus((AutoFocusCallback) msg.obj);
    260                         return;
    261 
    262                     case CANCEL_AUTO_FOCUS:
    263                         mCamera.cancelAutoFocus();
    264                         return;
    265 
    266                     case SET_AUTO_FOCUS_MOVE_CALLBACK:
    267                         setAutoFocusMoveCallback(mCamera, msg.obj);
    268                         return;
    269 
    270                     case SET_DISPLAY_ORIENTATION:
    271                         mCamera.setDisplayOrientation(msg.arg1);
    272                         return;
    273 
    274                     case SET_ZOOM_CHANGE_LISTENER:
    275                         mCamera.setZoomChangeListener(
    276                             (OnZoomChangeListener) msg.obj);
    277                         return;
    278 
    279                     case SET_FACE_DETECTION_LISTENER:
    280                         setFaceDetectionListener((FaceDetectionListener) msg.obj);
    281                         return;
    282 
    283                     case START_FACE_DETECTION:
    284                         startFaceDetection();
    285                         return;
    286 
    287                     case STOP_FACE_DETECTION:
    288                         stopFaceDetection();
    289                         return;
    290 
    291                     case SET_ERROR_CALLBACK:
    292                         mCamera.setErrorCallback((ErrorCallback) msg.obj);
    293                         return;
    294 
    295                     case SET_PARAMETERS:
    296                         mParametersIsDirty = true;
    297                         mParamsToSet.unflatten((String) msg.obj);
    298                         mCamera.setParameters(mParamsToSet);
    299                         return;
    300 
    301                     case GET_PARAMETERS:
    302                         if (mParametersIsDirty) {
    303                             mParameters = mCamera.getParameters();
    304                             mParametersIsDirty = false;
    305                         }
    306                         return;
    307 
    308                     case SET_PREVIEW_CALLBACK:
    309                         mCamera.setPreviewCallback((PreviewCallback) msg.obj);
    310                         return;
    311 
    312                     case ENABLE_SHUTTER_SOUND:
    313                         enableShutterSound((msg.arg1 == 1) ? true : false);
    314                         return;
    315 
    316                     case REFRESH_PARAMETERS:
    317                         mParametersIsDirty = true;
    318                         return;
    319 
    320                     default:
    321                         throw new RuntimeException("Invalid CameraProxy message=" + msg.what);
    322                 }
    323             } catch (RuntimeException e) {
    324                 if (msg.what != RELEASE && mCamera != null) {
    325                     try {
    326                         mCamera.release();
    327                     } catch (Exception ex) {
    328                         Log.e(TAG, "Fail to release the camera.");
    329                     }
    330                     mCamera = null;
    331                 } else if (mCamera == null) {
    332                     if (msg.what == OPEN_CAMERA) {
    333                         if (msg.obj != null) {
    334                             ((CameraOpenErrorCallback) msg.obj).onDeviceOpenFailure(msg.arg1);
    335                         }
    336                     } else {
    337                         Log.w(TAG, "Cannot handle message, mCamera is null.");
    338                     }
    339                     return;
    340                 }
    341                 throw e;
    342             }
    343         }
    344     }
    345 
    346     @Override
    347     public CameraManager.CameraProxy cameraOpen(
    348         Handler handler, int cameraId, CameraOpenErrorCallback callback) {
    349         mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0,
    350                 CameraOpenErrorCallbackForward.getNewInstance(
    351                         handler, callback)).sendToTarget();
    352         mCameraHandler.waitDone();
    353         if (mCamera != null) {
    354             return new AndroidCameraProxyImpl();
    355         } else {
    356             return null;
    357         }
    358     }
    359 
    360     /**
    361      * A class which implements {@link CameraManager.CameraProxy} and
    362      * camera handler thread.
    363      * TODO: Save the handler for the callback here to avoid passing the same
    364      * handler multiple times.
    365      */
    366     public class AndroidCameraProxyImpl implements CameraManager.CameraProxy {
    367 
    368         private AndroidCameraProxyImpl() {
    369             Assert(mCamera != null);
    370         }
    371 
    372         @Override
    373         public android.hardware.Camera getCamera() {
    374             return mCamera;
    375         }
    376 
    377         @Override
    378         public void release() {
    379             // release() must be synchronous so we know exactly when the camera
    380             // is released and can continue on.
    381             mCameraHandler.sendEmptyMessage(RELEASE);
    382             mCameraHandler.waitDone();
    383         }
    384 
    385         @Override
    386         public boolean reconnect(Handler handler, CameraOpenErrorCallback cb) {
    387             mCameraHandler.sendEmptyMessage(RECONNECT);
    388             mCameraHandler.waitDone();
    389             CameraOpenErrorCallback cbforward =
    390                     CameraOpenErrorCallbackForward.getNewInstance(handler, cb);
    391             if (mReconnectIOException != null) {
    392                 if (cbforward != null) {
    393                     cbforward.onReconnectionFailure(AndroidCameraManagerImpl.this);
    394                 }
    395                 return false;
    396             }
    397             return true;
    398         }
    399 
    400         @Override
    401         public void unlock() {
    402             mCameraHandler.sendEmptyMessage(UNLOCK);
    403             mCameraHandler.waitDone();
    404         }
    405 
    406         @Override
    407         public void lock() {
    408             mCameraHandler.sendEmptyMessage(LOCK);
    409         }
    410 
    411         @Override
    412         public void setPreviewTexture(SurfaceTexture surfaceTexture) {
    413             mCameraHandler.obtainMessage(SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture).sendToTarget();
    414         }
    415 
    416         @Override
    417         public void setPreviewDisplay(SurfaceHolder surfaceHolder) {
    418             mCameraHandler.obtainMessage(SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder).sendToTarget();
    419         }
    420 
    421         @Override
    422         public void startPreview() {
    423             mCameraHandler.sendEmptyMessage(START_PREVIEW_ASYNC);
    424         }
    425 
    426         @Override
    427         public void stopPreview() {
    428             mCameraHandler.sendEmptyMessage(STOP_PREVIEW);
    429             mCameraHandler.waitDone();
    430         }
    431 
    432         @Override
    433         public void setPreviewDataCallback(
    434                 Handler handler, CameraPreviewDataCallback cb) {
    435             mCameraHandler.obtainMessage(
    436                     SET_PREVIEW_CALLBACK,
    437                     PreviewCallbackForward.getNewInstance(handler, this, cb)).sendToTarget();
    438         }
    439 
    440         @Override
    441         public void setPreviewDataCallbackWithBuffer(
    442                 Handler handler, CameraPreviewDataCallback cb) {
    443             mCameraHandler.obtainMessage(
    444                     SET_PREVIEW_CALLBACK_WITH_BUFFER,
    445                     PreviewCallbackForward.getNewInstance(handler, this, cb)).sendToTarget();
    446         }
    447 
    448         @Override
    449         public void addCallbackBuffer(byte[] callbackBuffer) {
    450             mCameraHandler.obtainMessage(ADD_CALLBACK_BUFFER, callbackBuffer).sendToTarget();
    451         }
    452 
    453         @Override
    454         public void autoFocus(Handler handler, CameraAFCallback cb) {
    455             mCameraHandler.obtainMessage(
    456                     AUTO_FOCUS,
    457                     AFCallbackForward.getNewInstance(handler, this, cb)).sendToTarget();
    458         }
    459 
    460         @Override
    461         public void cancelAutoFocus() {
    462             mCameraHandler.removeMessages(AUTO_FOCUS);
    463             mCameraHandler.sendEmptyMessage(CANCEL_AUTO_FOCUS);
    464         }
    465 
    466         @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    467         @Override
    468         public void setAutoFocusMoveCallback(
    469                 Handler handler, CameraAFMoveCallback cb) {
    470             mCameraHandler.obtainMessage(
    471                     SET_AUTO_FOCUS_MOVE_CALLBACK,
    472                     AFMoveCallbackForward.getNewInstance(handler, this, cb)).sendToTarget();
    473         }
    474 
    475         @Override
    476         public void takePicture(
    477                 Handler handler,
    478                 CameraShutterCallback shutter,
    479                 CameraPictureCallback raw,
    480                 CameraPictureCallback post,
    481                 CameraPictureCallback jpeg) {
    482             mCameraHandler.requestTakePicture(
    483                     ShutterCallbackForward.getNewInstance(handler, this, shutter),
    484                     PictureCallbackForward.getNewInstance(handler, this, raw),
    485                     PictureCallbackForward.getNewInstance(handler, this, post),
    486                     PictureCallbackForward.getNewInstance(handler, this, jpeg));
    487         }
    488 
    489         @Override
    490         public void setDisplayOrientation(int degrees) {
    491             mCameraHandler.obtainMessage(SET_DISPLAY_ORIENTATION, degrees, 0)
    492                     .sendToTarget();
    493         }
    494 
    495         @Override
    496         public void setZoomChangeListener(OnZoomChangeListener listener) {
    497             mCameraHandler.obtainMessage(SET_ZOOM_CHANGE_LISTENER, listener).sendToTarget();
    498         }
    499 
    500         public void setFaceDetectionCallback(
    501                 Handler handler, CameraFaceDetectionCallback cb) {
    502             mCameraHandler.obtainMessage(
    503                     SET_FACE_DETECTION_LISTENER,
    504                     FaceDetectionCallbackForward.getNewInstance(handler, this, cb)).sendToTarget();
    505         }
    506 
    507         @Override
    508         public void startFaceDetection() {
    509             mCameraHandler.sendEmptyMessage(START_FACE_DETECTION);
    510         }
    511 
    512         @Override
    513         public void stopFaceDetection() {
    514             mCameraHandler.sendEmptyMessage(STOP_FACE_DETECTION);
    515         }
    516 
    517         @Override
    518         public void setErrorCallback(ErrorCallback cb) {
    519             mCameraHandler.obtainMessage(SET_ERROR_CALLBACK, cb).sendToTarget();
    520         }
    521 
    522         @Override
    523         public void setParameters(Parameters params) {
    524             if (params == null) {
    525                 Log.v(TAG, "null parameters in setParameters()");
    526                 return;
    527             }
    528             mCameraHandler.obtainMessage(SET_PARAMETERS, params.flatten())
    529                     .sendToTarget();
    530         }
    531 
    532         @Override
    533         public Parameters getParameters() {
    534             mCameraHandler.sendEmptyMessage(GET_PARAMETERS);
    535             mCameraHandler.waitDone();
    536             return mParameters;
    537         }
    538 
    539         @Override
    540         public void refreshParameters() {
    541             mCameraHandler.sendEmptyMessage(REFRESH_PARAMETERS);
    542         }
    543 
    544         @Override
    545         public void enableShutterSound(boolean enable) {
    546             mCameraHandler.obtainMessage(
    547                     ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0).sendToTarget();
    548         }
    549     }
    550 
    551     /**
    552      * A helper class to forward AutoFocusCallback to another thread.
    553      */
    554     private static class AFCallbackForward implements AutoFocusCallback {
    555         private final Handler mHandler;
    556         private final CameraProxy mCamera;
    557         private final CameraAFCallback mCallback;
    558 
    559         /**
    560          * Returns a new instance of {@link AFCallbackForward}.
    561          *
    562          * @param handler The handler in which the callback will be invoked in.
    563          * @param camera  The {@link CameraProxy} which the callback is from.
    564          * @param cb      The callback to be invoked.
    565          * @return        The instance of the {@link AFCallbackForward},
    566          *                or null if any parameter is null.
    567          */
    568         public static AFCallbackForward getNewInstance(
    569                 Handler handler, CameraProxy camera, CameraAFCallback cb) {
    570             if (handler == null || camera == null || cb == null) return null;
    571             return new AFCallbackForward(handler, camera, cb);
    572         }
    573 
    574         private AFCallbackForward(
    575                 Handler h, CameraProxy camera, CameraAFCallback cb) {
    576             mHandler = h;
    577             mCamera = camera;
    578             mCallback = cb;
    579         }
    580 
    581         @Override
    582         public void onAutoFocus(final boolean b, Camera camera) {
    583             mHandler.post(new Runnable() {
    584                 @Override
    585                 public void run() {
    586                     mCallback.onAutoFocus(b, mCamera);
    587                 }
    588             });
    589         }
    590     }
    591 
    592     /** A helper class to forward AutoFocusMoveCallback to another thread. */
    593     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    594     private static class AFMoveCallbackForward implements AutoFocusMoveCallback {
    595         private final Handler mHandler;
    596         private final CameraAFMoveCallback mCallback;
    597         private final CameraProxy mCamera;
    598 
    599         /**
    600          * Returns a new instance of {@link AFMoveCallbackForward}.
    601          *
    602          * @param handler The handler in which the callback will be invoked in.
    603          * @param camera  The {@link CameraProxy} which the callback is from.
    604          * @param cb      The callback to be invoked.
    605          * @return        The instance of the {@link AFMoveCallbackForward},
    606          *                or null if any parameter is null.
    607          */
    608         public static AFMoveCallbackForward getNewInstance(
    609                 Handler handler, CameraProxy camera, CameraAFMoveCallback cb) {
    610             if (handler == null || camera == null || cb == null) return null;
    611             return new AFMoveCallbackForward(handler, camera, cb);
    612         }
    613 
    614         private AFMoveCallbackForward(
    615                 Handler h, CameraProxy camera, CameraAFMoveCallback cb) {
    616             mHandler = h;
    617             mCamera = camera;
    618             mCallback = cb;
    619         }
    620 
    621         @Override
    622         public void onAutoFocusMoving(
    623                 final boolean moving, android.hardware.Camera camera) {
    624             mHandler.post(new Runnable() {
    625                 @Override
    626                 public void run() {
    627                     mCallback.onAutoFocusMoving(moving, mCamera);
    628                 }
    629             });
    630         }
    631     }
    632 
    633     /**
    634      * A helper class to forward ShutterCallback to to another thread.
    635      */
    636     private static class ShutterCallbackForward implements ShutterCallback {
    637         private final Handler mHandler;
    638         private final CameraShutterCallback mCallback;
    639         private final CameraProxy mCamera;
    640 
    641         /**
    642          * Returns a new instance of {@link ShutterCallbackForward}.
    643          *
    644          * @param handler The handler in which the callback will be invoked in.
    645          * @param camera  The {@link CameraProxy} which the callback is from.
    646          * @param cb      The callback to be invoked.
    647          * @return        The instance of the {@link ShutterCallbackForward},
    648          *                or null if any parameter is null.
    649          */
    650         public static ShutterCallbackForward getNewInstance(
    651                 Handler handler, CameraProxy camera, CameraShutterCallback cb) {
    652             if (handler == null || camera == null || cb == null) return null;
    653             return new ShutterCallbackForward(handler, camera, cb);
    654         }
    655 
    656         private ShutterCallbackForward(
    657                 Handler h, CameraProxy camera, CameraShutterCallback cb) {
    658             mHandler = h;
    659             mCamera = camera;
    660             mCallback = cb;
    661         }
    662 
    663         @Override
    664         public void onShutter() {
    665             mHandler.post(new Runnable() {
    666                 @Override
    667                 public void run() {
    668                     mCallback.onShutter(mCamera);
    669                 }
    670             });
    671         }
    672     }
    673 
    674     /**
    675      * A helper class to forward PictureCallback to another thread.
    676      */
    677     private static class PictureCallbackForward implements PictureCallback {
    678         private final Handler mHandler;
    679         private final CameraPictureCallback mCallback;
    680         private final CameraProxy mCamera;
    681 
    682         /**
    683          * Returns a new instance of {@link PictureCallbackForward}.
    684          *
    685          * @param handler The handler in which the callback will be invoked in.
    686          * @param camera  The {@link CameraProxy} which the callback is from.
    687          * @param cb      The callback to be invoked.
    688          * @return        The instance of the {@link PictureCallbackForward},
    689          *                or null if any parameters is null.
    690          */
    691         public static PictureCallbackForward getNewInstance(
    692                 Handler handler, CameraProxy camera, CameraPictureCallback cb) {
    693             if (handler == null || camera == null || cb == null) return null;
    694             return new PictureCallbackForward(handler, camera, cb);
    695         }
    696 
    697         private PictureCallbackForward(
    698                 Handler h, CameraProxy camera, CameraPictureCallback cb) {
    699             mHandler = h;
    700             mCamera = camera;
    701             mCallback = cb;
    702         }
    703 
    704         @Override
    705         public void onPictureTaken(
    706                 final byte[] data, android.hardware.Camera camera) {
    707             mHandler.post(new Runnable() {
    708                 @Override
    709                 public void run() {
    710                     mCallback.onPictureTaken(data, mCamera);
    711                 }
    712             });
    713         }
    714     }
    715 
    716     /**
    717      * A helper class to forward PreviewCallback to another thread.
    718      */
    719     private static class PreviewCallbackForward implements PreviewCallback {
    720         private final Handler mHandler;
    721         private final CameraPreviewDataCallback mCallback;
    722         private final CameraProxy mCamera;
    723 
    724         /**
    725          * Returns a new instance of {@link PreviewCallbackForward}.
    726          *
    727          * @param handler The handler in which the callback will be invoked in.
    728          * @param camera  The {@link CameraProxy} which the callback is from.
    729          * @param cb      The callback to be invoked.
    730          * @return        The instance of the {@link PreviewCallbackForward},
    731          *                or null if any parameters is null.
    732          */
    733         public static PreviewCallbackForward getNewInstance(
    734                 Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) {
    735             if (handler == null || camera == null || cb == null) return null;
    736             return new PreviewCallbackForward(handler, camera, cb);
    737         }
    738 
    739         private PreviewCallbackForward(
    740                 Handler h, CameraProxy camera, CameraPreviewDataCallback cb) {
    741             mHandler = h;
    742             mCamera = camera;
    743             mCallback = cb;
    744         }
    745 
    746         @Override
    747         public void onPreviewFrame(
    748                 final byte[] data, android.hardware.Camera camera) {
    749             mHandler.post(new Runnable() {
    750                 @Override
    751                 public void run() {
    752                     mCallback.onPreviewFrame(data, mCamera);
    753                 }
    754             });
    755         }
    756     }
    757 
    758     private static class FaceDetectionCallbackForward implements FaceDetectionListener {
    759         private final Handler mHandler;
    760         private final CameraFaceDetectionCallback mCallback;
    761         private final CameraProxy mCamera;
    762 
    763         /**
    764          * Returns a new instance of {@link FaceDetectionCallbackForward}.
    765          *
    766          * @param handler The handler in which the callback will be invoked in.
    767          * @param camera  The {@link CameraProxy} which the callback is from.
    768          * @param cb      The callback to be invoked.
    769          * @return        The instance of the {@link FaceDetectionCallbackForward},
    770          *                or null if any parameter is null.
    771          */
    772         public static FaceDetectionCallbackForward getNewInstance(
    773                 Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) {
    774             if (handler == null || camera == null || cb == null) return null;
    775             return new FaceDetectionCallbackForward(handler, camera, cb);
    776         }
    777 
    778         private FaceDetectionCallbackForward(
    779                 Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) {
    780             mHandler = h;
    781             mCamera = camera;
    782             mCallback = cb;
    783         }
    784 
    785         @Override
    786         public void onFaceDetection(
    787                 final Camera.Face[] faces, Camera camera) {
    788             mHandler.post(new Runnable() {
    789                 @Override
    790                 public void run() {
    791                     mCallback.onFaceDetection(faces, mCamera);
    792                 }
    793             });
    794         }
    795     }
    796 
    797     /**
    798      * A callback helps to invoke the original callback on another
    799      * {@link android.os.Handler}.
    800      */
    801     private static class CameraOpenErrorCallbackForward implements CameraOpenErrorCallback {
    802         private final Handler mHandler;
    803         private final CameraOpenErrorCallback mCallback;
    804 
    805         /**
    806          * Returns a new instance of {@link FaceDetectionCallbackForward}.
    807          *
    808          * @param handler The handler in which the callback will be invoked in.
    809          * @param cb The callback to be invoked.
    810          * @return The instance of the {@link FaceDetectionCallbackForward}, or
    811          *         null if any parameter is null.
    812          */
    813         public static CameraOpenErrorCallbackForward getNewInstance(
    814                 Handler handler, CameraOpenErrorCallback cb) {
    815             if (handler == null || cb == null) {
    816                 return null;
    817             }
    818             return new CameraOpenErrorCallbackForward(handler, cb);
    819         }
    820 
    821         private CameraOpenErrorCallbackForward(
    822                 Handler h, CameraOpenErrorCallback cb) {
    823             // Given that we are using the main thread handler, we can create it
    824             // here instead of holding onto the PhotoModule objects. In this
    825             // way, we can avoid memory leak.
    826             mHandler = new Handler(Looper.getMainLooper());
    827             mCallback = cb;
    828         }
    829 
    830         @Override
    831         public void onCameraDisabled(final int cameraId) {
    832             mHandler.post(new Runnable() {
    833                 @Override
    834                 public void run() {
    835                     mCallback.onCameraDisabled(cameraId);
    836                 }
    837             });
    838         }
    839 
    840         @Override
    841         public void onDeviceOpenFailure(final int cameraId) {
    842             mHandler.post(new Runnable() {
    843                 @Override
    844                 public void run() {
    845                     mCallback.onDeviceOpenFailure(cameraId);
    846                 }
    847             });
    848         }
    849 
    850         @Override
    851         public void onReconnectionFailure(final CameraManager mgr) {
    852             mHandler.post(new Runnable() {
    853                 @Override
    854                 public void run() {
    855                     mCallback.onReconnectionFailure(mgr);
    856                 }
    857             });
    858         }
    859     }
    860 }
    861