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