├── .gitignore ├── .google └── packaging.yaml ├── .idea └── runConfigurations.xml ├── Application ├── build.gradle ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── android │ │ │ └── camera2basic │ │ │ ├── AutoFitTextureView.java │ │ │ ├── Camera2BasicFragment.java │ │ │ ├── CameraActivity.java │ │ │ └── OverlayView.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 ├── packaging.yaml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/osx,windows,android,androidstudio 2 | 3 | ### Android ### 4 | # Built application files 5 | *.apk 6 | *.ap_ 7 | 8 | # Files for the ART/Dalvik VM 9 | *.dex 10 | 11 | # Java class files 12 | *.class 13 | 14 | # Generated files 15 | bin/ 16 | gen/ 17 | out/ 18 | 19 | # Gradle files 20 | .gradle/ 21 | build/ 22 | 23 | # Local configuration file (sdk path, etc) 24 | local.properties 25 | 26 | # Proguard folder generated by Eclipse 27 | proguard/ 28 | 29 | # Log Files 30 | *.log 31 | 32 | # Android Studio Navigation editor temp files 33 | .navigation/ 34 | 35 | # Android Studio captures folder 36 | captures/ 37 | 38 | # Intellij 39 | *.iml 40 | .idea/workspace.xml 41 | .idea/tasks.xml 42 | .idea/gradle.xml 43 | .idea/dictionaries 44 | .idea/libraries 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | 49 | # Freeline 50 | freeline.py 51 | freeline/ 52 | freeline_project_description.json 53 | 54 | ### Android Patch ### 55 | gen-external-apklibs 56 | 57 | ### AndroidStudio ### 58 | # Covers files to be ignored for android development using Android Studio. 59 | 60 | # Built application files 61 | 62 | # Files for the ART/Dalvik VM 63 | 64 | # Java class files 65 | 66 | # Generated files 67 | 68 | # Gradle files 69 | .gradle 70 | 71 | # Signing files 72 | .signing/ 73 | 74 | # Local configuration file (sdk path, etc) 75 | 76 | # Proguard folder generated by Eclipse 77 | 78 | # Log Files 79 | 80 | # Android Studio 81 | /*/build/ 82 | /*/local.properties 83 | /*/out 84 | /*/*/build 85 | /*/*/production 86 | *.ipr 87 | *~ 88 | *.swp 89 | 90 | # Android Patch 91 | 92 | # External native build folder generated in Android Studio 2.2 and later 93 | 94 | # NDK 95 | obj/ 96 | 97 | # IntelliJ IDEA 98 | *.iws 99 | /out/ 100 | 101 | # User-specific configurations 102 | .idea/libraries/ 103 | .idea/.name 104 | .idea/compiler.xml 105 | .idea/copyright/profiles_settings.xml 106 | .idea/encodings.xml 107 | .idea/misc.xml 108 | .idea/modules.xml 109 | .idea/scopes/scope_settings.xml 110 | .idea/vcs.xml 111 | .idea/jsLibraryMappings.xml 112 | .idea/datasources.xml 113 | .idea/dataSources.ids 114 | .idea/sqlDataSources.xml 115 | .idea/dynamic.xml 116 | .idea/uiDesigner.xml 117 | 118 | # OS-specific files 119 | .DS_Store 120 | .DS_Store? 121 | ._* 122 | .Spotlight-V100 123 | .Trashes 124 | ehthumbs.db 125 | Thumbs.db 126 | 127 | # Legacy Eclipse project files 128 | .classpath 129 | .project 130 | .cproject 131 | .settings/ 132 | 133 | # Mobile Tools for Java (J2ME) 134 | .mtj.tmp/ 135 | 136 | # Package Files # 137 | *.war 138 | *.ear 139 | 140 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 141 | hs_err_pid* 142 | 143 | ## Plugin-specific files: 144 | 145 | # mpeltonen/sbt-idea plugin 146 | .idea_modules/ 147 | 148 | # JIRA plugin 149 | atlassian-ide-plugin.xml 150 | 151 | # Mongo Explorer plugin 152 | .idea/mongoSettings.xml 153 | 154 | # Crashlytics plugin (for Android Studio and IntelliJ) 155 | com_crashlytics_export_strings.xml 156 | crashlytics.properties 157 | crashlytics-build.properties 158 | fabric.properties 159 | 160 | ### AndroidStudio Patch ### 161 | 162 | !/gradle/wrapper/gradle-wrapper.jar 163 | 164 | ### OSX ### 165 | *.DS_Store 166 | .AppleDouble 167 | .LSOverride 168 | 169 | # Icon must end with two \r 170 | Icon 171 | 172 | # Thumbnails 173 | 174 | # Files that might appear in the root of a volume 175 | .DocumentRevisions-V100 176 | .fseventsd 177 | .TemporaryItems 178 | .VolumeIcon.icns 179 | .com.apple.timemachine.donotpresent 180 | 181 | # Directories potentially created on remote AFP share 182 | .AppleDB 183 | .AppleDesktop 184 | Network Trash Folder 185 | Temporary Items 186 | .apdisk 187 | 188 | ### Windows ### 189 | # Windows thumbnail cache files 190 | ehthumbs_vista.db 191 | 192 | # Folder config file 193 | Desktop.ini 194 | 195 | # Recycle Bin used on file shares 196 | $RECYCLE.BIN/ 197 | 198 | # Windows Installer files 199 | *.cab 200 | *.msi 201 | *.msm 202 | *.msp 203 | 204 | # Windows shortcuts 205 | *.lnk 206 | 207 | 208 | # End of https://www.gitignore.io/api/osx,windows,android,androidstudio 209 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /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 | buildToolsVersion "27.0.3" 38 | 39 | defaultConfig { 40 | minSdkVersion 21 41 | targetSdkVersion 27 42 | } 43 | 44 | compileOptions { 45 | sourceCompatibility JavaVersion.VERSION_1_7 46 | targetCompatibility JavaVersion.VERSION_1_7 47 | } 48 | 49 | sourceSets { 50 | main { 51 | dirs.each { dir -> 52 | java.srcDirs "src/${dir}/java" 53 | res.srcDirs "src/${dir}/res" 54 | } 55 | } 56 | androidTest.setRoot('tests') 57 | androidTest.java.srcDirs = ['tests/src'] 58 | 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /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 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.Manifest; 20 | import android.app.Activity; 21 | import android.app.AlertDialog; 22 | import android.app.Dialog; 23 | import android.app.DialogFragment; 24 | import android.app.Fragment; 25 | import android.content.Context; 26 | import android.content.DialogInterface; 27 | import android.content.pm.PackageManager; 28 | import android.content.res.Configuration; 29 | import android.graphics.ImageFormat; 30 | import android.graphics.Matrix; 31 | import android.graphics.Point; 32 | import android.graphics.Rect; 33 | import android.graphics.RectF; 34 | import android.graphics.SurfaceTexture; 35 | import android.hardware.camera2.CameraAccessException; 36 | import android.hardware.camera2.CameraCaptureSession; 37 | import android.hardware.camera2.CameraCharacteristics; 38 | import android.hardware.camera2.CameraDevice; 39 | import android.hardware.camera2.CameraManager; 40 | import android.hardware.camera2.CameraMetadata; 41 | import android.hardware.camera2.CaptureRequest; 42 | import android.hardware.camera2.CaptureResult; 43 | import android.hardware.camera2.TotalCaptureResult; 44 | import android.hardware.camera2.params.Face; 45 | import android.hardware.camera2.params.StreamConfigurationMap; 46 | import android.media.Image; 47 | import android.media.ImageReader; 48 | import android.os.Bundle; 49 | import android.os.Handler; 50 | import android.os.HandlerThread; 51 | import android.support.annotation.NonNull; 52 | import android.support.v13.app.FragmentCompat; 53 | import android.support.v4.content.ContextCompat; 54 | import android.util.Log; 55 | import android.util.Size; 56 | import android.util.SparseIntArray; 57 | import android.view.LayoutInflater; 58 | import android.view.Surface; 59 | import android.view.TextureView; 60 | import android.view.View; 61 | import android.view.ViewGroup; 62 | import android.widget.Toast; 63 | 64 | import java.io.File; 65 | import java.io.FileOutputStream; 66 | import java.io.IOException; 67 | import java.nio.ByteBuffer; 68 | import java.util.ArrayList; 69 | import java.util.Arrays; 70 | import java.util.Collections; 71 | import java.util.Comparator; 72 | import java.util.List; 73 | import java.util.concurrent.Semaphore; 74 | import java.util.concurrent.TimeUnit; 75 | 76 | public class Camera2BasicFragment extends Fragment 77 | implements View.OnClickListener, FragmentCompat.OnRequestPermissionsResultCallback { 78 | 79 | /** 80 | * Conversion from screen rotation to JPEG orientation. 81 | */ 82 | private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); 83 | private static final int REQUEST_CAMERA_PERMISSION = 1; 84 | private static final String FRAGMENT_DIALOG = "dialog"; 85 | 86 | static { 87 | ORIENTATIONS.append(Surface.ROTATION_0, 90); 88 | ORIENTATIONS.append(Surface.ROTATION_90, 0); 89 | ORIENTATIONS.append(Surface.ROTATION_180, 270); 90 | ORIENTATIONS.append(Surface.ROTATION_270, 180); 91 | } 92 | 93 | /** 94 | * Tag for the {@link Log}. 95 | */ 96 | private static final String TAG = "Camera2BasicFragment"; 97 | 98 | /** 99 | * Camera state: Showing camera preview. 100 | */ 101 | private static final int STATE_PREVIEW = 0; 102 | 103 | /** 104 | * Camera state: Waiting for the focus to be locked. 105 | */ 106 | private static final int STATE_WAITING_LOCK = 1; 107 | 108 | /** 109 | * Camera state: Waiting for the exposure to be precapture state. 110 | */ 111 | private static final int STATE_WAITING_PRECAPTURE = 2; 112 | 113 | /** 114 | * Camera state: Waiting for the exposure state to be something other than precapture. 115 | */ 116 | private static final int STATE_WAITING_NON_PRECAPTURE = 3; 117 | 118 | /** 119 | * Camera state: Picture was taken. 120 | */ 121 | private static final int STATE_PICTURE_TAKEN = 4; 122 | 123 | /** 124 | * Max preview width that is guaranteed by Camera2 API 125 | */ 126 | private static final int MAX_PREVIEW_WIDTH = 1920; 127 | 128 | /** 129 | * Max preview height that is guaranteed by Camera2 API 130 | */ 131 | private static final int MAX_PREVIEW_HEIGHT = 1080; 132 | 133 | /** 134 | * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a 135 | * {@link TextureView}. 136 | */ 137 | private final TextureView.SurfaceTextureListener mSurfaceTextureListener 138 | = new TextureView.SurfaceTextureListener() { 139 | 140 | @Override 141 | public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { 142 | openCamera(width, height); 143 | } 144 | 145 | @Override 146 | public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { 147 | configureTransform(width, height); 148 | } 149 | 150 | @Override 151 | public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { 152 | return true; 153 | } 154 | 155 | @Override 156 | public void onSurfaceTextureUpdated(SurfaceTexture texture) { 157 | } 158 | 159 | }; 160 | 161 | /** 162 | * ID of the current {@link CameraDevice}. 163 | */ 164 | private String mCameraId; 165 | 166 | /** 167 | * An {@link AutoFitTextureView} for camera preview. 168 | */ 169 | private AutoFitTextureView mTextureView; 170 | private OverlayView mOverlayView; 171 | private Matrix mFaceDetectionMatrix; 172 | private CameraManager mCameraManager; 173 | private CameraCharacteristics mCameraCharacteristics; 174 | private boolean mSwappedDimensions; 175 | 176 | /** 177 | * A {@link CameraCaptureSession } for camera preview. 178 | */ 179 | private CameraCaptureSession mCaptureSession; 180 | 181 | /** 182 | * A reference to the opened {@link CameraDevice}. 183 | */ 184 | private CameraDevice mCameraDevice; 185 | 186 | /** 187 | * The {@link android.util.Size} of camera preview. 188 | */ 189 | private Size mPreviewSize; 190 | 191 | /** 192 | * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state. 193 | */ 194 | private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { 195 | 196 | @Override 197 | public void onOpened(@NonNull CameraDevice cameraDevice) { 198 | // This method is called when the camera is opened. We start camera preview here. 199 | mCameraOpenCloseLock.release(); 200 | mCameraDevice = cameraDevice; 201 | createCameraPreviewSession(); 202 | } 203 | 204 | @Override 205 | public void onDisconnected(@NonNull CameraDevice cameraDevice) { 206 | mCameraOpenCloseLock.release(); 207 | cameraDevice.close(); 208 | mCameraDevice = null; 209 | } 210 | 211 | @Override 212 | public void onError(@NonNull CameraDevice cameraDevice, int error) { 213 | mCameraOpenCloseLock.release(); 214 | cameraDevice.close(); 215 | mCameraDevice = null; 216 | Activity activity = getActivity(); 217 | if (null != activity) { 218 | activity.finish(); 219 | } 220 | } 221 | 222 | }; 223 | 224 | /** 225 | * An additional thread for running tasks that shouldn't block the UI. 226 | */ 227 | private HandlerThread mBackgroundThread; 228 | 229 | /** 230 | * A {@link Handler} for running tasks in the background. 231 | */ 232 | private Handler mBackgroundHandler; 233 | 234 | /** 235 | * An {@link ImageReader} that handles still image capture. 236 | */ 237 | private ImageReader mImageReader; 238 | 239 | /** 240 | * This is the output file for our picture. 241 | */ 242 | private File mFile; 243 | 244 | /** 245 | * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a 246 | * still image is ready to be saved. 247 | */ 248 | private final ImageReader.OnImageAvailableListener mOnImageAvailableListener 249 | = new ImageReader.OnImageAvailableListener() { 250 | 251 | @Override 252 | public void onImageAvailable(ImageReader reader) { 253 | mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile)); 254 | } 255 | 256 | }; 257 | 258 | /** 259 | * {@link CaptureRequest.Builder} for the camera preview 260 | */ 261 | private CaptureRequest.Builder mPreviewRequestBuilder; 262 | 263 | /** 264 | * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder} 265 | */ 266 | private CaptureRequest mPreviewRequest; 267 | 268 | /** 269 | * The current state of camera state for taking pictures. 270 | * 271 | * @see #mCaptureCallback 272 | */ 273 | private int mState = STATE_PREVIEW; 274 | 275 | /** 276 | * A {@link Semaphore} to prevent the app from exiting before closing the camera. 277 | */ 278 | private Semaphore mCameraOpenCloseLock = new Semaphore(1); 279 | 280 | /** 281 | * Whether the current camera device supports Flash or not. 282 | */ 283 | private boolean mFlashSupported; 284 | 285 | /** 286 | * Orientation of the camera sensor 287 | */ 288 | private int mSensorOrientation; 289 | 290 | /** 291 | * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture. 292 | */ 293 | private CameraCaptureSession.CaptureCallback mCaptureCallback 294 | = new CameraCaptureSession.CaptureCallback() { 295 | 296 | private void process(CaptureResult result) { 297 | Integer mode = result.get(CaptureResult.STATISTICS_FACE_DETECT_MODE); 298 | Face[] faces = result.get(CaptureResult.STATISTICS_FACES); 299 | if (faces != null && mode != null) { 300 | if (faces.length > 0) { 301 | for(int i = 0; i < faces.length; i++) { 302 | if (faces[i].getScore() > 50) { 303 | Log.i("Test", "faces : " + faces.length + " , mode : " + mode); 304 | int left = faces[i].getBounds().left; 305 | int top = faces[i].getBounds().top; 306 | int right = faces[i].getBounds().right; 307 | int bottom = faces[i].getBounds().bottom; 308 | //float points[] = {(float)left, (float)top, (float)right, (float)bottom}; 309 | 310 | Rect uRect = new Rect(left, top, right, bottom); 311 | RectF rectF = new RectF(uRect); 312 | mFaceDetectionMatrix.mapRect(rectF); 313 | //mFaceDetectionMatrix.mapPoints(points); 314 | rectF.round(uRect); 315 | //uRect.set((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom); 316 | Log.i("Test", "Activity rect" + i + " bounds: " + uRect); 317 | 318 | final Rect rect = uRect; 319 | getActivity().runOnUiThread(new Runnable() { 320 | @Override 321 | public void run() { 322 | mOverlayView.setRect(rect); 323 | mOverlayView.requestLayout(); 324 | } 325 | }); 326 | break; 327 | } 328 | } 329 | } 330 | } 331 | 332 | switch (mState) { 333 | case STATE_PREVIEW: { 334 | // We have nothing to do when the camera preview is working normally. 335 | break; 336 | } 337 | case STATE_WAITING_LOCK: { 338 | Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); 339 | if (afState == null) { 340 | captureStillPicture(); 341 | } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || 342 | CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { 343 | // CONTROL_AE_STATE can be null on some devices 344 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 345 | if (aeState == null || 346 | aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { 347 | mState = STATE_PICTURE_TAKEN; 348 | captureStillPicture(); 349 | } else { 350 | runPrecaptureSequence(); 351 | } 352 | } 353 | break; 354 | } 355 | case STATE_WAITING_PRECAPTURE: { 356 | // CONTROL_AE_STATE can be null on some devices 357 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 358 | if (aeState == null || 359 | aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || 360 | aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { 361 | mState = STATE_WAITING_NON_PRECAPTURE; 362 | } 363 | break; 364 | } 365 | case STATE_WAITING_NON_PRECAPTURE: { 366 | // CONTROL_AE_STATE can be null on some devices 367 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 368 | if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { 369 | mState = STATE_PICTURE_TAKEN; 370 | captureStillPicture(); 371 | } 372 | break; 373 | } 374 | } 375 | } 376 | 377 | @Override 378 | public void onCaptureProgressed(@NonNull CameraCaptureSession session, 379 | @NonNull CaptureRequest request, 380 | @NonNull CaptureResult partialResult) { 381 | process(partialResult); 382 | } 383 | 384 | @Override 385 | public void onCaptureCompleted(@NonNull CameraCaptureSession session, 386 | @NonNull CaptureRequest request, 387 | @NonNull TotalCaptureResult result) { 388 | process(result); 389 | } 390 | 391 | }; 392 | 393 | /** 394 | * Shows a {@link Toast} on the UI thread. 395 | * 396 | * @param text The message to show 397 | */ 398 | private void showToast(final String text) { 399 | final Activity activity = getActivity(); 400 | if (activity != null) { 401 | activity.runOnUiThread(new Runnable() { 402 | @Override 403 | public void run() { 404 | Toast.makeText(activity, text, Toast.LENGTH_SHORT).show(); 405 | } 406 | }); 407 | } 408 | } 409 | 410 | /** 411 | * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that 412 | * is at least as large as the respective texture view size, and that is at most as large as the 413 | * respective max size, and whose aspect ratio matches with the specified value. If such size 414 | * doesn't exist, choose the largest one that is at most as large as the respective max size, 415 | * and whose aspect ratio matches with the specified value. 416 | * 417 | * @param choices The list of sizes that the camera supports for the intended output 418 | * class 419 | * @param textureViewWidth The width of the texture view relative to sensor coordinate 420 | * @param textureViewHeight The height of the texture view relative to sensor coordinate 421 | * @param maxWidth The maximum width that can be chosen 422 | * @param maxHeight The maximum height that can be chosen 423 | * @param aspectRatio The aspect ratio 424 | * @return The optimal {@code Size}, or an arbitrary one if none were big enough 425 | */ 426 | private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, 427 | int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { 428 | 429 | // Collect the supported resolutions that are at least as big as the preview Surface 430 | List bigEnough = new ArrayList<>(); 431 | // Collect the supported resolutions that are smaller than the preview Surface 432 | List notBigEnough = new ArrayList<>(); 433 | int w = aspectRatio.getWidth(); 434 | int h = aspectRatio.getHeight(); 435 | for (Size option : choices) { 436 | if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && 437 | option.getHeight() == option.getWidth() * h / w) { 438 | if (option.getWidth() >= textureViewWidth && 439 | option.getHeight() >= textureViewHeight) { 440 | bigEnough.add(option); 441 | } else { 442 | notBigEnough.add(option); 443 | } 444 | } 445 | } 446 | 447 | // Pick the smallest of those big enough. If there is no one big enough, pick the 448 | // largest of those not big enough. 449 | if (bigEnough.size() > 0) { 450 | return Collections.min(bigEnough, new CompareSizesByArea()); 451 | } else if (notBigEnough.size() > 0) { 452 | return Collections.max(notBigEnough, new CompareSizesByArea()); 453 | } else { 454 | Log.e(TAG, "Couldn't find any suitable preview size"); 455 | return choices[0]; 456 | } 457 | } 458 | 459 | public static Camera2BasicFragment newInstance() { 460 | return new Camera2BasicFragment(); 461 | } 462 | 463 | @Override 464 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 465 | Bundle savedInstanceState) { 466 | return inflater.inflate(R.layout.fragment_camera2_basic, container, false); 467 | } 468 | 469 | @Override 470 | public void onViewCreated(final View view, Bundle savedInstanceState) { 471 | view.findViewById(R.id.picture).setOnClickListener(this); 472 | view.findViewById(R.id.info).setOnClickListener(this); 473 | mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture); 474 | mOverlayView = (OverlayView) view.findViewById(R.id.overlay_view); 475 | } 476 | 477 | @Override 478 | public void onActivityCreated(Bundle savedInstanceState) { 479 | super.onActivityCreated(savedInstanceState); 480 | mFile = new File(getActivity().getExternalFilesDir(null), "pic.jpg"); 481 | } 482 | 483 | @Override 484 | public void onResume() { 485 | super.onResume(); 486 | startBackgroundThread(); 487 | 488 | // When the screen is turned off and turned back on, the SurfaceTexture is already 489 | // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open 490 | // a camera and start preview from here (otherwise, we wait until the surface is ready in 491 | // the SurfaceTextureListener). 492 | if (mTextureView.isAvailable()) { 493 | openCamera(mTextureView.getWidth(), mTextureView.getHeight()); 494 | } else { 495 | mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); 496 | } 497 | } 498 | 499 | @Override 500 | public void onPause() { 501 | closeCamera(); 502 | stopBackgroundThread(); 503 | super.onPause(); 504 | } 505 | 506 | private void requestCameraPermission() { 507 | if (FragmentCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { 508 | new ConfirmationDialog().show(getChildFragmentManager(), FRAGMENT_DIALOG); 509 | } else { 510 | FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 511 | REQUEST_CAMERA_PERMISSION); 512 | } 513 | } 514 | 515 | @Override 516 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 517 | @NonNull int[] grantResults) { 518 | if (requestCode == REQUEST_CAMERA_PERMISSION) { 519 | if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { 520 | ErrorDialog.newInstance(getString(R.string.request_permission)) 521 | .show(getChildFragmentManager(), FRAGMENT_DIALOG); 522 | } 523 | } else { 524 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 525 | } 526 | } 527 | 528 | /** 529 | * Sets up member variables related to camera. 530 | * 531 | * @param width The width of available size for camera preview 532 | * @param height The height of available size for camera preview 533 | */ 534 | private void setUpCameraOutputs(int width, int height) { 535 | Activity activity = getActivity(); 536 | mCameraManager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 537 | try { 538 | /* for(final String cameraId : manager.getCameraIdList()){ 539 | CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); 540 | int cOrientation = characteristics.get(CameraCharacteristics.LENS_FACING); 541 | if(cOrientation == CameraCharacteristics.LENS_FACING_FRONT) 542 | { 543 | System.out.println("cam id f"+cameraId); 544 | 545 | } 546 | System.out.println("cam id "+cameraId); 547 | 548 | }*/ 549 | for (String cameraId : mCameraManager.getCameraIdList()) { 550 | mCameraCharacteristics 551 | = mCameraManager.getCameraCharacteristics(cameraId); 552 | System.out.println("cam id " + cameraId); 553 | // We don't use a front facing camera in this sample. 554 | int facing = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING); 555 | if (facing == CameraCharacteristics.LENS_FACING_FRONT) { 556 | 557 | 558 | StreamConfigurationMap map = mCameraCharacteristics.get( 559 | CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 560 | if (map == null) { 561 | continue; 562 | } 563 | 564 | // For still image captures, we use the largest available size. 565 | Size largest = Collections.max( 566 | Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), 567 | new CompareSizesByArea()); 568 | mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), 569 | ImageFormat.JPEG, /*maxImages*/2); 570 | mImageReader.setOnImageAvailableListener( 571 | mOnImageAvailableListener, mBackgroundHandler); 572 | 573 | // Find out if we need to swap dimension to get the preview size relative to sensor 574 | // coordinate. 575 | int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 576 | //noinspection ConstantConditions 577 | mSensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 578 | mSwappedDimensions = false; 579 | 580 | switch (displayRotation) { 581 | case Surface.ROTATION_0: 582 | case Surface.ROTATION_180: 583 | if (mSensorOrientation == 90 || mSensorOrientation == 270) { 584 | mSwappedDimensions = true; 585 | } 586 | break; 587 | case Surface.ROTATION_90: 588 | case Surface.ROTATION_270: 589 | if (mSensorOrientation == 0 || mSensorOrientation == 180) { 590 | mSwappedDimensions = true; 591 | } 592 | break; 593 | default: 594 | Log.e(TAG, "Display rotation is invalid: " + displayRotation); 595 | } 596 | 597 | Point displaySize = new Point(); 598 | activity.getWindowManager().getDefaultDisplay().getSize(displaySize); 599 | int rotatedPreviewWidth = width; 600 | int rotatedPreviewHeight = height; 601 | int maxPreviewWidth = displaySize.x; 602 | int maxPreviewHeight = displaySize.y; 603 | 604 | if (mSwappedDimensions) { 605 | rotatedPreviewWidth = height; 606 | rotatedPreviewHeight = width; 607 | maxPreviewWidth = displaySize.y; 608 | maxPreviewHeight = displaySize.x; 609 | } 610 | 611 | if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { 612 | maxPreviewWidth = MAX_PREVIEW_WIDTH; 613 | } 614 | 615 | if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { 616 | maxPreviewHeight = MAX_PREVIEW_HEIGHT; 617 | } 618 | 619 | // Danger, W.R.! Attempting to use too large a preview size could exceed the camera 620 | // bus' bandwidth limitation, resulting in gorgeous previews but the storage of 621 | // garbage capture data. 622 | mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), 623 | rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, 624 | maxPreviewHeight, largest); 625 | 626 | // We fit the aspect ratio of TextureView to the size of preview we picked. 627 | int orientation = getResources().getConfiguration().orientation; 628 | if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 629 | mTextureView.setAspectRatio( 630 | mPreviewSize.getWidth(), mPreviewSize.getHeight()); 631 | } else { 632 | mTextureView.setAspectRatio( 633 | mPreviewSize.getHeight(), mPreviewSize.getWidth()); 634 | } 635 | 636 | // Check if the flash is supported. 637 | Boolean available = mCameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); 638 | mFlashSupported = available == null ? false : available; 639 | 640 | mCameraId = cameraId; 641 | 642 | int orientationOffset = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 643 | Rect activeArraySizeRect = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 644 | 645 | // Face Detection Matrix 646 | mFaceDetectionMatrix = new Matrix(); 647 | // TODO - I guess that is not enough if we have a landscape layout too... 648 | mFaceDetectionMatrix.setRotate(orientationOffset); 649 | 650 | Log.i("Test", "activeArraySizeRect1: (" + activeArraySizeRect + ") -> " + activeArraySizeRect.width() + ", " + activeArraySizeRect.height()); 651 | Log.i("Test", "activeArraySizeRect2: " + mPreviewSize.getWidth() + ", " + mPreviewSize.getHeight()); 652 | float s1 = mPreviewSize.getWidth() / (float)activeArraySizeRect.width(); 653 | float s2 = mPreviewSize.getHeight() / (float)activeArraySizeRect.height(); 654 | //float s1 = mOverlayView.getWidth(); 655 | //float s2 = mOverlayView.getHeight(); 656 | boolean mirror = true; // we always use front face camera 657 | boolean weAreinPortrait = true; 658 | mFaceDetectionMatrix.postScale(mirror ? -s1 : s1, s2); 659 | if (mSwappedDimensions) { 660 | mFaceDetectionMatrix.postTranslate(mPreviewSize.getHeight(), mPreviewSize.getWidth()); 661 | } else { 662 | // TODO - ... 663 | } 664 | 665 | } 666 | // return; 667 | } 668 | } catch (CameraAccessException e) { 669 | e.printStackTrace(); 670 | } catch (NullPointerException e) { 671 | // Currently an NPE is thrown when the Camera2API is used but not supported on the 672 | // device this code runs. 673 | ErrorDialog.newInstance(getString(R.string.camera_error)) 674 | .show(getChildFragmentManager(), FRAGMENT_DIALOG); 675 | } 676 | } 677 | 678 | /** 679 | * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}. 680 | */ 681 | private void openCamera(int width, int height) { 682 | if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) 683 | != PackageManager.PERMISSION_GRANTED) { 684 | requestCameraPermission(); 685 | return; 686 | } 687 | setUpCameraOutputs(width, height); 688 | configureTransform(width, height); 689 | Activity activity = getActivity(); 690 | CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 691 | try { 692 | if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { 693 | throw new RuntimeException("Time out waiting to lock camera opening."); 694 | } 695 | manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); 696 | } catch (CameraAccessException e) { 697 | e.printStackTrace(); 698 | } catch (InterruptedException e) { 699 | throw new RuntimeException("Interrupted while trying to lock camera opening.", e); 700 | } 701 | } 702 | 703 | /** 704 | * Closes the current {@link CameraDevice}. 705 | */ 706 | private void closeCamera() { 707 | try { 708 | mCameraOpenCloseLock.acquire(); 709 | if (null != mCaptureSession) { 710 | mCaptureSession.close(); 711 | mCaptureSession = null; 712 | } 713 | if (null != mCameraDevice) { 714 | mCameraDevice.close(); 715 | mCameraDevice = null; 716 | } 717 | if (null != mImageReader) { 718 | mImageReader.close(); 719 | mImageReader = null; 720 | } 721 | } catch (InterruptedException e) { 722 | throw new RuntimeException("Interrupted while trying to lock camera closing.", e); 723 | } finally { 724 | mCameraOpenCloseLock.release(); 725 | } 726 | } 727 | 728 | /** 729 | * Starts a background thread and its {@link Handler}. 730 | */ 731 | private void startBackgroundThread() { 732 | mBackgroundThread = new HandlerThread("CameraBackground"); 733 | mBackgroundThread.start(); 734 | mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); 735 | } 736 | 737 | /** 738 | * Stops the background thread and its {@link Handler}. 739 | */ 740 | private void stopBackgroundThread() { 741 | mBackgroundThread.quitSafely(); 742 | try { 743 | mBackgroundThread.join(); 744 | mBackgroundThread = null; 745 | mBackgroundHandler = null; 746 | } catch (InterruptedException e) { 747 | e.printStackTrace(); 748 | } 749 | } 750 | 751 | /** 752 | * Creates a new {@link CameraCaptureSession} for camera preview. 753 | */ 754 | private void createCameraPreviewSession() { 755 | try { 756 | SurfaceTexture texture = mTextureView.getSurfaceTexture(); 757 | assert texture != null; 758 | 759 | // We configure the size of default buffer to be the size of camera preview we want. 760 | texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 761 | 762 | // This is the output Surface we need to start preview. 763 | Surface surface = new Surface(texture); 764 | 765 | // We set up a CaptureRequest.Builder with the output Surface. 766 | mPreviewRequestBuilder 767 | = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 768 | mPreviewRequestBuilder.addTarget(surface); 769 | 770 | // Here, we create a CameraCaptureSession for camera preview. 771 | mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), 772 | new CameraCaptureSession.StateCallback() { 773 | 774 | @Override 775 | public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { 776 | // The camera is already closed 777 | if (null == mCameraDevice) { 778 | return; 779 | } 780 | 781 | // When the session is ready, we start displaying the preview. 782 | mCaptureSession = cameraCaptureSession; 783 | try { 784 | // Auto focus should be continuous for camera preview. 785 | mPreviewRequestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, 786 | CameraMetadata.STATISTICS_FACE_DETECT_MODE_FULL); 787 | // Flash is automatically enabled when necessary. 788 | setAutoFlash(mPreviewRequestBuilder); 789 | 790 | // Finally, we start displaying the camera preview. 791 | mPreviewRequest = mPreviewRequestBuilder.build(); 792 | mCaptureSession.setRepeatingRequest(mPreviewRequest, 793 | mCaptureCallback, mBackgroundHandler); 794 | } catch (CameraAccessException e) { 795 | e.printStackTrace(); 796 | } 797 | } 798 | 799 | @Override 800 | public void onConfigureFailed( 801 | @NonNull CameraCaptureSession cameraCaptureSession) { 802 | showToast("Failed"); 803 | } 804 | }, null 805 | ); 806 | } catch (CameraAccessException e) { 807 | e.printStackTrace(); 808 | } 809 | } 810 | 811 | /** 812 | * Configures the necessary {@link android.graphics.Matrix} transformation to `mTextureView`. 813 | * This method should be called after the camera preview size is determined in 814 | * setUpCameraOutputs and also the size of `mTextureView` is fixed. 815 | * 816 | * @param viewWidth The width of `mTextureView` 817 | * @param viewHeight The height of `mTextureView` 818 | */ 819 | private void configureTransform(int viewWidth, int viewHeight) { 820 | Activity activity = getActivity(); 821 | if (null == mTextureView || null == mPreviewSize || null == activity) { 822 | return; 823 | } 824 | int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 825 | Matrix matrix = new Matrix(); 826 | RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); 827 | RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); 828 | float centerX = viewRect.centerX(); 829 | float centerY = viewRect.centerY(); 830 | if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { 831 | bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); 832 | matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); 833 | float scale = Math.max( 834 | (float) viewHeight / mPreviewSize.getHeight(), 835 | (float) viewWidth / mPreviewSize.getWidth()); 836 | matrix.postScale(scale, scale, centerX, centerY); 837 | matrix.postRotate(90 * (rotation - 2), centerX, centerY); 838 | } else if (Surface.ROTATION_180 == rotation) { 839 | matrix.postRotate(180, centerX, centerY); 840 | } 841 | mTextureView.setTransform(matrix); 842 | } 843 | 844 | /** 845 | * Initiate a still image capture. 846 | */ 847 | private void takePicture() { 848 | lockFocus(); 849 | } 850 | 851 | /** 852 | * Lock the focus as the first step for a still image capture. 853 | */ 854 | private void lockFocus() { 855 | try { 856 | // This is how to tell the camera to lock focus. 857 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 858 | CameraMetadata.CONTROL_AF_TRIGGER_START); 859 | // Tell #mCaptureCallback to wait for the lock. 860 | mState = STATE_WAITING_LOCK; 861 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 862 | mBackgroundHandler); 863 | } catch (CameraAccessException e) { 864 | e.printStackTrace(); 865 | } 866 | } 867 | 868 | /** 869 | * Run the precapture sequence for capturing a still image. This method should be called when 870 | * we get a response in {@link #mCaptureCallback} from {@link #lockFocus()}. 871 | */ 872 | private void runPrecaptureSequence() { 873 | try { 874 | // This is how to tell the camera to trigger. 875 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 876 | CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 877 | // Tell #mCaptureCallback to wait for the precapture sequence to be set. 878 | mState = STATE_WAITING_PRECAPTURE; 879 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 880 | mBackgroundHandler); 881 | } catch (CameraAccessException e) { 882 | e.printStackTrace(); 883 | } 884 | } 885 | 886 | /** 887 | * Capture a still picture. This method should be called when we get a response in 888 | * {@link #mCaptureCallback} from both {@link #lockFocus()}. 889 | */ 890 | private void captureStillPicture() { 891 | try { 892 | final Activity activity = getActivity(); 893 | if (null == activity || null == mCameraDevice) { 894 | return; 895 | } 896 | // This is the CaptureRequest.Builder that we use to take a picture. 897 | final CaptureRequest.Builder captureBuilder = 898 | mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 899 | captureBuilder.addTarget(mImageReader.getSurface()); 900 | 901 | // Use the same AE and AF modes as the preview. 902 | captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, 903 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 904 | setAutoFlash(captureBuilder); 905 | 906 | // Orientation 907 | int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 908 | captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation)); 909 | 910 | CameraCaptureSession.CaptureCallback CaptureCallback 911 | = new CameraCaptureSession.CaptureCallback() { 912 | 913 | @Override 914 | public void onCaptureCompleted(@NonNull CameraCaptureSession session, 915 | @NonNull CaptureRequest request, 916 | @NonNull TotalCaptureResult result) { 917 | showToast("Saved: " + mFile); 918 | Log.d(TAG, mFile.toString()); 919 | unlockFocus(); 920 | } 921 | }; 922 | 923 | mCaptureSession.stopRepeating(); 924 | mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null); 925 | } catch (CameraAccessException e) { 926 | e.printStackTrace(); 927 | } 928 | } 929 | 930 | /** 931 | * Retrieves the JPEG orientation from the specified screen rotation. 932 | * 933 | * @param rotation The screen rotation. 934 | * @return The JPEG orientation (one of 0, 90, 270, and 360) 935 | */ 936 | private int getOrientation(int rotation) { 937 | // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X) 938 | // We have to take that into account and rotate JPEG properly. 939 | // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS. 940 | // For devices with orientation of 270, we need to rotate the JPEG 180 degrees. 941 | return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360; 942 | } 943 | 944 | /** 945 | * Unlock the focus. This method should be called when still image capture sequence is 946 | * finished. 947 | */ 948 | private void unlockFocus() { 949 | try { 950 | // Reset the auto-focus trigger 951 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 952 | CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); 953 | setAutoFlash(mPreviewRequestBuilder); 954 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 955 | mBackgroundHandler); 956 | // After this, the camera will go back to the normal state of preview. 957 | mState = STATE_PREVIEW; 958 | mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, 959 | mBackgroundHandler); 960 | } catch (CameraAccessException e) { 961 | e.printStackTrace(); 962 | } 963 | } 964 | 965 | @Override 966 | public void onClick(View view) { 967 | switch (view.getId()) { 968 | case R.id.picture: { 969 | takePicture(); 970 | break; 971 | } 972 | case R.id.info: { 973 | Activity activity = getActivity(); 974 | if (null != activity) { 975 | new AlertDialog.Builder(activity) 976 | .setMessage(R.string.intro_message) 977 | .setPositiveButton(android.R.string.ok, null) 978 | .show(); 979 | } 980 | break; 981 | } 982 | } 983 | } 984 | 985 | private void setAutoFlash(CaptureRequest.Builder requestBuilder) { 986 | if (mFlashSupported) { 987 | requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, 988 | CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); 989 | } 990 | } 991 | 992 | /** 993 | * Saves a JPEG {@link Image} into the specified {@link File}. 994 | */ 995 | private static class ImageSaver implements Runnable { 996 | 997 | /** 998 | * The JPEG image 999 | */ 1000 | private final Image mImage; 1001 | /** 1002 | * The file we save the image into. 1003 | */ 1004 | private final File mFile; 1005 | 1006 | public ImageSaver(Image image, File file) { 1007 | mImage = image; 1008 | mFile = file; 1009 | } 1010 | 1011 | @Override 1012 | public void run() { 1013 | ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); 1014 | byte[] bytes = new byte[buffer.remaining()]; 1015 | buffer.get(bytes); 1016 | FileOutputStream output = null; 1017 | try { 1018 | output = new FileOutputStream(mFile); 1019 | output.write(bytes); 1020 | } catch (IOException e) { 1021 | e.printStackTrace(); 1022 | } finally { 1023 | mImage.close(); 1024 | if (null != output) { 1025 | try { 1026 | output.close(); 1027 | } catch (IOException e) { 1028 | e.printStackTrace(); 1029 | } 1030 | } 1031 | } 1032 | } 1033 | 1034 | } 1035 | 1036 | /** 1037 | * Compares two {@code Size}s based on their areas. 1038 | */ 1039 | static class CompareSizesByArea implements Comparator { 1040 | 1041 | @Override 1042 | public int compare(Size lhs, Size rhs) { 1043 | // We cast here to ensure the multiplications won't overflow 1044 | return Long.signum((long) lhs.getWidth() * lhs.getHeight() - 1045 | (long) rhs.getWidth() * rhs.getHeight()); 1046 | } 1047 | 1048 | } 1049 | 1050 | /** 1051 | * Shows an error message dialog. 1052 | */ 1053 | public static class ErrorDialog extends DialogFragment { 1054 | 1055 | private static final String ARG_MESSAGE = "message"; 1056 | 1057 | public static ErrorDialog newInstance(String message) { 1058 | ErrorDialog dialog = new ErrorDialog(); 1059 | Bundle args = new Bundle(); 1060 | args.putString(ARG_MESSAGE, message); 1061 | dialog.setArguments(args); 1062 | return dialog; 1063 | } 1064 | 1065 | @Override 1066 | public Dialog onCreateDialog(Bundle savedInstanceState) { 1067 | final Activity activity = getActivity(); 1068 | return new AlertDialog.Builder(activity) 1069 | .setMessage(getArguments().getString(ARG_MESSAGE)) 1070 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 1071 | @Override 1072 | public void onClick(DialogInterface dialogInterface, int i) { 1073 | activity.finish(); 1074 | } 1075 | }) 1076 | .create(); 1077 | } 1078 | 1079 | } 1080 | 1081 | /** 1082 | * Shows OK/Cancel confirmation dialog about camera permission. 1083 | */ 1084 | public static class ConfirmationDialog extends DialogFragment { 1085 | 1086 | @Override 1087 | public Dialog onCreateDialog(Bundle savedInstanceState) { 1088 | final Fragment parent = getParentFragment(); 1089 | return new AlertDialog.Builder(getActivity()) 1090 | .setMessage(R.string.request_permission) 1091 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 1092 | @Override 1093 | public void onClick(DialogInterface dialog, int which) { 1094 | FragmentCompat.requestPermissions(parent, 1095 | new String[]{Manifest.permission.CAMERA}, 1096 | REQUEST_CAMERA_PERMISSION); 1097 | } 1098 | }) 1099 | .setNegativeButton(android.R.string.cancel, 1100 | new DialogInterface.OnClickListener() { 1101 | @Override 1102 | public void onClick(DialogInterface dialog, int which) { 1103 | Activity activity = parent.getActivity(); 1104 | if (activity != null) { 1105 | activity.finish(); 1106 | } 1107 | } 1108 | }) 1109 | .create(); 1110 | } 1111 | } 1112 | } 1113 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/camera2basic/CameraActivity.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.os.Bundle; 21 | 22 | public class CameraActivity extends Activity { 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_camera); 28 | if (null == savedInstanceState) { 29 | getFragmentManager().beginTransaction() 30 | .replace(R.id.container, Camera2BasicFragment.newInstance()) 31 | .commit(); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/camera2basic/OverlayView.java: -------------------------------------------------------------------------------- 1 | package com.example.android.camera2basic; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.graphics.Path; 10 | import android.graphics.Point; 11 | import android.graphics.Rect; 12 | import android.util.AttributeSet; 13 | import android.util.TypedValue; 14 | import android.view.MotionEvent; 15 | import android.view.View; 16 | 17 | import java.util.List; 18 | 19 | public class OverlayView extends View { 20 | 21 | private Bitmap mBitmap; 22 | private Canvas mCanvas; 23 | private Paint mPaint; 24 | private Rect mRect; 25 | private static final int TOUCH_TOLERANCE_DP = 24; 26 | private static final int BACKGROUND = Color.TRANSPARENT; 27 | private int mTouchTolerance; 28 | 29 | public OverlayView(Context context) { 30 | this(context, null); 31 | } 32 | 33 | public OverlayView(Context context, AttributeSet attrs) { 34 | this(context, attrs, 0); 35 | } 36 | 37 | public OverlayView(Context context, AttributeSet attrs, int defStyle) { 38 | super(context, attrs, defStyle); 39 | mCanvas = new Canvas(); 40 | //mRect = new Rect(10,10, 200, 200); 41 | initPaint(); 42 | } 43 | 44 | public void clear() { 45 | mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); 46 | mBitmap.eraseColor(BACKGROUND); 47 | mCanvas.setBitmap(mBitmap); 48 | invalidate(); 49 | } 50 | 51 | @Override 52 | protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { 53 | super.onSizeChanged(width, height, oldWidth, oldHeight); 54 | clear(); 55 | 56 | } 57 | 58 | @Override 59 | protected void onDraw(Canvas canvas) { 60 | canvas.drawColor(BACKGROUND); 61 | canvas.drawBitmap(mBitmap, 0, 0, null); 62 | 63 | mPaint.setColor(Color.YELLOW); 64 | if (null != mRect) { 65 | canvas.drawRect(mRect, mPaint); 66 | } 67 | } 68 | 69 | /** 70 | * Sets up paint attributes. 71 | */ 72 | private void initPaint() { 73 | mPaint = new Paint(); 74 | mPaint.setAntiAlias(true); 75 | mPaint.setDither(true); 76 | mPaint.setColor(Color.BLACK); 77 | mPaint.setStyle(Paint.Style.STROKE); 78 | mPaint.setStrokeJoin(Paint.Join.ROUND); 79 | mPaint.setStrokeCap(Paint.Cap.ROUND); 80 | mPaint.setStrokeWidth(12); 81 | mTouchTolerance = dp2px(TOUCH_TOLERANCE_DP); 82 | } 83 | 84 | /** 85 | * Converts dpi units to px 86 | * 87 | * @param dp 88 | * @return 89 | */ 90 | private int dp2px(int dp) { 91 | Resources r = getContext().getResources(); 92 | float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()); 93 | return (int) px; 94 | } 95 | 96 | public void setPaint(Paint paint) { 97 | this.mPaint = paint; 98 | } 99 | 100 | public Bitmap getBitmap() { 101 | return mBitmap; 102 | } 103 | 104 | public Rect getRect() { 105 | return mRect; 106 | } 107 | 108 | public void setRect(Rect rect) { 109 | this.mRect = rect; 110 | this.clear(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable-hdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowsheep1/android-camera2-api-face-recon/4b0cb97dc49e4cbaf5f85be7d327e44d48671d13/Application/src/main/res/drawable-hdpi/ic_action_info.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowsheep1/android-camera2-api-face-recon/4b0cb97dc49e4cbaf5f85be7d327e44d48671d13/Application/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-hdpi/tile.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowsheep1/android-camera2-api-face-recon/4b0cb97dc49e4cbaf5f85be7d327e44d48671d13/Application/src/main/res/drawable-hdpi/tile.9.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-mdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowsheep1/android-camera2-api-face-recon/4b0cb97dc49e4cbaf5f85be7d327e44d48671d13/Application/src/main/res/drawable-mdpi/ic_action_info.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowsheep1/android-camera2-api-face-recon/4b0cb97dc49e4cbaf5f85be7d327e44d48671d13/Application/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xhdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowsheep1/android-camera2-api-face-recon/4b0cb97dc49e4cbaf5f85be7d327e44d48671d13/Application/src/main/res/drawable-xhdpi/ic_action_info.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowsheep1/android-camera2-api-face-recon/4b0cb97dc49e4cbaf5f85be7d327e44d48671d13/Application/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xxhdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowsheep1/android-camera2-api-face-recon/4b0cb97dc49e4cbaf5f85be7d327e44d48671d13/Application/src/main/res/drawable-xxhdpi/ic_action_info.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadowsheep1/android-camera2-api-face-recon/4b0cb97dc49e4cbaf5f85be7d327e44d48671d13/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 | 34 | 35 | 45 | 46 |