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