加载中...
The Wayback Machine - https://sup1a9wrlpyh5li9ro.vcoronado.top/web/20150408143540/http://developer.android.com:80/samples/Camera2Basic/src/com.example.android.camera2basic/Camera2BasicFragment.html
Camera2Basic / src / com.example.android.camera2basic /

Camera2BasicFragment.java

1
/*
2
 * Copyright 2014 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *       http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
package com.example.android.camera2basic;
18
 
19
import android.app.Activity;
20
import android.app.AlertDialog;
21
import android.app.Dialog;
22
import android.app.DialogFragment;
23
import android.app.Fragment;
24
import android.content.Context;
25
import android.content.DialogInterface;
26
import android.content.res.Configuration;
27
import android.graphics.ImageFormat;
28
import android.graphics.Matrix;
29
import android.graphics.RectF;
30
import android.graphics.SurfaceTexture;
31
import android.hardware.camera2.CameraAccessException;
32
import android.hardware.camera2.CameraCaptureSession;
33
import android.hardware.camera2.CameraCharacteristics;
34
import android.hardware.camera2.CameraDevice;
35
import android.hardware.camera2.CameraManager;
36
import android.hardware.camera2.CameraMetadata;
37
import android.hardware.camera2.CaptureRequest;
38
import android.hardware.camera2.CaptureResult;
39
import android.hardware.camera2.TotalCaptureResult;
40
import android.hardware.camera2.params.StreamConfigurationMap;
41
import android.media.Image;
42
import android.media.ImageReader;
43
import android.os.Bundle;
44
import android.os.Handler;
45
import android.os.HandlerThread;
46
import android.os.Message;
47
import android.util.Log;
48
import android.util.Size;
49
import android.util.SparseIntArray;
50
import android.view.LayoutInflater;
51
import android.view.Surface;
52
import android.view.TextureView;
53
import android.view.View;
54
import android.view.ViewGroup;
55
import android.widget.Toast;
56
 
57
import java.io.File;
58
import java.io.FileNotFoundException;
59
import java.io.FileOutputStream;
60
import java.io.IOException;
61
import java.nio.ByteBuffer;
62
import java.util.ArrayList;
63
import java.util.Arrays;
64
import java.util.Collections;
65
import java.util.Comparator;
66
import java.util.List;
67
import java.util.concurrent.Semaphore;
68
import java.util.concurrent.TimeUnit;
69
 
70
public class Camera2BasicFragment extends Fragment implements View.OnClickListener {
71
 
72
    /**
73
     * Conversion from screen rotation to JPEG orientation.
74
     */
75
    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
76
 
77
    static {
78
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
79
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
80
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
81
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
82
    }
83
 
84
    /**
85
     * Tag for the {@link Log}.
86
     */
87
    private static final String TAG = "Camera2BasicFragment";
88
 
89
    /**
90
     * Camera state: Showing camera preview.
91
     */
92
    private static final int STATE_PREVIEW = 0;
93
 
94
    /**
95
     * Camera state: Waiting for the focus to be locked.
96
     */
97
    private static final int STATE_WAITING_LOCK = 1;
98
    /**
99
     * Camera state: Waiting for the exposure to be precapture state.
100
     */
101
    private static final int STATE_WAITING_PRECAPTURE = 2;
102
    /**
103
     * Camera state: Waiting for the exposure state to be something other than precapture.
104
     */
105
    private static final int STATE_WAITING_NON_PRECAPTURE = 3;
106
    /**
107
     * Camera state: Picture was taken.
108
     */
109
    private static final int STATE_PICTURE_TAKEN = 4;
110
 
111
    /**
112
     * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
113
     * {@link TextureView}.
114
     */
115
    private final TextureView.SurfaceTextureListener mSurfaceTextureListener
116
            = new TextureView.SurfaceTextureListener() {
117
 
118
        @Override
119
        public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
120
            openCamera(width, height);
121
        }
122
 
123
        @Override
124
        public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
125
            configureTransform(width, height);
126
        }
127
 
128
        @Override
129
        public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
130
            return true;
131
        }
132
 
133
        @Override
134
        public void onSurfaceTextureUpdated(SurfaceTexture texture) {
135
        }
136
 
137
    };
138
 
139
    /**
140
     * ID of the current {@link CameraDevice}.
141
     */
142
    private String mCameraId;
143
 
144
    /**
145
     * An {@link AutoFitTextureView} for camera preview.
146
     */
147
    private AutoFitTextureView mTextureView;
148
 
149
    /**
150
     * A {@link CameraCaptureSession } for camera preview.
151
     */
152
 
153
    private CameraCaptureSession mCaptureSession;
154
    /**
155
     * A reference to the opened {@link CameraDevice}.
156
     */
157
 
158
    private CameraDevice mCameraDevice;
159
    /**
160
     * The {@link android.util.Size} of camera preview.
161
     */
162
 
163
    private Size mPreviewSize;
164
 
165
    /**
166
     * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.
167
     */
168
    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
169
 
170
        @Override
171
        public void onOpened(CameraDevice cameraDevice) {
172
            // This method is called when the camera is opened.  We start camera preview here.
173
            mCameraOpenCloseLock.release();
174
            mCameraDevice = cameraDevice;
175
            createCameraPreviewSession();
176
        }
177
 
178
        @Override
179
        public void onDisconnected(CameraDevice cameraDevice) {
180
            mCameraOpenCloseLock.release();
181
            cameraDevice.close();
182
            mCameraDevice = null;
183
        }
184
 
185
        @Override
186
        public void onError(CameraDevice cameraDevice, int error) {
187
            mCameraOpenCloseLock.release();
188
            cameraDevice.close();
189
            mCameraDevice = null;
190
            Activity activity = getActivity();
191
            if (null != activity) {
192
                activity.finish();
193
            }
194
        }
195
 
196
    };
197
 
198
    /**
199
     * An additional thread for running tasks that shouldn't block the UI.
200
     */
201
    private HandlerThread mBackgroundThread;
202
 
203
    /**
204
     * A {@link Handler} for running tasks in the background.
205
     */
206
    private Handler mBackgroundHandler;
207
 
208
    /**
209
     * An {@link ImageReader} that handles still image capture.
210
     */
211
    private ImageReader mImageReader;
212
 
213
    /**
214
     * This is the output file for our picture.
215
     */
216
    private File mFile;
217
 
218
    /**
219
     * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a
220
     * still image is ready to be saved.
221
     */
222
    private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
223
            = new ImageReader.OnImageAvailableListener() {
224
 
225
        @Override
226
        public void onImageAvailable(ImageReader reader) {
227
            mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
228
        }
229
 
230
    };
231
 
232
    /**
233
     * {@link CaptureRequest.Builder} for the camera preview
234
     */
235
    private CaptureRequest.Builder mPreviewRequestBuilder;
236
 
237
    /**
238
     * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
239
     */
240
    private CaptureRequest mPreviewRequest;
241
 
242
    /**
243
     * The current state of camera state for taking pictures.
244
     *
245
     * @see #mCaptureCallback
246
     */
247
    private int mState = STATE_PREVIEW;
248
 
249
    /**
250
     * A {@link Semaphore} to prevent the app from exiting before closing the camera.
251
     */
252
    private Semaphore mCameraOpenCloseLock = new Semaphore(1);
253
 
254
    /**
255
     * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
256
     */
257
    private CameraCaptureSession.CaptureCallback mCaptureCallback
258
            = new CameraCaptureSession.CaptureCallback() {
259
 
260
        private void process(CaptureResult result) {
261
            switch (mState) {
262
                case STATE_PREVIEW: {
263
                    // We have nothing to do when the camera preview is working normally.
264
                    break;
265
                }
266
                case STATE_WAITING_LOCK: {
267
                    int afState = result.get(CaptureResult.CONTROL_AF_STATE);
268
                    if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
269
                            CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
270
                        // CONTROL_AE_STATE can be null on some devices
271
                        Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
272
                        if (aeState == null ||
273
                                aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
274
                            mState = STATE_WAITING_NON_PRECAPTURE;
275
                            captureStillPicture();
276
                        } else {
277
                            runPrecaptureSequence();
278
                        }
279
                    }
280
                    break;
281
                }
282
                case STATE_WAITING_PRECAPTURE: {
283
                    // CONTROL_AE_STATE can be null on some devices
284
                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
285
                    if (aeState == null ||
286
                            aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
287
                            aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
288
                        mState = STATE_WAITING_NON_PRECAPTURE;
289
                    }
290
                    break;
291
                }
292
                case STATE_WAITING_NON_PRECAPTURE: {
293
                    // CONTROL_AE_STATE can be null on some devices
294
                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
295
                    if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
296
                        mState = STATE_PICTURE_TAKEN;
297
                        captureStillPicture();
298
                    }
299
                    break;
300
                }
301
            }
302
        }
303
 
304
        @Override
305
        public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
306
                                        CaptureResult partialResult) {
307
            process(partialResult);
308
        }
309
 
310
        @Override
311
        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
312
                                       TotalCaptureResult result) {
313
            process(result);
314
        }
315
 
316
    };
317
 
318
    /**
319
     * A {@link Handler} for showing {@link Toast}s.
320
     */
321
    private Handler mMessageHandler = new Handler() {
322
        @Override
323
        public void handleMessage(Message msg) {
324
            Activity activity = getActivity();
325
            if (activity != null) {
326
                Toast.makeText(activity, (String) msg.obj, Toast.LENGTH_SHORT).show();
327
            }
328
        }
329
    };
330
 
331
    /**
332
     * Shows a {@link Toast} on the UI thread.
333
     *
334
     * @param text The message to show
335
     */
336
    private void showToast(String text) {
337
        // We show a Toast by sending request message to mMessageHandler. This makes sure that the
338
        // Toast is shown on the UI thread.
339
        Message message = Message.obtain();
340
        message.obj = text;
341
        mMessageHandler.sendMessage(message);
342
    }
343
 
344
    /**
345
     * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
346
     * width and height are at least as large as the respective requested values, and whose aspect
347
     * ratio matches with the specified value.
348
     *
349
     * @param choices     The list of sizes that the camera supports for the intended output class
350
     * @param width       The minimum desired width
351
     * @param height      The minimum desired height
352
     * @param aspectRatio The aspect ratio
353
     * @return The optimal {@code Size}, or an arbitrary one if none were big enough
354
     */
355
    private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
356
        // Collect the supported resolutions that are at least as big as the preview Surface
357
        List<Size> bigEnough = new ArrayList<Size>();
358
        int w = aspectRatio.getWidth();
359
        int h = aspectRatio.getHeight();
360
        for (Size option : choices) {
361
            if (option.getHeight() == option.getWidth() * h / w &&
362
                    option.getWidth() >= width && option.getHeight() >= height) {
363
                bigEnough.add(option);
364
            }
365
        }
366
 
367
        // Pick the smallest of those, assuming we found any
368
        if (bigEnough.size() > 0) {
369
            return Collections.min(bigEnough, new CompareSizesByArea());
370
        } else {
371
            Log.e(TAG, "Couldn't find any suitable preview size");
372
            return choices[0];
373
        }
374
    }
375
 
376
    public static Camera2BasicFragment newInstance() {
377
        Camera2BasicFragment fragment = new Camera2BasicFragment();
378
        fragment.setRetainInstance(true);
379
        return fragment;
380
    }
381
 
382
    @Override
383
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
384
                             Bundle savedInstanceState) {
385
        return inflater.inflate(R.layout.fragment_camera2_basic, container, false);
386
    }
387
 
388
    @Override
389
    public void onViewCreated(final View view, Bundle savedInstanceState) {
390
        view.findViewById(R.id.picture).setOnClickListener(this);
391
        view.findViewById(R.id.info).setOnClickListener(this);
392
        mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture);
393
    }
394
 
395
    @Override
396
    public void onActivityCreated(Bundle savedInstanceState) {
397
        super.onActivityCreated(savedInstanceState);
398
        mFile = new File(getActivity().getExternalFilesDir(null), "pic.jpg");
399
    }
400
 
401
    @Override
402
    public void onResume() {
403
        super.onResume();
404
        startBackgroundThread();
405
 
406
        // When the screen is turned off and turned back on, the SurfaceTexture is already
407
        // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
408
        // a camera and start preview from here (otherwise, we wait until the surface is ready in
409
        // the SurfaceTextureListener).
410
        if (mTextureView.isAvailable()) {
411
            openCamera(mTextureView.getWidth(), mTextureView.getHeight());
412
        } else {
413
            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
414
        }
415
    }
416
 
417
    @Override
418
    public void onPause() {
419
        closeCamera();
420
        stopBackgroundThread();
421
        super.onPause();
422
    }
423
 
424
    /**
425
     * Sets up member variables related to camera.
426
     *
427
     * @param width  The width of available size for camera preview
428
     * @param height The height of available size for camera preview
429
     */
430
    private void setUpCameraOutputs(int width, int height) {
431
        Activity activity = getActivity();
432
        CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
433
        try {
434
            for (String cameraId : manager.getCameraIdList()) {
435
                CameraCharacteristics characteristics
436
                        = manager.getCameraCharacteristics(cameraId);
437
 
438
                // We don't use a front facing camera in this sample.
439
                if (characteristics.get(CameraCharacteristics.LENS_FACING)
440
                        == CameraCharacteristics.LENS_FACING_FRONT) {
441
                    continue;
442
                }
443
 
444
                StreamConfigurationMap map = characteristics.get(
445
                        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
446
 
447
                // For still image captures, we use the largest available size.
448
                Size largest = Collections.max(
449
                        Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
450
                        new CompareSizesByArea());
451
                mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
452
                        ImageFormat.JPEG, /*maxImages*/2);
453
                mImageReader.setOnImageAvailableListener(
454
                        mOnImageAvailableListener, mBackgroundHandler);
455
 
456
                // Danger, W.R.! Attempting to use too large a preview size could  exceed the camera
457
                // bus' bandwidth limitation, resulting in gorgeous previews but the storage of
458
                // garbage capture data.
459
                mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
460
                        width, height, largest);
461
 
462
                // We fit the aspect ratio of TextureView to the size of preview we picked.
463
                int orientation = getResources().getConfiguration().orientation;
464
                if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
465
                    mTextureView.setAspectRatio(
466
                            mPreviewSize.getWidth(), mPreviewSize.getHeight());
467
                } else {
468
                    mTextureView.setAspectRatio(
469
                            mPreviewSize.getHeight(), mPreviewSize.getWidth());
470
                }
471
 
472
                mCameraId = cameraId;
473
                return;
474
            }
475
        } catch (CameraAccessException e) {
476
            e.printStackTrace();
477
        } catch (NullPointerException e) {
478
            // Currently an NPE is thrown when the Camera2API is used but not supported on the
479
            // device this code runs.
480
            new ErrorDialog().show(getFragmentManager(), "dialog");
481
        }
482
    }
483
 
484
    /**
485
     * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}.
486
     */
487
    private void openCamera(int width, int height) {
488
        setUpCameraOutputs(width, height);
489
        configureTransform(width, height);
490
        Activity activity = getActivity();
491
        CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
492
        try {
493
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
494
                throw new RuntimeException("Time out waiting to lock camera opening.");
495
            }
496
            manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
497
        } catch (CameraAccessException e) {
498
            e.printStackTrace();
499
        } catch (InterruptedException e) {
500
            throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
501
        }
502
    }
503
 
504
    /**
505
     * Closes the current {@link CameraDevice}.
506
     */
507
    private void closeCamera() {
508
        try {
509
            mCameraOpenCloseLock.acquire();
510
            if (null != mCaptureSession) {
511
                mCaptureSession.close();
512
                mCaptureSession = null;
513
            }
514
            if (null != mCameraDevice) {
515
                mCameraDevice.close();
516
                mCameraDevice = null;
517
            }
518
            if (null != mImageReader) {
519
                mImageReader.close();
520
                mImageReader = null;
521
            }
522
        } catch (InterruptedException e) {
523
            throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
524
        } finally {
525
            mCameraOpenCloseLock.release();
526
        }
527
    }
528
 
529
    /**
530
     * Starts a background thread and its {@link Handler}.
531
     */
532
    private void startBackgroundThread() {
533
        mBackgroundThread = new HandlerThread("CameraBackground");
534
        mBackgroundThread.start();
535
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
536
    }
537
 
538
    /**
539
     * Stops the background thread and its {@link Handler}.
540
     */
541
    private void stopBackgroundThread() {
542
        mBackgroundThread.quitSafely();
543
        try {
544
            mBackgroundThread.join();
545
            mBackgroundThread = null;
546
            mBackgroundHandler = null;
547
        } catch (InterruptedException e) {
548
            e.printStackTrace();
549
        }
550
    }
551
 
552
    /**
553
     * Creates a new {@link CameraCaptureSession} for camera preview.
554
     */
555
    private void createCameraPreviewSession() {
556
        try {
557
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
558
            assert texture != null;
559
 
560
            // We configure the size of default buffer to be the size of camera preview we want.
561
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
562
 
563
            // This is the output Surface we need to start preview.
564
            Surface surface = new Surface(texture);
565
 
566
            // We set up a CaptureRequest.Builder with the output Surface.
567
            mPreviewRequestBuilder
568
                    = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
569
            mPreviewRequestBuilder.addTarget(surface);
570
 
571
            // Here, we create a CameraCaptureSession for camera preview.
572
            mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
573
                    new CameraCaptureSession.StateCallback() {
574
 
575
                        @Override
576
                        public void onConfigured(CameraCaptureSession cameraCaptureSession) {
577
                            // The camera is already closed
578
                            if (null == mCameraDevice) {
579
                                return;
580
                            }
581
 
582
                            // When the session is ready, we start displaying the preview.
583
                            mCaptureSession = cameraCaptureSession;
584
                            try {
585
                                // Auto focus should be continuous for camera preview.
586
                                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
587
                                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
588
                                // Flash is automatically enabled when necessary.
589
                                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
590
                                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
591
 
592
                                // Finally, we start displaying the camera preview.
593
                                mPreviewRequest = mPreviewRequestBuilder.build();
594
                                mCaptureSession.setRepeatingRequest(mPreviewRequest,
595
                                        mCaptureCallback, mBackgroundHandler);
596
                            } catch (CameraAccessException e) {
597
                                e.printStackTrace();
598
                            }
599
                        }
600
 
601
                        @Override
602
                        public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
603
                            showToast("Failed");
604
                        }
605
                    }, null
606
            );
607
        } catch (CameraAccessException e) {
608
            e.printStackTrace();
609
        }
610
    }
611
 
612
    /**
613
     * Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`.
614
     * This method should be called after the camera preview size is determined in
615
     * setUpCameraOutputs and also the size of `mTextureView` is fixed.
616
     *
617
     * @param viewWidth  The width of `mTextureView`
618
     * @param viewHeight The height of `mTextureView`
619
     */
620
    private void configureTransform(int viewWidth, int viewHeight) {
621
        Activity activity = getActivity();
622
        if (null == mTextureView || null == mPreviewSize || null == activity) {
623
            return;
624
        }
625
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
626
        Matrix matrix = new Matrix();
627
        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
628
        RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
629
        float centerX = viewRect.centerX();
630
        float centerY = viewRect.centerY();
631
        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
632
            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
633
            matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
634
            float scale = Math.max(
635
                    (float) viewHeight / mPreviewSize.getHeight(),
636
                    (float) viewWidth / mPreviewSize.getWidth());
637
            matrix.postScale(scale, scale, centerX, centerY);
638
            matrix.postRotate(90 * (rotation - 2), centerX, centerY);
639
        }
640
        mTextureView.setTransform(matrix);
641
    }
642
 
643
    /**
644
     * Initiate a still image capture.
645
     */
646
    private void takePicture() {
647
        lockFocus();
648
    }
649
 
650
    /**
651
     * Lock the focus as the first step for a still image capture.
652
     */
653
    private void lockFocus() {
654
        try {
655
            // This is how to tell the camera to lock focus.
656
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
657
                    CameraMetadata.CONTROL_AF_TRIGGER_START);
658
            // Tell #mCaptureCallback to wait for the lock.
659
            mState = STATE_WAITING_LOCK;
660
            mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback,
661
                    mBackgroundHandler);
662
        } catch (CameraAccessException e) {
663
            e.printStackTrace();
664
        }
665
    }
666
 
667
    /**
668
     * Run the precapture sequence for capturing a still image. This method should be called when we
669
     * get a response in {@link #mCaptureCallback} from {@link #lockFocus()}.
670
     */
671
    private void runPrecaptureSequence() {
672
        try {
673
            // This is how to tell the camera to trigger.
674
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
675
                    CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
676
            // Tell #mCaptureCallback to wait for the precapture sequence to be set.
677
            mState = STATE_WAITING_PRECAPTURE;
678
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
679
                    mBackgroundHandler);
680
        } catch (CameraAccessException e) {
681
            e.printStackTrace();
682
        }
683
    }
684
 
685
    /**
686
     * Capture a still picture. This method should be called when we get a response in
687
     * {@link #mCaptureCallback} from both {@link #lockFocus()}.
688
     */
689
    private void captureStillPicture() {
690
        try {
691
            final Activity activity = getActivity();
692
            if (null == activity || null == mCameraDevice) {
693
                return;
694
            }
695
            // This is the CaptureRequest.Builder that we use to take a picture.
696
            final CaptureRequest.Builder captureBuilder =
697
                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
698
            captureBuilder.addTarget(mImageReader.getSurface());
699
 
700
            // Use the same AE and AF modes as the preview.
701
            captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
702
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
703
            captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
704
                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
705
 
706
            // Orientation
707
            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
708
            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
709
 
710
            CameraCaptureSession.CaptureCallback CaptureCallback
711
                    = new CameraCaptureSession.CaptureCallback() {
712
 
713
                @Override
714
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
715
                                               TotalCaptureResult result) {
716
                    showToast("Saved: " + mFile);
717
                    unlockFocus();
718
                }
719
            };
720
 
721
            mCaptureSession.stopRepeating();
722
            mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
723
        } catch (CameraAccessException e) {
724
            e.printStackTrace();
725
        }
726
    }
727
 
728
    /**
729
     * Unlock the focus. This method should be called when still image capture sequence is finished.
730
     */
731
    private void unlockFocus() {
732
        try {
733
            // Reset the autofucos trigger
734
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
735
                    CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
736
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
737
                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
738
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
739
                    mBackgroundHandler);
740
            // After this, the camera will go back to the normal state of preview.
741
            mState = STATE_PREVIEW;
742
            mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback,
743
                    mBackgroundHandler);
744
        } catch (CameraAccessException e) {
745
            e.printStackTrace();
746
        }
747
    }
748
 
749
    @Override
750
    public void onClick(View view) {
751
        switch (view.getId()) {
752
            case R.id.picture: {
753
                takePicture();
754
                break;
755
            }
756
            case R.id.info: {
757
                Activity activity = getActivity();
758
                if (null != activity) {
759
                    new AlertDialog.Builder(activity)
760
                            .setMessage(R.string.intro_message)
761
                            .setPositiveButton(android.R.string.ok, null)
762
                            .show();
763
                }
764
                break;
765
            }
766
        }
767
    }
768
 
769
    /**
770
     * Saves a JPEG {@link Image} into the specified {@link File}.
771
     */
772
    private static class ImageSaver implements Runnable {
773
 
774
        /**
775
         * The JPEG image
776
         */
777
        private final Image mImage;
778
        /**
779
         * The file we save the image into.
780
         */
781
        private final File mFile;
782
 
783
        public ImageSaver(Image image, File file) {
784
            mImage = image;
785
            mFile = file;
786
        }
787
 
788
        @Override
789
        public void run() {
790
            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
791
            byte[] bytes = new byte[buffer.remaining()];
792
            buffer.get(bytes);
793
            FileOutputStream output = null;
794
            try {
795
                output = new FileOutputStream(mFile);
796
                output.write(bytes);
797
            } catch (FileNotFoundException e) {
798
                e.printStackTrace();
799
            } catch (IOException e) {
800
                e.printStackTrace();
801
            } finally {
802
                mImage.close();
803
                if (null != output) {
804
                    try {
805
                        output.close();
806
                    } catch (IOException e) {
807
                        e.printStackTrace();
808
                    }
809
                }
810
            }
811
        }
812
 
813
    }
814
 
815
    /**
816
     * Compares two {@code Size}s based on their areas.
817
     */
818
    static class CompareSizesByArea implements Comparator<Size> {
819
 
820
        @Override
821
        public int compare(Size lhs, Size rhs) {
822
            // We cast here to ensure the multiplications won't overflow
823
            return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
824
                    (long) rhs.getWidth() * rhs.getHeight());
825
        }
826
 
827
    }
828
 
829
    public static class ErrorDialog extends DialogFragment {
830
 
831
        @Override
832
        public Dialog onCreateDialog(Bundle savedInstanceState) {
833
            final Activity activity = getActivity();
834
            return new AlertDialog.Builder(activity)
835
                    .setMessage("This device doesn't support Camera2 API.")
836
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
837
                        @Override
838
                        public void onClick(DialogInterface dialogInterface, int i) {
839
                            activity.finish();
840
                        }
841
                    })
842
                    .create();
843
        }
844
 
845
    }
846
 
847
}