1*90c8c64dSAndroid Build Coastguard Worker /* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright 2015 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License. 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Worker package com.example.android.camera2raw; 18*90c8c64dSAndroid Build Coastguard Worker 19*90c8c64dSAndroid Build Coastguard Worker import android.Manifest; 20*90c8c64dSAndroid Build Coastguard Worker import android.app.Activity; 21*90c8c64dSAndroid Build Coastguard Worker import android.app.AlertDialog; 22*90c8c64dSAndroid Build Coastguard Worker import android.app.Dialog; 23*90c8c64dSAndroid Build Coastguard Worker import android.app.DialogFragment; 24*90c8c64dSAndroid Build Coastguard Worker import android.app.Fragment; 25*90c8c64dSAndroid Build Coastguard Worker import android.content.Context; 26*90c8c64dSAndroid Build Coastguard Worker import android.content.DialogInterface; 27*90c8c64dSAndroid Build Coastguard Worker import android.content.pm.PackageManager; 28*90c8c64dSAndroid Build Coastguard Worker import android.graphics.ImageFormat; 29*90c8c64dSAndroid Build Coastguard Worker import android.graphics.Matrix; 30*90c8c64dSAndroid Build Coastguard Worker import android.graphics.Point; 31*90c8c64dSAndroid Build Coastguard Worker import android.graphics.RectF; 32*90c8c64dSAndroid Build Coastguard Worker import android.graphics.SurfaceTexture; 33*90c8c64dSAndroid Build Coastguard Worker import android.hardware.SensorManager; 34*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.CameraAccessException; 35*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.CameraCaptureSession; 36*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.CameraCharacteristics; 37*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.CameraDevice; 38*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.CameraManager; 39*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.CameraMetadata; 40*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.CaptureFailure; 41*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.CaptureRequest; 42*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.CaptureResult; 43*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.DngCreator; 44*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.TotalCaptureResult; 45*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.params.StreamConfigurationMap; 46*90c8c64dSAndroid Build Coastguard Worker import android.media.Image; 47*90c8c64dSAndroid Build Coastguard Worker import android.media.ImageReader; 48*90c8c64dSAndroid Build Coastguard Worker import android.media.MediaScannerConnection; 49*90c8c64dSAndroid Build Coastguard Worker import android.net.Uri; 50*90c8c64dSAndroid Build Coastguard Worker import android.os.AsyncTask; 51*90c8c64dSAndroid Build Coastguard Worker import android.os.Bundle; 52*90c8c64dSAndroid Build Coastguard Worker import android.os.Environment; 53*90c8c64dSAndroid Build Coastguard Worker import android.os.Handler; 54*90c8c64dSAndroid Build Coastguard Worker import android.os.HandlerThread; 55*90c8c64dSAndroid Build Coastguard Worker import android.os.Looper; 56*90c8c64dSAndroid Build Coastguard Worker import android.os.Message; 57*90c8c64dSAndroid Build Coastguard Worker import android.os.SystemClock; 58*90c8c64dSAndroid Build Coastguard Worker import android.support.v13.app.FragmentCompat; 59*90c8c64dSAndroid Build Coastguard Worker import android.support.v4.app.ActivityCompat; 60*90c8c64dSAndroid Build Coastguard Worker import android.util.Log; 61*90c8c64dSAndroid Build Coastguard Worker import android.util.Size; 62*90c8c64dSAndroid Build Coastguard Worker import android.util.SparseIntArray; 63*90c8c64dSAndroid Build Coastguard Worker import android.view.LayoutInflater; 64*90c8c64dSAndroid Build Coastguard Worker import android.view.OrientationEventListener; 65*90c8c64dSAndroid Build Coastguard Worker import android.view.Surface; 66*90c8c64dSAndroid Build Coastguard Worker import android.view.TextureView; 67*90c8c64dSAndroid Build Coastguard Worker import android.view.View; 68*90c8c64dSAndroid Build Coastguard Worker import android.view.ViewGroup; 69*90c8c64dSAndroid Build Coastguard Worker import android.widget.Toast; 70*90c8c64dSAndroid Build Coastguard Worker 71*90c8c64dSAndroid Build Coastguard Worker import java.io.File; 72*90c8c64dSAndroid Build Coastguard Worker import java.io.FileOutputStream; 73*90c8c64dSAndroid Build Coastguard Worker import java.io.IOException; 74*90c8c64dSAndroid Build Coastguard Worker import java.io.OutputStream; 75*90c8c64dSAndroid Build Coastguard Worker import java.nio.ByteBuffer; 76*90c8c64dSAndroid Build Coastguard Worker import java.text.SimpleDateFormat; 77*90c8c64dSAndroid Build Coastguard Worker import java.util.ArrayList; 78*90c8c64dSAndroid Build Coastguard Worker import java.util.Arrays; 79*90c8c64dSAndroid Build Coastguard Worker import java.util.Collections; 80*90c8c64dSAndroid Build Coastguard Worker import java.util.Comparator; 81*90c8c64dSAndroid Build Coastguard Worker import java.util.Date; 82*90c8c64dSAndroid Build Coastguard Worker import java.util.List; 83*90c8c64dSAndroid Build Coastguard Worker import java.util.Locale; 84*90c8c64dSAndroid Build Coastguard Worker import java.util.Map; 85*90c8c64dSAndroid Build Coastguard Worker import java.util.TreeMap; 86*90c8c64dSAndroid Build Coastguard Worker import java.util.concurrent.Semaphore; 87*90c8c64dSAndroid Build Coastguard Worker import java.util.concurrent.TimeUnit; 88*90c8c64dSAndroid Build Coastguard Worker import java.util.concurrent.atomic.AtomicInteger; 89*90c8c64dSAndroid Build Coastguard Worker 90*90c8c64dSAndroid Build Coastguard Worker /** 91*90c8c64dSAndroid Build Coastguard Worker * A fragment that demonstrates use of the Camera2 API to capture RAW and JPEG photos. 92*90c8c64dSAndroid Build Coastguard Worker * <p/> 93*90c8c64dSAndroid Build Coastguard Worker * In this example, the lifecycle of a single request to take a photo is: 94*90c8c64dSAndroid Build Coastguard Worker * <ul> 95*90c8c64dSAndroid Build Coastguard Worker * <li> 96*90c8c64dSAndroid Build Coastguard Worker * The user presses the "Picture" button, resulting in a call to {@link #takePicture()}. 97*90c8c64dSAndroid Build Coastguard Worker * </li> 98*90c8c64dSAndroid Build Coastguard Worker * <li> 99*90c8c64dSAndroid Build Coastguard Worker * {@link #takePicture()} initiates a pre-capture sequence that triggers the camera's built-in 100*90c8c64dSAndroid Build Coastguard Worker * auto-focus, auto-exposure, and auto-white-balance algorithms (aka. "3A") to run. 101*90c8c64dSAndroid Build Coastguard Worker * </li> 102*90c8c64dSAndroid Build Coastguard Worker * <li> 103*90c8c64dSAndroid Build Coastguard Worker * When the pre-capture sequence has finished, a {@link CaptureRequest} with a monotonically 104*90c8c64dSAndroid Build Coastguard Worker * increasing request ID set by calls to {@link CaptureRequest.Builder#setTag(Object)} is sent to 105*90c8c64dSAndroid Build Coastguard Worker * the camera to begin the JPEG and RAW capture sequence, and an 106*90c8c64dSAndroid Build Coastguard Worker * {@link ImageSaver.ImageSaverBuilder} is stored for this request in the 107*90c8c64dSAndroid Build Coastguard Worker * {@link #mJpegResultQueue} and {@link #mRawResultQueue}. 108*90c8c64dSAndroid Build Coastguard Worker * </li> 109*90c8c64dSAndroid Build Coastguard Worker * <li> 110*90c8c64dSAndroid Build Coastguard Worker * As {@link CaptureResult}s and {@link Image}s become available via callbacks in a background 111*90c8c64dSAndroid Build Coastguard Worker * thread, a {@link ImageSaver.ImageSaverBuilder} is looked up by the request ID in 112*90c8c64dSAndroid Build Coastguard Worker * {@link #mJpegResultQueue} and {@link #mRawResultQueue} and updated. 113*90c8c64dSAndroid Build Coastguard Worker * </li> 114*90c8c64dSAndroid Build Coastguard Worker * <li> 115*90c8c64dSAndroid Build Coastguard Worker * When all of the necessary results to save an image are available, the an {@link ImageSaver} is 116*90c8c64dSAndroid Build Coastguard Worker * constructed by the {@link ImageSaver.ImageSaverBuilder} and passed to a separate background 117*90c8c64dSAndroid Build Coastguard Worker * thread to save to a file. 118*90c8c64dSAndroid Build Coastguard Worker * </li> 119*90c8c64dSAndroid Build Coastguard Worker * </ul> 120*90c8c64dSAndroid Build Coastguard Worker */ 121*90c8c64dSAndroid Build Coastguard Worker public class Camera2RawFragment extends Fragment 122*90c8c64dSAndroid Build Coastguard Worker implements View.OnClickListener, FragmentCompat.OnRequestPermissionsResultCallback { 123*90c8c64dSAndroid Build Coastguard Worker 124*90c8c64dSAndroid Build Coastguard Worker /** 125*90c8c64dSAndroid Build Coastguard Worker * Conversion from screen rotation to JPEG orientation. 126*90c8c64dSAndroid Build Coastguard Worker */ 127*90c8c64dSAndroid Build Coastguard Worker private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); 128*90c8c64dSAndroid Build Coastguard Worker 129*90c8c64dSAndroid Build Coastguard Worker static { ORIENTATIONS.append(Surface.ROTATION_0, 0)130*90c8c64dSAndroid Build Coastguard Worker ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90)131*90c8c64dSAndroid Build Coastguard Worker ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180)132*90c8c64dSAndroid Build Coastguard Worker ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270)133*90c8c64dSAndroid Build Coastguard Worker ORIENTATIONS.append(Surface.ROTATION_270, 270); 134*90c8c64dSAndroid Build Coastguard Worker } 135*90c8c64dSAndroid Build Coastguard Worker 136*90c8c64dSAndroid Build Coastguard Worker /** 137*90c8c64dSAndroid Build Coastguard Worker * Request code for camera permissions. 138*90c8c64dSAndroid Build Coastguard Worker */ 139*90c8c64dSAndroid Build Coastguard Worker private static final int REQUEST_CAMERA_PERMISSIONS = 1; 140*90c8c64dSAndroid Build Coastguard Worker 141*90c8c64dSAndroid Build Coastguard Worker /** 142*90c8c64dSAndroid Build Coastguard Worker * Permissions required to take a picture. 143*90c8c64dSAndroid Build Coastguard Worker */ 144*90c8c64dSAndroid Build Coastguard Worker private static final String[] CAMERA_PERMISSIONS = { 145*90c8c64dSAndroid Build Coastguard Worker Manifest.permission.CAMERA, 146*90c8c64dSAndroid Build Coastguard Worker Manifest.permission.READ_EXTERNAL_STORAGE, 147*90c8c64dSAndroid Build Coastguard Worker Manifest.permission.WRITE_EXTERNAL_STORAGE, 148*90c8c64dSAndroid Build Coastguard Worker }; 149*90c8c64dSAndroid Build Coastguard Worker 150*90c8c64dSAndroid Build Coastguard Worker /** 151*90c8c64dSAndroid Build Coastguard Worker * Timeout for the pre-capture sequence. 152*90c8c64dSAndroid Build Coastguard Worker */ 153*90c8c64dSAndroid Build Coastguard Worker private static final long PRECAPTURE_TIMEOUT_MS = 1000; 154*90c8c64dSAndroid Build Coastguard Worker 155*90c8c64dSAndroid Build Coastguard Worker /** 156*90c8c64dSAndroid Build Coastguard Worker * Tolerance when comparing aspect ratios. 157*90c8c64dSAndroid Build Coastguard Worker */ 158*90c8c64dSAndroid Build Coastguard Worker private static final double ASPECT_RATIO_TOLERANCE = 0.005; 159*90c8c64dSAndroid Build Coastguard Worker 160*90c8c64dSAndroid Build Coastguard Worker /** 161*90c8c64dSAndroid Build Coastguard Worker * Max preview width that is guaranteed by Camera2 API 162*90c8c64dSAndroid Build Coastguard Worker */ 163*90c8c64dSAndroid Build Coastguard Worker private static final int MAX_PREVIEW_WIDTH = 1920; 164*90c8c64dSAndroid Build Coastguard Worker 165*90c8c64dSAndroid Build Coastguard Worker /** 166*90c8c64dSAndroid Build Coastguard Worker * Max preview height that is guaranteed by Camera2 API 167*90c8c64dSAndroid Build Coastguard Worker */ 168*90c8c64dSAndroid Build Coastguard Worker private static final int MAX_PREVIEW_HEIGHT = 1080; 169*90c8c64dSAndroid Build Coastguard Worker 170*90c8c64dSAndroid Build Coastguard Worker /** 171*90c8c64dSAndroid Build Coastguard Worker * Tag for the {@link Log}. 172*90c8c64dSAndroid Build Coastguard Worker */ 173*90c8c64dSAndroid Build Coastguard Worker private static final String TAG = "Camera2RawFragment"; 174*90c8c64dSAndroid Build Coastguard Worker 175*90c8c64dSAndroid Build Coastguard Worker /** 176*90c8c64dSAndroid Build Coastguard Worker * Camera state: Device is closed. 177*90c8c64dSAndroid Build Coastguard Worker */ 178*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_CLOSED = 0; 179*90c8c64dSAndroid Build Coastguard Worker 180*90c8c64dSAndroid Build Coastguard Worker /** 181*90c8c64dSAndroid Build Coastguard Worker * Camera state: Device is opened, but is not capturing. 182*90c8c64dSAndroid Build Coastguard Worker */ 183*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_OPENED = 1; 184*90c8c64dSAndroid Build Coastguard Worker 185*90c8c64dSAndroid Build Coastguard Worker /** 186*90c8c64dSAndroid Build Coastguard Worker * Camera state: Showing camera preview. 187*90c8c64dSAndroid Build Coastguard Worker */ 188*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_PREVIEW = 2; 189*90c8c64dSAndroid Build Coastguard Worker 190*90c8c64dSAndroid Build Coastguard Worker /** 191*90c8c64dSAndroid Build Coastguard Worker * Camera state: Waiting for 3A convergence before capturing a photo. 192*90c8c64dSAndroid Build Coastguard Worker */ 193*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_WAITING_FOR_3A_CONVERGENCE = 3; 194*90c8c64dSAndroid Build Coastguard Worker 195*90c8c64dSAndroid Build Coastguard Worker /** 196*90c8c64dSAndroid Build Coastguard Worker * An {@link OrientationEventListener} used to determine when device rotation has occurred. 197*90c8c64dSAndroid Build Coastguard Worker * This is mainly necessary for when the device is rotated by 180 degrees, in which case 198*90c8c64dSAndroid Build Coastguard Worker * onCreate or onConfigurationChanged is not called as the view dimensions remain the same, 199*90c8c64dSAndroid Build Coastguard Worker * but the orientation of the has changed, and thus the preview rotation must be updated. 200*90c8c64dSAndroid Build Coastguard Worker */ 201*90c8c64dSAndroid Build Coastguard Worker private OrientationEventListener mOrientationListener; 202*90c8c64dSAndroid Build Coastguard Worker 203*90c8c64dSAndroid Build Coastguard Worker /** 204*90c8c64dSAndroid Build Coastguard Worker * {@link TextureView.SurfaceTextureListener} handles several lifecycle events of a 205*90c8c64dSAndroid Build Coastguard Worker * {@link TextureView}. 206*90c8c64dSAndroid Build Coastguard Worker */ 207*90c8c64dSAndroid Build Coastguard Worker private final TextureView.SurfaceTextureListener mSurfaceTextureListener 208*90c8c64dSAndroid Build Coastguard Worker = new TextureView.SurfaceTextureListener() { 209*90c8c64dSAndroid Build Coastguard Worker 210*90c8c64dSAndroid Build Coastguard Worker @Override 211*90c8c64dSAndroid Build Coastguard Worker public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { 212*90c8c64dSAndroid Build Coastguard Worker configureTransform(width, height); 213*90c8c64dSAndroid Build Coastguard Worker } 214*90c8c64dSAndroid Build Coastguard Worker 215*90c8c64dSAndroid Build Coastguard Worker @Override 216*90c8c64dSAndroid Build Coastguard Worker public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { 217*90c8c64dSAndroid Build Coastguard Worker configureTransform(width, height); 218*90c8c64dSAndroid Build Coastguard Worker } 219*90c8c64dSAndroid Build Coastguard Worker 220*90c8c64dSAndroid Build Coastguard Worker @Override 221*90c8c64dSAndroid Build Coastguard Worker public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { 222*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 223*90c8c64dSAndroid Build Coastguard Worker mPreviewSize = null; 224*90c8c64dSAndroid Build Coastguard Worker } 225*90c8c64dSAndroid Build Coastguard Worker return true; 226*90c8c64dSAndroid Build Coastguard Worker } 227*90c8c64dSAndroid Build Coastguard Worker 228*90c8c64dSAndroid Build Coastguard Worker @Override 229*90c8c64dSAndroid Build Coastguard Worker public void onSurfaceTextureUpdated(SurfaceTexture texture) { 230*90c8c64dSAndroid Build Coastguard Worker } 231*90c8c64dSAndroid Build Coastguard Worker 232*90c8c64dSAndroid Build Coastguard Worker }; 233*90c8c64dSAndroid Build Coastguard Worker 234*90c8c64dSAndroid Build Coastguard Worker /** 235*90c8c64dSAndroid Build Coastguard Worker * An {@link AutoFitTextureView} for camera preview. 236*90c8c64dSAndroid Build Coastguard Worker */ 237*90c8c64dSAndroid Build Coastguard Worker private AutoFitTextureView mTextureView; 238*90c8c64dSAndroid Build Coastguard Worker 239*90c8c64dSAndroid Build Coastguard Worker /** 240*90c8c64dSAndroid Build Coastguard Worker * An additional thread for running tasks that shouldn't block the UI. This is used for all 241*90c8c64dSAndroid Build Coastguard Worker * callbacks from the {@link CameraDevice} and {@link CameraCaptureSession}s. 242*90c8c64dSAndroid Build Coastguard Worker */ 243*90c8c64dSAndroid Build Coastguard Worker private HandlerThread mBackgroundThread; 244*90c8c64dSAndroid Build Coastguard Worker 245*90c8c64dSAndroid Build Coastguard Worker /** 246*90c8c64dSAndroid Build Coastguard Worker * A counter for tracking corresponding {@link CaptureRequest}s and {@link CaptureResult}s 247*90c8c64dSAndroid Build Coastguard Worker * across the {@link CameraCaptureSession} capture callbacks. 248*90c8c64dSAndroid Build Coastguard Worker */ 249*90c8c64dSAndroid Build Coastguard Worker private final AtomicInteger mRequestCounter = new AtomicInteger(); 250*90c8c64dSAndroid Build Coastguard Worker 251*90c8c64dSAndroid Build Coastguard Worker /** 252*90c8c64dSAndroid Build Coastguard Worker * A {@link Semaphore} to prevent the app from exiting before closing the camera. 253*90c8c64dSAndroid Build Coastguard Worker */ 254*90c8c64dSAndroid Build Coastguard Worker private final Semaphore mCameraOpenCloseLock = new Semaphore(1); 255*90c8c64dSAndroid Build Coastguard Worker 256*90c8c64dSAndroid Build Coastguard Worker /** 257*90c8c64dSAndroid Build Coastguard Worker * A lock protecting camera state. 258*90c8c64dSAndroid Build Coastguard Worker */ 259*90c8c64dSAndroid Build Coastguard Worker private final Object mCameraStateLock = new Object(); 260*90c8c64dSAndroid Build Coastguard Worker 261*90c8c64dSAndroid Build Coastguard Worker // ********************************************************************************************* 262*90c8c64dSAndroid Build Coastguard Worker // State protected by mCameraStateLock. 263*90c8c64dSAndroid Build Coastguard Worker // 264*90c8c64dSAndroid Build Coastguard Worker // The following state is used across both the UI and background threads. Methods with "Locked" 265*90c8c64dSAndroid Build Coastguard Worker // in the name expect mCameraStateLock to be held while calling. 266*90c8c64dSAndroid Build Coastguard Worker 267*90c8c64dSAndroid Build Coastguard Worker /** 268*90c8c64dSAndroid Build Coastguard Worker * ID of the current {@link CameraDevice}. 269*90c8c64dSAndroid Build Coastguard Worker */ 270*90c8c64dSAndroid Build Coastguard Worker private String mCameraId; 271*90c8c64dSAndroid Build Coastguard Worker 272*90c8c64dSAndroid Build Coastguard Worker /** 273*90c8c64dSAndroid Build Coastguard Worker * A {@link CameraCaptureSession } for camera preview. 274*90c8c64dSAndroid Build Coastguard Worker */ 275*90c8c64dSAndroid Build Coastguard Worker private CameraCaptureSession mCaptureSession; 276*90c8c64dSAndroid Build Coastguard Worker 277*90c8c64dSAndroid Build Coastguard Worker /** 278*90c8c64dSAndroid Build Coastguard Worker * A reference to the open {@link CameraDevice}. 279*90c8c64dSAndroid Build Coastguard Worker */ 280*90c8c64dSAndroid Build Coastguard Worker private CameraDevice mCameraDevice; 281*90c8c64dSAndroid Build Coastguard Worker 282*90c8c64dSAndroid Build Coastguard Worker /** 283*90c8c64dSAndroid Build Coastguard Worker * The {@link Size} of camera preview. 284*90c8c64dSAndroid Build Coastguard Worker */ 285*90c8c64dSAndroid Build Coastguard Worker private Size mPreviewSize; 286*90c8c64dSAndroid Build Coastguard Worker 287*90c8c64dSAndroid Build Coastguard Worker /** 288*90c8c64dSAndroid Build Coastguard Worker * The {@link CameraCharacteristics} for the currently configured camera device. 289*90c8c64dSAndroid Build Coastguard Worker */ 290*90c8c64dSAndroid Build Coastguard Worker private CameraCharacteristics mCharacteristics; 291*90c8c64dSAndroid Build Coastguard Worker 292*90c8c64dSAndroid Build Coastguard Worker /** 293*90c8c64dSAndroid Build Coastguard Worker * A {@link Handler} for running tasks in the background. 294*90c8c64dSAndroid Build Coastguard Worker */ 295*90c8c64dSAndroid Build Coastguard Worker private Handler mBackgroundHandler; 296*90c8c64dSAndroid Build Coastguard Worker 297*90c8c64dSAndroid Build Coastguard Worker /** 298*90c8c64dSAndroid Build Coastguard Worker * A reference counted holder wrapping the {@link ImageReader} that handles JPEG image 299*90c8c64dSAndroid Build Coastguard Worker * captures. This is used to allow us to clean up the {@link ImageReader} when all background 300*90c8c64dSAndroid Build Coastguard Worker * tasks using its {@link Image}s have completed. 301*90c8c64dSAndroid Build Coastguard Worker */ 302*90c8c64dSAndroid Build Coastguard Worker private RefCountedAutoCloseable<ImageReader> mJpegImageReader; 303*90c8c64dSAndroid Build Coastguard Worker 304*90c8c64dSAndroid Build Coastguard Worker /** 305*90c8c64dSAndroid Build Coastguard Worker * A reference counted holder wrapping the {@link ImageReader} that handles RAW image captures. 306*90c8c64dSAndroid Build Coastguard Worker * This is used to allow us to clean up the {@link ImageReader} when all background tasks using 307*90c8c64dSAndroid Build Coastguard Worker * its {@link Image}s have completed. 308*90c8c64dSAndroid Build Coastguard Worker */ 309*90c8c64dSAndroid Build Coastguard Worker private RefCountedAutoCloseable<ImageReader> mRawImageReader; 310*90c8c64dSAndroid Build Coastguard Worker 311*90c8c64dSAndroid Build Coastguard Worker /** 312*90c8c64dSAndroid Build Coastguard Worker * Whether or not the currently configured camera device is fixed-focus. 313*90c8c64dSAndroid Build Coastguard Worker */ 314*90c8c64dSAndroid Build Coastguard Worker private boolean mNoAFRun = false; 315*90c8c64dSAndroid Build Coastguard Worker 316*90c8c64dSAndroid Build Coastguard Worker /** 317*90c8c64dSAndroid Build Coastguard Worker * Number of pending user requests to capture a photo. 318*90c8c64dSAndroid Build Coastguard Worker */ 319*90c8c64dSAndroid Build Coastguard Worker private int mPendingUserCaptures = 0; 320*90c8c64dSAndroid Build Coastguard Worker 321*90c8c64dSAndroid Build Coastguard Worker /** 322*90c8c64dSAndroid Build Coastguard Worker * Request ID to {@link ImageSaver.ImageSaverBuilder} mapping for in-progress JPEG captures. 323*90c8c64dSAndroid Build Coastguard Worker */ 324*90c8c64dSAndroid Build Coastguard Worker private final TreeMap<Integer, ImageSaver.ImageSaverBuilder> mJpegResultQueue = new TreeMap<>(); 325*90c8c64dSAndroid Build Coastguard Worker 326*90c8c64dSAndroid Build Coastguard Worker /** 327*90c8c64dSAndroid Build Coastguard Worker * Request ID to {@link ImageSaver.ImageSaverBuilder} mapping for in-progress RAW captures. 328*90c8c64dSAndroid Build Coastguard Worker */ 329*90c8c64dSAndroid Build Coastguard Worker private final TreeMap<Integer, ImageSaver.ImageSaverBuilder> mRawResultQueue = new TreeMap<>(); 330*90c8c64dSAndroid Build Coastguard Worker 331*90c8c64dSAndroid Build Coastguard Worker /** 332*90c8c64dSAndroid Build Coastguard Worker * {@link CaptureRequest.Builder} for the camera preview 333*90c8c64dSAndroid Build Coastguard Worker */ 334*90c8c64dSAndroid Build Coastguard Worker private CaptureRequest.Builder mPreviewRequestBuilder; 335*90c8c64dSAndroid Build Coastguard Worker 336*90c8c64dSAndroid Build Coastguard Worker /** 337*90c8c64dSAndroid Build Coastguard Worker * The state of the camera device. 338*90c8c64dSAndroid Build Coastguard Worker * 339*90c8c64dSAndroid Build Coastguard Worker * @see #mPreCaptureCallback 340*90c8c64dSAndroid Build Coastguard Worker */ 341*90c8c64dSAndroid Build Coastguard Worker private int mState = STATE_CLOSED; 342*90c8c64dSAndroid Build Coastguard Worker 343*90c8c64dSAndroid Build Coastguard Worker /** 344*90c8c64dSAndroid Build Coastguard Worker * Timer to use with pre-capture sequence to ensure a timely capture if 3A convergence is 345*90c8c64dSAndroid Build Coastguard Worker * taking too long. 346*90c8c64dSAndroid Build Coastguard Worker */ 347*90c8c64dSAndroid Build Coastguard Worker private long mCaptureTimer; 348*90c8c64dSAndroid Build Coastguard Worker 349*90c8c64dSAndroid Build Coastguard Worker //********************************************************************************************** 350*90c8c64dSAndroid Build Coastguard Worker 351*90c8c64dSAndroid Build Coastguard Worker /** 352*90c8c64dSAndroid Build Coastguard Worker * {@link CameraDevice.StateCallback} is called when the currently active {@link CameraDevice} 353*90c8c64dSAndroid Build Coastguard Worker * changes its state. 354*90c8c64dSAndroid Build Coastguard Worker */ 355*90c8c64dSAndroid Build Coastguard Worker private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { 356*90c8c64dSAndroid Build Coastguard Worker 357*90c8c64dSAndroid Build Coastguard Worker @Override 358*90c8c64dSAndroid Build Coastguard Worker public void onOpened(CameraDevice cameraDevice) { 359*90c8c64dSAndroid Build Coastguard Worker // This method is called when the camera is opened. We start camera preview here if 360*90c8c64dSAndroid Build Coastguard Worker // the TextureView displaying this has been set up. 361*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 362*90c8c64dSAndroid Build Coastguard Worker mState = STATE_OPENED; 363*90c8c64dSAndroid Build Coastguard Worker mCameraOpenCloseLock.release(); 364*90c8c64dSAndroid Build Coastguard Worker mCameraDevice = cameraDevice; 365*90c8c64dSAndroid Build Coastguard Worker 366*90c8c64dSAndroid Build Coastguard Worker // Start the preview session if the TextureView has been set up already. 367*90c8c64dSAndroid Build Coastguard Worker if (mPreviewSize != null && mTextureView.isAvailable()) { 368*90c8c64dSAndroid Build Coastguard Worker createCameraPreviewSessionLocked(); 369*90c8c64dSAndroid Build Coastguard Worker } 370*90c8c64dSAndroid Build Coastguard Worker } 371*90c8c64dSAndroid Build Coastguard Worker } 372*90c8c64dSAndroid Build Coastguard Worker 373*90c8c64dSAndroid Build Coastguard Worker @Override 374*90c8c64dSAndroid Build Coastguard Worker public void onDisconnected(CameraDevice cameraDevice) { 375*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 376*90c8c64dSAndroid Build Coastguard Worker mState = STATE_CLOSED; 377*90c8c64dSAndroid Build Coastguard Worker mCameraOpenCloseLock.release(); 378*90c8c64dSAndroid Build Coastguard Worker cameraDevice.close(); 379*90c8c64dSAndroid Build Coastguard Worker mCameraDevice = null; 380*90c8c64dSAndroid Build Coastguard Worker } 381*90c8c64dSAndroid Build Coastguard Worker } 382*90c8c64dSAndroid Build Coastguard Worker 383*90c8c64dSAndroid Build Coastguard Worker @Override 384*90c8c64dSAndroid Build Coastguard Worker public void onError(CameraDevice cameraDevice, int error) { 385*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Received camera device error: " + error); 386*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 387*90c8c64dSAndroid Build Coastguard Worker mState = STATE_CLOSED; 388*90c8c64dSAndroid Build Coastguard Worker mCameraOpenCloseLock.release(); 389*90c8c64dSAndroid Build Coastguard Worker cameraDevice.close(); 390*90c8c64dSAndroid Build Coastguard Worker mCameraDevice = null; 391*90c8c64dSAndroid Build Coastguard Worker } 392*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 393*90c8c64dSAndroid Build Coastguard Worker if (null != activity) { 394*90c8c64dSAndroid Build Coastguard Worker activity.finish(); 395*90c8c64dSAndroid Build Coastguard Worker } 396*90c8c64dSAndroid Build Coastguard Worker } 397*90c8c64dSAndroid Build Coastguard Worker 398*90c8c64dSAndroid Build Coastguard Worker }; 399*90c8c64dSAndroid Build Coastguard Worker 400*90c8c64dSAndroid Build Coastguard Worker /** 401*90c8c64dSAndroid Build Coastguard Worker * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a 402*90c8c64dSAndroid Build Coastguard Worker * JPEG image is ready to be saved. 403*90c8c64dSAndroid Build Coastguard Worker */ 404*90c8c64dSAndroid Build Coastguard Worker private final ImageReader.OnImageAvailableListener mOnJpegImageAvailableListener 405*90c8c64dSAndroid Build Coastguard Worker = new ImageReader.OnImageAvailableListener() { 406*90c8c64dSAndroid Build Coastguard Worker 407*90c8c64dSAndroid Build Coastguard Worker @Override 408*90c8c64dSAndroid Build Coastguard Worker public void onImageAvailable(ImageReader reader) { 409*90c8c64dSAndroid Build Coastguard Worker dequeueAndSaveImage(mJpegResultQueue, mJpegImageReader); 410*90c8c64dSAndroid Build Coastguard Worker } 411*90c8c64dSAndroid Build Coastguard Worker 412*90c8c64dSAndroid Build Coastguard Worker }; 413*90c8c64dSAndroid Build Coastguard Worker 414*90c8c64dSAndroid Build Coastguard Worker /** 415*90c8c64dSAndroid Build Coastguard Worker * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a 416*90c8c64dSAndroid Build Coastguard Worker * RAW image is ready to be saved. 417*90c8c64dSAndroid Build Coastguard Worker */ 418*90c8c64dSAndroid Build Coastguard Worker private final ImageReader.OnImageAvailableListener mOnRawImageAvailableListener 419*90c8c64dSAndroid Build Coastguard Worker = new ImageReader.OnImageAvailableListener() { 420*90c8c64dSAndroid Build Coastguard Worker 421*90c8c64dSAndroid Build Coastguard Worker @Override 422*90c8c64dSAndroid Build Coastguard Worker public void onImageAvailable(ImageReader reader) { 423*90c8c64dSAndroid Build Coastguard Worker dequeueAndSaveImage(mRawResultQueue, mRawImageReader); 424*90c8c64dSAndroid Build Coastguard Worker } 425*90c8c64dSAndroid Build Coastguard Worker 426*90c8c64dSAndroid Build Coastguard Worker }; 427*90c8c64dSAndroid Build Coastguard Worker 428*90c8c64dSAndroid Build Coastguard Worker /** 429*90c8c64dSAndroid Build Coastguard Worker * A {@link CameraCaptureSession.CaptureCallback} that handles events for the preview and 430*90c8c64dSAndroid Build Coastguard Worker * pre-capture sequence. 431*90c8c64dSAndroid Build Coastguard Worker */ 432*90c8c64dSAndroid Build Coastguard Worker private CameraCaptureSession.CaptureCallback mPreCaptureCallback 433*90c8c64dSAndroid Build Coastguard Worker = new CameraCaptureSession.CaptureCallback() { 434*90c8c64dSAndroid Build Coastguard Worker 435*90c8c64dSAndroid Build Coastguard Worker private void process(CaptureResult result) { 436*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 437*90c8c64dSAndroid Build Coastguard Worker switch (mState) { 438*90c8c64dSAndroid Build Coastguard Worker case STATE_PREVIEW: { 439*90c8c64dSAndroid Build Coastguard Worker // We have nothing to do when the camera preview is running normally. 440*90c8c64dSAndroid Build Coastguard Worker break; 441*90c8c64dSAndroid Build Coastguard Worker } 442*90c8c64dSAndroid Build Coastguard Worker case STATE_WAITING_FOR_3A_CONVERGENCE: { 443*90c8c64dSAndroid Build Coastguard Worker boolean readyToCapture = true; 444*90c8c64dSAndroid Build Coastguard Worker if (!mNoAFRun) { 445*90c8c64dSAndroid Build Coastguard Worker Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); 446*90c8c64dSAndroid Build Coastguard Worker if (afState == null) { 447*90c8c64dSAndroid Build Coastguard Worker break; 448*90c8c64dSAndroid Build Coastguard Worker } 449*90c8c64dSAndroid Build Coastguard Worker 450*90c8c64dSAndroid Build Coastguard Worker // If auto-focus has reached locked state, we are ready to capture 451*90c8c64dSAndroid Build Coastguard Worker readyToCapture = 452*90c8c64dSAndroid Build Coastguard Worker (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED || 453*90c8c64dSAndroid Build Coastguard Worker afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED); 454*90c8c64dSAndroid Build Coastguard Worker } 455*90c8c64dSAndroid Build Coastguard Worker 456*90c8c64dSAndroid Build Coastguard Worker // If we are running on an non-legacy device, we should also wait until 457*90c8c64dSAndroid Build Coastguard Worker // auto-exposure and auto-white-balance have converged as well before 458*90c8c64dSAndroid Build Coastguard Worker // taking a picture. 459*90c8c64dSAndroid Build Coastguard Worker if (!isLegacyLocked()) { 460*90c8c64dSAndroid Build Coastguard Worker Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 461*90c8c64dSAndroid Build Coastguard Worker Integer awbState = result.get(CaptureResult.CONTROL_AWB_STATE); 462*90c8c64dSAndroid Build Coastguard Worker if (aeState == null || awbState == null) { 463*90c8c64dSAndroid Build Coastguard Worker break; 464*90c8c64dSAndroid Build Coastguard Worker } 465*90c8c64dSAndroid Build Coastguard Worker 466*90c8c64dSAndroid Build Coastguard Worker readyToCapture = readyToCapture && 467*90c8c64dSAndroid Build Coastguard Worker aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED && 468*90c8c64dSAndroid Build Coastguard Worker awbState == CaptureResult.CONTROL_AWB_STATE_CONVERGED; 469*90c8c64dSAndroid Build Coastguard Worker } 470*90c8c64dSAndroid Build Coastguard Worker 471*90c8c64dSAndroid Build Coastguard Worker // If we haven't finished the pre-capture sequence but have hit our maximum 472*90c8c64dSAndroid Build Coastguard Worker // wait timeout, too bad! Begin capture anyway. 473*90c8c64dSAndroid Build Coastguard Worker if (!readyToCapture && hitTimeoutLocked()) { 474*90c8c64dSAndroid Build Coastguard Worker Log.w(TAG, "Timed out waiting for pre-capture sequence to complete."); 475*90c8c64dSAndroid Build Coastguard Worker readyToCapture = true; 476*90c8c64dSAndroid Build Coastguard Worker } 477*90c8c64dSAndroid Build Coastguard Worker 478*90c8c64dSAndroid Build Coastguard Worker if (readyToCapture && mPendingUserCaptures > 0) { 479*90c8c64dSAndroid Build Coastguard Worker // Capture once for each user tap of the "Picture" button. 480*90c8c64dSAndroid Build Coastguard Worker while (mPendingUserCaptures > 0) { 481*90c8c64dSAndroid Build Coastguard Worker captureStillPictureLocked(); 482*90c8c64dSAndroid Build Coastguard Worker mPendingUserCaptures--; 483*90c8c64dSAndroid Build Coastguard Worker } 484*90c8c64dSAndroid Build Coastguard Worker // After this, the camera will go back to the normal state of preview. 485*90c8c64dSAndroid Build Coastguard Worker mState = STATE_PREVIEW; 486*90c8c64dSAndroid Build Coastguard Worker } 487*90c8c64dSAndroid Build Coastguard Worker } 488*90c8c64dSAndroid Build Coastguard Worker } 489*90c8c64dSAndroid Build Coastguard Worker } 490*90c8c64dSAndroid Build Coastguard Worker } 491*90c8c64dSAndroid Build Coastguard Worker 492*90c8c64dSAndroid Build Coastguard Worker @Override 493*90c8c64dSAndroid Build Coastguard Worker public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, 494*90c8c64dSAndroid Build Coastguard Worker CaptureResult partialResult) { 495*90c8c64dSAndroid Build Coastguard Worker process(partialResult); 496*90c8c64dSAndroid Build Coastguard Worker } 497*90c8c64dSAndroid Build Coastguard Worker 498*90c8c64dSAndroid Build Coastguard Worker @Override 499*90c8c64dSAndroid Build Coastguard Worker public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, 500*90c8c64dSAndroid Build Coastguard Worker TotalCaptureResult result) { 501*90c8c64dSAndroid Build Coastguard Worker process(result); 502*90c8c64dSAndroid Build Coastguard Worker } 503*90c8c64dSAndroid Build Coastguard Worker 504*90c8c64dSAndroid Build Coastguard Worker }; 505*90c8c64dSAndroid Build Coastguard Worker 506*90c8c64dSAndroid Build Coastguard Worker /** 507*90c8c64dSAndroid Build Coastguard Worker * A {@link CameraCaptureSession.CaptureCallback} that handles the still JPEG and RAW capture 508*90c8c64dSAndroid Build Coastguard Worker * request. 509*90c8c64dSAndroid Build Coastguard Worker */ 510*90c8c64dSAndroid Build Coastguard Worker private final CameraCaptureSession.CaptureCallback mCaptureCallback 511*90c8c64dSAndroid Build Coastguard Worker = new CameraCaptureSession.CaptureCallback() { 512*90c8c64dSAndroid Build Coastguard Worker @Override 513*90c8c64dSAndroid Build Coastguard Worker public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, 514*90c8c64dSAndroid Build Coastguard Worker long timestamp, long frameNumber) { 515*90c8c64dSAndroid Build Coastguard Worker String currentDateTime = generateTimestamp(); 516*90c8c64dSAndroid Build Coastguard Worker File rawFile = new File(Environment. 517*90c8c64dSAndroid Build Coastguard Worker getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), 518*90c8c64dSAndroid Build Coastguard Worker "RAW_" + currentDateTime + ".dng"); 519*90c8c64dSAndroid Build Coastguard Worker File jpegFile = new File(Environment. 520*90c8c64dSAndroid Build Coastguard Worker getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), 521*90c8c64dSAndroid Build Coastguard Worker "JPEG_" + currentDateTime + ".jpg"); 522*90c8c64dSAndroid Build Coastguard Worker 523*90c8c64dSAndroid Build Coastguard Worker // Look up the ImageSaverBuilder for this request and update it with the file name 524*90c8c64dSAndroid Build Coastguard Worker // based on the capture start time. 525*90c8c64dSAndroid Build Coastguard Worker ImageSaver.ImageSaverBuilder jpegBuilder; 526*90c8c64dSAndroid Build Coastguard Worker ImageSaver.ImageSaverBuilder rawBuilder; 527*90c8c64dSAndroid Build Coastguard Worker int requestId = (int) request.getTag(); 528*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 529*90c8c64dSAndroid Build Coastguard Worker jpegBuilder = mJpegResultQueue.get(requestId); 530*90c8c64dSAndroid Build Coastguard Worker rawBuilder = mRawResultQueue.get(requestId); 531*90c8c64dSAndroid Build Coastguard Worker } 532*90c8c64dSAndroid Build Coastguard Worker 533*90c8c64dSAndroid Build Coastguard Worker if (jpegBuilder != null) jpegBuilder.setFile(jpegFile); 534*90c8c64dSAndroid Build Coastguard Worker if (rawBuilder != null) rawBuilder.setFile(rawFile); 535*90c8c64dSAndroid Build Coastguard Worker } 536*90c8c64dSAndroid Build Coastguard Worker 537*90c8c64dSAndroid Build Coastguard Worker @Override 538*90c8c64dSAndroid Build Coastguard Worker public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, 539*90c8c64dSAndroid Build Coastguard Worker TotalCaptureResult result) { 540*90c8c64dSAndroid Build Coastguard Worker int requestId = (int) request.getTag(); 541*90c8c64dSAndroid Build Coastguard Worker ImageSaver.ImageSaverBuilder jpegBuilder; 542*90c8c64dSAndroid Build Coastguard Worker ImageSaver.ImageSaverBuilder rawBuilder; 543*90c8c64dSAndroid Build Coastguard Worker StringBuilder sb = new StringBuilder(); 544*90c8c64dSAndroid Build Coastguard Worker 545*90c8c64dSAndroid Build Coastguard Worker // Look up the ImageSaverBuilder for this request and update it with the CaptureResult 546*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 547*90c8c64dSAndroid Build Coastguard Worker jpegBuilder = mJpegResultQueue.get(requestId); 548*90c8c64dSAndroid Build Coastguard Worker rawBuilder = mRawResultQueue.get(requestId); 549*90c8c64dSAndroid Build Coastguard Worker 550*90c8c64dSAndroid Build Coastguard Worker // If we have all the results necessary, save the image to a file in the background. 551*90c8c64dSAndroid Build Coastguard Worker handleCompletionLocked(requestId, jpegBuilder, mJpegResultQueue); 552*90c8c64dSAndroid Build Coastguard Worker handleCompletionLocked(requestId, rawBuilder, mRawResultQueue); 553*90c8c64dSAndroid Build Coastguard Worker 554*90c8c64dSAndroid Build Coastguard Worker if (jpegBuilder != null) { 555*90c8c64dSAndroid Build Coastguard Worker jpegBuilder.setResult(result); 556*90c8c64dSAndroid Build Coastguard Worker sb.append("Saving JPEG as: "); 557*90c8c64dSAndroid Build Coastguard Worker sb.append(jpegBuilder.getSaveLocation()); 558*90c8c64dSAndroid Build Coastguard Worker } 559*90c8c64dSAndroid Build Coastguard Worker if (rawBuilder != null) { 560*90c8c64dSAndroid Build Coastguard Worker rawBuilder.setResult(result); 561*90c8c64dSAndroid Build Coastguard Worker if (jpegBuilder != null) sb.append(", "); 562*90c8c64dSAndroid Build Coastguard Worker sb.append("Saving RAW as: "); 563*90c8c64dSAndroid Build Coastguard Worker sb.append(rawBuilder.getSaveLocation()); 564*90c8c64dSAndroid Build Coastguard Worker } 565*90c8c64dSAndroid Build Coastguard Worker finishedCaptureLocked(); 566*90c8c64dSAndroid Build Coastguard Worker } 567*90c8c64dSAndroid Build Coastguard Worker 568*90c8c64dSAndroid Build Coastguard Worker showToast(sb.toString()); 569*90c8c64dSAndroid Build Coastguard Worker } 570*90c8c64dSAndroid Build Coastguard Worker 571*90c8c64dSAndroid Build Coastguard Worker @Override 572*90c8c64dSAndroid Build Coastguard Worker public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, 573*90c8c64dSAndroid Build Coastguard Worker CaptureFailure failure) { 574*90c8c64dSAndroid Build Coastguard Worker int requestId = (int) request.getTag(); 575*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 576*90c8c64dSAndroid Build Coastguard Worker mJpegResultQueue.remove(requestId); 577*90c8c64dSAndroid Build Coastguard Worker mRawResultQueue.remove(requestId); 578*90c8c64dSAndroid Build Coastguard Worker finishedCaptureLocked(); 579*90c8c64dSAndroid Build Coastguard Worker } 580*90c8c64dSAndroid Build Coastguard Worker showToast("Capture failed!"); 581*90c8c64dSAndroid Build Coastguard Worker } 582*90c8c64dSAndroid Build Coastguard Worker 583*90c8c64dSAndroid Build Coastguard Worker }; 584*90c8c64dSAndroid Build Coastguard Worker 585*90c8c64dSAndroid Build Coastguard Worker /** 586*90c8c64dSAndroid Build Coastguard Worker * A {@link Handler} for showing {@link Toast}s on the UI thread. 587*90c8c64dSAndroid Build Coastguard Worker */ 588*90c8c64dSAndroid Build Coastguard Worker private final Handler mMessageHandler = new Handler(Looper.getMainLooper()) { 589*90c8c64dSAndroid Build Coastguard Worker @Override 590*90c8c64dSAndroid Build Coastguard Worker public void handleMessage(Message msg) { 591*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 592*90c8c64dSAndroid Build Coastguard Worker if (activity != null) { 593*90c8c64dSAndroid Build Coastguard Worker Toast.makeText(activity, (String) msg.obj, Toast.LENGTH_SHORT).show(); 594*90c8c64dSAndroid Build Coastguard Worker } 595*90c8c64dSAndroid Build Coastguard Worker } 596*90c8c64dSAndroid Build Coastguard Worker }; 597*90c8c64dSAndroid Build Coastguard Worker newInstance()598*90c8c64dSAndroid Build Coastguard Worker public static Camera2RawFragment newInstance() { 599*90c8c64dSAndroid Build Coastguard Worker return new Camera2RawFragment(); 600*90c8c64dSAndroid Build Coastguard Worker } 601*90c8c64dSAndroid Build Coastguard Worker 602*90c8c64dSAndroid Build Coastguard Worker @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)603*90c8c64dSAndroid Build Coastguard Worker public View onCreateView(LayoutInflater inflater, ViewGroup container, 604*90c8c64dSAndroid Build Coastguard Worker Bundle savedInstanceState) { 605*90c8c64dSAndroid Build Coastguard Worker return inflater.inflate(R.layout.fragment_camera2_basic, container, false); 606*90c8c64dSAndroid Build Coastguard Worker } 607*90c8c64dSAndroid Build Coastguard Worker 608*90c8c64dSAndroid Build Coastguard Worker @Override onViewCreated(final View view, Bundle savedInstanceState)609*90c8c64dSAndroid Build Coastguard Worker public void onViewCreated(final View view, Bundle savedInstanceState) { 610*90c8c64dSAndroid Build Coastguard Worker view.findViewById(R.id.picture).setOnClickListener(this); 611*90c8c64dSAndroid Build Coastguard Worker view.findViewById(R.id.info).setOnClickListener(this); 612*90c8c64dSAndroid Build Coastguard Worker mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture); 613*90c8c64dSAndroid Build Coastguard Worker 614*90c8c64dSAndroid Build Coastguard Worker // Setup a new OrientationEventListener. This is used to handle rotation events like a 615*90c8c64dSAndroid Build Coastguard Worker // 180 degree rotation that do not normally trigger a call to onCreate to do view re-layout 616*90c8c64dSAndroid Build Coastguard Worker // or otherwise cause the preview TextureView's size to change. 617*90c8c64dSAndroid Build Coastguard Worker mOrientationListener = new OrientationEventListener(getActivity(), 618*90c8c64dSAndroid Build Coastguard Worker SensorManager.SENSOR_DELAY_NORMAL) { 619*90c8c64dSAndroid Build Coastguard Worker @Override 620*90c8c64dSAndroid Build Coastguard Worker public void onOrientationChanged(int orientation) { 621*90c8c64dSAndroid Build Coastguard Worker if (mTextureView != null && mTextureView.isAvailable()) { 622*90c8c64dSAndroid Build Coastguard Worker configureTransform(mTextureView.getWidth(), mTextureView.getHeight()); 623*90c8c64dSAndroid Build Coastguard Worker } 624*90c8c64dSAndroid Build Coastguard Worker } 625*90c8c64dSAndroid Build Coastguard Worker }; 626*90c8c64dSAndroid Build Coastguard Worker } 627*90c8c64dSAndroid Build Coastguard Worker 628*90c8c64dSAndroid Build Coastguard Worker @Override onResume()629*90c8c64dSAndroid Build Coastguard Worker public void onResume() { 630*90c8c64dSAndroid Build Coastguard Worker super.onResume(); 631*90c8c64dSAndroid Build Coastguard Worker startBackgroundThread(); 632*90c8c64dSAndroid Build Coastguard Worker openCamera(); 633*90c8c64dSAndroid Build Coastguard Worker 634*90c8c64dSAndroid Build Coastguard Worker // When the screen is turned off and turned back on, the SurfaceTexture is already 635*90c8c64dSAndroid Build Coastguard Worker // available, and "onSurfaceTextureAvailable" will not be called. In that case, we should 636*90c8c64dSAndroid Build Coastguard Worker // configure the preview bounds here (otherwise, we wait until the surface is ready in 637*90c8c64dSAndroid Build Coastguard Worker // the SurfaceTextureListener). 638*90c8c64dSAndroid Build Coastguard Worker if (mTextureView.isAvailable()) { 639*90c8c64dSAndroid Build Coastguard Worker configureTransform(mTextureView.getWidth(), mTextureView.getHeight()); 640*90c8c64dSAndroid Build Coastguard Worker } else { 641*90c8c64dSAndroid Build Coastguard Worker mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); 642*90c8c64dSAndroid Build Coastguard Worker } 643*90c8c64dSAndroid Build Coastguard Worker if (mOrientationListener != null && mOrientationListener.canDetectOrientation()) { 644*90c8c64dSAndroid Build Coastguard Worker mOrientationListener.enable(); 645*90c8c64dSAndroid Build Coastguard Worker } 646*90c8c64dSAndroid Build Coastguard Worker } 647*90c8c64dSAndroid Build Coastguard Worker 648*90c8c64dSAndroid Build Coastguard Worker @Override onPause()649*90c8c64dSAndroid Build Coastguard Worker public void onPause() { 650*90c8c64dSAndroid Build Coastguard Worker if (mOrientationListener != null) { 651*90c8c64dSAndroid Build Coastguard Worker mOrientationListener.disable(); 652*90c8c64dSAndroid Build Coastguard Worker } 653*90c8c64dSAndroid Build Coastguard Worker closeCamera(); 654*90c8c64dSAndroid Build Coastguard Worker stopBackgroundThread(); 655*90c8c64dSAndroid Build Coastguard Worker super.onPause(); 656*90c8c64dSAndroid Build Coastguard Worker } 657*90c8c64dSAndroid Build Coastguard Worker 658*90c8c64dSAndroid Build Coastguard Worker @Override onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)659*90c8c64dSAndroid Build Coastguard Worker public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 660*90c8c64dSAndroid Build Coastguard Worker if (requestCode == REQUEST_CAMERA_PERMISSIONS) { 661*90c8c64dSAndroid Build Coastguard Worker for (int result : grantResults) { 662*90c8c64dSAndroid Build Coastguard Worker if (result != PackageManager.PERMISSION_GRANTED) { 663*90c8c64dSAndroid Build Coastguard Worker showMissingPermissionError(); 664*90c8c64dSAndroid Build Coastguard Worker return; 665*90c8c64dSAndroid Build Coastguard Worker } 666*90c8c64dSAndroid Build Coastguard Worker } 667*90c8c64dSAndroid Build Coastguard Worker } else { 668*90c8c64dSAndroid Build Coastguard Worker super.onRequestPermissionsResult(requestCode, permissions, grantResults); 669*90c8c64dSAndroid Build Coastguard Worker } 670*90c8c64dSAndroid Build Coastguard Worker } 671*90c8c64dSAndroid Build Coastguard Worker 672*90c8c64dSAndroid Build Coastguard Worker @Override onClick(View view)673*90c8c64dSAndroid Build Coastguard Worker public void onClick(View view) { 674*90c8c64dSAndroid Build Coastguard Worker switch (view.getId()) { 675*90c8c64dSAndroid Build Coastguard Worker case R.id.picture: { 676*90c8c64dSAndroid Build Coastguard Worker takePicture(); 677*90c8c64dSAndroid Build Coastguard Worker break; 678*90c8c64dSAndroid Build Coastguard Worker } 679*90c8c64dSAndroid Build Coastguard Worker case R.id.info: { 680*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 681*90c8c64dSAndroid Build Coastguard Worker if (null != activity) { 682*90c8c64dSAndroid Build Coastguard Worker new AlertDialog.Builder(activity) 683*90c8c64dSAndroid Build Coastguard Worker .setMessage(R.string.intro_message) 684*90c8c64dSAndroid Build Coastguard Worker .setPositiveButton(android.R.string.ok, null) 685*90c8c64dSAndroid Build Coastguard Worker .show(); 686*90c8c64dSAndroid Build Coastguard Worker } 687*90c8c64dSAndroid Build Coastguard Worker break; 688*90c8c64dSAndroid Build Coastguard Worker } 689*90c8c64dSAndroid Build Coastguard Worker } 690*90c8c64dSAndroid Build Coastguard Worker } 691*90c8c64dSAndroid Build Coastguard Worker 692*90c8c64dSAndroid Build Coastguard Worker /** 693*90c8c64dSAndroid Build Coastguard Worker * Sets up state related to camera that is needed before opening a {@link CameraDevice}. 694*90c8c64dSAndroid Build Coastguard Worker */ setUpCameraOutputs()695*90c8c64dSAndroid Build Coastguard Worker private boolean setUpCameraOutputs() { 696*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 697*90c8c64dSAndroid Build Coastguard Worker CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 698*90c8c64dSAndroid Build Coastguard Worker if (manager == null) { 699*90c8c64dSAndroid Build Coastguard Worker ErrorDialog.buildErrorDialog("This device doesn't support Camera2 API."). 700*90c8c64dSAndroid Build Coastguard Worker show(getFragmentManager(), "dialog"); 701*90c8c64dSAndroid Build Coastguard Worker return false; 702*90c8c64dSAndroid Build Coastguard Worker } 703*90c8c64dSAndroid Build Coastguard Worker try { 704*90c8c64dSAndroid Build Coastguard Worker // Find a CameraDevice that supports RAW captures, and configure state. 705*90c8c64dSAndroid Build Coastguard Worker for (String cameraId : manager.getCameraIdList()) { 706*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics characteristics 707*90c8c64dSAndroid Build Coastguard Worker = manager.getCameraCharacteristics(cameraId); 708*90c8c64dSAndroid Build Coastguard Worker 709*90c8c64dSAndroid Build Coastguard Worker // We only use a camera that supports RAW in this sample. 710*90c8c64dSAndroid Build Coastguard Worker if (!contains(characteristics.get( 711*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES), 712*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) { 713*90c8c64dSAndroid Build Coastguard Worker continue; 714*90c8c64dSAndroid Build Coastguard Worker } 715*90c8c64dSAndroid Build Coastguard Worker 716*90c8c64dSAndroid Build Coastguard Worker StreamConfigurationMap map = characteristics.get( 717*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 718*90c8c64dSAndroid Build Coastguard Worker 719*90c8c64dSAndroid Build Coastguard Worker // For still image captures, we use the largest available size. 720*90c8c64dSAndroid Build Coastguard Worker Size largestJpeg = Collections.max( 721*90c8c64dSAndroid Build Coastguard Worker Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), 722*90c8c64dSAndroid Build Coastguard Worker new CompareSizesByArea()); 723*90c8c64dSAndroid Build Coastguard Worker 724*90c8c64dSAndroid Build Coastguard Worker Size largestRaw = Collections.max( 725*90c8c64dSAndroid Build Coastguard Worker Arrays.asList(map.getOutputSizes(ImageFormat.RAW_SENSOR)), 726*90c8c64dSAndroid Build Coastguard Worker new CompareSizesByArea()); 727*90c8c64dSAndroid Build Coastguard Worker 728*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 729*90c8c64dSAndroid Build Coastguard Worker // Set up ImageReaders for JPEG and RAW outputs. Place these in a reference 730*90c8c64dSAndroid Build Coastguard Worker // counted wrapper to ensure they are only closed when all background tasks 731*90c8c64dSAndroid Build Coastguard Worker // using them are finished. 732*90c8c64dSAndroid Build Coastguard Worker if (mJpegImageReader == null || mJpegImageReader.getAndRetain() == null) { 733*90c8c64dSAndroid Build Coastguard Worker mJpegImageReader = new RefCountedAutoCloseable<>( 734*90c8c64dSAndroid Build Coastguard Worker ImageReader.newInstance(largestJpeg.getWidth(), 735*90c8c64dSAndroid Build Coastguard Worker largestJpeg.getHeight(), ImageFormat.JPEG, /*maxImages*/5)); 736*90c8c64dSAndroid Build Coastguard Worker } 737*90c8c64dSAndroid Build Coastguard Worker mJpegImageReader.get().setOnImageAvailableListener( 738*90c8c64dSAndroid Build Coastguard Worker mOnJpegImageAvailableListener, mBackgroundHandler); 739*90c8c64dSAndroid Build Coastguard Worker 740*90c8c64dSAndroid Build Coastguard Worker if (mRawImageReader == null || mRawImageReader.getAndRetain() == null) { 741*90c8c64dSAndroid Build Coastguard Worker mRawImageReader = new RefCountedAutoCloseable<>( 742*90c8c64dSAndroid Build Coastguard Worker ImageReader.newInstance(largestRaw.getWidth(), 743*90c8c64dSAndroid Build Coastguard Worker largestRaw.getHeight(), ImageFormat.RAW_SENSOR, /*maxImages*/ 5)); 744*90c8c64dSAndroid Build Coastguard Worker } 745*90c8c64dSAndroid Build Coastguard Worker mRawImageReader.get().setOnImageAvailableListener( 746*90c8c64dSAndroid Build Coastguard Worker mOnRawImageAvailableListener, mBackgroundHandler); 747*90c8c64dSAndroid Build Coastguard Worker 748*90c8c64dSAndroid Build Coastguard Worker mCharacteristics = characteristics; 749*90c8c64dSAndroid Build Coastguard Worker mCameraId = cameraId; 750*90c8c64dSAndroid Build Coastguard Worker } 751*90c8c64dSAndroid Build Coastguard Worker return true; 752*90c8c64dSAndroid Build Coastguard Worker } 753*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 754*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 755*90c8c64dSAndroid Build Coastguard Worker } 756*90c8c64dSAndroid Build Coastguard Worker 757*90c8c64dSAndroid Build Coastguard Worker // If we found no suitable cameras for capturing RAW, warn the user. 758*90c8c64dSAndroid Build Coastguard Worker ErrorDialog.buildErrorDialog("This device doesn't support capturing RAW photos"). 759*90c8c64dSAndroid Build Coastguard Worker show(getFragmentManager(), "dialog"); 760*90c8c64dSAndroid Build Coastguard Worker return false; 761*90c8c64dSAndroid Build Coastguard Worker } 762*90c8c64dSAndroid Build Coastguard Worker 763*90c8c64dSAndroid Build Coastguard Worker /** 764*90c8c64dSAndroid Build Coastguard Worker * Opens the camera specified by {@link #mCameraId}. 765*90c8c64dSAndroid Build Coastguard Worker */ openCamera()766*90c8c64dSAndroid Build Coastguard Worker private void openCamera() { 767*90c8c64dSAndroid Build Coastguard Worker if (!setUpCameraOutputs()) { 768*90c8c64dSAndroid Build Coastguard Worker return; 769*90c8c64dSAndroid Build Coastguard Worker } 770*90c8c64dSAndroid Build Coastguard Worker if (!hasAllPermissionsGranted()) { 771*90c8c64dSAndroid Build Coastguard Worker requestCameraPermissions(); 772*90c8c64dSAndroid Build Coastguard Worker return; 773*90c8c64dSAndroid Build Coastguard Worker } 774*90c8c64dSAndroid Build Coastguard Worker 775*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 776*90c8c64dSAndroid Build Coastguard Worker CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 777*90c8c64dSAndroid Build Coastguard Worker try { 778*90c8c64dSAndroid Build Coastguard Worker // Wait for any previously running session to finish. 779*90c8c64dSAndroid Build Coastguard Worker if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { 780*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException("Time out waiting to lock camera opening."); 781*90c8c64dSAndroid Build Coastguard Worker } 782*90c8c64dSAndroid Build Coastguard Worker 783*90c8c64dSAndroid Build Coastguard Worker String cameraId; 784*90c8c64dSAndroid Build Coastguard Worker Handler backgroundHandler; 785*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 786*90c8c64dSAndroid Build Coastguard Worker cameraId = mCameraId; 787*90c8c64dSAndroid Build Coastguard Worker backgroundHandler = mBackgroundHandler; 788*90c8c64dSAndroid Build Coastguard Worker } 789*90c8c64dSAndroid Build Coastguard Worker 790*90c8c64dSAndroid Build Coastguard Worker // Attempt to open the camera. mStateCallback will be called on the background handler's 791*90c8c64dSAndroid Build Coastguard Worker // thread when this succeeds or fails. 792*90c8c64dSAndroid Build Coastguard Worker manager.openCamera(cameraId, mStateCallback, backgroundHandler); 793*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 794*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 795*90c8c64dSAndroid Build Coastguard Worker } catch (InterruptedException e) { 796*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException("Interrupted while trying to lock camera opening.", e); 797*90c8c64dSAndroid Build Coastguard Worker } 798*90c8c64dSAndroid Build Coastguard Worker } 799*90c8c64dSAndroid Build Coastguard Worker 800*90c8c64dSAndroid Build Coastguard Worker /** 801*90c8c64dSAndroid Build Coastguard Worker * Requests permissions necessary to use camera and save pictures. 802*90c8c64dSAndroid Build Coastguard Worker */ requestCameraPermissions()803*90c8c64dSAndroid Build Coastguard Worker private void requestCameraPermissions() { 804*90c8c64dSAndroid Build Coastguard Worker if (shouldShowRationale()) { 805*90c8c64dSAndroid Build Coastguard Worker PermissionConfirmationDialog.newInstance().show(getChildFragmentManager(), "dialog"); 806*90c8c64dSAndroid Build Coastguard Worker } else { 807*90c8c64dSAndroid Build Coastguard Worker FragmentCompat.requestPermissions(this, CAMERA_PERMISSIONS, REQUEST_CAMERA_PERMISSIONS); 808*90c8c64dSAndroid Build Coastguard Worker } 809*90c8c64dSAndroid Build Coastguard Worker } 810*90c8c64dSAndroid Build Coastguard Worker 811*90c8c64dSAndroid Build Coastguard Worker /** 812*90c8c64dSAndroid Build Coastguard Worker * Tells whether all the necessary permissions are granted to this app. 813*90c8c64dSAndroid Build Coastguard Worker * 814*90c8c64dSAndroid Build Coastguard Worker * @return True if all the required permissions are granted. 815*90c8c64dSAndroid Build Coastguard Worker */ hasAllPermissionsGranted()816*90c8c64dSAndroid Build Coastguard Worker private boolean hasAllPermissionsGranted() { 817*90c8c64dSAndroid Build Coastguard Worker for (String permission : CAMERA_PERMISSIONS) { 818*90c8c64dSAndroid Build Coastguard Worker if (ActivityCompat.checkSelfPermission(getActivity(), permission) 819*90c8c64dSAndroid Build Coastguard Worker != PackageManager.PERMISSION_GRANTED) { 820*90c8c64dSAndroid Build Coastguard Worker return false; 821*90c8c64dSAndroid Build Coastguard Worker } 822*90c8c64dSAndroid Build Coastguard Worker } 823*90c8c64dSAndroid Build Coastguard Worker return true; 824*90c8c64dSAndroid Build Coastguard Worker } 825*90c8c64dSAndroid Build Coastguard Worker 826*90c8c64dSAndroid Build Coastguard Worker /** 827*90c8c64dSAndroid Build Coastguard Worker * Gets whether you should show UI with rationale for requesting the permissions. 828*90c8c64dSAndroid Build Coastguard Worker * 829*90c8c64dSAndroid Build Coastguard Worker * @return True if the UI should be shown. 830*90c8c64dSAndroid Build Coastguard Worker */ shouldShowRationale()831*90c8c64dSAndroid Build Coastguard Worker private boolean shouldShowRationale() { 832*90c8c64dSAndroid Build Coastguard Worker for (String permission : CAMERA_PERMISSIONS) { 833*90c8c64dSAndroid Build Coastguard Worker if (FragmentCompat.shouldShowRequestPermissionRationale(this, permission)) { 834*90c8c64dSAndroid Build Coastguard Worker return true; 835*90c8c64dSAndroid Build Coastguard Worker } 836*90c8c64dSAndroid Build Coastguard Worker } 837*90c8c64dSAndroid Build Coastguard Worker return false; 838*90c8c64dSAndroid Build Coastguard Worker } 839*90c8c64dSAndroid Build Coastguard Worker 840*90c8c64dSAndroid Build Coastguard Worker /** 841*90c8c64dSAndroid Build Coastguard Worker * Shows that this app really needs the permission and finishes the app. 842*90c8c64dSAndroid Build Coastguard Worker */ showMissingPermissionError()843*90c8c64dSAndroid Build Coastguard Worker private void showMissingPermissionError() { 844*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 845*90c8c64dSAndroid Build Coastguard Worker if (activity != null) { 846*90c8c64dSAndroid Build Coastguard Worker Toast.makeText(activity, R.string.request_permission, Toast.LENGTH_SHORT).show(); 847*90c8c64dSAndroid Build Coastguard Worker activity.finish(); 848*90c8c64dSAndroid Build Coastguard Worker } 849*90c8c64dSAndroid Build Coastguard Worker } 850*90c8c64dSAndroid Build Coastguard Worker 851*90c8c64dSAndroid Build Coastguard Worker /** 852*90c8c64dSAndroid Build Coastguard Worker * Closes the current {@link CameraDevice}. 853*90c8c64dSAndroid Build Coastguard Worker */ closeCamera()854*90c8c64dSAndroid Build Coastguard Worker private void closeCamera() { 855*90c8c64dSAndroid Build Coastguard Worker try { 856*90c8c64dSAndroid Build Coastguard Worker mCameraOpenCloseLock.acquire(); 857*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 858*90c8c64dSAndroid Build Coastguard Worker 859*90c8c64dSAndroid Build Coastguard Worker // Reset state and clean up resources used by the camera. 860*90c8c64dSAndroid Build Coastguard Worker // Note: After calling this, the ImageReaders will be closed after any background 861*90c8c64dSAndroid Build Coastguard Worker // tasks saving Images from these readers have been completed. 862*90c8c64dSAndroid Build Coastguard Worker mPendingUserCaptures = 0; 863*90c8c64dSAndroid Build Coastguard Worker mState = STATE_CLOSED; 864*90c8c64dSAndroid Build Coastguard Worker if (null != mCaptureSession) { 865*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.close(); 866*90c8c64dSAndroid Build Coastguard Worker mCaptureSession = null; 867*90c8c64dSAndroid Build Coastguard Worker } 868*90c8c64dSAndroid Build Coastguard Worker if (null != mCameraDevice) { 869*90c8c64dSAndroid Build Coastguard Worker mCameraDevice.close(); 870*90c8c64dSAndroid Build Coastguard Worker mCameraDevice = null; 871*90c8c64dSAndroid Build Coastguard Worker } 872*90c8c64dSAndroid Build Coastguard Worker if (null != mJpegImageReader) { 873*90c8c64dSAndroid Build Coastguard Worker mJpegImageReader.close(); 874*90c8c64dSAndroid Build Coastguard Worker mJpegImageReader = null; 875*90c8c64dSAndroid Build Coastguard Worker } 876*90c8c64dSAndroid Build Coastguard Worker if (null != mRawImageReader) { 877*90c8c64dSAndroid Build Coastguard Worker mRawImageReader.close(); 878*90c8c64dSAndroid Build Coastguard Worker mRawImageReader = null; 879*90c8c64dSAndroid Build Coastguard Worker } 880*90c8c64dSAndroid Build Coastguard Worker } 881*90c8c64dSAndroid Build Coastguard Worker } catch (InterruptedException e) { 882*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException("Interrupted while trying to lock camera closing.", e); 883*90c8c64dSAndroid Build Coastguard Worker } finally { 884*90c8c64dSAndroid Build Coastguard Worker mCameraOpenCloseLock.release(); 885*90c8c64dSAndroid Build Coastguard Worker } 886*90c8c64dSAndroid Build Coastguard Worker } 887*90c8c64dSAndroid Build Coastguard Worker 888*90c8c64dSAndroid Build Coastguard Worker /** 889*90c8c64dSAndroid Build Coastguard Worker * Starts a background thread and its {@link Handler}. 890*90c8c64dSAndroid Build Coastguard Worker */ startBackgroundThread()891*90c8c64dSAndroid Build Coastguard Worker private void startBackgroundThread() { 892*90c8c64dSAndroid Build Coastguard Worker mBackgroundThread = new HandlerThread("CameraBackground"); 893*90c8c64dSAndroid Build Coastguard Worker mBackgroundThread.start(); 894*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 895*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); 896*90c8c64dSAndroid Build Coastguard Worker } 897*90c8c64dSAndroid Build Coastguard Worker } 898*90c8c64dSAndroid Build Coastguard Worker 899*90c8c64dSAndroid Build Coastguard Worker /** 900*90c8c64dSAndroid Build Coastguard Worker * Stops the background thread and its {@link Handler}. 901*90c8c64dSAndroid Build Coastguard Worker */ stopBackgroundThread()902*90c8c64dSAndroid Build Coastguard Worker private void stopBackgroundThread() { 903*90c8c64dSAndroid Build Coastguard Worker mBackgroundThread.quitSafely(); 904*90c8c64dSAndroid Build Coastguard Worker try { 905*90c8c64dSAndroid Build Coastguard Worker mBackgroundThread.join(); 906*90c8c64dSAndroid Build Coastguard Worker mBackgroundThread = null; 907*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 908*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler = null; 909*90c8c64dSAndroid Build Coastguard Worker } 910*90c8c64dSAndroid Build Coastguard Worker } catch (InterruptedException e) { 911*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 912*90c8c64dSAndroid Build Coastguard Worker } 913*90c8c64dSAndroid Build Coastguard Worker } 914*90c8c64dSAndroid Build Coastguard Worker 915*90c8c64dSAndroid Build Coastguard Worker /** 916*90c8c64dSAndroid Build Coastguard Worker * Creates a new {@link CameraCaptureSession} for camera preview. 917*90c8c64dSAndroid Build Coastguard Worker * <p/> 918*90c8c64dSAndroid Build Coastguard Worker * Call this only with {@link #mCameraStateLock} held. 919*90c8c64dSAndroid Build Coastguard Worker */ createCameraPreviewSessionLocked()920*90c8c64dSAndroid Build Coastguard Worker private void createCameraPreviewSessionLocked() { 921*90c8c64dSAndroid Build Coastguard Worker try { 922*90c8c64dSAndroid Build Coastguard Worker SurfaceTexture texture = mTextureView.getSurfaceTexture(); 923*90c8c64dSAndroid Build Coastguard Worker // We configure the size of default buffer to be the size of camera preview we want. 924*90c8c64dSAndroid Build Coastguard Worker texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 925*90c8c64dSAndroid Build Coastguard Worker 926*90c8c64dSAndroid Build Coastguard Worker // This is the output Surface we need to start preview. 927*90c8c64dSAndroid Build Coastguard Worker Surface surface = new Surface(texture); 928*90c8c64dSAndroid Build Coastguard Worker 929*90c8c64dSAndroid Build Coastguard Worker // We set up a CaptureRequest.Builder with the output Surface. 930*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder 931*90c8c64dSAndroid Build Coastguard Worker = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 932*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.addTarget(surface); 933*90c8c64dSAndroid Build Coastguard Worker 934*90c8c64dSAndroid Build Coastguard Worker // Here, we create a CameraCaptureSession for camera preview. 935*90c8c64dSAndroid Build Coastguard Worker mCameraDevice.createCaptureSession(Arrays.asList(surface, 936*90c8c64dSAndroid Build Coastguard Worker mJpegImageReader.get().getSurface(), 937*90c8c64dSAndroid Build Coastguard Worker mRawImageReader.get().getSurface()), new CameraCaptureSession.StateCallback() { 938*90c8c64dSAndroid Build Coastguard Worker @Override 939*90c8c64dSAndroid Build Coastguard Worker public void onConfigured(CameraCaptureSession cameraCaptureSession) { 940*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 941*90c8c64dSAndroid Build Coastguard Worker // The camera is already closed 942*90c8c64dSAndroid Build Coastguard Worker if (null == mCameraDevice) { 943*90c8c64dSAndroid Build Coastguard Worker return; 944*90c8c64dSAndroid Build Coastguard Worker } 945*90c8c64dSAndroid Build Coastguard Worker 946*90c8c64dSAndroid Build Coastguard Worker try { 947*90c8c64dSAndroid Build Coastguard Worker setup3AControlsLocked(mPreviewRequestBuilder); 948*90c8c64dSAndroid Build Coastguard Worker // Finally, we start displaying the camera preview. 949*90c8c64dSAndroid Build Coastguard Worker cameraCaptureSession.setRepeatingRequest( 950*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.build(), 951*90c8c64dSAndroid Build Coastguard Worker mPreCaptureCallback, mBackgroundHandler); 952*90c8c64dSAndroid Build Coastguard Worker mState = STATE_PREVIEW; 953*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException | IllegalStateException e) { 954*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 955*90c8c64dSAndroid Build Coastguard Worker return; 956*90c8c64dSAndroid Build Coastguard Worker } 957*90c8c64dSAndroid Build Coastguard Worker // When the session is ready, we start displaying the preview. 958*90c8c64dSAndroid Build Coastguard Worker mCaptureSession = cameraCaptureSession; 959*90c8c64dSAndroid Build Coastguard Worker } 960*90c8c64dSAndroid Build Coastguard Worker } 961*90c8c64dSAndroid Build Coastguard Worker 962*90c8c64dSAndroid Build Coastguard Worker @Override 963*90c8c64dSAndroid Build Coastguard Worker public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { 964*90c8c64dSAndroid Build Coastguard Worker showToast("Failed to configure camera."); 965*90c8c64dSAndroid Build Coastguard Worker } 966*90c8c64dSAndroid Build Coastguard Worker }, mBackgroundHandler 967*90c8c64dSAndroid Build Coastguard Worker ); 968*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 969*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 970*90c8c64dSAndroid Build Coastguard Worker } 971*90c8c64dSAndroid Build Coastguard Worker } 972*90c8c64dSAndroid Build Coastguard Worker 973*90c8c64dSAndroid Build Coastguard Worker /** 974*90c8c64dSAndroid Build Coastguard Worker * Configure the given {@link CaptureRequest.Builder} to use auto-focus, auto-exposure, and 975*90c8c64dSAndroid Build Coastguard Worker * auto-white-balance controls if available. 976*90c8c64dSAndroid Build Coastguard Worker * <p/> 977*90c8c64dSAndroid Build Coastguard Worker * Call this only with {@link #mCameraStateLock} held. 978*90c8c64dSAndroid Build Coastguard Worker * 979*90c8c64dSAndroid Build Coastguard Worker * @param builder the builder to configure. 980*90c8c64dSAndroid Build Coastguard Worker */ setup3AControlsLocked(CaptureRequest.Builder builder)981*90c8c64dSAndroid Build Coastguard Worker private void setup3AControlsLocked(CaptureRequest.Builder builder) { 982*90c8c64dSAndroid Build Coastguard Worker // Enable auto-magical 3A run by camera device 983*90c8c64dSAndroid Build Coastguard Worker builder.set(CaptureRequest.CONTROL_MODE, 984*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_MODE_AUTO); 985*90c8c64dSAndroid Build Coastguard Worker 986*90c8c64dSAndroid Build Coastguard Worker Float minFocusDist = 987*90c8c64dSAndroid Build Coastguard Worker mCharacteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); 988*90c8c64dSAndroid Build Coastguard Worker 989*90c8c64dSAndroid Build Coastguard Worker // If MINIMUM_FOCUS_DISTANCE is 0, lens is fixed-focus and we need to skip the AF run. 990*90c8c64dSAndroid Build Coastguard Worker mNoAFRun = (minFocusDist == null || minFocusDist == 0); 991*90c8c64dSAndroid Build Coastguard Worker 992*90c8c64dSAndroid Build Coastguard Worker if (!mNoAFRun) { 993*90c8c64dSAndroid Build Coastguard Worker // If there is a "continuous picture" mode available, use it, otherwise default to AUTO. 994*90c8c64dSAndroid Build Coastguard Worker if (contains(mCharacteristics.get( 995*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES), 996*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)) { 997*90c8c64dSAndroid Build Coastguard Worker builder.set(CaptureRequest.CONTROL_AF_MODE, 998*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 999*90c8c64dSAndroid Build Coastguard Worker } else { 1000*90c8c64dSAndroid Build Coastguard Worker builder.set(CaptureRequest.CONTROL_AF_MODE, 1001*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AF_MODE_AUTO); 1002*90c8c64dSAndroid Build Coastguard Worker } 1003*90c8c64dSAndroid Build Coastguard Worker } 1004*90c8c64dSAndroid Build Coastguard Worker 1005*90c8c64dSAndroid Build Coastguard Worker // If there is an auto-magical flash control mode available, use it, otherwise default to 1006*90c8c64dSAndroid Build Coastguard Worker // the "on" mode, which is guaranteed to always be available. 1007*90c8c64dSAndroid Build Coastguard Worker if (contains(mCharacteristics.get( 1008*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES), 1009*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)) { 1010*90c8c64dSAndroid Build Coastguard Worker builder.set(CaptureRequest.CONTROL_AE_MODE, 1011*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); 1012*90c8c64dSAndroid Build Coastguard Worker } else { 1013*90c8c64dSAndroid Build Coastguard Worker builder.set(CaptureRequest.CONTROL_AE_MODE, 1014*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AE_MODE_ON); 1015*90c8c64dSAndroid Build Coastguard Worker } 1016*90c8c64dSAndroid Build Coastguard Worker 1017*90c8c64dSAndroid Build Coastguard Worker // If there is an auto-magical white balance control mode available, use it. 1018*90c8c64dSAndroid Build Coastguard Worker if (contains(mCharacteristics.get( 1019*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES), 1020*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AWB_MODE_AUTO)) { 1021*90c8c64dSAndroid Build Coastguard Worker // Allow AWB to run auto-magically if this device supports this 1022*90c8c64dSAndroid Build Coastguard Worker builder.set(CaptureRequest.CONTROL_AWB_MODE, 1023*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AWB_MODE_AUTO); 1024*90c8c64dSAndroid Build Coastguard Worker } 1025*90c8c64dSAndroid Build Coastguard Worker } 1026*90c8c64dSAndroid Build Coastguard Worker 1027*90c8c64dSAndroid Build Coastguard Worker /** 1028*90c8c64dSAndroid Build Coastguard Worker * Configure the necessary {@link android.graphics.Matrix} transformation to `mTextureView`, 1029*90c8c64dSAndroid Build Coastguard Worker * and start/restart the preview capture session if necessary. 1030*90c8c64dSAndroid Build Coastguard Worker * <p/> 1031*90c8c64dSAndroid Build Coastguard Worker * This method should be called after the camera state has been initialized in 1032*90c8c64dSAndroid Build Coastguard Worker * setUpCameraOutputs. 1033*90c8c64dSAndroid Build Coastguard Worker * 1034*90c8c64dSAndroid Build Coastguard Worker * @param viewWidth The width of `mTextureView` 1035*90c8c64dSAndroid Build Coastguard Worker * @param viewHeight The height of `mTextureView` 1036*90c8c64dSAndroid Build Coastguard Worker */ configureTransform(int viewWidth, int viewHeight)1037*90c8c64dSAndroid Build Coastguard Worker private void configureTransform(int viewWidth, int viewHeight) { 1038*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 1039*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 1040*90c8c64dSAndroid Build Coastguard Worker if (null == mTextureView || null == activity) { 1041*90c8c64dSAndroid Build Coastguard Worker return; 1042*90c8c64dSAndroid Build Coastguard Worker } 1043*90c8c64dSAndroid Build Coastguard Worker 1044*90c8c64dSAndroid Build Coastguard Worker StreamConfigurationMap map = mCharacteristics.get( 1045*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1046*90c8c64dSAndroid Build Coastguard Worker 1047*90c8c64dSAndroid Build Coastguard Worker // For still image captures, we always use the largest available size. 1048*90c8c64dSAndroid Build Coastguard Worker Size largestJpeg = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), 1049*90c8c64dSAndroid Build Coastguard Worker new CompareSizesByArea()); 1050*90c8c64dSAndroid Build Coastguard Worker 1051*90c8c64dSAndroid Build Coastguard Worker // Find the rotation of the device relative to the native device orientation. 1052*90c8c64dSAndroid Build Coastguard Worker int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 1053*90c8c64dSAndroid Build Coastguard Worker Point displaySize = new Point(); 1054*90c8c64dSAndroid Build Coastguard Worker activity.getWindowManager().getDefaultDisplay().getSize(displaySize); 1055*90c8c64dSAndroid Build Coastguard Worker 1056*90c8c64dSAndroid Build Coastguard Worker // Find the rotation of the device relative to the camera sensor's orientation. 1057*90c8c64dSAndroid Build Coastguard Worker int totalRotation = sensorToDeviceRotation(mCharacteristics, deviceRotation); 1058*90c8c64dSAndroid Build Coastguard Worker 1059*90c8c64dSAndroid Build Coastguard Worker // Swap the view dimensions for calculation as needed if they are rotated relative to 1060*90c8c64dSAndroid Build Coastguard Worker // the sensor. 1061*90c8c64dSAndroid Build Coastguard Worker boolean swappedDimensions = totalRotation == 90 || totalRotation == 270; 1062*90c8c64dSAndroid Build Coastguard Worker int rotatedViewWidth = viewWidth; 1063*90c8c64dSAndroid Build Coastguard Worker int rotatedViewHeight = viewHeight; 1064*90c8c64dSAndroid Build Coastguard Worker int maxPreviewWidth = displaySize.x; 1065*90c8c64dSAndroid Build Coastguard Worker int maxPreviewHeight = displaySize.y; 1066*90c8c64dSAndroid Build Coastguard Worker 1067*90c8c64dSAndroid Build Coastguard Worker if (swappedDimensions) { 1068*90c8c64dSAndroid Build Coastguard Worker rotatedViewWidth = viewHeight; 1069*90c8c64dSAndroid Build Coastguard Worker rotatedViewHeight = viewWidth; 1070*90c8c64dSAndroid Build Coastguard Worker maxPreviewWidth = displaySize.y; 1071*90c8c64dSAndroid Build Coastguard Worker maxPreviewHeight = displaySize.x; 1072*90c8c64dSAndroid Build Coastguard Worker } 1073*90c8c64dSAndroid Build Coastguard Worker 1074*90c8c64dSAndroid Build Coastguard Worker // Preview should not be larger than display size and 1080p. 1075*90c8c64dSAndroid Build Coastguard Worker if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { 1076*90c8c64dSAndroid Build Coastguard Worker maxPreviewWidth = MAX_PREVIEW_WIDTH; 1077*90c8c64dSAndroid Build Coastguard Worker } 1078*90c8c64dSAndroid Build Coastguard Worker 1079*90c8c64dSAndroid Build Coastguard Worker if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { 1080*90c8c64dSAndroid Build Coastguard Worker maxPreviewHeight = MAX_PREVIEW_HEIGHT; 1081*90c8c64dSAndroid Build Coastguard Worker } 1082*90c8c64dSAndroid Build Coastguard Worker 1083*90c8c64dSAndroid Build Coastguard Worker // Find the best preview size for these view dimensions and configured JPEG size. 1084*90c8c64dSAndroid Build Coastguard Worker Size previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), 1085*90c8c64dSAndroid Build Coastguard Worker rotatedViewWidth, rotatedViewHeight, maxPreviewWidth, maxPreviewHeight, 1086*90c8c64dSAndroid Build Coastguard Worker largestJpeg); 1087*90c8c64dSAndroid Build Coastguard Worker 1088*90c8c64dSAndroid Build Coastguard Worker if (swappedDimensions) { 1089*90c8c64dSAndroid Build Coastguard Worker mTextureView.setAspectRatio( 1090*90c8c64dSAndroid Build Coastguard Worker previewSize.getHeight(), previewSize.getWidth()); 1091*90c8c64dSAndroid Build Coastguard Worker } else { 1092*90c8c64dSAndroid Build Coastguard Worker mTextureView.setAspectRatio( 1093*90c8c64dSAndroid Build Coastguard Worker previewSize.getWidth(), previewSize.getHeight()); 1094*90c8c64dSAndroid Build Coastguard Worker } 1095*90c8c64dSAndroid Build Coastguard Worker 1096*90c8c64dSAndroid Build Coastguard Worker // Find rotation of device in degrees (reverse device orientation for front-facing 1097*90c8c64dSAndroid Build Coastguard Worker // cameras). 1098*90c8c64dSAndroid Build Coastguard Worker int rotation = (mCharacteristics.get(CameraCharacteristics.LENS_FACING) == 1099*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics.LENS_FACING_FRONT) ? 1100*90c8c64dSAndroid Build Coastguard Worker (360 + ORIENTATIONS.get(deviceRotation)) % 360 : 1101*90c8c64dSAndroid Build Coastguard Worker (360 - ORIENTATIONS.get(deviceRotation)) % 360; 1102*90c8c64dSAndroid Build Coastguard Worker 1103*90c8c64dSAndroid Build Coastguard Worker Matrix matrix = new Matrix(); 1104*90c8c64dSAndroid Build Coastguard Worker RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); 1105*90c8c64dSAndroid Build Coastguard Worker RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth()); 1106*90c8c64dSAndroid Build Coastguard Worker float centerX = viewRect.centerX(); 1107*90c8c64dSAndroid Build Coastguard Worker float centerY = viewRect.centerY(); 1108*90c8c64dSAndroid Build Coastguard Worker 1109*90c8c64dSAndroid Build Coastguard Worker // Initially, output stream images from the Camera2 API will be rotated to the native 1110*90c8c64dSAndroid Build Coastguard Worker // device orientation from the sensor's orientation, and the TextureView will default to 1111*90c8c64dSAndroid Build Coastguard Worker // scaling these buffers to fill it's view bounds. If the aspect ratios and relative 1112*90c8c64dSAndroid Build Coastguard Worker // orientations are correct, this is fine. 1113*90c8c64dSAndroid Build Coastguard Worker // 1114*90c8c64dSAndroid Build Coastguard Worker // However, if the device orientation has been rotated relative to its native 1115*90c8c64dSAndroid Build Coastguard Worker // orientation so that the TextureView's dimensions are swapped relative to the 1116*90c8c64dSAndroid Build Coastguard Worker // native device orientation, we must do the following to ensure the output stream 1117*90c8c64dSAndroid Build Coastguard Worker // images are not incorrectly scaled by the TextureView: 1118*90c8c64dSAndroid Build Coastguard Worker // - Undo the scale-to-fill from the output buffer's dimensions (i.e. its dimensions 1119*90c8c64dSAndroid Build Coastguard Worker // in the native device orientation) to the TextureView's dimension. 1120*90c8c64dSAndroid Build Coastguard Worker // - Apply a scale-to-fill from the output buffer's rotated dimensions 1121*90c8c64dSAndroid Build Coastguard Worker // (i.e. its dimensions in the current device orientation) to the TextureView's 1122*90c8c64dSAndroid Build Coastguard Worker // dimensions. 1123*90c8c64dSAndroid Build Coastguard Worker // - Apply the rotation from the native device orientation to the current device 1124*90c8c64dSAndroid Build Coastguard Worker // rotation. 1125*90c8c64dSAndroid Build Coastguard Worker if (Surface.ROTATION_90 == deviceRotation || Surface.ROTATION_270 == deviceRotation) { 1126*90c8c64dSAndroid Build Coastguard Worker bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); 1127*90c8c64dSAndroid Build Coastguard Worker matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); 1128*90c8c64dSAndroid Build Coastguard Worker float scale = Math.max( 1129*90c8c64dSAndroid Build Coastguard Worker (float) viewHeight / previewSize.getHeight(), 1130*90c8c64dSAndroid Build Coastguard Worker (float) viewWidth / previewSize.getWidth()); 1131*90c8c64dSAndroid Build Coastguard Worker matrix.postScale(scale, scale, centerX, centerY); 1132*90c8c64dSAndroid Build Coastguard Worker 1133*90c8c64dSAndroid Build Coastguard Worker } 1134*90c8c64dSAndroid Build Coastguard Worker matrix.postRotate(rotation, centerX, centerY); 1135*90c8c64dSAndroid Build Coastguard Worker 1136*90c8c64dSAndroid Build Coastguard Worker mTextureView.setTransform(matrix); 1137*90c8c64dSAndroid Build Coastguard Worker 1138*90c8c64dSAndroid Build Coastguard Worker // Start or restart the active capture session if the preview was initialized or 1139*90c8c64dSAndroid Build Coastguard Worker // if its aspect ratio changed significantly. 1140*90c8c64dSAndroid Build Coastguard Worker if (mPreviewSize == null || !checkAspectsEqual(previewSize, mPreviewSize)) { 1141*90c8c64dSAndroid Build Coastguard Worker mPreviewSize = previewSize; 1142*90c8c64dSAndroid Build Coastguard Worker if (mState != STATE_CLOSED) { 1143*90c8c64dSAndroid Build Coastguard Worker createCameraPreviewSessionLocked(); 1144*90c8c64dSAndroid Build Coastguard Worker } 1145*90c8c64dSAndroid Build Coastguard Worker } 1146*90c8c64dSAndroid Build Coastguard Worker } 1147*90c8c64dSAndroid Build Coastguard Worker } 1148*90c8c64dSAndroid Build Coastguard Worker 1149*90c8c64dSAndroid Build Coastguard Worker /** 1150*90c8c64dSAndroid Build Coastguard Worker * Initiate a still image capture. 1151*90c8c64dSAndroid Build Coastguard Worker * <p/> 1152*90c8c64dSAndroid Build Coastguard Worker * This function sends a capture request that initiates a pre-capture sequence in our state 1153*90c8c64dSAndroid Build Coastguard Worker * machine that waits for auto-focus to finish, ending in a "locked" state where the lens is no 1154*90c8c64dSAndroid Build Coastguard Worker * longer moving, waits for auto-exposure to choose a good exposure value, and waits for 1155*90c8c64dSAndroid Build Coastguard Worker * auto-white-balance to converge. 1156*90c8c64dSAndroid Build Coastguard Worker */ takePicture()1157*90c8c64dSAndroid Build Coastguard Worker private void takePicture() { 1158*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 1159*90c8c64dSAndroid Build Coastguard Worker mPendingUserCaptures++; 1160*90c8c64dSAndroid Build Coastguard Worker 1161*90c8c64dSAndroid Build Coastguard Worker // If we already triggered a pre-capture sequence, or are in a state where we cannot 1162*90c8c64dSAndroid Build Coastguard Worker // do this, return immediately. 1163*90c8c64dSAndroid Build Coastguard Worker if (mState != STATE_PREVIEW) { 1164*90c8c64dSAndroid Build Coastguard Worker return; 1165*90c8c64dSAndroid Build Coastguard Worker } 1166*90c8c64dSAndroid Build Coastguard Worker 1167*90c8c64dSAndroid Build Coastguard Worker try { 1168*90c8c64dSAndroid Build Coastguard Worker // Trigger an auto-focus run if camera is capable. If the camera is already focused, 1169*90c8c64dSAndroid Build Coastguard Worker // this should do nothing. 1170*90c8c64dSAndroid Build Coastguard Worker if (!mNoAFRun) { 1171*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 1172*90c8c64dSAndroid Build Coastguard Worker CameraMetadata.CONTROL_AF_TRIGGER_START); 1173*90c8c64dSAndroid Build Coastguard Worker } 1174*90c8c64dSAndroid Build Coastguard Worker 1175*90c8c64dSAndroid Build Coastguard Worker // If this is not a legacy device, we can also trigger an auto-exposure metering 1176*90c8c64dSAndroid Build Coastguard Worker // run. 1177*90c8c64dSAndroid Build Coastguard Worker if (!isLegacyLocked()) { 1178*90c8c64dSAndroid Build Coastguard Worker // Tell the camera to lock focus. 1179*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 1180*90c8c64dSAndroid Build Coastguard Worker CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START); 1181*90c8c64dSAndroid Build Coastguard Worker } 1182*90c8c64dSAndroid Build Coastguard Worker 1183*90c8c64dSAndroid Build Coastguard Worker // Update state machine to wait for auto-focus, auto-exposure, and 1184*90c8c64dSAndroid Build Coastguard Worker // auto-white-balance (aka. "3A") to converge. 1185*90c8c64dSAndroid Build Coastguard Worker mState = STATE_WAITING_FOR_3A_CONVERGENCE; 1186*90c8c64dSAndroid Build Coastguard Worker 1187*90c8c64dSAndroid Build Coastguard Worker // Start a timer for the pre-capture sequence. 1188*90c8c64dSAndroid Build Coastguard Worker startTimerLocked(); 1189*90c8c64dSAndroid Build Coastguard Worker 1190*90c8c64dSAndroid Build Coastguard Worker // Replace the existing repeating request with one with updated 3A triggers. 1191*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.capture(mPreviewRequestBuilder.build(), mPreCaptureCallback, 1192*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler); 1193*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 1194*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 1195*90c8c64dSAndroid Build Coastguard Worker } 1196*90c8c64dSAndroid Build Coastguard Worker } 1197*90c8c64dSAndroid Build Coastguard Worker } 1198*90c8c64dSAndroid Build Coastguard Worker 1199*90c8c64dSAndroid Build Coastguard Worker /** 1200*90c8c64dSAndroid Build Coastguard Worker * Send a capture request to the camera device that initiates a capture targeting the JPEG and 1201*90c8c64dSAndroid Build Coastguard Worker * RAW outputs. 1202*90c8c64dSAndroid Build Coastguard Worker * <p/> 1203*90c8c64dSAndroid Build Coastguard Worker * Call this only with {@link #mCameraStateLock} held. 1204*90c8c64dSAndroid Build Coastguard Worker */ captureStillPictureLocked()1205*90c8c64dSAndroid Build Coastguard Worker private void captureStillPictureLocked() { 1206*90c8c64dSAndroid Build Coastguard Worker try { 1207*90c8c64dSAndroid Build Coastguard Worker final Activity activity = getActivity(); 1208*90c8c64dSAndroid Build Coastguard Worker if (null == activity || null == mCameraDevice) { 1209*90c8c64dSAndroid Build Coastguard Worker return; 1210*90c8c64dSAndroid Build Coastguard Worker } 1211*90c8c64dSAndroid Build Coastguard Worker // This is the CaptureRequest.Builder that we use to take a picture. 1212*90c8c64dSAndroid Build Coastguard Worker final CaptureRequest.Builder captureBuilder = 1213*90c8c64dSAndroid Build Coastguard Worker mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 1214*90c8c64dSAndroid Build Coastguard Worker 1215*90c8c64dSAndroid Build Coastguard Worker captureBuilder.addTarget(mJpegImageReader.get().getSurface()); 1216*90c8c64dSAndroid Build Coastguard Worker captureBuilder.addTarget(mRawImageReader.get().getSurface()); 1217*90c8c64dSAndroid Build Coastguard Worker 1218*90c8c64dSAndroid Build Coastguard Worker // Use the same AE and AF modes as the preview. 1219*90c8c64dSAndroid Build Coastguard Worker setup3AControlsLocked(captureBuilder); 1220*90c8c64dSAndroid Build Coastguard Worker 1221*90c8c64dSAndroid Build Coastguard Worker // Set orientation. 1222*90c8c64dSAndroid Build Coastguard Worker int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 1223*90c8c64dSAndroid Build Coastguard Worker captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, 1224*90c8c64dSAndroid Build Coastguard Worker sensorToDeviceRotation(mCharacteristics, rotation)); 1225*90c8c64dSAndroid Build Coastguard Worker 1226*90c8c64dSAndroid Build Coastguard Worker // Set request tag to easily track results in callbacks. 1227*90c8c64dSAndroid Build Coastguard Worker captureBuilder.setTag(mRequestCounter.getAndIncrement()); 1228*90c8c64dSAndroid Build Coastguard Worker 1229*90c8c64dSAndroid Build Coastguard Worker CaptureRequest request = captureBuilder.build(); 1230*90c8c64dSAndroid Build Coastguard Worker 1231*90c8c64dSAndroid Build Coastguard Worker // Create an ImageSaverBuilder in which to collect results, and add it to the queue 1232*90c8c64dSAndroid Build Coastguard Worker // of active requests. 1233*90c8c64dSAndroid Build Coastguard Worker ImageSaver.ImageSaverBuilder jpegBuilder = new ImageSaver.ImageSaverBuilder(activity) 1234*90c8c64dSAndroid Build Coastguard Worker .setCharacteristics(mCharacteristics); 1235*90c8c64dSAndroid Build Coastguard Worker ImageSaver.ImageSaverBuilder rawBuilder = new ImageSaver.ImageSaverBuilder(activity) 1236*90c8c64dSAndroid Build Coastguard Worker .setCharacteristics(mCharacteristics); 1237*90c8c64dSAndroid Build Coastguard Worker 1238*90c8c64dSAndroid Build Coastguard Worker mJpegResultQueue.put((int) request.getTag(), jpegBuilder); 1239*90c8c64dSAndroid Build Coastguard Worker mRawResultQueue.put((int) request.getTag(), rawBuilder); 1240*90c8c64dSAndroid Build Coastguard Worker 1241*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.capture(request, mCaptureCallback, mBackgroundHandler); 1242*90c8c64dSAndroid Build Coastguard Worker 1243*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 1244*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 1245*90c8c64dSAndroid Build Coastguard Worker } 1246*90c8c64dSAndroid Build Coastguard Worker } 1247*90c8c64dSAndroid Build Coastguard Worker 1248*90c8c64dSAndroid Build Coastguard Worker /** 1249*90c8c64dSAndroid Build Coastguard Worker * Called after a RAW/JPEG capture has completed; resets the AF trigger state for the 1250*90c8c64dSAndroid Build Coastguard Worker * pre-capture sequence. 1251*90c8c64dSAndroid Build Coastguard Worker * <p/> 1252*90c8c64dSAndroid Build Coastguard Worker * Call this only with {@link #mCameraStateLock} held. 1253*90c8c64dSAndroid Build Coastguard Worker */ finishedCaptureLocked()1254*90c8c64dSAndroid Build Coastguard Worker private void finishedCaptureLocked() { 1255*90c8c64dSAndroid Build Coastguard Worker try { 1256*90c8c64dSAndroid Build Coastguard Worker // Reset the auto-focus trigger in case AF didn't run quickly enough. 1257*90c8c64dSAndroid Build Coastguard Worker if (!mNoAFRun) { 1258*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 1259*90c8c64dSAndroid Build Coastguard Worker CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); 1260*90c8c64dSAndroid Build Coastguard Worker 1261*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.capture(mPreviewRequestBuilder.build(), mPreCaptureCallback, 1262*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler); 1263*90c8c64dSAndroid Build Coastguard Worker 1264*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 1265*90c8c64dSAndroid Build Coastguard Worker CameraMetadata.CONTROL_AF_TRIGGER_IDLE); 1266*90c8c64dSAndroid Build Coastguard Worker } 1267*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 1268*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 1269*90c8c64dSAndroid Build Coastguard Worker } 1270*90c8c64dSAndroid Build Coastguard Worker } 1271*90c8c64dSAndroid Build Coastguard Worker 1272*90c8c64dSAndroid Build Coastguard Worker /** 1273*90c8c64dSAndroid Build Coastguard Worker * Retrieve the next {@link Image} from a reference counted {@link ImageReader}, retaining 1274*90c8c64dSAndroid Build Coastguard Worker * that {@link ImageReader} until that {@link Image} is no longer in use, and set this 1275*90c8c64dSAndroid Build Coastguard Worker * {@link Image} as the result for the next request in the queue of pending requests. If 1276*90c8c64dSAndroid Build Coastguard Worker * all necessary information is available, begin saving the image to a file in a background 1277*90c8c64dSAndroid Build Coastguard Worker * thread. 1278*90c8c64dSAndroid Build Coastguard Worker * 1279*90c8c64dSAndroid Build Coastguard Worker * @param pendingQueue the currently active requests. 1280*90c8c64dSAndroid Build Coastguard Worker * @param reader a reference counted wrapper containing an {@link ImageReader} from which 1281*90c8c64dSAndroid Build Coastguard Worker * to acquire an image. 1282*90c8c64dSAndroid Build Coastguard Worker */ dequeueAndSaveImage(TreeMap<Integer, ImageSaver.ImageSaverBuilder> pendingQueue, RefCountedAutoCloseable<ImageReader> reader)1283*90c8c64dSAndroid Build Coastguard Worker private void dequeueAndSaveImage(TreeMap<Integer, ImageSaver.ImageSaverBuilder> pendingQueue, 1284*90c8c64dSAndroid Build Coastguard Worker RefCountedAutoCloseable<ImageReader> reader) { 1285*90c8c64dSAndroid Build Coastguard Worker synchronized (mCameraStateLock) { 1286*90c8c64dSAndroid Build Coastguard Worker Map.Entry<Integer, ImageSaver.ImageSaverBuilder> entry = 1287*90c8c64dSAndroid Build Coastguard Worker pendingQueue.firstEntry(); 1288*90c8c64dSAndroid Build Coastguard Worker ImageSaver.ImageSaverBuilder builder = entry.getValue(); 1289*90c8c64dSAndroid Build Coastguard Worker 1290*90c8c64dSAndroid Build Coastguard Worker // Increment reference count to prevent ImageReader from being closed while we 1291*90c8c64dSAndroid Build Coastguard Worker // are saving its Images in a background thread (otherwise their resources may 1292*90c8c64dSAndroid Build Coastguard Worker // be freed while we are writing to a file). 1293*90c8c64dSAndroid Build Coastguard Worker if (reader == null || reader.getAndRetain() == null) { 1294*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Paused the activity before we could save the image," + 1295*90c8c64dSAndroid Build Coastguard Worker " ImageReader already closed."); 1296*90c8c64dSAndroid Build Coastguard Worker pendingQueue.remove(entry.getKey()); 1297*90c8c64dSAndroid Build Coastguard Worker return; 1298*90c8c64dSAndroid Build Coastguard Worker } 1299*90c8c64dSAndroid Build Coastguard Worker 1300*90c8c64dSAndroid Build Coastguard Worker Image image; 1301*90c8c64dSAndroid Build Coastguard Worker try { 1302*90c8c64dSAndroid Build Coastguard Worker image = reader.get().acquireNextImage(); 1303*90c8c64dSAndroid Build Coastguard Worker } catch (IllegalStateException e) { 1304*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Too many images queued for saving, dropping image for request: " + 1305*90c8c64dSAndroid Build Coastguard Worker entry.getKey()); 1306*90c8c64dSAndroid Build Coastguard Worker pendingQueue.remove(entry.getKey()); 1307*90c8c64dSAndroid Build Coastguard Worker return; 1308*90c8c64dSAndroid Build Coastguard Worker } 1309*90c8c64dSAndroid Build Coastguard Worker 1310*90c8c64dSAndroid Build Coastguard Worker builder.setRefCountedReader(reader).setImage(image); 1311*90c8c64dSAndroid Build Coastguard Worker 1312*90c8c64dSAndroid Build Coastguard Worker handleCompletionLocked(entry.getKey(), builder, pendingQueue); 1313*90c8c64dSAndroid Build Coastguard Worker } 1314*90c8c64dSAndroid Build Coastguard Worker } 1315*90c8c64dSAndroid Build Coastguard Worker 1316*90c8c64dSAndroid Build Coastguard Worker /** 1317*90c8c64dSAndroid Build Coastguard Worker * Runnable that saves an {@link Image} into the specified {@link File}, and updates 1318*90c8c64dSAndroid Build Coastguard Worker * {@link android.provider.MediaStore} to include the resulting file. 1319*90c8c64dSAndroid Build Coastguard Worker * <p/> 1320*90c8c64dSAndroid Build Coastguard Worker * This can be constructed through an {@link ImageSaverBuilder} as the necessary image and 1321*90c8c64dSAndroid Build Coastguard Worker * result information becomes available. 1322*90c8c64dSAndroid Build Coastguard Worker */ 1323*90c8c64dSAndroid Build Coastguard Worker private static class ImageSaver implements Runnable { 1324*90c8c64dSAndroid Build Coastguard Worker 1325*90c8c64dSAndroid Build Coastguard Worker /** 1326*90c8c64dSAndroid Build Coastguard Worker * The image to save. 1327*90c8c64dSAndroid Build Coastguard Worker */ 1328*90c8c64dSAndroid Build Coastguard Worker private final Image mImage; 1329*90c8c64dSAndroid Build Coastguard Worker /** 1330*90c8c64dSAndroid Build Coastguard Worker * The file we save the image into. 1331*90c8c64dSAndroid Build Coastguard Worker */ 1332*90c8c64dSAndroid Build Coastguard Worker private final File mFile; 1333*90c8c64dSAndroid Build Coastguard Worker 1334*90c8c64dSAndroid Build Coastguard Worker /** 1335*90c8c64dSAndroid Build Coastguard Worker * The CaptureResult for this image capture. 1336*90c8c64dSAndroid Build Coastguard Worker */ 1337*90c8c64dSAndroid Build Coastguard Worker private final CaptureResult mCaptureResult; 1338*90c8c64dSAndroid Build Coastguard Worker 1339*90c8c64dSAndroid Build Coastguard Worker /** 1340*90c8c64dSAndroid Build Coastguard Worker * The CameraCharacteristics for this camera device. 1341*90c8c64dSAndroid Build Coastguard Worker */ 1342*90c8c64dSAndroid Build Coastguard Worker private final CameraCharacteristics mCharacteristics; 1343*90c8c64dSAndroid Build Coastguard Worker 1344*90c8c64dSAndroid Build Coastguard Worker /** 1345*90c8c64dSAndroid Build Coastguard Worker * The Context to use when updating MediaStore with the saved images. 1346*90c8c64dSAndroid Build Coastguard Worker */ 1347*90c8c64dSAndroid Build Coastguard Worker private final Context mContext; 1348*90c8c64dSAndroid Build Coastguard Worker 1349*90c8c64dSAndroid Build Coastguard Worker /** 1350*90c8c64dSAndroid Build Coastguard Worker * A reference counted wrapper for the ImageReader that owns the given image. 1351*90c8c64dSAndroid Build Coastguard Worker */ 1352*90c8c64dSAndroid Build Coastguard Worker private final RefCountedAutoCloseable<ImageReader> mReader; 1353*90c8c64dSAndroid Build Coastguard Worker ImageSaver(Image image, File file, CaptureResult result, CameraCharacteristics characteristics, Context context, RefCountedAutoCloseable<ImageReader> reader)1354*90c8c64dSAndroid Build Coastguard Worker private ImageSaver(Image image, File file, CaptureResult result, 1355*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics characteristics, Context context, 1356*90c8c64dSAndroid Build Coastguard Worker RefCountedAutoCloseable<ImageReader> reader) { 1357*90c8c64dSAndroid Build Coastguard Worker mImage = image; 1358*90c8c64dSAndroid Build Coastguard Worker mFile = file; 1359*90c8c64dSAndroid Build Coastguard Worker mCaptureResult = result; 1360*90c8c64dSAndroid Build Coastguard Worker mCharacteristics = characteristics; 1361*90c8c64dSAndroid Build Coastguard Worker mContext = context; 1362*90c8c64dSAndroid Build Coastguard Worker mReader = reader; 1363*90c8c64dSAndroid Build Coastguard Worker } 1364*90c8c64dSAndroid Build Coastguard Worker 1365*90c8c64dSAndroid Build Coastguard Worker @Override run()1366*90c8c64dSAndroid Build Coastguard Worker public void run() { 1367*90c8c64dSAndroid Build Coastguard Worker boolean success = false; 1368*90c8c64dSAndroid Build Coastguard Worker int format = mImage.getFormat(); 1369*90c8c64dSAndroid Build Coastguard Worker switch (format) { 1370*90c8c64dSAndroid Build Coastguard Worker case ImageFormat.JPEG: { 1371*90c8c64dSAndroid Build Coastguard Worker ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); 1372*90c8c64dSAndroid Build Coastguard Worker byte[] bytes = new byte[buffer.remaining()]; 1373*90c8c64dSAndroid Build Coastguard Worker buffer.get(bytes); 1374*90c8c64dSAndroid Build Coastguard Worker FileOutputStream output = null; 1375*90c8c64dSAndroid Build Coastguard Worker try { 1376*90c8c64dSAndroid Build Coastguard Worker output = new FileOutputStream(mFile); 1377*90c8c64dSAndroid Build Coastguard Worker output.write(bytes); 1378*90c8c64dSAndroid Build Coastguard Worker success = true; 1379*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 1380*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 1381*90c8c64dSAndroid Build Coastguard Worker } finally { 1382*90c8c64dSAndroid Build Coastguard Worker mImage.close(); 1383*90c8c64dSAndroid Build Coastguard Worker closeOutput(output); 1384*90c8c64dSAndroid Build Coastguard Worker } 1385*90c8c64dSAndroid Build Coastguard Worker break; 1386*90c8c64dSAndroid Build Coastguard Worker } 1387*90c8c64dSAndroid Build Coastguard Worker case ImageFormat.RAW_SENSOR: { 1388*90c8c64dSAndroid Build Coastguard Worker DngCreator dngCreator = new DngCreator(mCharacteristics, mCaptureResult); 1389*90c8c64dSAndroid Build Coastguard Worker FileOutputStream output = null; 1390*90c8c64dSAndroid Build Coastguard Worker try { 1391*90c8c64dSAndroid Build Coastguard Worker output = new FileOutputStream(mFile); 1392*90c8c64dSAndroid Build Coastguard Worker dngCreator.writeImage(output, mImage); 1393*90c8c64dSAndroid Build Coastguard Worker success = true; 1394*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 1395*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 1396*90c8c64dSAndroid Build Coastguard Worker } finally { 1397*90c8c64dSAndroid Build Coastguard Worker mImage.close(); 1398*90c8c64dSAndroid Build Coastguard Worker closeOutput(output); 1399*90c8c64dSAndroid Build Coastguard Worker } 1400*90c8c64dSAndroid Build Coastguard Worker break; 1401*90c8c64dSAndroid Build Coastguard Worker } 1402*90c8c64dSAndroid Build Coastguard Worker default: { 1403*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Cannot save image, unexpected image format:" + format); 1404*90c8c64dSAndroid Build Coastguard Worker break; 1405*90c8c64dSAndroid Build Coastguard Worker } 1406*90c8c64dSAndroid Build Coastguard Worker } 1407*90c8c64dSAndroid Build Coastguard Worker 1408*90c8c64dSAndroid Build Coastguard Worker // Decrement reference count to allow ImageReader to be closed to free up resources. 1409*90c8c64dSAndroid Build Coastguard Worker mReader.close(); 1410*90c8c64dSAndroid Build Coastguard Worker 1411*90c8c64dSAndroid Build Coastguard Worker // If saving the file succeeded, update MediaStore. 1412*90c8c64dSAndroid Build Coastguard Worker if (success) { 1413*90c8c64dSAndroid Build Coastguard Worker MediaScannerConnection.scanFile(mContext, new String[]{mFile.getPath()}, 1414*90c8c64dSAndroid Build Coastguard Worker /*mimeTypes*/null, new MediaScannerConnection.MediaScannerConnectionClient() { 1415*90c8c64dSAndroid Build Coastguard Worker @Override 1416*90c8c64dSAndroid Build Coastguard Worker public void onMediaScannerConnected() { 1417*90c8c64dSAndroid Build Coastguard Worker // Do nothing 1418*90c8c64dSAndroid Build Coastguard Worker } 1419*90c8c64dSAndroid Build Coastguard Worker 1420*90c8c64dSAndroid Build Coastguard Worker @Override 1421*90c8c64dSAndroid Build Coastguard Worker public void onScanCompleted(String path, Uri uri) { 1422*90c8c64dSAndroid Build Coastguard Worker Log.i(TAG, "Scanned " + path + ":"); 1423*90c8c64dSAndroid Build Coastguard Worker Log.i(TAG, "-> uri=" + uri); 1424*90c8c64dSAndroid Build Coastguard Worker } 1425*90c8c64dSAndroid Build Coastguard Worker }); 1426*90c8c64dSAndroid Build Coastguard Worker } 1427*90c8c64dSAndroid Build Coastguard Worker } 1428*90c8c64dSAndroid Build Coastguard Worker 1429*90c8c64dSAndroid Build Coastguard Worker /** 1430*90c8c64dSAndroid Build Coastguard Worker * Builder class for constructing {@link ImageSaver}s. 1431*90c8c64dSAndroid Build Coastguard Worker * <p/> 1432*90c8c64dSAndroid Build Coastguard Worker * This class is thread safe. 1433*90c8c64dSAndroid Build Coastguard Worker */ 1434*90c8c64dSAndroid Build Coastguard Worker public static class ImageSaverBuilder { 1435*90c8c64dSAndroid Build Coastguard Worker private Image mImage; 1436*90c8c64dSAndroid Build Coastguard Worker private File mFile; 1437*90c8c64dSAndroid Build Coastguard Worker private CaptureResult mCaptureResult; 1438*90c8c64dSAndroid Build Coastguard Worker private CameraCharacteristics mCharacteristics; 1439*90c8c64dSAndroid Build Coastguard Worker private Context mContext; 1440*90c8c64dSAndroid Build Coastguard Worker private RefCountedAutoCloseable<ImageReader> mReader; 1441*90c8c64dSAndroid Build Coastguard Worker 1442*90c8c64dSAndroid Build Coastguard Worker /** 1443*90c8c64dSAndroid Build Coastguard Worker * Construct a new ImageSaverBuilder using the given {@link Context}. 1444*90c8c64dSAndroid Build Coastguard Worker * 1445*90c8c64dSAndroid Build Coastguard Worker * @param context a {@link Context} to for accessing the 1446*90c8c64dSAndroid Build Coastguard Worker * {@link android.provider.MediaStore}. 1447*90c8c64dSAndroid Build Coastguard Worker */ ImageSaverBuilder(final Context context)1448*90c8c64dSAndroid Build Coastguard Worker public ImageSaverBuilder(final Context context) { 1449*90c8c64dSAndroid Build Coastguard Worker mContext = context; 1450*90c8c64dSAndroid Build Coastguard Worker } 1451*90c8c64dSAndroid Build Coastguard Worker setRefCountedReader( RefCountedAutoCloseable<ImageReader> reader)1452*90c8c64dSAndroid Build Coastguard Worker public synchronized ImageSaverBuilder setRefCountedReader( 1453*90c8c64dSAndroid Build Coastguard Worker RefCountedAutoCloseable<ImageReader> reader) { 1454*90c8c64dSAndroid Build Coastguard Worker if (reader == null) throw new NullPointerException(); 1455*90c8c64dSAndroid Build Coastguard Worker 1456*90c8c64dSAndroid Build Coastguard Worker mReader = reader; 1457*90c8c64dSAndroid Build Coastguard Worker return this; 1458*90c8c64dSAndroid Build Coastguard Worker } 1459*90c8c64dSAndroid Build Coastguard Worker setImage(final Image image)1460*90c8c64dSAndroid Build Coastguard Worker public synchronized ImageSaverBuilder setImage(final Image image) { 1461*90c8c64dSAndroid Build Coastguard Worker if (image == null) throw new NullPointerException(); 1462*90c8c64dSAndroid Build Coastguard Worker mImage = image; 1463*90c8c64dSAndroid Build Coastguard Worker return this; 1464*90c8c64dSAndroid Build Coastguard Worker } 1465*90c8c64dSAndroid Build Coastguard Worker setFile(final File file)1466*90c8c64dSAndroid Build Coastguard Worker public synchronized ImageSaverBuilder setFile(final File file) { 1467*90c8c64dSAndroid Build Coastguard Worker if (file == null) throw new NullPointerException(); 1468*90c8c64dSAndroid Build Coastguard Worker mFile = file; 1469*90c8c64dSAndroid Build Coastguard Worker return this; 1470*90c8c64dSAndroid Build Coastguard Worker } 1471*90c8c64dSAndroid Build Coastguard Worker setResult(final CaptureResult result)1472*90c8c64dSAndroid Build Coastguard Worker public synchronized ImageSaverBuilder setResult(final CaptureResult result) { 1473*90c8c64dSAndroid Build Coastguard Worker if (result == null) throw new NullPointerException(); 1474*90c8c64dSAndroid Build Coastguard Worker mCaptureResult = result; 1475*90c8c64dSAndroid Build Coastguard Worker return this; 1476*90c8c64dSAndroid Build Coastguard Worker } 1477*90c8c64dSAndroid Build Coastguard Worker setCharacteristics( final CameraCharacteristics characteristics)1478*90c8c64dSAndroid Build Coastguard Worker public synchronized ImageSaverBuilder setCharacteristics( 1479*90c8c64dSAndroid Build Coastguard Worker final CameraCharacteristics characteristics) { 1480*90c8c64dSAndroid Build Coastguard Worker if (characteristics == null) throw new NullPointerException(); 1481*90c8c64dSAndroid Build Coastguard Worker mCharacteristics = characteristics; 1482*90c8c64dSAndroid Build Coastguard Worker return this; 1483*90c8c64dSAndroid Build Coastguard Worker } 1484*90c8c64dSAndroid Build Coastguard Worker buildIfComplete()1485*90c8c64dSAndroid Build Coastguard Worker public synchronized ImageSaver buildIfComplete() { 1486*90c8c64dSAndroid Build Coastguard Worker if (!isComplete()) { 1487*90c8c64dSAndroid Build Coastguard Worker return null; 1488*90c8c64dSAndroid Build Coastguard Worker } 1489*90c8c64dSAndroid Build Coastguard Worker return new ImageSaver(mImage, mFile, mCaptureResult, mCharacteristics, mContext, 1490*90c8c64dSAndroid Build Coastguard Worker mReader); 1491*90c8c64dSAndroid Build Coastguard Worker } 1492*90c8c64dSAndroid Build Coastguard Worker getSaveLocation()1493*90c8c64dSAndroid Build Coastguard Worker public synchronized String getSaveLocation() { 1494*90c8c64dSAndroid Build Coastguard Worker return (mFile == null) ? "Unknown" : mFile.toString(); 1495*90c8c64dSAndroid Build Coastguard Worker } 1496*90c8c64dSAndroid Build Coastguard Worker isComplete()1497*90c8c64dSAndroid Build Coastguard Worker private boolean isComplete() { 1498*90c8c64dSAndroid Build Coastguard Worker return mImage != null && mFile != null && mCaptureResult != null 1499*90c8c64dSAndroid Build Coastguard Worker && mCharacteristics != null; 1500*90c8c64dSAndroid Build Coastguard Worker } 1501*90c8c64dSAndroid Build Coastguard Worker } 1502*90c8c64dSAndroid Build Coastguard Worker } 1503*90c8c64dSAndroid Build Coastguard Worker 1504*90c8c64dSAndroid Build Coastguard Worker // Utility classes and methods: 1505*90c8c64dSAndroid Build Coastguard Worker // ********************************************************************************************* 1506*90c8c64dSAndroid Build Coastguard Worker 1507*90c8c64dSAndroid Build Coastguard Worker /** 1508*90c8c64dSAndroid Build Coastguard Worker * Comparator based on area of the given {@link Size} objects. 1509*90c8c64dSAndroid Build Coastguard Worker */ 1510*90c8c64dSAndroid Build Coastguard Worker static class CompareSizesByArea implements Comparator<Size> { 1511*90c8c64dSAndroid Build Coastguard Worker 1512*90c8c64dSAndroid Build Coastguard Worker @Override compare(Size lhs, Size rhs)1513*90c8c64dSAndroid Build Coastguard Worker public int compare(Size lhs, Size rhs) { 1514*90c8c64dSAndroid Build Coastguard Worker // We cast here to ensure the multiplications won't overflow 1515*90c8c64dSAndroid Build Coastguard Worker return Long.signum((long) lhs.getWidth() * lhs.getHeight() - 1516*90c8c64dSAndroid Build Coastguard Worker (long) rhs.getWidth() * rhs.getHeight()); 1517*90c8c64dSAndroid Build Coastguard Worker } 1518*90c8c64dSAndroid Build Coastguard Worker 1519*90c8c64dSAndroid Build Coastguard Worker } 1520*90c8c64dSAndroid Build Coastguard Worker 1521*90c8c64dSAndroid Build Coastguard Worker /** 1522*90c8c64dSAndroid Build Coastguard Worker * A dialog fragment for displaying non-recoverable errors; this {@ling Activity} will be 1523*90c8c64dSAndroid Build Coastguard Worker * finished once the dialog has been acknowledged by the user. 1524*90c8c64dSAndroid Build Coastguard Worker */ 1525*90c8c64dSAndroid Build Coastguard Worker public static class ErrorDialog extends DialogFragment { 1526*90c8c64dSAndroid Build Coastguard Worker 1527*90c8c64dSAndroid Build Coastguard Worker private String mErrorMessage; 1528*90c8c64dSAndroid Build Coastguard Worker ErrorDialog()1529*90c8c64dSAndroid Build Coastguard Worker public ErrorDialog() { 1530*90c8c64dSAndroid Build Coastguard Worker mErrorMessage = "Unknown error occurred!"; 1531*90c8c64dSAndroid Build Coastguard Worker } 1532*90c8c64dSAndroid Build Coastguard Worker 1533*90c8c64dSAndroid Build Coastguard Worker // Build a dialog with a custom message (Fragments require default constructor). buildErrorDialog(String errorMessage)1534*90c8c64dSAndroid Build Coastguard Worker public static ErrorDialog buildErrorDialog(String errorMessage) { 1535*90c8c64dSAndroid Build Coastguard Worker ErrorDialog dialog = new ErrorDialog(); 1536*90c8c64dSAndroid Build Coastguard Worker dialog.mErrorMessage = errorMessage; 1537*90c8c64dSAndroid Build Coastguard Worker return dialog; 1538*90c8c64dSAndroid Build Coastguard Worker } 1539*90c8c64dSAndroid Build Coastguard Worker 1540*90c8c64dSAndroid Build Coastguard Worker @Override onCreateDialog(Bundle savedInstanceState)1541*90c8c64dSAndroid Build Coastguard Worker public Dialog onCreateDialog(Bundle savedInstanceState) { 1542*90c8c64dSAndroid Build Coastguard Worker final Activity activity = getActivity(); 1543*90c8c64dSAndroid Build Coastguard Worker return new AlertDialog.Builder(activity) 1544*90c8c64dSAndroid Build Coastguard Worker .setMessage(mErrorMessage) 1545*90c8c64dSAndroid Build Coastguard Worker .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 1546*90c8c64dSAndroid Build Coastguard Worker @Override 1547*90c8c64dSAndroid Build Coastguard Worker public void onClick(DialogInterface dialogInterface, int i) { 1548*90c8c64dSAndroid Build Coastguard Worker activity.finish(); 1549*90c8c64dSAndroid Build Coastguard Worker } 1550*90c8c64dSAndroid Build Coastguard Worker }) 1551*90c8c64dSAndroid Build Coastguard Worker .create(); 1552*90c8c64dSAndroid Build Coastguard Worker } 1553*90c8c64dSAndroid Build Coastguard Worker } 1554*90c8c64dSAndroid Build Coastguard Worker 1555*90c8c64dSAndroid Build Coastguard Worker /** 1556*90c8c64dSAndroid Build Coastguard Worker * A wrapper for an {@link AutoCloseable} object that implements reference counting to allow 1557*90c8c64dSAndroid Build Coastguard Worker * for resource management. 1558*90c8c64dSAndroid Build Coastguard Worker */ 1559*90c8c64dSAndroid Build Coastguard Worker public static class RefCountedAutoCloseable<T extends AutoCloseable> implements AutoCloseable { 1560*90c8c64dSAndroid Build Coastguard Worker private T mObject; 1561*90c8c64dSAndroid Build Coastguard Worker private long mRefCount = 0; 1562*90c8c64dSAndroid Build Coastguard Worker 1563*90c8c64dSAndroid Build Coastguard Worker /** 1564*90c8c64dSAndroid Build Coastguard Worker * Wrap the given object. 1565*90c8c64dSAndroid Build Coastguard Worker * 1566*90c8c64dSAndroid Build Coastguard Worker * @param object an object to wrap. 1567*90c8c64dSAndroid Build Coastguard Worker */ 1568*90c8c64dSAndroid Build Coastguard Worker public RefCountedAutoCloseable(T object) { 1569*90c8c64dSAndroid Build Coastguard Worker if (object == null) throw new NullPointerException(); 1570*90c8c64dSAndroid Build Coastguard Worker mObject = object; 1571*90c8c64dSAndroid Build Coastguard Worker } 1572*90c8c64dSAndroid Build Coastguard Worker 1573*90c8c64dSAndroid Build Coastguard Worker /** 1574*90c8c64dSAndroid Build Coastguard Worker * Increment the reference count and return the wrapped object. 1575*90c8c64dSAndroid Build Coastguard Worker * 1576*90c8c64dSAndroid Build Coastguard Worker * @return the wrapped object, or null if the object has been released. 1577*90c8c64dSAndroid Build Coastguard Worker */ 1578*90c8c64dSAndroid Build Coastguard Worker public synchronized T getAndRetain() { 1579*90c8c64dSAndroid Build Coastguard Worker if (mRefCount < 0) { 1580*90c8c64dSAndroid Build Coastguard Worker return null; 1581*90c8c64dSAndroid Build Coastguard Worker } 1582*90c8c64dSAndroid Build Coastguard Worker mRefCount++; 1583*90c8c64dSAndroid Build Coastguard Worker return mObject; 1584*90c8c64dSAndroid Build Coastguard Worker } 1585*90c8c64dSAndroid Build Coastguard Worker 1586*90c8c64dSAndroid Build Coastguard Worker /** 1587*90c8c64dSAndroid Build Coastguard Worker * Return the wrapped object. 1588*90c8c64dSAndroid Build Coastguard Worker * 1589*90c8c64dSAndroid Build Coastguard Worker * @return the wrapped object, or null if the object has been released. 1590*90c8c64dSAndroid Build Coastguard Worker */ 1591*90c8c64dSAndroid Build Coastguard Worker public synchronized T get() { 1592*90c8c64dSAndroid Build Coastguard Worker return mObject; 1593*90c8c64dSAndroid Build Coastguard Worker } 1594*90c8c64dSAndroid Build Coastguard Worker 1595*90c8c64dSAndroid Build Coastguard Worker /** 1596*90c8c64dSAndroid Build Coastguard Worker * Decrement the reference count and release the wrapped object if there are no other 1597*90c8c64dSAndroid Build Coastguard Worker * users retaining this object. 1598*90c8c64dSAndroid Build Coastguard Worker */ 1599*90c8c64dSAndroid Build Coastguard Worker @Override 1600*90c8c64dSAndroid Build Coastguard Worker public synchronized void close() { 1601*90c8c64dSAndroid Build Coastguard Worker if (mRefCount >= 0) { 1602*90c8c64dSAndroid Build Coastguard Worker mRefCount--; 1603*90c8c64dSAndroid Build Coastguard Worker if (mRefCount < 0) { 1604*90c8c64dSAndroid Build Coastguard Worker try { 1605*90c8c64dSAndroid Build Coastguard Worker mObject.close(); 1606*90c8c64dSAndroid Build Coastguard Worker } catch (Exception e) { 1607*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException(e); 1608*90c8c64dSAndroid Build Coastguard Worker } finally { 1609*90c8c64dSAndroid Build Coastguard Worker mObject = null; 1610*90c8c64dSAndroid Build Coastguard Worker } 1611*90c8c64dSAndroid Build Coastguard Worker } 1612*90c8c64dSAndroid Build Coastguard Worker } 1613*90c8c64dSAndroid Build Coastguard Worker } 1614*90c8c64dSAndroid Build Coastguard Worker } 1615*90c8c64dSAndroid Build Coastguard Worker 1616*90c8c64dSAndroid Build Coastguard Worker /** 1617*90c8c64dSAndroid Build Coastguard Worker * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that 1618*90c8c64dSAndroid Build Coastguard Worker * is at least as large as the respective texture view size, and that is at most as large as the 1619*90c8c64dSAndroid Build Coastguard Worker * respective max size, and whose aspect ratio matches with the specified value. If such size 1620*90c8c64dSAndroid Build Coastguard Worker * doesn't exist, choose the largest one that is at most as large as the respective max size, 1621*90c8c64dSAndroid Build Coastguard Worker * and whose aspect ratio matches with the specified value. 1622*90c8c64dSAndroid Build Coastguard Worker * 1623*90c8c64dSAndroid Build Coastguard Worker * @param choices The list of sizes that the camera supports for the intended output 1624*90c8c64dSAndroid Build Coastguard Worker * class 1625*90c8c64dSAndroid Build Coastguard Worker * @param textureViewWidth The width of the texture view relative to sensor coordinate 1626*90c8c64dSAndroid Build Coastguard Worker * @param textureViewHeight The height of the texture view relative to sensor coordinate 1627*90c8c64dSAndroid Build Coastguard Worker * @param maxWidth The maximum width that can be chosen 1628*90c8c64dSAndroid Build Coastguard Worker * @param maxHeight The maximum height that can be chosen 1629*90c8c64dSAndroid Build Coastguard Worker * @param aspectRatio The aspect ratio 1630*90c8c64dSAndroid Build Coastguard Worker * @return The optimal {@code Size}, or an arbitrary one if none were big enough 1631*90c8c64dSAndroid Build Coastguard Worker */ 1632*90c8c64dSAndroid Build Coastguard Worker private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, 1633*90c8c64dSAndroid Build Coastguard Worker int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { 1634*90c8c64dSAndroid Build Coastguard Worker // Collect the supported resolutions that are at least as big as the preview Surface 1635*90c8c64dSAndroid Build Coastguard Worker List<Size> bigEnough = new ArrayList<>(); 1636*90c8c64dSAndroid Build Coastguard Worker // Collect the supported resolutions that are smaller than the preview Surface 1637*90c8c64dSAndroid Build Coastguard Worker List<Size> notBigEnough = new ArrayList<>(); 1638*90c8c64dSAndroid Build Coastguard Worker int w = aspectRatio.getWidth(); 1639*90c8c64dSAndroid Build Coastguard Worker int h = aspectRatio.getHeight(); 1640*90c8c64dSAndroid Build Coastguard Worker for (Size option : choices) { 1641*90c8c64dSAndroid Build Coastguard Worker if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && 1642*90c8c64dSAndroid Build Coastguard Worker option.getHeight() == option.getWidth() * h / w) { 1643*90c8c64dSAndroid Build Coastguard Worker if (option.getWidth() >= textureViewWidth && 1644*90c8c64dSAndroid Build Coastguard Worker option.getHeight() >= textureViewHeight) { 1645*90c8c64dSAndroid Build Coastguard Worker bigEnough.add(option); 1646*90c8c64dSAndroid Build Coastguard Worker } else { 1647*90c8c64dSAndroid Build Coastguard Worker notBigEnough.add(option); 1648*90c8c64dSAndroid Build Coastguard Worker } 1649*90c8c64dSAndroid Build Coastguard Worker } 1650*90c8c64dSAndroid Build Coastguard Worker } 1651*90c8c64dSAndroid Build Coastguard Worker 1652*90c8c64dSAndroid Build Coastguard Worker // Pick the smallest of those big enough. If there is no one big enough, pick the 1653*90c8c64dSAndroid Build Coastguard Worker // largest of those not big enough. 1654*90c8c64dSAndroid Build Coastguard Worker if (bigEnough.size() > 0) { 1655*90c8c64dSAndroid Build Coastguard Worker return Collections.min(bigEnough, new CompareSizesByArea()); 1656*90c8c64dSAndroid Build Coastguard Worker } else if (notBigEnough.size() > 0) { 1657*90c8c64dSAndroid Build Coastguard Worker return Collections.max(notBigEnough, new CompareSizesByArea()); 1658*90c8c64dSAndroid Build Coastguard Worker } else { 1659*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Couldn't find any suitable preview size"); 1660*90c8c64dSAndroid Build Coastguard Worker return choices[0]; 1661*90c8c64dSAndroid Build Coastguard Worker } 1662*90c8c64dSAndroid Build Coastguard Worker } 1663*90c8c64dSAndroid Build Coastguard Worker 1664*90c8c64dSAndroid Build Coastguard Worker /** 1665*90c8c64dSAndroid Build Coastguard Worker * Generate a string containing a formatted timestamp with the current date and time. 1666*90c8c64dSAndroid Build Coastguard Worker * 1667*90c8c64dSAndroid Build Coastguard Worker * @return a {@link String} representing a time. 1668*90c8c64dSAndroid Build Coastguard Worker */ 1669*90c8c64dSAndroid Build Coastguard Worker private static String generateTimestamp() { 1670*90c8c64dSAndroid Build Coastguard Worker SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS", Locale.US); 1671*90c8c64dSAndroid Build Coastguard Worker return sdf.format(new Date()); 1672*90c8c64dSAndroid Build Coastguard Worker } 1673*90c8c64dSAndroid Build Coastguard Worker 1674*90c8c64dSAndroid Build Coastguard Worker /** 1675*90c8c64dSAndroid Build Coastguard Worker * Cleanup the given {@link OutputStream}. 1676*90c8c64dSAndroid Build Coastguard Worker * 1677*90c8c64dSAndroid Build Coastguard Worker * @param outputStream the stream to close. 1678*90c8c64dSAndroid Build Coastguard Worker */ 1679*90c8c64dSAndroid Build Coastguard Worker private static void closeOutput(OutputStream outputStream) { 1680*90c8c64dSAndroid Build Coastguard Worker if (null != outputStream) { 1681*90c8c64dSAndroid Build Coastguard Worker try { 1682*90c8c64dSAndroid Build Coastguard Worker outputStream.close(); 1683*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 1684*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 1685*90c8c64dSAndroid Build Coastguard Worker } 1686*90c8c64dSAndroid Build Coastguard Worker } 1687*90c8c64dSAndroid Build Coastguard Worker } 1688*90c8c64dSAndroid Build Coastguard Worker 1689*90c8c64dSAndroid Build Coastguard Worker /** 1690*90c8c64dSAndroid Build Coastguard Worker * Return true if the given array contains the given integer. 1691*90c8c64dSAndroid Build Coastguard Worker * 1692*90c8c64dSAndroid Build Coastguard Worker * @param modes array to check. 1693*90c8c64dSAndroid Build Coastguard Worker * @param mode integer to get for. 1694*90c8c64dSAndroid Build Coastguard Worker * @return true if the array contains the given integer, otherwise false. 1695*90c8c64dSAndroid Build Coastguard Worker */ 1696*90c8c64dSAndroid Build Coastguard Worker private static boolean contains(int[] modes, int mode) { 1697*90c8c64dSAndroid Build Coastguard Worker if (modes == null) { 1698*90c8c64dSAndroid Build Coastguard Worker return false; 1699*90c8c64dSAndroid Build Coastguard Worker } 1700*90c8c64dSAndroid Build Coastguard Worker for (int i : modes) { 1701*90c8c64dSAndroid Build Coastguard Worker if (i == mode) { 1702*90c8c64dSAndroid Build Coastguard Worker return true; 1703*90c8c64dSAndroid Build Coastguard Worker } 1704*90c8c64dSAndroid Build Coastguard Worker } 1705*90c8c64dSAndroid Build Coastguard Worker return false; 1706*90c8c64dSAndroid Build Coastguard Worker } 1707*90c8c64dSAndroid Build Coastguard Worker 1708*90c8c64dSAndroid Build Coastguard Worker /** 1709*90c8c64dSAndroid Build Coastguard Worker * Return true if the two given {@link Size}s have the same aspect ratio. 1710*90c8c64dSAndroid Build Coastguard Worker * 1711*90c8c64dSAndroid Build Coastguard Worker * @param a first {@link Size} to compare. 1712*90c8c64dSAndroid Build Coastguard Worker * @param b second {@link Size} to compare. 1713*90c8c64dSAndroid Build Coastguard Worker * @return true if the sizes have the same aspect ratio, otherwise false. 1714*90c8c64dSAndroid Build Coastguard Worker */ 1715*90c8c64dSAndroid Build Coastguard Worker private static boolean checkAspectsEqual(Size a, Size b) { 1716*90c8c64dSAndroid Build Coastguard Worker double aAspect = a.getWidth() / (double) a.getHeight(); 1717*90c8c64dSAndroid Build Coastguard Worker double bAspect = b.getWidth() / (double) b.getHeight(); 1718*90c8c64dSAndroid Build Coastguard Worker return Math.abs(aAspect - bAspect) <= ASPECT_RATIO_TOLERANCE; 1719*90c8c64dSAndroid Build Coastguard Worker } 1720*90c8c64dSAndroid Build Coastguard Worker 1721*90c8c64dSAndroid Build Coastguard Worker /** 1722*90c8c64dSAndroid Build Coastguard Worker * Rotation need to transform from the camera sensor orientation to the device's current 1723*90c8c64dSAndroid Build Coastguard Worker * orientation. 1724*90c8c64dSAndroid Build Coastguard Worker * 1725*90c8c64dSAndroid Build Coastguard Worker * @param c the {@link CameraCharacteristics} to query for the camera sensor 1726*90c8c64dSAndroid Build Coastguard Worker * orientation. 1727*90c8c64dSAndroid Build Coastguard Worker * @param deviceOrientation the current device orientation relative to the native device 1728*90c8c64dSAndroid Build Coastguard Worker * orientation. 1729*90c8c64dSAndroid Build Coastguard Worker * @return the total rotation from the sensor orientation to the current device orientation. 1730*90c8c64dSAndroid Build Coastguard Worker */ 1731*90c8c64dSAndroid Build Coastguard Worker private static int sensorToDeviceRotation(CameraCharacteristics c, int deviceOrientation) { 1732*90c8c64dSAndroid Build Coastguard Worker int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION); 1733*90c8c64dSAndroid Build Coastguard Worker 1734*90c8c64dSAndroid Build Coastguard Worker // Get device orientation in degrees 1735*90c8c64dSAndroid Build Coastguard Worker deviceOrientation = ORIENTATIONS.get(deviceOrientation); 1736*90c8c64dSAndroid Build Coastguard Worker 1737*90c8c64dSAndroid Build Coastguard Worker // Reverse device orientation for front-facing cameras 1738*90c8c64dSAndroid Build Coastguard Worker if (c.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) { 1739*90c8c64dSAndroid Build Coastguard Worker deviceOrientation = -deviceOrientation; 1740*90c8c64dSAndroid Build Coastguard Worker } 1741*90c8c64dSAndroid Build Coastguard Worker 1742*90c8c64dSAndroid Build Coastguard Worker // Calculate desired JPEG orientation relative to camera orientation to make 1743*90c8c64dSAndroid Build Coastguard Worker // the image upright relative to the device orientation 1744*90c8c64dSAndroid Build Coastguard Worker return (sensorOrientation + deviceOrientation + 360) % 360; 1745*90c8c64dSAndroid Build Coastguard Worker } 1746*90c8c64dSAndroid Build Coastguard Worker 1747*90c8c64dSAndroid Build Coastguard Worker /** 1748*90c8c64dSAndroid Build Coastguard Worker * Shows a {@link Toast} on the UI thread. 1749*90c8c64dSAndroid Build Coastguard Worker * 1750*90c8c64dSAndroid Build Coastguard Worker * @param text The message to show. 1751*90c8c64dSAndroid Build Coastguard Worker */ 1752*90c8c64dSAndroid Build Coastguard Worker private void showToast(String text) { 1753*90c8c64dSAndroid Build Coastguard Worker // We show a Toast by sending request message to mMessageHandler. This makes sure that the 1754*90c8c64dSAndroid Build Coastguard Worker // Toast is shown on the UI thread. 1755*90c8c64dSAndroid Build Coastguard Worker Message message = Message.obtain(); 1756*90c8c64dSAndroid Build Coastguard Worker message.obj = text; 1757*90c8c64dSAndroid Build Coastguard Worker mMessageHandler.sendMessage(message); 1758*90c8c64dSAndroid Build Coastguard Worker } 1759*90c8c64dSAndroid Build Coastguard Worker 1760*90c8c64dSAndroid Build Coastguard Worker /** 1761*90c8c64dSAndroid Build Coastguard Worker * If the given request has been completed, remove it from the queue of active requests and 1762*90c8c64dSAndroid Build Coastguard Worker * send an {@link ImageSaver} with the results from this request to a background thread to 1763*90c8c64dSAndroid Build Coastguard Worker * save a file. 1764*90c8c64dSAndroid Build Coastguard Worker * <p/> 1765*90c8c64dSAndroid Build Coastguard Worker * Call this only with {@link #mCameraStateLock} held. 1766*90c8c64dSAndroid Build Coastguard Worker * 1767*90c8c64dSAndroid Build Coastguard Worker * @param requestId the ID of the {@link CaptureRequest} to handle. 1768*90c8c64dSAndroid Build Coastguard Worker * @param builder the {@link ImageSaver.ImageSaverBuilder} for this request. 1769*90c8c64dSAndroid Build Coastguard Worker * @param queue the queue to remove this request from, if completed. 1770*90c8c64dSAndroid Build Coastguard Worker */ 1771*90c8c64dSAndroid Build Coastguard Worker private void handleCompletionLocked(int requestId, ImageSaver.ImageSaverBuilder builder, 1772*90c8c64dSAndroid Build Coastguard Worker TreeMap<Integer, ImageSaver.ImageSaverBuilder> queue) { 1773*90c8c64dSAndroid Build Coastguard Worker if (builder == null) return; 1774*90c8c64dSAndroid Build Coastguard Worker ImageSaver saver = builder.buildIfComplete(); 1775*90c8c64dSAndroid Build Coastguard Worker if (saver != null) { 1776*90c8c64dSAndroid Build Coastguard Worker queue.remove(requestId); 1777*90c8c64dSAndroid Build Coastguard Worker AsyncTask.THREAD_POOL_EXECUTOR.execute(saver); 1778*90c8c64dSAndroid Build Coastguard Worker } 1779*90c8c64dSAndroid Build Coastguard Worker } 1780*90c8c64dSAndroid Build Coastguard Worker 1781*90c8c64dSAndroid Build Coastguard Worker /** 1782*90c8c64dSAndroid Build Coastguard Worker * Check if we are using a device that only supports the LEGACY hardware level. 1783*90c8c64dSAndroid Build Coastguard Worker * <p/> 1784*90c8c64dSAndroid Build Coastguard Worker * Call this only with {@link #mCameraStateLock} held. 1785*90c8c64dSAndroid Build Coastguard Worker * 1786*90c8c64dSAndroid Build Coastguard Worker * @return true if this is a legacy device. 1787*90c8c64dSAndroid Build Coastguard Worker */ 1788*90c8c64dSAndroid Build Coastguard Worker private boolean isLegacyLocked() { 1789*90c8c64dSAndroid Build Coastguard Worker return mCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == 1790*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; 1791*90c8c64dSAndroid Build Coastguard Worker } 1792*90c8c64dSAndroid Build Coastguard Worker 1793*90c8c64dSAndroid Build Coastguard Worker /** 1794*90c8c64dSAndroid Build Coastguard Worker * Start the timer for the pre-capture sequence. 1795*90c8c64dSAndroid Build Coastguard Worker * <p/> 1796*90c8c64dSAndroid Build Coastguard Worker * Call this only with {@link #mCameraStateLock} held. 1797*90c8c64dSAndroid Build Coastguard Worker */ 1798*90c8c64dSAndroid Build Coastguard Worker private void startTimerLocked() { 1799*90c8c64dSAndroid Build Coastguard Worker mCaptureTimer = SystemClock.elapsedRealtime(); 1800*90c8c64dSAndroid Build Coastguard Worker } 1801*90c8c64dSAndroid Build Coastguard Worker 1802*90c8c64dSAndroid Build Coastguard Worker /** 1803*90c8c64dSAndroid Build Coastguard Worker * Check if the timer for the pre-capture sequence has been hit. 1804*90c8c64dSAndroid Build Coastguard Worker * <p/> 1805*90c8c64dSAndroid Build Coastguard Worker * Call this only with {@link #mCameraStateLock} held. 1806*90c8c64dSAndroid Build Coastguard Worker * 1807*90c8c64dSAndroid Build Coastguard Worker * @return true if the timeout occurred. 1808*90c8c64dSAndroid Build Coastguard Worker */ 1809*90c8c64dSAndroid Build Coastguard Worker private boolean hitTimeoutLocked() { 1810*90c8c64dSAndroid Build Coastguard Worker return (SystemClock.elapsedRealtime() - mCaptureTimer) > PRECAPTURE_TIMEOUT_MS; 1811*90c8c64dSAndroid Build Coastguard Worker } 1812*90c8c64dSAndroid Build Coastguard Worker 1813*90c8c64dSAndroid Build Coastguard Worker /** 1814*90c8c64dSAndroid Build Coastguard Worker * A dialog that explains about the necessary permissions. 1815*90c8c64dSAndroid Build Coastguard Worker */ 1816*90c8c64dSAndroid Build Coastguard Worker public static class PermissionConfirmationDialog extends DialogFragment { 1817*90c8c64dSAndroid Build Coastguard Worker 1818*90c8c64dSAndroid Build Coastguard Worker public static PermissionConfirmationDialog newInstance() { 1819*90c8c64dSAndroid Build Coastguard Worker return new PermissionConfirmationDialog(); 1820*90c8c64dSAndroid Build Coastguard Worker } 1821*90c8c64dSAndroid Build Coastguard Worker 1822*90c8c64dSAndroid Build Coastguard Worker @Override 1823*90c8c64dSAndroid Build Coastguard Worker public Dialog onCreateDialog(Bundle savedInstanceState) { 1824*90c8c64dSAndroid Build Coastguard Worker final Fragment parent = getParentFragment(); 1825*90c8c64dSAndroid Build Coastguard Worker return new AlertDialog.Builder(getActivity()) 1826*90c8c64dSAndroid Build Coastguard Worker .setMessage(R.string.request_permission) 1827*90c8c64dSAndroid Build Coastguard Worker .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 1828*90c8c64dSAndroid Build Coastguard Worker @Override 1829*90c8c64dSAndroid Build Coastguard Worker public void onClick(DialogInterface dialog, int which) { 1830*90c8c64dSAndroid Build Coastguard Worker FragmentCompat.requestPermissions(parent, CAMERA_PERMISSIONS, 1831*90c8c64dSAndroid Build Coastguard Worker REQUEST_CAMERA_PERMISSIONS); 1832*90c8c64dSAndroid Build Coastguard Worker } 1833*90c8c64dSAndroid Build Coastguard Worker }) 1834*90c8c64dSAndroid Build Coastguard Worker .setNegativeButton(android.R.string.cancel, 1835*90c8c64dSAndroid Build Coastguard Worker new DialogInterface.OnClickListener() { 1836*90c8c64dSAndroid Build Coastguard Worker @Override 1837*90c8c64dSAndroid Build Coastguard Worker public void onClick(DialogInterface dialog, int which) { 1838*90c8c64dSAndroid Build Coastguard Worker getActivity().finish(); 1839*90c8c64dSAndroid Build Coastguard Worker } 1840*90c8c64dSAndroid Build Coastguard Worker }) 1841*90c8c64dSAndroid Build Coastguard Worker .create(); 1842*90c8c64dSAndroid Build Coastguard Worker } 1843*90c8c64dSAndroid Build Coastguard Worker 1844*90c8c64dSAndroid Build Coastguard Worker } 1845*90c8c64dSAndroid Build Coastguard Worker 1846*90c8c64dSAndroid Build Coastguard Worker } 1847