| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.hdrviewfinder; |
| |
| import android.hardware.camera2.CameraAccessException; |
| import android.hardware.camera2.CameraCaptureSession; |
| import android.hardware.camera2.CameraDevice; |
| import android.hardware.camera2.CameraManager; |
| import android.hardware.camera2.CaptureRequest; |
| import android.os.ConditionVariable; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.util.Log; |
| import android.view.Surface; |
| |
| import java.util.List; |
| |
| /** |
| * Simple interface for operating the camera, with major camera operations |
| * all performed on a background handler thread. |
| */ |
| public class CameraOps { |
| |
| private static final String TAG = "CameraOps"; |
| |
| public static final long CAMERA_CLOSE_TIMEOUT = 2000; // ms |
| |
| private final CameraManager mCameraManager; |
| private CameraDevice mCameraDevice; |
| private CameraCaptureSession mCameraSession; |
| private List<Surface> mSurfaces; |
| |
| private final ConditionVariable mCloseWaiter = new ConditionVariable(); |
| |
| private HandlerThread mCameraThread; |
| private Handler mCameraHandler; |
| |
| private final ErrorDisplayer mErrorDisplayer; |
| |
| private final CameraReadyListener mReadyListener; |
| private final Handler mReadyHandler; |
| |
| /** |
| * Create a new camera ops thread. |
| * |
| * @param errorDisplayer listener for displaying error messages |
| * @param readyListener listener for notifying when camera is ready for requests |
| * @param readyHandler the handler for calling readyListener methods on |
| */ |
| CameraOps(CameraManager manager, ErrorDisplayer errorDisplayer, |
| CameraReadyListener readyListener, Handler readyHandler) { |
| mCameraThread = new HandlerThread("CameraOpsThread"); |
| mCameraThread.start(); |
| |
| if (manager == null || errorDisplayer == null || |
| readyListener == null || readyHandler == null) { |
| throw new IllegalArgumentException("Need valid displayer, listener, handler"); |
| } |
| |
| mCameraManager = manager; |
| mErrorDisplayer = errorDisplayer; |
| mReadyListener = readyListener; |
| mReadyHandler = readyHandler; |
| } |
| |
| /** |
| * Open the first back-facing camera listed by the camera manager. |
| * Displays a dialog if it cannot open a camera. |
| */ |
| public void openCamera(final String cameraId) { |
| mCameraHandler = new Handler(mCameraThread.getLooper()); |
| |
| mCameraHandler.post(new Runnable() { |
| public void run() { |
| if (mCameraDevice != null) { |
| throw new IllegalStateException("Camera already open"); |
| } |
| try { |
| mCameraManager.openCamera(cameraId, mCameraDeviceListener, mCameraHandler); |
| } catch (CameraAccessException e) { |
| String errorMessage = mErrorDisplayer.getErrorString(e); |
| mErrorDisplayer.showErrorDialog(errorMessage); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Close the camera and wait for the close callback to be called in the camera thread. |
| * Times out after @{value CAMERA_CLOSE_TIMEOUT} ms. |
| */ |
| public void closeCameraAndWait() { |
| mCloseWaiter.close(); |
| mCameraHandler.post(mCloseCameraRunnable); |
| boolean closed = mCloseWaiter.block(CAMERA_CLOSE_TIMEOUT); |
| if (!closed) { |
| Log.e(TAG, "Timeout closing camera"); |
| } |
| } |
| |
| private Runnable mCloseCameraRunnable = new Runnable() { |
| public void run() { |
| if (mCameraDevice != null) { |
| mCameraDevice.close(); |
| } |
| mCameraDevice = null; |
| mCameraSession = null; |
| mSurfaces = null; |
| } |
| }; |
| |
| /** |
| * Set the output Surfaces, and finish configuration if otherwise ready. |
| */ |
| public void setSurfaces(final List<Surface> surfaces) { |
| mCameraHandler.post(new Runnable() { |
| public void run() { |
| mSurfaces = surfaces; |
| startCameraSession(); |
| } |
| }); |
| } |
| |
| /** |
| * Get a request builder for the current camera. |
| */ |
| public CaptureRequest.Builder createCaptureRequest(int template) throws CameraAccessException { |
| CameraDevice device = mCameraDevice; |
| if (device == null) { |
| throw new IllegalStateException("Can't get requests when no camera is open"); |
| } |
| return device.createCaptureRequest(template); |
| } |
| |
| /** |
| * Set a repeating request. |
| */ |
| public void setRepeatingRequest(final CaptureRequest request, |
| final CameraCaptureSession.CaptureCallback listener, |
| final Handler handler) { |
| mCameraHandler.post(new Runnable() { |
| public void run() { |
| try { |
| mCameraSession.setRepeatingRequest(request, listener, handler); |
| } catch (CameraAccessException e) { |
| String errorMessage = mErrorDisplayer.getErrorString(e); |
| mErrorDisplayer.showErrorDialog(errorMessage); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Set a repeating request. |
| */ |
| public void setRepeatingBurst(final List<CaptureRequest> requests, |
| final CameraCaptureSession.CaptureCallback listener, |
| final Handler handler) { |
| mCameraHandler.post(new Runnable() { |
| public void run() { |
| try { |
| mCameraSession.setRepeatingBurst(requests, listener, handler); |
| } catch (CameraAccessException e) { |
| String errorMessage = mErrorDisplayer.getErrorString(e); |
| mErrorDisplayer.showErrorDialog(errorMessage); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Configure the camera session. |
| */ |
| private void startCameraSession() { |
| // Wait until both the camera device is open and the SurfaceView is ready |
| if (mCameraDevice == null || mSurfaces == null) return; |
| |
| try { |
| mCameraDevice.createCaptureSession( |
| mSurfaces, mCameraSessionListener, mCameraHandler); |
| } catch (CameraAccessException e) { |
| String errorMessage = mErrorDisplayer.getErrorString(e); |
| mErrorDisplayer.showErrorDialog(errorMessage); |
| mCameraDevice.close(); |
| mCameraDevice = null; |
| } |
| } |
| |
| /** |
| * Main listener for camera session events |
| * Invoked on mCameraThread |
| */ |
| private CameraCaptureSession.StateCallback mCameraSessionListener = |
| new CameraCaptureSession.StateCallback() { |
| |
| @Override |
| public void onConfigured(CameraCaptureSession session) { |
| mCameraSession = session; |
| mReadyHandler.post(new Runnable() { |
| public void run() { |
| // This can happen when the screen is turned off and turned back on. |
| if (null == mCameraDevice) { |
| return; |
| } |
| |
| mReadyListener.onCameraReady(); |
| } |
| }); |
| |
| } |
| |
| @Override |
| public void onConfigureFailed(CameraCaptureSession session) { |
| mErrorDisplayer.showErrorDialog("Unable to configure the capture session"); |
| mCameraDevice.close(); |
| mCameraDevice = null; |
| } |
| }; |
| |
| /** |
| * Main listener for camera device events. |
| * Invoked on mCameraThread |
| */ |
| private CameraDevice.StateCallback mCameraDeviceListener = new CameraDevice.StateCallback() { |
| |
| @Override |
| public void onOpened(CameraDevice camera) { |
| mCameraDevice = camera; |
| startCameraSession(); |
| } |
| |
| @Override |
| public void onClosed(CameraDevice camera) { |
| mCloseWaiter.open(); |
| } |
| |
| @Override |
| public void onDisconnected(CameraDevice camera) { |
| mErrorDisplayer.showErrorDialog("The camera device has been disconnected."); |
| camera.close(); |
| mCameraDevice = null; |
| } |
| |
| @Override |
| public void onError(CameraDevice camera, int error) { |
| mErrorDisplayer.showErrorDialog("The camera encountered an error:" + error); |
| camera.close(); |
| mCameraDevice = null; |
| } |
| |
| }; |
| |
| /** |
| * Simple listener for main code to know the camera is ready for requests, or failed to |
| * start. |
| */ |
| public interface CameraReadyListener { |
| public void onCameraReady(); |
| } |
| |
| /** |
| * Simple listener for displaying error messages |
| */ |
| public interface ErrorDisplayer { |
| public void showErrorDialog(String errorMessage); |
| |
| public String getErrorString(CameraAccessException e); |
| } |
| |
| } |