CameraController.java revision 395bc17b71d13d97b3fd9f258fcf4d5748e66e5e
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
17package com.android.camera.app;
18
19import android.content.Context;
20import android.hardware.Camera;
21import android.os.Handler;
22
23import com.android.camera.CameraDisabledException;
24import com.android.camera.debug.Log;
25import com.android.camera.util.CameraUtil;
26import com.android.ex.camera2.portability.CameraDeviceInfo;
27import com.android.ex.camera2.portability.CameraAgent;
28import com.android.ex.camera2.portability.CameraAgent.CameraExceptionCallback;
29
30/**
31 * A class which implements {@link com.android.camera.app.CameraProvider} used
32 * by {@link com.android.camera.CameraActivity}.
33 * TODO: Make this class package private.
34 */
35public class CameraController implements CameraAgent.CameraOpenCallback, CameraProvider {
36    private static final Log.Tag TAG = new Log.Tag("CameraController");
37    private static final int EMPTY_REQUEST = -1;
38    private final Context mContext;
39    private CameraAgent.CameraOpenCallback mCallbackReceiver;
40    private final Handler mCallbackHandler;
41    private final CameraAgent mCameraAgent;
42    private final CameraAgent mCameraAgentNg;
43
44    /** The one for the API that is currently in use (deprecated one by default). */
45    private CameraDeviceInfo mInfo;
46
47    private CameraAgent.CameraProxy mCameraProxy;
48    private int mRequestingCameraId = EMPTY_REQUEST;
49
50    /**
51     * Determines which of mCameraAgent and mCameraAgentNg is currently in use.
52     * <p>It's only possible to enable this if the new API is actually
53     * supported.</p>
54     */
55    private boolean mUsingNewApi = false;
56
57    /**
58     * Constructor.
59     *
60     * @param context The {@link android.content.Context} used to check if the
61     *                camera is disabled.
62     * @param handler The {@link android.os.Handler} to post the camera
63     *                callbacks to.
64     * @param cameraManager Used for camera open/close.
65     * @param cameraManagerNg Used for camera open/close with the new API. If
66     *                        {@code null} or the same object as
67     *                        {@code cameraManager}, the new API will not be
68     *                        exposed and requests for it will get the old one.
69     */
70    public CameraController(Context context, CameraAgent.CameraOpenCallback callbackReceiver,
71            Handler handler, CameraAgent cameraManager, CameraAgent cameraManagerNg) {
72        mContext = context;
73        mCallbackReceiver = callbackReceiver;
74        mCallbackHandler = handler;
75        mCameraAgent = cameraManager;
76        // If the new implementation is the same as the old, the
77        // CameraAgentFactory decided this device doesn't support the new API.
78        mCameraAgentNg = cameraManagerNg != cameraManager ? cameraManagerNg : null;
79        mInfo = mCameraAgent.getCameraDeviceInfo();
80        if (mInfo == null && mCallbackReceiver != null) {
81            mCallbackReceiver.onDeviceOpenFailure(-1, "GETTING_CAMERA_INFO");
82        }
83    }
84
85    @Override
86    public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback,
87            Handler handler) {
88        mCameraAgent.setCameraDefaultExceptionCallback(callback, handler);
89        if (mCameraAgentNg != null) {
90            mCameraAgentNg.setCameraDefaultExceptionCallback(callback, handler);
91        }
92    }
93
94    @Override
95    public CameraDeviceInfo.Characteristics getCharacteristics(int cameraId) {
96        if (mInfo == null) {
97            return null;
98        }
99        return mInfo.getCharacteristics(cameraId);
100    }
101
102    @Override
103    public int getCurrentCameraId() {
104        if (mCameraProxy != null) {
105            return mCameraProxy.getCameraId();
106        } else {
107            Log.v(TAG, "getCurrentCameraId without an open camera... returning requested id");
108            return mRequestingCameraId;
109        }
110    }
111
112    @Override
113    public int getNumberOfCameras() {
114        if (mInfo == null) {
115            return 0;
116        }
117        return mInfo.getNumberOfCameras();
118    }
119
120    @Override
121    public int getFirstBackCameraId() {
122        if (mInfo == null) {
123            return -1;
124        }
125        return mInfo.getFirstBackCameraId();
126    }
127
128    @Override
129    public int getFirstFrontCameraId() {
130        if (mInfo == null) {
131            return -1;
132        }
133        return mInfo.getFirstFrontCameraId();
134    }
135
136    @Override
137    public boolean isFrontFacingCamera(int id) {
138        if (mInfo == null) {
139            return false;
140        }
141        if (id >= mInfo.getNumberOfCameras() || mInfo.getCharacteristics(id) == null) {
142            Log.e(TAG, "Camera info not available:" + id);
143            return false;
144        }
145        return mInfo.getCharacteristics(id).isFacingFront();
146    }
147
148    @Override
149    public boolean isBackFacingCamera(int id) {
150        if (mInfo == null) {
151            return false;
152        }
153        if (id >= mInfo.getNumberOfCameras() || mInfo.getCharacteristics(id) == null) {
154            Log.e(TAG, "Camera info not available:" + id);
155            return false;
156        }
157        return mInfo.getCharacteristics(id).isFacingBack();
158    }
159
160    @Override
161    public void onCameraOpened(CameraAgent.CameraProxy camera) {
162        Log.v(TAG, "onCameraOpened");
163        if (mRequestingCameraId != camera.getCameraId()) {
164            // Not requesting any camera or not waiting for this one.
165            return;
166        }
167        mCameraProxy = camera;
168        mRequestingCameraId = EMPTY_REQUEST;
169        if (mCallbackReceiver != null) {
170            mCallbackReceiver.onCameraOpened(camera);
171        }
172    }
173
174    @Override
175    public void onCameraDisabled(int cameraId) {
176        if (mCallbackReceiver != null) {
177            mCallbackReceiver.onCameraDisabled(cameraId);
178        }
179    }
180
181    @Override
182    public void onDeviceOpenFailure(int cameraId, String info) {
183        if (mCallbackReceiver != null) {
184            mCallbackReceiver.onDeviceOpenFailure(cameraId, info);
185        }
186    }
187
188    @Override
189    public void onDeviceOpenedAlready(int cameraId, String info) {
190        if (mCallbackReceiver != null) {
191            mCallbackReceiver.onDeviceOpenedAlready(cameraId, info);
192        }
193    }
194
195    @Override
196    public void onReconnectionFailure(CameraAgent mgr, String info) {
197        if (mCallbackReceiver != null) {
198            mCallbackReceiver.onReconnectionFailure(mgr, info);
199        }
200    }
201
202    @Override
203    public void requestCamera(int id) {
204        requestCamera(id, false);
205    }
206
207    @Override
208    public void requestCamera(int id, boolean useNewApi) {
209        // Based on
210        // (mRequestingCameraId == id, mRequestingCameraId == EMPTY_REQUEST),
211        // we have (T, T), (T, F), (F, T), (F, F).
212        // (T, T): implies id == EMPTY_REQUEST. We don't allow this to happen
213        //         here. Return.
214        // (F, F): A previous request hasn't been fulfilled yet. Return.
215        // (T, F): Already requested the same camera. No-op. Return.
216        // (F, T): Nothing is going on. Continue.
217        if (mRequestingCameraId != EMPTY_REQUEST || mRequestingCameraId == id) {
218            return;
219        }
220        if (mInfo == null) {
221            return;
222        }
223        mRequestingCameraId = id;
224
225        // Only actually use the new API if it's supported on this device.
226        useNewApi = mCameraAgentNg != null && useNewApi;
227        CameraAgent cameraManager = useNewApi ? mCameraAgentNg : mCameraAgent;
228
229        if (mCameraProxy == null) {
230            // No camera yet.
231            checkAndOpenCamera(mContext, cameraManager, id, mCallbackHandler, this);
232        } else if (mCameraProxy.getCameraId() != id || mUsingNewApi != useNewApi) {
233            // Already has camera opened, and is switching cameras and/or APIs.
234            if (mUsingNewApi) {
235                mCameraAgentNg.closeCamera(mCameraProxy, true);
236            } else {
237                mCameraAgent.closeCamera(mCameraProxy, true);
238            }
239            checkAndOpenCamera(mContext, cameraManager, id, mCallbackHandler, this);
240        } else {
241            // The same camera, just do a reconnect.
242            Log.v(TAG, "reconnecting to use the existing camera");
243            mCameraProxy.reconnect(mCallbackHandler, this);
244            mCameraProxy = null;
245        }
246
247        mUsingNewApi = useNewApi;
248        mInfo = cameraManager.getCameraDeviceInfo();
249    }
250
251    @Override
252    public boolean waitingForCamera() {
253        return mRequestingCameraId != EMPTY_REQUEST;
254    }
255
256    @Override
257    public void releaseCamera(int id) {
258        if (mCameraProxy == null) {
259            if (mRequestingCameraId == EMPTY_REQUEST) {
260                // Camera not requested yet.
261                Log.w(TAG, "Trying to release the camera before requesting");
262            }
263            // Camera requested but not available yet.
264            mRequestingCameraId = EMPTY_REQUEST;
265            return;
266        }
267        if (mCameraProxy.getCameraId() != id) {
268            throw new IllegalStateException("Trying to release an unopened camera.");
269        }
270        mRequestingCameraId = EMPTY_REQUEST;
271    }
272
273    public void removeCallbackReceiver() {
274        mCallbackReceiver = null;
275    }
276
277    /**
278     * Closes the opened camera device.
279     * TODO: Make this method package private.
280     */
281    public void closeCamera(boolean synced) {
282        if (mCameraProxy == null) {
283            Log.v(TAG, "No camera open, not closing");
284            return;
285        }
286        Log.v(TAG, "Closing camera");
287        mCameraProxy = null;
288        if (mUsingNewApi) {
289            mCameraAgentNg.closeCamera(mCameraProxy, synced);
290        } else {
291            mCameraAgent.closeCamera(mCameraProxy, synced);
292        }
293        mRequestingCameraId = EMPTY_REQUEST;
294        mUsingNewApi = false;
295    }
296
297    private static void checkAndOpenCamera(Context context, CameraAgent cameraManager,
298            final int cameraId, Handler handler, final CameraAgent.CameraOpenCallback cb) {
299        try {
300            CameraUtil.throwIfCameraDisabled(context);
301            cameraManager.openCamera(handler, cameraId, cb);
302        } catch (CameraDisabledException ex) {
303            handler.post(new Runnable() {
304                @Override
305                public void run() {
306                    cb.onCameraDisabled(cameraId);
307                }
308            });
309        }
310    }
311
312    public void setOneShotPreviewCallback(Handler handler,
313            CameraAgent.CameraPreviewDataCallback cb) {
314        mCameraProxy.setOneShotPreviewCallback(handler, cb);
315    }
316}
317