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