1/*
2 * Copyright (C) 2014 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.one.v2;
18
19import android.annotation.TargetApi;
20import android.content.Context;
21import android.hardware.camera2.CameraAccessException;
22import android.hardware.camera2.CameraCharacteristics;
23import android.hardware.camera2.CameraDevice;
24import android.hardware.camera2.CameraManager;
25import android.os.Build;
26import android.os.Handler;
27import android.util.DisplayMetrics;
28
29import com.android.camera.FatalErrorHandler;
30import com.android.camera.SoundPlayer;
31import com.android.camera.async.MainThread;
32import com.android.camera.burst.BurstFacade;
33import com.android.camera.debug.Log;
34import com.android.camera.debug.Log.Tag;
35import com.android.camera.device.ActiveCameraDeviceTracker;
36import com.android.camera.device.CameraId;
37import com.android.camera.one.OneCamera;
38import com.android.camera.one.OneCamera.OpenCallback;
39import com.android.camera.one.OneCameraAccessException;
40import com.android.camera.one.OneCameraCaptureSetting;
41import com.android.camera.one.OneCameraOpener;
42import com.android.camera.one.config.OneCameraFeatureConfig;
43import com.android.camera.one.v2.photo.ImageRotationCalculator;
44import com.android.camera.util.AndroidServices;
45import com.android.camera.util.ApiHelper;
46import com.google.common.base.Optional;
47
48/**
49 * The {@link com.android.camera.one.OneCameraOpener} implementation on top of Camera2 API.
50 */
51@TargetApi(Build.VERSION_CODES.LOLLIPOP)
52public class Camera2OneCameraOpenerImpl implements OneCameraOpener {
53    private static final Tag TAG = new Tag("OneCamera1Opnr");
54
55    private final Context mContext;
56    private final OneCameraFeatureConfig mFeatureConfig;
57    private final ActiveCameraDeviceTracker mActiveCameraDeviceTracker;
58    private final CameraManager mCameraManager;
59    private final DisplayMetrics mDisplayMetrics;
60
61    public static Optional<OneCameraOpener> create(
62            OneCameraFeatureConfig featureConfig,
63            Context context,
64            ActiveCameraDeviceTracker activeCameraDeviceTracker,
65            DisplayMetrics displayMetrics) {
66        if (!ApiHelper.HAS_CAMERA_2_API) {
67            return Optional.absent();
68        }
69        CameraManager cameraManager;
70        try {
71            cameraManager = AndroidServices.instance().provideCameraManager();
72        } catch (IllegalStateException ex) {
73            Log.e(TAG, "camera2.CameraManager is not available.");
74            return Optional.absent();
75        }
76        OneCameraOpener oneCameraOpener = new Camera2OneCameraOpenerImpl(
77                featureConfig,
78                context,
79                cameraManager,
80                activeCameraDeviceTracker,
81                displayMetrics);
82        return Optional.of(oneCameraOpener);
83    }
84
85    /**
86     * Instantiates a new {@link com.android.camera.one.OneCameraOpener} for Camera2 API.
87     *
88     * @param cameraManager the underlying Camera2 camera manager.
89     */
90    public Camera2OneCameraOpenerImpl(OneCameraFeatureConfig featureConfig,
91            Context context,
92            CameraManager cameraManager,
93            ActiveCameraDeviceTracker activeCameraDeviceTracker,
94            DisplayMetrics displayMetrics) {
95        mFeatureConfig = featureConfig;
96        mContext = context;
97        mCameraManager = cameraManager;
98        mActiveCameraDeviceTracker = activeCameraDeviceTracker;
99        mDisplayMetrics = displayMetrics;
100    }
101
102    @Override
103    public void open(
104            final CameraId cameraKey,
105            final OneCameraCaptureSetting captureSetting,
106            final Handler handler,
107            final MainThread mainThread,
108            final ImageRotationCalculator imageRotationCalculator,
109            final BurstFacade burstController,
110            final SoundPlayer soundPlayer,
111            final OpenCallback openCallback,
112            final FatalErrorHandler fatalErrorHandler) {
113        try {
114            Log.i(TAG, "Opening Camera: " + cameraKey);
115
116            mActiveCameraDeviceTracker.onCameraOpening(cameraKey);
117
118            mCameraManager.openCamera(cameraKey.getValue(), new CameraDevice.StateCallback() {
119                // We may get multiple calls to StateCallback, but only the
120                // first callback indicates the status of the camera-opening
121                // operation. For example, we may receive onOpened() and later
122                // onClosed(), but only the first should be relayed to
123                // openCallback.
124                private boolean isFirstCallback = true;
125
126                @Override
127                public void onDisconnected(CameraDevice device) {
128                    if (isFirstCallback) {
129                        isFirstCallback = false;
130                        // If the camera is disconnected before it is opened
131                        // then we must call close.
132                        device.close();
133                        openCallback.onCameraClosed();
134                    }
135                }
136
137                @Override
138                public void onClosed(CameraDevice device) {
139                    if (isFirstCallback) {
140                        isFirstCallback = false;
141                        openCallback.onCameraClosed();
142                    }
143                }
144
145                @Override
146                public void onError(CameraDevice device, int error) {
147                    if (isFirstCallback) {
148                        isFirstCallback = false;
149                        device.close();
150                        openCallback.onFailure();
151                    } else {
152                        // Ensures we handle the case where an error occurs
153                        // after the camera has been opened.
154                        fatalErrorHandler.onGenericCameraAccessFailure();
155                    }
156                }
157
158                @Override
159                public void onOpened(CameraDevice device) {
160                    if (isFirstCallback) {
161                        isFirstCallback = false;
162                        try {
163                            CameraCharacteristics characteristics = mCameraManager
164                                    .getCameraCharacteristics(device.getId());
165                            // TODO: Set boolean based on whether HDR+ is
166                            // enabled.
167                            OneCamera oneCamera = OneCameraCreator.create(
168                                    device,
169                                    characteristics,
170                                    mFeatureConfig,
171                                    captureSetting,
172                                    mDisplayMetrics,
173                                    mContext,
174                                    mainThread,
175                                    imageRotationCalculator,
176                                    burstController,
177                                    soundPlayer, fatalErrorHandler);
178
179                            if (oneCamera != null) {
180                                openCallback.onCameraOpened(oneCamera);
181                            } else {
182                                Log.d(TAG, "Could not construct a OneCamera object!");
183                                openCallback.onFailure();
184                            }
185                        } catch (CameraAccessException e) {
186                            Log.d(TAG, "Could not get camera characteristics", e);
187                            openCallback.onFailure();
188                        } catch (OneCameraAccessException e) {
189                            Log.d(TAG, "Could not create OneCamera", e);
190                            openCallback.onFailure();
191                        }
192                    }
193                }
194            }, handler);
195        } catch (CameraAccessException ex) {
196            Log.e(TAG, "Could not open camera. " + ex.getMessage());
197            handler.post(new Runnable() {
198                @Override
199                public void run() {
200                    openCallback.onFailure();
201                }
202            });
203        } catch (UnsupportedOperationException ex) {
204            Log.e(TAG, "Could not open camera. " + ex.getMessage());
205            handler.post(new Runnable() {
206                @Override
207                public void run() {
208                    openCallback.onFailure();
209                }
210            });
211        } catch (SecurityException ex) {
212            fatalErrorHandler.onCameraDisabledFailure();
213        }
214    }
215}
216