1*90c8c64dSAndroid Build Coastguard Worker /* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright 2014 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.camera2basic; 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.content.res.Configuration; 29*90c8c64dSAndroid Build Coastguard Worker import android.graphics.ImageFormat; 30*90c8c64dSAndroid Build Coastguard Worker import android.graphics.Matrix; 31*90c8c64dSAndroid Build Coastguard Worker import android.graphics.Point; 32*90c8c64dSAndroid Build Coastguard Worker import android.graphics.RectF; 33*90c8c64dSAndroid Build Coastguard Worker import android.graphics.SurfaceTexture; 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.CaptureRequest; 41*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.CaptureResult; 42*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.TotalCaptureResult; 43*90c8c64dSAndroid Build Coastguard Worker import android.hardware.camera2.params.StreamConfigurationMap; 44*90c8c64dSAndroid Build Coastguard Worker import android.media.Image; 45*90c8c64dSAndroid Build Coastguard Worker import android.media.ImageReader; 46*90c8c64dSAndroid Build Coastguard Worker import android.os.Bundle; 47*90c8c64dSAndroid Build Coastguard Worker import android.os.Handler; 48*90c8c64dSAndroid Build Coastguard Worker import android.os.HandlerThread; 49*90c8c64dSAndroid Build Coastguard Worker import android.support.annotation.NonNull; 50*90c8c64dSAndroid Build Coastguard Worker import android.support.v13.app.FragmentCompat; 51*90c8c64dSAndroid Build Coastguard Worker import android.support.v4.content.ContextCompat; 52*90c8c64dSAndroid Build Coastguard Worker import android.util.Log; 53*90c8c64dSAndroid Build Coastguard Worker import android.util.Size; 54*90c8c64dSAndroid Build Coastguard Worker import android.util.SparseIntArray; 55*90c8c64dSAndroid Build Coastguard Worker import android.view.LayoutInflater; 56*90c8c64dSAndroid Build Coastguard Worker import android.view.Surface; 57*90c8c64dSAndroid Build Coastguard Worker import android.view.TextureView; 58*90c8c64dSAndroid Build Coastguard Worker import android.view.View; 59*90c8c64dSAndroid Build Coastguard Worker import android.view.ViewGroup; 60*90c8c64dSAndroid Build Coastguard Worker import android.widget.Toast; 61*90c8c64dSAndroid Build Coastguard Worker 62*90c8c64dSAndroid Build Coastguard Worker import java.io.File; 63*90c8c64dSAndroid Build Coastguard Worker import java.io.FileOutputStream; 64*90c8c64dSAndroid Build Coastguard Worker import java.io.IOException; 65*90c8c64dSAndroid Build Coastguard Worker import java.nio.ByteBuffer; 66*90c8c64dSAndroid Build Coastguard Worker import java.util.ArrayList; 67*90c8c64dSAndroid Build Coastguard Worker import java.util.Arrays; 68*90c8c64dSAndroid Build Coastguard Worker import java.util.Collections; 69*90c8c64dSAndroid Build Coastguard Worker import java.util.Comparator; 70*90c8c64dSAndroid Build Coastguard Worker import java.util.List; 71*90c8c64dSAndroid Build Coastguard Worker import java.util.concurrent.Semaphore; 72*90c8c64dSAndroid Build Coastguard Worker import java.util.concurrent.TimeUnit; 73*90c8c64dSAndroid Build Coastguard Worker 74*90c8c64dSAndroid Build Coastguard Worker public class Camera2BasicFragment extends Fragment 75*90c8c64dSAndroid Build Coastguard Worker implements View.OnClickListener, FragmentCompat.OnRequestPermissionsResultCallback { 76*90c8c64dSAndroid Build Coastguard Worker 77*90c8c64dSAndroid Build Coastguard Worker /** 78*90c8c64dSAndroid Build Coastguard Worker * Conversion from screen rotation to JPEG orientation. 79*90c8c64dSAndroid Build Coastguard Worker */ 80*90c8c64dSAndroid Build Coastguard Worker private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); 81*90c8c64dSAndroid Build Coastguard Worker private static final int REQUEST_CAMERA_PERMISSION = 1; 82*90c8c64dSAndroid Build Coastguard Worker private static final String FRAGMENT_DIALOG = "dialog"; 83*90c8c64dSAndroid Build Coastguard Worker 84*90c8c64dSAndroid Build Coastguard Worker static { ORIENTATIONS.append(Surface.ROTATION_0, 90)85*90c8c64dSAndroid Build Coastguard Worker ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0)86*90c8c64dSAndroid Build Coastguard Worker ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270)87*90c8c64dSAndroid Build Coastguard Worker ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180)88*90c8c64dSAndroid Build Coastguard Worker ORIENTATIONS.append(Surface.ROTATION_270, 180); 89*90c8c64dSAndroid Build Coastguard Worker } 90*90c8c64dSAndroid Build Coastguard Worker 91*90c8c64dSAndroid Build Coastguard Worker /** 92*90c8c64dSAndroid Build Coastguard Worker * Tag for the {@link Log}. 93*90c8c64dSAndroid Build Coastguard Worker */ 94*90c8c64dSAndroid Build Coastguard Worker private static final String TAG = "Camera2BasicFragment"; 95*90c8c64dSAndroid Build Coastguard Worker 96*90c8c64dSAndroid Build Coastguard Worker /** 97*90c8c64dSAndroid Build Coastguard Worker * Camera state: Showing camera preview. 98*90c8c64dSAndroid Build Coastguard Worker */ 99*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_PREVIEW = 0; 100*90c8c64dSAndroid Build Coastguard Worker 101*90c8c64dSAndroid Build Coastguard Worker /** 102*90c8c64dSAndroid Build Coastguard Worker * Camera state: Waiting for the focus to be locked. 103*90c8c64dSAndroid Build Coastguard Worker */ 104*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_WAITING_LOCK = 1; 105*90c8c64dSAndroid Build Coastguard Worker 106*90c8c64dSAndroid Build Coastguard Worker /** 107*90c8c64dSAndroid Build Coastguard Worker * Camera state: Waiting for the exposure to be precapture state. 108*90c8c64dSAndroid Build Coastguard Worker */ 109*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_WAITING_PRECAPTURE = 2; 110*90c8c64dSAndroid Build Coastguard Worker 111*90c8c64dSAndroid Build Coastguard Worker /** 112*90c8c64dSAndroid Build Coastguard Worker * Camera state: Waiting for the exposure state to be something other than precapture. 113*90c8c64dSAndroid Build Coastguard Worker */ 114*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_WAITING_NON_PRECAPTURE = 3; 115*90c8c64dSAndroid Build Coastguard Worker 116*90c8c64dSAndroid Build Coastguard Worker /** 117*90c8c64dSAndroid Build Coastguard Worker * Camera state: Picture was taken. 118*90c8c64dSAndroid Build Coastguard Worker */ 119*90c8c64dSAndroid Build Coastguard Worker private static final int STATE_PICTURE_TAKEN = 4; 120*90c8c64dSAndroid Build Coastguard Worker 121*90c8c64dSAndroid Build Coastguard Worker /** 122*90c8c64dSAndroid Build Coastguard Worker * Max preview width that is guaranteed by Camera2 API 123*90c8c64dSAndroid Build Coastguard Worker */ 124*90c8c64dSAndroid Build Coastguard Worker private static final int MAX_PREVIEW_WIDTH = 1920; 125*90c8c64dSAndroid Build Coastguard Worker 126*90c8c64dSAndroid Build Coastguard Worker /** 127*90c8c64dSAndroid Build Coastguard Worker * Max preview height that is guaranteed by Camera2 API 128*90c8c64dSAndroid Build Coastguard Worker */ 129*90c8c64dSAndroid Build Coastguard Worker private static final int MAX_PREVIEW_HEIGHT = 1080; 130*90c8c64dSAndroid Build Coastguard Worker 131*90c8c64dSAndroid Build Coastguard Worker /** 132*90c8c64dSAndroid Build Coastguard Worker * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a 133*90c8c64dSAndroid Build Coastguard Worker * {@link TextureView}. 134*90c8c64dSAndroid Build Coastguard Worker */ 135*90c8c64dSAndroid Build Coastguard Worker private final TextureView.SurfaceTextureListener mSurfaceTextureListener 136*90c8c64dSAndroid Build Coastguard Worker = new TextureView.SurfaceTextureListener() { 137*90c8c64dSAndroid Build Coastguard Worker 138*90c8c64dSAndroid Build Coastguard Worker @Override 139*90c8c64dSAndroid Build Coastguard Worker public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { 140*90c8c64dSAndroid Build Coastguard Worker openCamera(width, height); 141*90c8c64dSAndroid Build Coastguard Worker } 142*90c8c64dSAndroid Build Coastguard Worker 143*90c8c64dSAndroid Build Coastguard Worker @Override 144*90c8c64dSAndroid Build Coastguard Worker public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { 145*90c8c64dSAndroid Build Coastguard Worker configureTransform(width, height); 146*90c8c64dSAndroid Build Coastguard Worker } 147*90c8c64dSAndroid Build Coastguard Worker 148*90c8c64dSAndroid Build Coastguard Worker @Override 149*90c8c64dSAndroid Build Coastguard Worker public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { 150*90c8c64dSAndroid Build Coastguard Worker return true; 151*90c8c64dSAndroid Build Coastguard Worker } 152*90c8c64dSAndroid Build Coastguard Worker 153*90c8c64dSAndroid Build Coastguard Worker @Override 154*90c8c64dSAndroid Build Coastguard Worker public void onSurfaceTextureUpdated(SurfaceTexture texture) { 155*90c8c64dSAndroid Build Coastguard Worker } 156*90c8c64dSAndroid Build Coastguard Worker 157*90c8c64dSAndroid Build Coastguard Worker }; 158*90c8c64dSAndroid Build Coastguard Worker 159*90c8c64dSAndroid Build Coastguard Worker /** 160*90c8c64dSAndroid Build Coastguard Worker * ID of the current {@link CameraDevice}. 161*90c8c64dSAndroid Build Coastguard Worker */ 162*90c8c64dSAndroid Build Coastguard Worker private String mCameraId; 163*90c8c64dSAndroid Build Coastguard Worker 164*90c8c64dSAndroid Build Coastguard Worker /** 165*90c8c64dSAndroid Build Coastguard Worker * An {@link AutoFitTextureView} for camera preview. 166*90c8c64dSAndroid Build Coastguard Worker */ 167*90c8c64dSAndroid Build Coastguard Worker private AutoFitTextureView mTextureView; 168*90c8c64dSAndroid Build Coastguard Worker 169*90c8c64dSAndroid Build Coastguard Worker /** 170*90c8c64dSAndroid Build Coastguard Worker * A {@link CameraCaptureSession } for camera preview. 171*90c8c64dSAndroid Build Coastguard Worker */ 172*90c8c64dSAndroid Build Coastguard Worker private CameraCaptureSession mCaptureSession; 173*90c8c64dSAndroid Build Coastguard Worker 174*90c8c64dSAndroid Build Coastguard Worker /** 175*90c8c64dSAndroid Build Coastguard Worker * A reference to the opened {@link CameraDevice}. 176*90c8c64dSAndroid Build Coastguard Worker */ 177*90c8c64dSAndroid Build Coastguard Worker private CameraDevice mCameraDevice; 178*90c8c64dSAndroid Build Coastguard Worker 179*90c8c64dSAndroid Build Coastguard Worker /** 180*90c8c64dSAndroid Build Coastguard Worker * The {@link android.util.Size} of camera preview. 181*90c8c64dSAndroid Build Coastguard Worker */ 182*90c8c64dSAndroid Build Coastguard Worker private Size mPreviewSize; 183*90c8c64dSAndroid Build Coastguard Worker 184*90c8c64dSAndroid Build Coastguard Worker /** 185*90c8c64dSAndroid Build Coastguard Worker * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state. 186*90c8c64dSAndroid Build Coastguard Worker */ 187*90c8c64dSAndroid Build Coastguard Worker private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { 188*90c8c64dSAndroid Build Coastguard Worker 189*90c8c64dSAndroid Build Coastguard Worker @Override 190*90c8c64dSAndroid Build Coastguard Worker public void onOpened(@NonNull CameraDevice cameraDevice) { 191*90c8c64dSAndroid Build Coastguard Worker // This method is called when the camera is opened. We start camera preview here. 192*90c8c64dSAndroid Build Coastguard Worker mCameraOpenCloseLock.release(); 193*90c8c64dSAndroid Build Coastguard Worker mCameraDevice = cameraDevice; 194*90c8c64dSAndroid Build Coastguard Worker createCameraPreviewSession(); 195*90c8c64dSAndroid Build Coastguard Worker } 196*90c8c64dSAndroid Build Coastguard Worker 197*90c8c64dSAndroid Build Coastguard Worker @Override 198*90c8c64dSAndroid Build Coastguard Worker public void onDisconnected(@NonNull CameraDevice cameraDevice) { 199*90c8c64dSAndroid Build Coastguard Worker mCameraOpenCloseLock.release(); 200*90c8c64dSAndroid Build Coastguard Worker cameraDevice.close(); 201*90c8c64dSAndroid Build Coastguard Worker mCameraDevice = null; 202*90c8c64dSAndroid Build Coastguard Worker } 203*90c8c64dSAndroid Build Coastguard Worker 204*90c8c64dSAndroid Build Coastguard Worker @Override 205*90c8c64dSAndroid Build Coastguard Worker public void onError(@NonNull CameraDevice cameraDevice, int error) { 206*90c8c64dSAndroid Build Coastguard Worker mCameraOpenCloseLock.release(); 207*90c8c64dSAndroid Build Coastguard Worker cameraDevice.close(); 208*90c8c64dSAndroid Build Coastguard Worker mCameraDevice = null; 209*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 210*90c8c64dSAndroid Build Coastguard Worker if (null != activity) { 211*90c8c64dSAndroid Build Coastguard Worker activity.finish(); 212*90c8c64dSAndroid Build Coastguard Worker } 213*90c8c64dSAndroid Build Coastguard Worker } 214*90c8c64dSAndroid Build Coastguard Worker 215*90c8c64dSAndroid Build Coastguard Worker }; 216*90c8c64dSAndroid Build Coastguard Worker 217*90c8c64dSAndroid Build Coastguard Worker /** 218*90c8c64dSAndroid Build Coastguard Worker * An additional thread for running tasks that shouldn't block the UI. 219*90c8c64dSAndroid Build Coastguard Worker */ 220*90c8c64dSAndroid Build Coastguard Worker private HandlerThread mBackgroundThread; 221*90c8c64dSAndroid Build Coastguard Worker 222*90c8c64dSAndroid Build Coastguard Worker /** 223*90c8c64dSAndroid Build Coastguard Worker * A {@link Handler} for running tasks in the background. 224*90c8c64dSAndroid Build Coastguard Worker */ 225*90c8c64dSAndroid Build Coastguard Worker private Handler mBackgroundHandler; 226*90c8c64dSAndroid Build Coastguard Worker 227*90c8c64dSAndroid Build Coastguard Worker /** 228*90c8c64dSAndroid Build Coastguard Worker * An {@link ImageReader} that handles still image capture. 229*90c8c64dSAndroid Build Coastguard Worker */ 230*90c8c64dSAndroid Build Coastguard Worker private ImageReader mImageReader; 231*90c8c64dSAndroid Build Coastguard Worker 232*90c8c64dSAndroid Build Coastguard Worker /** 233*90c8c64dSAndroid Build Coastguard Worker * This is the output file for our picture. 234*90c8c64dSAndroid Build Coastguard Worker */ 235*90c8c64dSAndroid Build Coastguard Worker private File mFile; 236*90c8c64dSAndroid Build Coastguard Worker 237*90c8c64dSAndroid Build Coastguard Worker /** 238*90c8c64dSAndroid Build Coastguard Worker * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a 239*90c8c64dSAndroid Build Coastguard Worker * still image is ready to be saved. 240*90c8c64dSAndroid Build Coastguard Worker */ 241*90c8c64dSAndroid Build Coastguard Worker private final ImageReader.OnImageAvailableListener mOnImageAvailableListener 242*90c8c64dSAndroid Build Coastguard Worker = new ImageReader.OnImageAvailableListener() { 243*90c8c64dSAndroid Build Coastguard Worker 244*90c8c64dSAndroid Build Coastguard Worker @Override 245*90c8c64dSAndroid Build Coastguard Worker public void onImageAvailable(ImageReader reader) { 246*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile)); 247*90c8c64dSAndroid Build Coastguard Worker } 248*90c8c64dSAndroid Build Coastguard Worker 249*90c8c64dSAndroid Build Coastguard Worker }; 250*90c8c64dSAndroid Build Coastguard Worker 251*90c8c64dSAndroid Build Coastguard Worker /** 252*90c8c64dSAndroid Build Coastguard Worker * {@link CaptureRequest.Builder} for the camera preview 253*90c8c64dSAndroid Build Coastguard Worker */ 254*90c8c64dSAndroid Build Coastguard Worker private CaptureRequest.Builder mPreviewRequestBuilder; 255*90c8c64dSAndroid Build Coastguard Worker 256*90c8c64dSAndroid Build Coastguard Worker /** 257*90c8c64dSAndroid Build Coastguard Worker * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder} 258*90c8c64dSAndroid Build Coastguard Worker */ 259*90c8c64dSAndroid Build Coastguard Worker private CaptureRequest mPreviewRequest; 260*90c8c64dSAndroid Build Coastguard Worker 261*90c8c64dSAndroid Build Coastguard Worker /** 262*90c8c64dSAndroid Build Coastguard Worker * The current state of camera state for taking pictures. 263*90c8c64dSAndroid Build Coastguard Worker * 264*90c8c64dSAndroid Build Coastguard Worker * @see #mCaptureCallback 265*90c8c64dSAndroid Build Coastguard Worker */ 266*90c8c64dSAndroid Build Coastguard Worker private int mState = STATE_PREVIEW; 267*90c8c64dSAndroid Build Coastguard Worker 268*90c8c64dSAndroid Build Coastguard Worker /** 269*90c8c64dSAndroid Build Coastguard Worker * A {@link Semaphore} to prevent the app from exiting before closing the camera. 270*90c8c64dSAndroid Build Coastguard Worker */ 271*90c8c64dSAndroid Build Coastguard Worker private Semaphore mCameraOpenCloseLock = new Semaphore(1); 272*90c8c64dSAndroid Build Coastguard Worker 273*90c8c64dSAndroid Build Coastguard Worker /** 274*90c8c64dSAndroid Build Coastguard Worker * Whether the current camera device supports Flash or not. 275*90c8c64dSAndroid Build Coastguard Worker */ 276*90c8c64dSAndroid Build Coastguard Worker private boolean mFlashSupported; 277*90c8c64dSAndroid Build Coastguard Worker 278*90c8c64dSAndroid Build Coastguard Worker /** 279*90c8c64dSAndroid Build Coastguard Worker * Orientation of the camera sensor 280*90c8c64dSAndroid Build Coastguard Worker */ 281*90c8c64dSAndroid Build Coastguard Worker private int mSensorOrientation; 282*90c8c64dSAndroid Build Coastguard Worker 283*90c8c64dSAndroid Build Coastguard Worker /** 284*90c8c64dSAndroid Build Coastguard Worker * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture. 285*90c8c64dSAndroid Build Coastguard Worker */ 286*90c8c64dSAndroid Build Coastguard Worker private CameraCaptureSession.CaptureCallback mCaptureCallback 287*90c8c64dSAndroid Build Coastguard Worker = new CameraCaptureSession.CaptureCallback() { 288*90c8c64dSAndroid Build Coastguard Worker 289*90c8c64dSAndroid Build Coastguard Worker private void process(CaptureResult result) { 290*90c8c64dSAndroid Build Coastguard Worker switch (mState) { 291*90c8c64dSAndroid Build Coastguard Worker case STATE_PREVIEW: { 292*90c8c64dSAndroid Build Coastguard Worker // We have nothing to do when the camera preview is working normally. 293*90c8c64dSAndroid Build Coastguard Worker break; 294*90c8c64dSAndroid Build Coastguard Worker } 295*90c8c64dSAndroid Build Coastguard Worker case STATE_WAITING_LOCK: { 296*90c8c64dSAndroid Build Coastguard Worker Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); 297*90c8c64dSAndroid Build Coastguard Worker if (afState == null) { 298*90c8c64dSAndroid Build Coastguard Worker captureStillPicture(); 299*90c8c64dSAndroid Build Coastguard Worker } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || 300*90c8c64dSAndroid Build Coastguard Worker CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { 301*90c8c64dSAndroid Build Coastguard Worker // CONTROL_AE_STATE can be null on some devices 302*90c8c64dSAndroid Build Coastguard Worker Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 303*90c8c64dSAndroid Build Coastguard Worker if (aeState == null || 304*90c8c64dSAndroid Build Coastguard Worker aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { 305*90c8c64dSAndroid Build Coastguard Worker mState = STATE_PICTURE_TAKEN; 306*90c8c64dSAndroid Build Coastguard Worker captureStillPicture(); 307*90c8c64dSAndroid Build Coastguard Worker } else { 308*90c8c64dSAndroid Build Coastguard Worker runPrecaptureSequence(); 309*90c8c64dSAndroid Build Coastguard Worker } 310*90c8c64dSAndroid Build Coastguard Worker } 311*90c8c64dSAndroid Build Coastguard Worker break; 312*90c8c64dSAndroid Build Coastguard Worker } 313*90c8c64dSAndroid Build Coastguard Worker case STATE_WAITING_PRECAPTURE: { 314*90c8c64dSAndroid Build Coastguard Worker // CONTROL_AE_STATE can be null on some devices 315*90c8c64dSAndroid Build Coastguard Worker Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 316*90c8c64dSAndroid Build Coastguard Worker if (aeState == null || 317*90c8c64dSAndroid Build Coastguard Worker aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || 318*90c8c64dSAndroid Build Coastguard Worker aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { 319*90c8c64dSAndroid Build Coastguard Worker mState = STATE_WAITING_NON_PRECAPTURE; 320*90c8c64dSAndroid Build Coastguard Worker } 321*90c8c64dSAndroid Build Coastguard Worker break; 322*90c8c64dSAndroid Build Coastguard Worker } 323*90c8c64dSAndroid Build Coastguard Worker case STATE_WAITING_NON_PRECAPTURE: { 324*90c8c64dSAndroid Build Coastguard Worker // CONTROL_AE_STATE can be null on some devices 325*90c8c64dSAndroid Build Coastguard Worker Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 326*90c8c64dSAndroid Build Coastguard Worker if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { 327*90c8c64dSAndroid Build Coastguard Worker mState = STATE_PICTURE_TAKEN; 328*90c8c64dSAndroid Build Coastguard Worker captureStillPicture(); 329*90c8c64dSAndroid Build Coastguard Worker } 330*90c8c64dSAndroid Build Coastguard Worker break; 331*90c8c64dSAndroid Build Coastguard Worker } 332*90c8c64dSAndroid Build Coastguard Worker } 333*90c8c64dSAndroid Build Coastguard Worker } 334*90c8c64dSAndroid Build Coastguard Worker 335*90c8c64dSAndroid Build Coastguard Worker @Override 336*90c8c64dSAndroid Build Coastguard Worker public void onCaptureProgressed(@NonNull CameraCaptureSession session, 337*90c8c64dSAndroid Build Coastguard Worker @NonNull CaptureRequest request, 338*90c8c64dSAndroid Build Coastguard Worker @NonNull CaptureResult partialResult) { 339*90c8c64dSAndroid Build Coastguard Worker process(partialResult); 340*90c8c64dSAndroid Build Coastguard Worker } 341*90c8c64dSAndroid Build Coastguard Worker 342*90c8c64dSAndroid Build Coastguard Worker @Override 343*90c8c64dSAndroid Build Coastguard Worker public void onCaptureCompleted(@NonNull CameraCaptureSession session, 344*90c8c64dSAndroid Build Coastguard Worker @NonNull CaptureRequest request, 345*90c8c64dSAndroid Build Coastguard Worker @NonNull TotalCaptureResult result) { 346*90c8c64dSAndroid Build Coastguard Worker process(result); 347*90c8c64dSAndroid Build Coastguard Worker } 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 * Shows a {@link Toast} on the UI thread. 353*90c8c64dSAndroid Build Coastguard Worker * 354*90c8c64dSAndroid Build Coastguard Worker * @param text The message to show 355*90c8c64dSAndroid Build Coastguard Worker */ showToast(final String text)356*90c8c64dSAndroid Build Coastguard Worker private void showToast(final String text) { 357*90c8c64dSAndroid Build Coastguard Worker final Activity activity = getActivity(); 358*90c8c64dSAndroid Build Coastguard Worker if (activity != null) { 359*90c8c64dSAndroid Build Coastguard Worker activity.runOnUiThread(new Runnable() { 360*90c8c64dSAndroid Build Coastguard Worker @Override 361*90c8c64dSAndroid Build Coastguard Worker public void run() { 362*90c8c64dSAndroid Build Coastguard Worker Toast.makeText(activity, text, Toast.LENGTH_SHORT).show(); 363*90c8c64dSAndroid Build Coastguard Worker } 364*90c8c64dSAndroid Build Coastguard Worker }); 365*90c8c64dSAndroid Build Coastguard Worker } 366*90c8c64dSAndroid Build Coastguard Worker } 367*90c8c64dSAndroid Build Coastguard Worker 368*90c8c64dSAndroid Build Coastguard Worker /** 369*90c8c64dSAndroid Build Coastguard Worker * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that 370*90c8c64dSAndroid Build Coastguard Worker * is at least as large as the respective texture view size, and that is at most as large as the 371*90c8c64dSAndroid Build Coastguard Worker * respective max size, and whose aspect ratio matches with the specified value. If such size 372*90c8c64dSAndroid Build Coastguard Worker * doesn't exist, choose the largest one that is at most as large as the respective max size, 373*90c8c64dSAndroid Build Coastguard Worker * and whose aspect ratio matches with the specified value. 374*90c8c64dSAndroid Build Coastguard Worker * 375*90c8c64dSAndroid Build Coastguard Worker * @param choices The list of sizes that the camera supports for the intended output 376*90c8c64dSAndroid Build Coastguard Worker * class 377*90c8c64dSAndroid Build Coastguard Worker * @param textureViewWidth The width of the texture view relative to sensor coordinate 378*90c8c64dSAndroid Build Coastguard Worker * @param textureViewHeight The height of the texture view relative to sensor coordinate 379*90c8c64dSAndroid Build Coastguard Worker * @param maxWidth The maximum width that can be chosen 380*90c8c64dSAndroid Build Coastguard Worker * @param maxHeight The maximum height that can be chosen 381*90c8c64dSAndroid Build Coastguard Worker * @param aspectRatio The aspect ratio 382*90c8c64dSAndroid Build Coastguard Worker * @return The optimal {@code Size}, or an arbitrary one if none were big enough 383*90c8c64dSAndroid Build Coastguard Worker */ chooseOptimalSize(Size[] choices, int textureViewWidth, int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio)384*90c8c64dSAndroid Build Coastguard Worker private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, 385*90c8c64dSAndroid Build Coastguard Worker int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { 386*90c8c64dSAndroid Build Coastguard Worker 387*90c8c64dSAndroid Build Coastguard Worker // Collect the supported resolutions that are at least as big as the preview Surface 388*90c8c64dSAndroid Build Coastguard Worker List<Size> bigEnough = new ArrayList<>(); 389*90c8c64dSAndroid Build Coastguard Worker // Collect the supported resolutions that are smaller than the preview Surface 390*90c8c64dSAndroid Build Coastguard Worker List<Size> notBigEnough = new ArrayList<>(); 391*90c8c64dSAndroid Build Coastguard Worker int w = aspectRatio.getWidth(); 392*90c8c64dSAndroid Build Coastguard Worker int h = aspectRatio.getHeight(); 393*90c8c64dSAndroid Build Coastguard Worker for (Size option : choices) { 394*90c8c64dSAndroid Build Coastguard Worker if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && 395*90c8c64dSAndroid Build Coastguard Worker option.getHeight() == option.getWidth() * h / w) { 396*90c8c64dSAndroid Build Coastguard Worker if (option.getWidth() >= textureViewWidth && 397*90c8c64dSAndroid Build Coastguard Worker option.getHeight() >= textureViewHeight) { 398*90c8c64dSAndroid Build Coastguard Worker bigEnough.add(option); 399*90c8c64dSAndroid Build Coastguard Worker } else { 400*90c8c64dSAndroid Build Coastguard Worker notBigEnough.add(option); 401*90c8c64dSAndroid Build Coastguard Worker } 402*90c8c64dSAndroid Build Coastguard Worker } 403*90c8c64dSAndroid Build Coastguard Worker } 404*90c8c64dSAndroid Build Coastguard Worker 405*90c8c64dSAndroid Build Coastguard Worker // Pick the smallest of those big enough. If there is no one big enough, pick the 406*90c8c64dSAndroid Build Coastguard Worker // largest of those not big enough. 407*90c8c64dSAndroid Build Coastguard Worker if (bigEnough.size() > 0) { 408*90c8c64dSAndroid Build Coastguard Worker return Collections.min(bigEnough, new CompareSizesByArea()); 409*90c8c64dSAndroid Build Coastguard Worker } else if (notBigEnough.size() > 0) { 410*90c8c64dSAndroid Build Coastguard Worker return Collections.max(notBigEnough, new CompareSizesByArea()); 411*90c8c64dSAndroid Build Coastguard Worker } else { 412*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Couldn't find any suitable preview size"); 413*90c8c64dSAndroid Build Coastguard Worker return choices[0]; 414*90c8c64dSAndroid Build Coastguard Worker } 415*90c8c64dSAndroid Build Coastguard Worker } 416*90c8c64dSAndroid Build Coastguard Worker newInstance()417*90c8c64dSAndroid Build Coastguard Worker public static Camera2BasicFragment newInstance() { 418*90c8c64dSAndroid Build Coastguard Worker return new Camera2BasicFragment(); 419*90c8c64dSAndroid Build Coastguard Worker } 420*90c8c64dSAndroid Build Coastguard Worker 421*90c8c64dSAndroid Build Coastguard Worker @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)422*90c8c64dSAndroid Build Coastguard Worker public View onCreateView(LayoutInflater inflater, ViewGroup container, 423*90c8c64dSAndroid Build Coastguard Worker Bundle savedInstanceState) { 424*90c8c64dSAndroid Build Coastguard Worker return inflater.inflate(R.layout.fragment_camera2_basic, container, false); 425*90c8c64dSAndroid Build Coastguard Worker } 426*90c8c64dSAndroid Build Coastguard Worker 427*90c8c64dSAndroid Build Coastguard Worker @Override onViewCreated(final View view, Bundle savedInstanceState)428*90c8c64dSAndroid Build Coastguard Worker public void onViewCreated(final View view, Bundle savedInstanceState) { 429*90c8c64dSAndroid Build Coastguard Worker view.findViewById(R.id.picture).setOnClickListener(this); 430*90c8c64dSAndroid Build Coastguard Worker view.findViewById(R.id.info).setOnClickListener(this); 431*90c8c64dSAndroid Build Coastguard Worker mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture); 432*90c8c64dSAndroid Build Coastguard Worker } 433*90c8c64dSAndroid Build Coastguard Worker 434*90c8c64dSAndroid Build Coastguard Worker @Override onActivityCreated(Bundle savedInstanceState)435*90c8c64dSAndroid Build Coastguard Worker public void onActivityCreated(Bundle savedInstanceState) { 436*90c8c64dSAndroid Build Coastguard Worker super.onActivityCreated(savedInstanceState); 437*90c8c64dSAndroid Build Coastguard Worker mFile = new File(getActivity().getExternalFilesDir(null), "pic.jpg"); 438*90c8c64dSAndroid Build Coastguard Worker } 439*90c8c64dSAndroid Build Coastguard Worker 440*90c8c64dSAndroid Build Coastguard Worker @Override onResume()441*90c8c64dSAndroid Build Coastguard Worker public void onResume() { 442*90c8c64dSAndroid Build Coastguard Worker super.onResume(); 443*90c8c64dSAndroid Build Coastguard Worker startBackgroundThread(); 444*90c8c64dSAndroid Build Coastguard Worker 445*90c8c64dSAndroid Build Coastguard Worker // When the screen is turned off and turned back on, the SurfaceTexture is already 446*90c8c64dSAndroid Build Coastguard Worker // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open 447*90c8c64dSAndroid Build Coastguard Worker // a camera and start preview from here (otherwise, we wait until the surface is ready in 448*90c8c64dSAndroid Build Coastguard Worker // the SurfaceTextureListener). 449*90c8c64dSAndroid Build Coastguard Worker if (mTextureView.isAvailable()) { 450*90c8c64dSAndroid Build Coastguard Worker openCamera(mTextureView.getWidth(), mTextureView.getHeight()); 451*90c8c64dSAndroid Build Coastguard Worker } else { 452*90c8c64dSAndroid Build Coastguard Worker mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); 453*90c8c64dSAndroid Build Coastguard Worker } 454*90c8c64dSAndroid Build Coastguard Worker } 455*90c8c64dSAndroid Build Coastguard Worker 456*90c8c64dSAndroid Build Coastguard Worker @Override onPause()457*90c8c64dSAndroid Build Coastguard Worker public void onPause() { 458*90c8c64dSAndroid Build Coastguard Worker closeCamera(); 459*90c8c64dSAndroid Build Coastguard Worker stopBackgroundThread(); 460*90c8c64dSAndroid Build Coastguard Worker super.onPause(); 461*90c8c64dSAndroid Build Coastguard Worker } 462*90c8c64dSAndroid Build Coastguard Worker requestCameraPermission()463*90c8c64dSAndroid Build Coastguard Worker private void requestCameraPermission() { 464*90c8c64dSAndroid Build Coastguard Worker if (FragmentCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { 465*90c8c64dSAndroid Build Coastguard Worker new ConfirmationDialog().show(getChildFragmentManager(), FRAGMENT_DIALOG); 466*90c8c64dSAndroid Build Coastguard Worker } else { 467*90c8c64dSAndroid Build Coastguard Worker FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 468*90c8c64dSAndroid Build Coastguard Worker REQUEST_CAMERA_PERMISSION); 469*90c8c64dSAndroid Build Coastguard Worker } 470*90c8c64dSAndroid Build Coastguard Worker } 471*90c8c64dSAndroid Build Coastguard Worker 472*90c8c64dSAndroid Build Coastguard Worker @Override onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)473*90c8c64dSAndroid Build Coastguard Worker public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 474*90c8c64dSAndroid Build Coastguard Worker @NonNull int[] grantResults) { 475*90c8c64dSAndroid Build Coastguard Worker if (requestCode == REQUEST_CAMERA_PERMISSION) { 476*90c8c64dSAndroid Build Coastguard Worker if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { 477*90c8c64dSAndroid Build Coastguard Worker ErrorDialog.newInstance(getString(R.string.request_permission)) 478*90c8c64dSAndroid Build Coastguard Worker .show(getChildFragmentManager(), FRAGMENT_DIALOG); 479*90c8c64dSAndroid Build Coastguard Worker } 480*90c8c64dSAndroid Build Coastguard Worker } else { 481*90c8c64dSAndroid Build Coastguard Worker super.onRequestPermissionsResult(requestCode, permissions, grantResults); 482*90c8c64dSAndroid Build Coastguard Worker } 483*90c8c64dSAndroid Build Coastguard Worker } 484*90c8c64dSAndroid Build Coastguard Worker 485*90c8c64dSAndroid Build Coastguard Worker /** 486*90c8c64dSAndroid Build Coastguard Worker * Sets up member variables related to camera. 487*90c8c64dSAndroid Build Coastguard Worker * 488*90c8c64dSAndroid Build Coastguard Worker * @param width The width of available size for camera preview 489*90c8c64dSAndroid Build Coastguard Worker * @param height The height of available size for camera preview 490*90c8c64dSAndroid Build Coastguard Worker */ setUpCameraOutputs(int width, int height)491*90c8c64dSAndroid Build Coastguard Worker private void setUpCameraOutputs(int width, int height) { 492*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 493*90c8c64dSAndroid Build Coastguard Worker CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 494*90c8c64dSAndroid Build Coastguard Worker try { 495*90c8c64dSAndroid Build Coastguard Worker for (String cameraId : manager.getCameraIdList()) { 496*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics characteristics 497*90c8c64dSAndroid Build Coastguard Worker = manager.getCameraCharacteristics(cameraId); 498*90c8c64dSAndroid Build Coastguard Worker 499*90c8c64dSAndroid Build Coastguard Worker // We don't use a front facing camera in this sample. 500*90c8c64dSAndroid Build Coastguard Worker Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); 501*90c8c64dSAndroid Build Coastguard Worker if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { 502*90c8c64dSAndroid Build Coastguard Worker continue; 503*90c8c64dSAndroid Build Coastguard Worker } 504*90c8c64dSAndroid Build Coastguard Worker 505*90c8c64dSAndroid Build Coastguard Worker StreamConfigurationMap map = characteristics.get( 506*90c8c64dSAndroid Build Coastguard Worker CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 507*90c8c64dSAndroid Build Coastguard Worker if (map == null) { 508*90c8c64dSAndroid Build Coastguard Worker continue; 509*90c8c64dSAndroid Build Coastguard Worker } 510*90c8c64dSAndroid Build Coastguard Worker 511*90c8c64dSAndroid Build Coastguard Worker // For still image captures, we use the largest available size. 512*90c8c64dSAndroid Build Coastguard Worker Size largest = Collections.max( 513*90c8c64dSAndroid Build Coastguard Worker Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), 514*90c8c64dSAndroid Build Coastguard Worker new CompareSizesByArea()); 515*90c8c64dSAndroid Build Coastguard Worker mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), 516*90c8c64dSAndroid Build Coastguard Worker ImageFormat.JPEG, /*maxImages*/2); 517*90c8c64dSAndroid Build Coastguard Worker mImageReader.setOnImageAvailableListener( 518*90c8c64dSAndroid Build Coastguard Worker mOnImageAvailableListener, mBackgroundHandler); 519*90c8c64dSAndroid Build Coastguard Worker 520*90c8c64dSAndroid Build Coastguard Worker // Find out if we need to swap dimension to get the preview size relative to sensor 521*90c8c64dSAndroid Build Coastguard Worker // coordinate. 522*90c8c64dSAndroid Build Coastguard Worker int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 523*90c8c64dSAndroid Build Coastguard Worker //noinspection ConstantConditions 524*90c8c64dSAndroid Build Coastguard Worker mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 525*90c8c64dSAndroid Build Coastguard Worker boolean swappedDimensions = false; 526*90c8c64dSAndroid Build Coastguard Worker switch (displayRotation) { 527*90c8c64dSAndroid Build Coastguard Worker case Surface.ROTATION_0: 528*90c8c64dSAndroid Build Coastguard Worker case Surface.ROTATION_180: 529*90c8c64dSAndroid Build Coastguard Worker if (mSensorOrientation == 90 || mSensorOrientation == 270) { 530*90c8c64dSAndroid Build Coastguard Worker swappedDimensions = true; 531*90c8c64dSAndroid Build Coastguard Worker } 532*90c8c64dSAndroid Build Coastguard Worker break; 533*90c8c64dSAndroid Build Coastguard Worker case Surface.ROTATION_90: 534*90c8c64dSAndroid Build Coastguard Worker case Surface.ROTATION_270: 535*90c8c64dSAndroid Build Coastguard Worker if (mSensorOrientation == 0 || mSensorOrientation == 180) { 536*90c8c64dSAndroid Build Coastguard Worker swappedDimensions = true; 537*90c8c64dSAndroid Build Coastguard Worker } 538*90c8c64dSAndroid Build Coastguard Worker break; 539*90c8c64dSAndroid Build Coastguard Worker default: 540*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Display rotation is invalid: " + displayRotation); 541*90c8c64dSAndroid Build Coastguard Worker } 542*90c8c64dSAndroid Build Coastguard Worker 543*90c8c64dSAndroid Build Coastguard Worker Point displaySize = new Point(); 544*90c8c64dSAndroid Build Coastguard Worker activity.getWindowManager().getDefaultDisplay().getSize(displaySize); 545*90c8c64dSAndroid Build Coastguard Worker int rotatedPreviewWidth = width; 546*90c8c64dSAndroid Build Coastguard Worker int rotatedPreviewHeight = height; 547*90c8c64dSAndroid Build Coastguard Worker int maxPreviewWidth = displaySize.x; 548*90c8c64dSAndroid Build Coastguard Worker int maxPreviewHeight = displaySize.y; 549*90c8c64dSAndroid Build Coastguard Worker 550*90c8c64dSAndroid Build Coastguard Worker if (swappedDimensions) { 551*90c8c64dSAndroid Build Coastguard Worker rotatedPreviewWidth = height; 552*90c8c64dSAndroid Build Coastguard Worker rotatedPreviewHeight = width; 553*90c8c64dSAndroid Build Coastguard Worker maxPreviewWidth = displaySize.y; 554*90c8c64dSAndroid Build Coastguard Worker maxPreviewHeight = displaySize.x; 555*90c8c64dSAndroid Build Coastguard Worker } 556*90c8c64dSAndroid Build Coastguard Worker 557*90c8c64dSAndroid Build Coastguard Worker if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { 558*90c8c64dSAndroid Build Coastguard Worker maxPreviewWidth = MAX_PREVIEW_WIDTH; 559*90c8c64dSAndroid Build Coastguard Worker } 560*90c8c64dSAndroid Build Coastguard Worker 561*90c8c64dSAndroid Build Coastguard Worker if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { 562*90c8c64dSAndroid Build Coastguard Worker maxPreviewHeight = MAX_PREVIEW_HEIGHT; 563*90c8c64dSAndroid Build Coastguard Worker } 564*90c8c64dSAndroid Build Coastguard Worker 565*90c8c64dSAndroid Build Coastguard Worker // Danger, W.R.! Attempting to use too large a preview size could exceed the camera 566*90c8c64dSAndroid Build Coastguard Worker // bus' bandwidth limitation, resulting in gorgeous previews but the storage of 567*90c8c64dSAndroid Build Coastguard Worker // garbage capture data. 568*90c8c64dSAndroid Build Coastguard Worker mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), 569*90c8c64dSAndroid Build Coastguard Worker rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, 570*90c8c64dSAndroid Build Coastguard Worker maxPreviewHeight, largest); 571*90c8c64dSAndroid Build Coastguard Worker 572*90c8c64dSAndroid Build Coastguard Worker // We fit the aspect ratio of TextureView to the size of preview we picked. 573*90c8c64dSAndroid Build Coastguard Worker int orientation = getResources().getConfiguration().orientation; 574*90c8c64dSAndroid Build Coastguard Worker if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 575*90c8c64dSAndroid Build Coastguard Worker mTextureView.setAspectRatio( 576*90c8c64dSAndroid Build Coastguard Worker mPreviewSize.getWidth(), mPreviewSize.getHeight()); 577*90c8c64dSAndroid Build Coastguard Worker } else { 578*90c8c64dSAndroid Build Coastguard Worker mTextureView.setAspectRatio( 579*90c8c64dSAndroid Build Coastguard Worker mPreviewSize.getHeight(), mPreviewSize.getWidth()); 580*90c8c64dSAndroid Build Coastguard Worker } 581*90c8c64dSAndroid Build Coastguard Worker 582*90c8c64dSAndroid Build Coastguard Worker // Check if the flash is supported. 583*90c8c64dSAndroid Build Coastguard Worker Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); 584*90c8c64dSAndroid Build Coastguard Worker mFlashSupported = available == null ? false : available; 585*90c8c64dSAndroid Build Coastguard Worker 586*90c8c64dSAndroid Build Coastguard Worker mCameraId = cameraId; 587*90c8c64dSAndroid Build Coastguard Worker return; 588*90c8c64dSAndroid Build Coastguard Worker } 589*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 590*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 591*90c8c64dSAndroid Build Coastguard Worker } catch (NullPointerException e) { 592*90c8c64dSAndroid Build Coastguard Worker // Currently an NPE is thrown when the Camera2API is used but not supported on the 593*90c8c64dSAndroid Build Coastguard Worker // device this code runs. 594*90c8c64dSAndroid Build Coastguard Worker ErrorDialog.newInstance(getString(R.string.camera_error)) 595*90c8c64dSAndroid Build Coastguard Worker .show(getChildFragmentManager(), FRAGMENT_DIALOG); 596*90c8c64dSAndroid Build Coastguard Worker } 597*90c8c64dSAndroid Build Coastguard Worker } 598*90c8c64dSAndroid Build Coastguard Worker 599*90c8c64dSAndroid Build Coastguard Worker /** 600*90c8c64dSAndroid Build Coastguard Worker * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}. 601*90c8c64dSAndroid Build Coastguard Worker */ openCamera(int width, int height)602*90c8c64dSAndroid Build Coastguard Worker private void openCamera(int width, int height) { 603*90c8c64dSAndroid Build Coastguard Worker if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) 604*90c8c64dSAndroid Build Coastguard Worker != PackageManager.PERMISSION_GRANTED) { 605*90c8c64dSAndroid Build Coastguard Worker requestCameraPermission(); 606*90c8c64dSAndroid Build Coastguard Worker return; 607*90c8c64dSAndroid Build Coastguard Worker } 608*90c8c64dSAndroid Build Coastguard Worker setUpCameraOutputs(width, height); 609*90c8c64dSAndroid Build Coastguard Worker configureTransform(width, height); 610*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 611*90c8c64dSAndroid Build Coastguard Worker CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 612*90c8c64dSAndroid Build Coastguard Worker try { 613*90c8c64dSAndroid Build Coastguard Worker if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { 614*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException("Time out waiting to lock camera opening."); 615*90c8c64dSAndroid Build Coastguard Worker } 616*90c8c64dSAndroid Build Coastguard Worker manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); 617*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 618*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 619*90c8c64dSAndroid Build Coastguard Worker } catch (InterruptedException e) { 620*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException("Interrupted while trying to lock camera opening.", e); 621*90c8c64dSAndroid Build Coastguard Worker } 622*90c8c64dSAndroid Build Coastguard Worker } 623*90c8c64dSAndroid Build Coastguard Worker 624*90c8c64dSAndroid Build Coastguard Worker /** 625*90c8c64dSAndroid Build Coastguard Worker * Closes the current {@link CameraDevice}. 626*90c8c64dSAndroid Build Coastguard Worker */ closeCamera()627*90c8c64dSAndroid Build Coastguard Worker private void closeCamera() { 628*90c8c64dSAndroid Build Coastguard Worker try { 629*90c8c64dSAndroid Build Coastguard Worker mCameraOpenCloseLock.acquire(); 630*90c8c64dSAndroid Build Coastguard Worker if (null != mCaptureSession) { 631*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.close(); 632*90c8c64dSAndroid Build Coastguard Worker mCaptureSession = null; 633*90c8c64dSAndroid Build Coastguard Worker } 634*90c8c64dSAndroid Build Coastguard Worker if (null != mCameraDevice) { 635*90c8c64dSAndroid Build Coastguard Worker mCameraDevice.close(); 636*90c8c64dSAndroid Build Coastguard Worker mCameraDevice = null; 637*90c8c64dSAndroid Build Coastguard Worker } 638*90c8c64dSAndroid Build Coastguard Worker if (null != mImageReader) { 639*90c8c64dSAndroid Build Coastguard Worker mImageReader.close(); 640*90c8c64dSAndroid Build Coastguard Worker mImageReader = null; 641*90c8c64dSAndroid Build Coastguard Worker } 642*90c8c64dSAndroid Build Coastguard Worker } catch (InterruptedException e) { 643*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException("Interrupted while trying to lock camera closing.", e); 644*90c8c64dSAndroid Build Coastguard Worker } finally { 645*90c8c64dSAndroid Build Coastguard Worker mCameraOpenCloseLock.release(); 646*90c8c64dSAndroid Build Coastguard Worker } 647*90c8c64dSAndroid Build Coastguard Worker } 648*90c8c64dSAndroid Build Coastguard Worker 649*90c8c64dSAndroid Build Coastguard Worker /** 650*90c8c64dSAndroid Build Coastguard Worker * Starts a background thread and its {@link Handler}. 651*90c8c64dSAndroid Build Coastguard Worker */ startBackgroundThread()652*90c8c64dSAndroid Build Coastguard Worker private void startBackgroundThread() { 653*90c8c64dSAndroid Build Coastguard Worker mBackgroundThread = new HandlerThread("CameraBackground"); 654*90c8c64dSAndroid Build Coastguard Worker mBackgroundThread.start(); 655*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); 656*90c8c64dSAndroid Build Coastguard Worker } 657*90c8c64dSAndroid Build Coastguard Worker 658*90c8c64dSAndroid Build Coastguard Worker /** 659*90c8c64dSAndroid Build Coastguard Worker * Stops the background thread and its {@link Handler}. 660*90c8c64dSAndroid Build Coastguard Worker */ stopBackgroundThread()661*90c8c64dSAndroid Build Coastguard Worker private void stopBackgroundThread() { 662*90c8c64dSAndroid Build Coastguard Worker mBackgroundThread.quitSafely(); 663*90c8c64dSAndroid Build Coastguard Worker try { 664*90c8c64dSAndroid Build Coastguard Worker mBackgroundThread.join(); 665*90c8c64dSAndroid Build Coastguard Worker mBackgroundThread = null; 666*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler = null; 667*90c8c64dSAndroid Build Coastguard Worker } catch (InterruptedException e) { 668*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 669*90c8c64dSAndroid Build Coastguard Worker } 670*90c8c64dSAndroid Build Coastguard Worker } 671*90c8c64dSAndroid Build Coastguard Worker 672*90c8c64dSAndroid Build Coastguard Worker /** 673*90c8c64dSAndroid Build Coastguard Worker * Creates a new {@link CameraCaptureSession} for camera preview. 674*90c8c64dSAndroid Build Coastguard Worker */ createCameraPreviewSession()675*90c8c64dSAndroid Build Coastguard Worker private void createCameraPreviewSession() { 676*90c8c64dSAndroid Build Coastguard Worker try { 677*90c8c64dSAndroid Build Coastguard Worker SurfaceTexture texture = mTextureView.getSurfaceTexture(); 678*90c8c64dSAndroid Build Coastguard Worker assert texture != null; 679*90c8c64dSAndroid Build Coastguard Worker 680*90c8c64dSAndroid Build Coastguard Worker // We configure the size of default buffer to be the size of camera preview we want. 681*90c8c64dSAndroid Build Coastguard Worker texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 682*90c8c64dSAndroid Build Coastguard Worker 683*90c8c64dSAndroid Build Coastguard Worker // This is the output Surface we need to start preview. 684*90c8c64dSAndroid Build Coastguard Worker Surface surface = new Surface(texture); 685*90c8c64dSAndroid Build Coastguard Worker 686*90c8c64dSAndroid Build Coastguard Worker // We set up a CaptureRequest.Builder with the output Surface. 687*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder 688*90c8c64dSAndroid Build Coastguard Worker = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 689*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.addTarget(surface); 690*90c8c64dSAndroid Build Coastguard Worker 691*90c8c64dSAndroid Build Coastguard Worker // Here, we create a CameraCaptureSession for camera preview. 692*90c8c64dSAndroid Build Coastguard Worker mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), 693*90c8c64dSAndroid Build Coastguard Worker new CameraCaptureSession.StateCallback() { 694*90c8c64dSAndroid Build Coastguard Worker 695*90c8c64dSAndroid Build Coastguard Worker @Override 696*90c8c64dSAndroid Build Coastguard Worker public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { 697*90c8c64dSAndroid Build Coastguard Worker // The camera is already closed 698*90c8c64dSAndroid Build Coastguard Worker if (null == mCameraDevice) { 699*90c8c64dSAndroid Build Coastguard Worker return; 700*90c8c64dSAndroid Build Coastguard Worker } 701*90c8c64dSAndroid Build Coastguard Worker 702*90c8c64dSAndroid Build Coastguard Worker // When the session is ready, we start displaying the preview. 703*90c8c64dSAndroid Build Coastguard Worker mCaptureSession = cameraCaptureSession; 704*90c8c64dSAndroid Build Coastguard Worker try { 705*90c8c64dSAndroid Build Coastguard Worker // Auto focus should be continuous for camera preview. 706*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, 707*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 708*90c8c64dSAndroid Build Coastguard Worker // Flash is automatically enabled when necessary. 709*90c8c64dSAndroid Build Coastguard Worker setAutoFlash(mPreviewRequestBuilder); 710*90c8c64dSAndroid Build Coastguard Worker 711*90c8c64dSAndroid Build Coastguard Worker // Finally, we start displaying the camera preview. 712*90c8c64dSAndroid Build Coastguard Worker mPreviewRequest = mPreviewRequestBuilder.build(); 713*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.setRepeatingRequest(mPreviewRequest, 714*90c8c64dSAndroid Build Coastguard Worker mCaptureCallback, mBackgroundHandler); 715*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 716*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 717*90c8c64dSAndroid Build Coastguard Worker } 718*90c8c64dSAndroid Build Coastguard Worker } 719*90c8c64dSAndroid Build Coastguard Worker 720*90c8c64dSAndroid Build Coastguard Worker @Override 721*90c8c64dSAndroid Build Coastguard Worker public void onConfigureFailed( 722*90c8c64dSAndroid Build Coastguard Worker @NonNull CameraCaptureSession cameraCaptureSession) { 723*90c8c64dSAndroid Build Coastguard Worker showToast("Failed"); 724*90c8c64dSAndroid Build Coastguard Worker } 725*90c8c64dSAndroid Build Coastguard Worker }, null 726*90c8c64dSAndroid Build Coastguard Worker ); 727*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 728*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 729*90c8c64dSAndroid Build Coastguard Worker } 730*90c8c64dSAndroid Build Coastguard Worker } 731*90c8c64dSAndroid Build Coastguard Worker 732*90c8c64dSAndroid Build Coastguard Worker /** 733*90c8c64dSAndroid Build Coastguard Worker * Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`. 734*90c8c64dSAndroid Build Coastguard Worker * This method should be called after the camera preview size is determined in 735*90c8c64dSAndroid Build Coastguard Worker * setUpCameraOutputs and also the size of `mTextureView` is fixed. 736*90c8c64dSAndroid Build Coastguard Worker * 737*90c8c64dSAndroid Build Coastguard Worker * @param viewWidth The width of `mTextureView` 738*90c8c64dSAndroid Build Coastguard Worker * @param viewHeight The height of `mTextureView` 739*90c8c64dSAndroid Build Coastguard Worker */ configureTransform(int viewWidth, int viewHeight)740*90c8c64dSAndroid Build Coastguard Worker private void configureTransform(int viewWidth, int viewHeight) { 741*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 742*90c8c64dSAndroid Build Coastguard Worker if (null == mTextureView || null == mPreviewSize || null == activity) { 743*90c8c64dSAndroid Build Coastguard Worker return; 744*90c8c64dSAndroid Build Coastguard Worker } 745*90c8c64dSAndroid Build Coastguard Worker int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 746*90c8c64dSAndroid Build Coastguard Worker Matrix matrix = new Matrix(); 747*90c8c64dSAndroid Build Coastguard Worker RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); 748*90c8c64dSAndroid Build Coastguard Worker RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); 749*90c8c64dSAndroid Build Coastguard Worker float centerX = viewRect.centerX(); 750*90c8c64dSAndroid Build Coastguard Worker float centerY = viewRect.centerY(); 751*90c8c64dSAndroid Build Coastguard Worker if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { 752*90c8c64dSAndroid Build Coastguard Worker bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); 753*90c8c64dSAndroid Build Coastguard Worker matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); 754*90c8c64dSAndroid Build Coastguard Worker float scale = Math.max( 755*90c8c64dSAndroid Build Coastguard Worker (float) viewHeight / mPreviewSize.getHeight(), 756*90c8c64dSAndroid Build Coastguard Worker (float) viewWidth / mPreviewSize.getWidth()); 757*90c8c64dSAndroid Build Coastguard Worker matrix.postScale(scale, scale, centerX, centerY); 758*90c8c64dSAndroid Build Coastguard Worker matrix.postRotate(90 * (rotation - 2), centerX, centerY); 759*90c8c64dSAndroid Build Coastguard Worker } else if (Surface.ROTATION_180 == rotation) { 760*90c8c64dSAndroid Build Coastguard Worker matrix.postRotate(180, centerX, centerY); 761*90c8c64dSAndroid Build Coastguard Worker } 762*90c8c64dSAndroid Build Coastguard Worker mTextureView.setTransform(matrix); 763*90c8c64dSAndroid Build Coastguard Worker } 764*90c8c64dSAndroid Build Coastguard Worker 765*90c8c64dSAndroid Build Coastguard Worker /** 766*90c8c64dSAndroid Build Coastguard Worker * Initiate a still image capture. 767*90c8c64dSAndroid Build Coastguard Worker */ takePicture()768*90c8c64dSAndroid Build Coastguard Worker private void takePicture() { 769*90c8c64dSAndroid Build Coastguard Worker lockFocus(); 770*90c8c64dSAndroid Build Coastguard Worker } 771*90c8c64dSAndroid Build Coastguard Worker 772*90c8c64dSAndroid Build Coastguard Worker /** 773*90c8c64dSAndroid Build Coastguard Worker * Lock the focus as the first step for a still image capture. 774*90c8c64dSAndroid Build Coastguard Worker */ lockFocus()775*90c8c64dSAndroid Build Coastguard Worker private void lockFocus() { 776*90c8c64dSAndroid Build Coastguard Worker try { 777*90c8c64dSAndroid Build Coastguard Worker // This is how to tell the camera to lock focus. 778*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 779*90c8c64dSAndroid Build Coastguard Worker CameraMetadata.CONTROL_AF_TRIGGER_START); 780*90c8c64dSAndroid Build Coastguard Worker // Tell #mCaptureCallback to wait for the lock. 781*90c8c64dSAndroid Build Coastguard Worker mState = STATE_WAITING_LOCK; 782*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 783*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler); 784*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 785*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 786*90c8c64dSAndroid Build Coastguard Worker } 787*90c8c64dSAndroid Build Coastguard Worker } 788*90c8c64dSAndroid Build Coastguard Worker 789*90c8c64dSAndroid Build Coastguard Worker /** 790*90c8c64dSAndroid Build Coastguard Worker * Run the precapture sequence for capturing a still image. This method should be called when 791*90c8c64dSAndroid Build Coastguard Worker * we get a response in {@link #mCaptureCallback} from {@link #lockFocus()}. 792*90c8c64dSAndroid Build Coastguard Worker */ runPrecaptureSequence()793*90c8c64dSAndroid Build Coastguard Worker private void runPrecaptureSequence() { 794*90c8c64dSAndroid Build Coastguard Worker try { 795*90c8c64dSAndroid Build Coastguard Worker // This is how to tell the camera to trigger. 796*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 797*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 798*90c8c64dSAndroid Build Coastguard Worker // Tell #mCaptureCallback to wait for the precapture sequence to be set. 799*90c8c64dSAndroid Build Coastguard Worker mState = STATE_WAITING_PRECAPTURE; 800*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 801*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler); 802*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 803*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 804*90c8c64dSAndroid Build Coastguard Worker } 805*90c8c64dSAndroid Build Coastguard Worker } 806*90c8c64dSAndroid Build Coastguard Worker 807*90c8c64dSAndroid Build Coastguard Worker /** 808*90c8c64dSAndroid Build Coastguard Worker * Capture a still picture. This method should be called when we get a response in 809*90c8c64dSAndroid Build Coastguard Worker * {@link #mCaptureCallback} from both {@link #lockFocus()}. 810*90c8c64dSAndroid Build Coastguard Worker */ captureStillPicture()811*90c8c64dSAndroid Build Coastguard Worker private void captureStillPicture() { 812*90c8c64dSAndroid Build Coastguard Worker try { 813*90c8c64dSAndroid Build Coastguard Worker final Activity activity = getActivity(); 814*90c8c64dSAndroid Build Coastguard Worker if (null == activity || null == mCameraDevice) { 815*90c8c64dSAndroid Build Coastguard Worker return; 816*90c8c64dSAndroid Build Coastguard Worker } 817*90c8c64dSAndroid Build Coastguard Worker // This is the CaptureRequest.Builder that we use to take a picture. 818*90c8c64dSAndroid Build Coastguard Worker final CaptureRequest.Builder captureBuilder = 819*90c8c64dSAndroid Build Coastguard Worker mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 820*90c8c64dSAndroid Build Coastguard Worker captureBuilder.addTarget(mImageReader.getSurface()); 821*90c8c64dSAndroid Build Coastguard Worker 822*90c8c64dSAndroid Build Coastguard Worker // Use the same AE and AF modes as the preview. 823*90c8c64dSAndroid Build Coastguard Worker captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, 824*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 825*90c8c64dSAndroid Build Coastguard Worker setAutoFlash(captureBuilder); 826*90c8c64dSAndroid Build Coastguard Worker 827*90c8c64dSAndroid Build Coastguard Worker // Orientation 828*90c8c64dSAndroid Build Coastguard Worker int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 829*90c8c64dSAndroid Build Coastguard Worker captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation)); 830*90c8c64dSAndroid Build Coastguard Worker 831*90c8c64dSAndroid Build Coastguard Worker CameraCaptureSession.CaptureCallback CaptureCallback 832*90c8c64dSAndroid Build Coastguard Worker = new CameraCaptureSession.CaptureCallback() { 833*90c8c64dSAndroid Build Coastguard Worker 834*90c8c64dSAndroid Build Coastguard Worker @Override 835*90c8c64dSAndroid Build Coastguard Worker public void onCaptureCompleted(@NonNull CameraCaptureSession session, 836*90c8c64dSAndroid Build Coastguard Worker @NonNull CaptureRequest request, 837*90c8c64dSAndroid Build Coastguard Worker @NonNull TotalCaptureResult result) { 838*90c8c64dSAndroid Build Coastguard Worker showToast("Saved: " + mFile); 839*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, mFile.toString()); 840*90c8c64dSAndroid Build Coastguard Worker unlockFocus(); 841*90c8c64dSAndroid Build Coastguard Worker } 842*90c8c64dSAndroid Build Coastguard Worker }; 843*90c8c64dSAndroid Build Coastguard Worker 844*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.stopRepeating(); 845*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null); 846*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 847*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 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 * Retrieves the JPEG orientation from the specified screen rotation. 853*90c8c64dSAndroid Build Coastguard Worker * 854*90c8c64dSAndroid Build Coastguard Worker * @param rotation The screen rotation. 855*90c8c64dSAndroid Build Coastguard Worker * @return The JPEG orientation (one of 0, 90, 270, and 360) 856*90c8c64dSAndroid Build Coastguard Worker */ getOrientation(int rotation)857*90c8c64dSAndroid Build Coastguard Worker private int getOrientation(int rotation) { 858*90c8c64dSAndroid Build Coastguard Worker // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X) 859*90c8c64dSAndroid Build Coastguard Worker // We have to take that into account and rotate JPEG properly. 860*90c8c64dSAndroid Build Coastguard Worker // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS. 861*90c8c64dSAndroid Build Coastguard Worker // For devices with orientation of 270, we need to rotate the JPEG 180 degrees. 862*90c8c64dSAndroid Build Coastguard Worker return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360; 863*90c8c64dSAndroid Build Coastguard Worker } 864*90c8c64dSAndroid Build Coastguard Worker 865*90c8c64dSAndroid Build Coastguard Worker /** 866*90c8c64dSAndroid Build Coastguard Worker * Unlock the focus. This method should be called when still image capture sequence is 867*90c8c64dSAndroid Build Coastguard Worker * finished. 868*90c8c64dSAndroid Build Coastguard Worker */ unlockFocus()869*90c8c64dSAndroid Build Coastguard Worker private void unlockFocus() { 870*90c8c64dSAndroid Build Coastguard Worker try { 871*90c8c64dSAndroid Build Coastguard Worker // Reset the auto-focus trigger 872*90c8c64dSAndroid Build Coastguard Worker mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 873*90c8c64dSAndroid Build Coastguard Worker CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); 874*90c8c64dSAndroid Build Coastguard Worker setAutoFlash(mPreviewRequestBuilder); 875*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 876*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler); 877*90c8c64dSAndroid Build Coastguard Worker // After this, the camera will go back to the normal state of preview. 878*90c8c64dSAndroid Build Coastguard Worker mState = STATE_PREVIEW; 879*90c8c64dSAndroid Build Coastguard Worker mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, 880*90c8c64dSAndroid Build Coastguard Worker mBackgroundHandler); 881*90c8c64dSAndroid Build Coastguard Worker } catch (CameraAccessException e) { 882*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 883*90c8c64dSAndroid Build Coastguard Worker } 884*90c8c64dSAndroid Build Coastguard Worker } 885*90c8c64dSAndroid Build Coastguard Worker 886*90c8c64dSAndroid Build Coastguard Worker @Override onClick(View view)887*90c8c64dSAndroid Build Coastguard Worker public void onClick(View view) { 888*90c8c64dSAndroid Build Coastguard Worker switch (view.getId()) { 889*90c8c64dSAndroid Build Coastguard Worker case R.id.picture: { 890*90c8c64dSAndroid Build Coastguard Worker takePicture(); 891*90c8c64dSAndroid Build Coastguard Worker break; 892*90c8c64dSAndroid Build Coastguard Worker } 893*90c8c64dSAndroid Build Coastguard Worker case R.id.info: { 894*90c8c64dSAndroid Build Coastguard Worker Activity activity = getActivity(); 895*90c8c64dSAndroid Build Coastguard Worker if (null != activity) { 896*90c8c64dSAndroid Build Coastguard Worker new AlertDialog.Builder(activity) 897*90c8c64dSAndroid Build Coastguard Worker .setMessage(R.string.intro_message) 898*90c8c64dSAndroid Build Coastguard Worker .setPositiveButton(android.R.string.ok, null) 899*90c8c64dSAndroid Build Coastguard Worker .show(); 900*90c8c64dSAndroid Build Coastguard Worker } 901*90c8c64dSAndroid Build Coastguard Worker break; 902*90c8c64dSAndroid Build Coastguard Worker } 903*90c8c64dSAndroid Build Coastguard Worker } 904*90c8c64dSAndroid Build Coastguard Worker } 905*90c8c64dSAndroid Build Coastguard Worker setAutoFlash(CaptureRequest.Builder requestBuilder)906*90c8c64dSAndroid Build Coastguard Worker private void setAutoFlash(CaptureRequest.Builder requestBuilder) { 907*90c8c64dSAndroid Build Coastguard Worker if (mFlashSupported) { 908*90c8c64dSAndroid Build Coastguard Worker requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, 909*90c8c64dSAndroid Build Coastguard Worker CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); 910*90c8c64dSAndroid Build Coastguard Worker } 911*90c8c64dSAndroid Build Coastguard Worker } 912*90c8c64dSAndroid Build Coastguard Worker 913*90c8c64dSAndroid Build Coastguard Worker /** 914*90c8c64dSAndroid Build Coastguard Worker * Saves a JPEG {@link Image} into the specified {@link File}. 915*90c8c64dSAndroid Build Coastguard Worker */ 916*90c8c64dSAndroid Build Coastguard Worker private static class ImageSaver implements Runnable { 917*90c8c64dSAndroid Build Coastguard Worker 918*90c8c64dSAndroid Build Coastguard Worker /** 919*90c8c64dSAndroid Build Coastguard Worker * The JPEG image 920*90c8c64dSAndroid Build Coastguard Worker */ 921*90c8c64dSAndroid Build Coastguard Worker private final Image mImage; 922*90c8c64dSAndroid Build Coastguard Worker /** 923*90c8c64dSAndroid Build Coastguard Worker * The file we save the image into. 924*90c8c64dSAndroid Build Coastguard Worker */ 925*90c8c64dSAndroid Build Coastguard Worker private final File mFile; 926*90c8c64dSAndroid Build Coastguard Worker ImageSaver(Image image, File file)927*90c8c64dSAndroid Build Coastguard Worker public ImageSaver(Image image, File file) { 928*90c8c64dSAndroid Build Coastguard Worker mImage = image; 929*90c8c64dSAndroid Build Coastguard Worker mFile = file; 930*90c8c64dSAndroid Build Coastguard Worker } 931*90c8c64dSAndroid Build Coastguard Worker 932*90c8c64dSAndroid Build Coastguard Worker @Override run()933*90c8c64dSAndroid Build Coastguard Worker public void run() { 934*90c8c64dSAndroid Build Coastguard Worker ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); 935*90c8c64dSAndroid Build Coastguard Worker byte[] bytes = new byte[buffer.remaining()]; 936*90c8c64dSAndroid Build Coastguard Worker buffer.get(bytes); 937*90c8c64dSAndroid Build Coastguard Worker FileOutputStream output = null; 938*90c8c64dSAndroid Build Coastguard Worker try { 939*90c8c64dSAndroid Build Coastguard Worker output = new FileOutputStream(mFile); 940*90c8c64dSAndroid Build Coastguard Worker output.write(bytes); 941*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 942*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 943*90c8c64dSAndroid Build Coastguard Worker } finally { 944*90c8c64dSAndroid Build Coastguard Worker mImage.close(); 945*90c8c64dSAndroid Build Coastguard Worker if (null != output) { 946*90c8c64dSAndroid Build Coastguard Worker try { 947*90c8c64dSAndroid Build Coastguard Worker output.close(); 948*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 949*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 950*90c8c64dSAndroid Build Coastguard Worker } 951*90c8c64dSAndroid Build Coastguard Worker } 952*90c8c64dSAndroid Build Coastguard Worker } 953*90c8c64dSAndroid Build Coastguard Worker } 954*90c8c64dSAndroid Build Coastguard Worker 955*90c8c64dSAndroid Build Coastguard Worker } 956*90c8c64dSAndroid Build Coastguard Worker 957*90c8c64dSAndroid Build Coastguard Worker /** 958*90c8c64dSAndroid Build Coastguard Worker * Compares two {@code Size}s based on their areas. 959*90c8c64dSAndroid Build Coastguard Worker */ 960*90c8c64dSAndroid Build Coastguard Worker static class CompareSizesByArea implements Comparator<Size> { 961*90c8c64dSAndroid Build Coastguard Worker 962*90c8c64dSAndroid Build Coastguard Worker @Override compare(Size lhs, Size rhs)963*90c8c64dSAndroid Build Coastguard Worker public int compare(Size lhs, Size rhs) { 964*90c8c64dSAndroid Build Coastguard Worker // We cast here to ensure the multiplications won't overflow 965*90c8c64dSAndroid Build Coastguard Worker return Long.signum((long) lhs.getWidth() * lhs.getHeight() - 966*90c8c64dSAndroid Build Coastguard Worker (long) rhs.getWidth() * rhs.getHeight()); 967*90c8c64dSAndroid Build Coastguard Worker } 968*90c8c64dSAndroid Build Coastguard Worker 969*90c8c64dSAndroid Build Coastguard Worker } 970*90c8c64dSAndroid Build Coastguard Worker 971*90c8c64dSAndroid Build Coastguard Worker /** 972*90c8c64dSAndroid Build Coastguard Worker * Shows an error message dialog. 973*90c8c64dSAndroid Build Coastguard Worker */ 974*90c8c64dSAndroid Build Coastguard Worker public static class ErrorDialog extends DialogFragment { 975*90c8c64dSAndroid Build Coastguard Worker 976*90c8c64dSAndroid Build Coastguard Worker private static final String ARG_MESSAGE = "message"; 977*90c8c64dSAndroid Build Coastguard Worker newInstance(String message)978*90c8c64dSAndroid Build Coastguard Worker public static ErrorDialog newInstance(String message) { 979*90c8c64dSAndroid Build Coastguard Worker ErrorDialog dialog = new ErrorDialog(); 980*90c8c64dSAndroid Build Coastguard Worker Bundle args = new Bundle(); 981*90c8c64dSAndroid Build Coastguard Worker args.putString(ARG_MESSAGE, message); 982*90c8c64dSAndroid Build Coastguard Worker dialog.setArguments(args); 983*90c8c64dSAndroid Build Coastguard Worker return dialog; 984*90c8c64dSAndroid Build Coastguard Worker } 985*90c8c64dSAndroid Build Coastguard Worker 986*90c8c64dSAndroid Build Coastguard Worker @Override onCreateDialog(Bundle savedInstanceState)987*90c8c64dSAndroid Build Coastguard Worker public Dialog onCreateDialog(Bundle savedInstanceState) { 988*90c8c64dSAndroid Build Coastguard Worker final Activity activity = getActivity(); 989*90c8c64dSAndroid Build Coastguard Worker return new AlertDialog.Builder(activity) 990*90c8c64dSAndroid Build Coastguard Worker .setMessage(getArguments().getString(ARG_MESSAGE)) 991*90c8c64dSAndroid Build Coastguard Worker .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 992*90c8c64dSAndroid Build Coastguard Worker @Override 993*90c8c64dSAndroid Build Coastguard Worker public void onClick(DialogInterface dialogInterface, int i) { 994*90c8c64dSAndroid Build Coastguard Worker activity.finish(); 995*90c8c64dSAndroid Build Coastguard Worker } 996*90c8c64dSAndroid Build Coastguard Worker }) 997*90c8c64dSAndroid Build Coastguard Worker .create(); 998*90c8c64dSAndroid Build Coastguard Worker } 999*90c8c64dSAndroid Build Coastguard Worker 1000*90c8c64dSAndroid Build Coastguard Worker } 1001*90c8c64dSAndroid Build Coastguard Worker 1002*90c8c64dSAndroid Build Coastguard Worker /** 1003*90c8c64dSAndroid Build Coastguard Worker * Shows OK/Cancel confirmation dialog about camera permission. 1004*90c8c64dSAndroid Build Coastguard Worker */ 1005*90c8c64dSAndroid Build Coastguard Worker public static class ConfirmationDialog extends DialogFragment { 1006*90c8c64dSAndroid Build Coastguard Worker 1007*90c8c64dSAndroid Build Coastguard Worker @Override 1008*90c8c64dSAndroid Build Coastguard Worker public Dialog onCreateDialog(Bundle savedInstanceState) { 1009*90c8c64dSAndroid Build Coastguard Worker final Fragment parent = getParentFragment(); 1010*90c8c64dSAndroid Build Coastguard Worker return new AlertDialog.Builder(getActivity()) 1011*90c8c64dSAndroid Build Coastguard Worker .setMessage(R.string.request_permission) 1012*90c8c64dSAndroid Build Coastguard Worker .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 1013*90c8c64dSAndroid Build Coastguard Worker @Override 1014*90c8c64dSAndroid Build Coastguard Worker public void onClick(DialogInterface dialog, int which) { 1015*90c8c64dSAndroid Build Coastguard Worker FragmentCompat.requestPermissions(parent, 1016*90c8c64dSAndroid Build Coastguard Worker new String[]{Manifest.permission.CAMERA}, 1017*90c8c64dSAndroid Build Coastguard Worker REQUEST_CAMERA_PERMISSION); 1018*90c8c64dSAndroid Build Coastguard Worker } 1019*90c8c64dSAndroid Build Coastguard Worker }) 1020*90c8c64dSAndroid Build Coastguard Worker .setNegativeButton(android.R.string.cancel, 1021*90c8c64dSAndroid Build Coastguard Worker new DialogInterface.OnClickListener() { 1022*90c8c64dSAndroid Build Coastguard Worker @Override 1023*90c8c64dSAndroid Build Coastguard Worker public void onClick(DialogInterface dialog, int which) { 1024*90c8c64dSAndroid Build Coastguard Worker Activity activity = parent.getActivity(); 1025*90c8c64dSAndroid Build Coastguard Worker if (activity != null) { 1026*90c8c64dSAndroid Build Coastguard Worker activity.finish(); 1027*90c8c64dSAndroid Build Coastguard Worker } 1028*90c8c64dSAndroid Build Coastguard Worker } 1029*90c8c64dSAndroid Build Coastguard Worker }) 1030*90c8c64dSAndroid Build Coastguard Worker .create(); 1031*90c8c64dSAndroid Build Coastguard Worker } 1032*90c8c64dSAndroid Build Coastguard Worker } 1033*90c8c64dSAndroid Build Coastguard Worker 1034*90c8c64dSAndroid Build Coastguard Worker } 1035