├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── helin │ │ └── wallpaperdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── test1.mp4 │ │ └── ttt.mp4 │ ├── java │ │ └── com │ │ │ └── helin │ │ │ └── wallpaperdemo │ │ │ ├── AutoFitTextureView.java │ │ │ ├── Camera2Activity.java │ │ │ ├── Camera2BasicFragment.java │ │ │ ├── CameraLiveUtil.java │ │ │ ├── CameraLiveWallpaper.java │ │ │ ├── MainActivity.java │ │ │ └── MyCameraActivity.java │ └── res │ │ ├── drawable-xhdpi │ │ ├── ic_action_info.png │ │ └── ic_launcher.png │ │ ├── layout │ │ ├── activity_camera2.xml │ │ ├── activity_main.xml │ │ ├── activity_my_camera.xml │ │ └── fragment_camera2_basic.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── livewallpaper.xml │ └── test │ └── java │ └── com │ └── helin │ └── wallpaperdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WallpaperDemo 2 | Camera2Demo 3 | [http://www.jianshu.com/p/73fed068a795](http://www.jianshu.com/p/73fed068a795) 4 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | defaultConfig { 7 | applicationId "com.helin.wallpaperdemo" 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile "com.android.support:appcompat-v7:25.3.1" 28 | compile 'com.android.support:appcompat-v7:25.3.1' 29 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 30 | compile 'com.android.support:support-v4:25.3.1' 31 | compile 'com.android.support:support-v13:25.3.1' 32 | compile 'com.android.support:cardview-v7:25.3.1' 33 | testCompile 'junit:junit:4.12' 34 | } 35 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\qyhl2\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/helin/wallpaperdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.helin.wallpaperdemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.helin.wallpaperdemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/assets/test1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/assets/test1.mp4 -------------------------------------------------------------------------------- /app/src/main/assets/ttt.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/assets/ttt.mp4 -------------------------------------------------------------------------------- /app/src/main/java/com/helin/wallpaperdemo/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.helin.wallpaperdemo; 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 | -------------------------------------------------------------------------------- /app/src/main/java/com/helin/wallpaperdemo/Camera2Activity.java: -------------------------------------------------------------------------------- 1 | package com.helin.wallpaperdemo; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | public class Camera2Activity extends AppCompatActivity { 7 | 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | setContentView(R.layout.activity_camera2); 12 | if (null == savedInstanceState) { 13 | getFragmentManager().beginTransaction() 14 | .replace(R.id.container, Camera2BasicFragment.newInstance()) 15 | .commit(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/helin/wallpaperdemo/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.helin.wallpaperdemo; 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.RectF; 33 | import android.graphics.SurfaceTexture; 34 | import android.hardware.camera2.CameraAccessException; 35 | import android.hardware.camera2.CameraCaptureSession; 36 | import android.hardware.camera2.CameraCharacteristics; 37 | import android.hardware.camera2.CameraDevice; 38 | import android.hardware.camera2.CameraManager; 39 | import android.hardware.camera2.CameraMetadata; 40 | import android.hardware.camera2.CaptureRequest; 41 | import android.hardware.camera2.CaptureResult; 42 | import android.hardware.camera2.TotalCaptureResult; 43 | import android.hardware.camera2.params.StreamConfigurationMap; 44 | import android.media.Image; 45 | import android.media.ImageReader; 46 | import android.os.Bundle; 47 | import android.os.Handler; 48 | import android.os.HandlerThread; 49 | import android.support.annotation.NonNull; 50 | import android.support.v13.app.FragmentCompat; 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, FragmentCompat.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 | Log.e("SurfaceTextureListener","onSurfaceTextureAvailable"); 141 | openCamera(width, height); 142 | } 143 | 144 | @Override 145 | public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { 146 | configureTransform(width, height); 147 | } 148 | 149 | @Override 150 | public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { 151 | return true; 152 | } 153 | 154 | @Override 155 | public void onSurfaceTextureUpdated(SurfaceTexture texture) { 156 | } 157 | 158 | }; 159 | 160 | /** 161 | * ID of the current {@link CameraDevice}. 162 | */ 163 | private String mCameraId; 164 | 165 | /** 166 | * An {@link AutoFitTextureView} for camera preview. 167 | */ 168 | private AutoFitTextureView mTextureView; 169 | 170 | /** 171 | * A {@link CameraCaptureSession } for camera preview. 172 | */ 173 | private CameraCaptureSession mCaptureSession; 174 | 175 | /** 176 | * A reference to the opened {@link CameraDevice}. 177 | */ 178 | private CameraDevice mCameraDevice; 179 | 180 | /** 181 | * The {@link Size} of camera preview. 182 | */ 183 | private Size mPreviewSize; 184 | 185 | /** 186 | * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state. 187 | */ 188 | private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { 189 | 190 | @Override 191 | public void onOpened(@NonNull CameraDevice cameraDevice) { 192 | // This method is called when the camera is opened. We start camera preview here. 193 | mCameraOpenCloseLock.release(); 194 | mCameraDevice = cameraDevice; 195 | createCameraPreviewSession(); 196 | } 197 | 198 | @Override 199 | public void onDisconnected(@NonNull CameraDevice cameraDevice) { 200 | mCameraOpenCloseLock.release(); 201 | cameraDevice.close(); 202 | mCameraDevice = null; 203 | } 204 | 205 | @Override 206 | public void onError(@NonNull CameraDevice cameraDevice, int error) { 207 | mCameraOpenCloseLock.release(); 208 | cameraDevice.close(); 209 | mCameraDevice = null; 210 | Activity activity = getActivity(); 211 | if (null != activity) { 212 | activity.finish(); 213 | } 214 | } 215 | 216 | }; 217 | 218 | /** 219 | * An additional thread for running tasks that shouldn't block the UI. 220 | */ 221 | private HandlerThread mBackgroundThread; 222 | 223 | /** 224 | * A {@link Handler} for running tasks in the background. 225 | */ 226 | private Handler mBackgroundHandler; 227 | 228 | /** 229 | * An {@link ImageReader} that handles still image capture. 230 | */ 231 | private ImageReader mImageReader; 232 | 233 | /** 234 | * This is the output file for our picture. 235 | */ 236 | private File mFile; 237 | 238 | /** 239 | 静止图像可以保存onImageAvailable将被调用 240 | */ 241 | private final ImageReader.OnImageAvailableListener mOnImageAvailableListener 242 | = new ImageReader.OnImageAvailableListener() { 243 | 244 | @Override 245 | public void onImageAvailable(ImageReader reader) { 246 | Log.e(TAG,"创建图片"); 247 | mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile)); 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 | Log.e(TAG,"预览状态"); 294 | break; 295 | } 296 | case STATE_WAITING_LOCK: { 297 | Log.e(TAG,"对焦状态"); 298 | Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); 299 | if (afState == null) { 300 | captureStillPicture(); 301 | } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || 302 | CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { 303 | // CONTROL_AE_STATE can be null on some devices 304 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 305 | if (aeState == null || 306 | aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { 307 | mState = STATE_PICTURE_TAKEN; 308 | captureStillPicture(); 309 | } else { 310 | runPrecaptureSequence(); 311 | } 312 | } 313 | break; 314 | } 315 | case STATE_WAITING_PRECAPTURE: { 316 | Log.e(TAG,"等待对焦状态"); 317 | // CONTROL_AE_STATE can be null on some devices 318 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 319 | if (aeState == null || 320 | aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || 321 | aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { 322 | mState = STATE_WAITING_NON_PRECAPTURE; 323 | } 324 | break; 325 | } 326 | case STATE_WAITING_NON_PRECAPTURE: { 327 | Log.e(TAG,"STATE_WAITING_NON_PRECAPTURE"); 328 | // CONTROL_AE_STATE can be null on some devices 329 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 330 | if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { 331 | mState = STATE_PICTURE_TAKEN; 332 | captureStillPicture(); 333 | } 334 | break; 335 | } 336 | } 337 | } 338 | 339 | @Override 340 | public void onCaptureProgressed(@NonNull CameraCaptureSession session, 341 | @NonNull CaptureRequest request, 342 | @NonNull CaptureResult partialResult) { 343 | process(partialResult); 344 | } 345 | 346 | @Override 347 | public void onCaptureCompleted(@NonNull CameraCaptureSession session, 348 | @NonNull CaptureRequest request, 349 | @NonNull TotalCaptureResult result) { 350 | process(result); 351 | } 352 | 353 | }; 354 | 355 | /** 356 | * Shows a {@link Toast} on the UI thread. 357 | * 358 | * @param text The message to show 359 | */ 360 | private void showToast(final String text) { 361 | final Activity activity = getActivity(); 362 | if (activity != null) { 363 | activity.runOnUiThread(new Runnable() { 364 | @Override 365 | public void run() { 366 | Toast.makeText(activity, text, Toast.LENGTH_SHORT).show(); 367 | } 368 | }); 369 | } 370 | } 371 | 372 | /** 373 | * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that 374 | * is at least as large as the respective texture view size, and that is at most as large as the 375 | * respective max size, and whose aspect ratio matches with the specified value. If such size 376 | * doesn't exist, choose the largest one that is at most as large as the respective max size, 377 | * and whose aspect ratio matches with the specified value. 378 | * 379 | * @param choices The list of sizes that the camera supports for the intended output 380 | * class 381 | * @param textureViewWidth The width of the texture view relative to sensor coordinate 382 | * @param textureViewHeight The height of the texture view relative to sensor coordinate 383 | * @param maxWidth The maximum width that can be chosen 384 | * @param maxHeight The maximum height that can be chosen 385 | * @param aspectRatio The aspect ratio 386 | * @return The optimal {@code Size}, or an arbitrary one if none were big enough 387 | */ 388 | private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, 389 | int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { 390 | 391 | // Collect the supported resolutions that are at least as big as the preview Surface 392 | List bigEnough = new ArrayList<>(); 393 | // Collect the supported resolutions that are smaller than the preview Surface 394 | List notBigEnough = new ArrayList<>(); 395 | int w = aspectRatio.getWidth(); 396 | int h = aspectRatio.getHeight(); 397 | for (Size option : choices) { 398 | if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && 399 | option.getHeight() == option.getWidth() * h / w) { 400 | if (option.getWidth() >= textureViewWidth && 401 | option.getHeight() >= textureViewHeight) { 402 | bigEnough.add(option); 403 | } else { 404 | notBigEnough.add(option); 405 | } 406 | } 407 | } 408 | 409 | // Pick the smallest of those big enough. If there is no one big enough, pick the 410 | // largest of those not big enough. 411 | if (bigEnough.size() > 0) { 412 | return Collections.min(bigEnough, new CompareSizesByArea()); 413 | } else if (notBigEnough.size() > 0) { 414 | return Collections.max(notBigEnough, new CompareSizesByArea()); 415 | } else { 416 | Log.e(TAG, "Couldn't find any suitable preview size"); 417 | return choices[0]; 418 | } 419 | } 420 | 421 | public static Camera2BasicFragment newInstance() { 422 | return new Camera2BasicFragment(); 423 | } 424 | 425 | @Override 426 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 427 | Bundle savedInstanceState) { 428 | return inflater.inflate(R.layout.fragment_camera2_basic, container, false); 429 | } 430 | 431 | @Override 432 | public void onViewCreated(final View view, Bundle savedInstanceState) { 433 | view.findViewById(R.id.picture).setOnClickListener(this); 434 | view.findViewById(R.id.info).setOnClickListener(this); 435 | mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture); 436 | } 437 | 438 | @Override 439 | public void onActivityCreated(Bundle savedInstanceState) { 440 | super.onActivityCreated(savedInstanceState); 441 | mFile = new File(getActivity().getExternalFilesDir(null), "pic.jpg"); 442 | } 443 | 444 | @Override 445 | public void onResume() { 446 | super.onResume(); 447 | startBackgroundThread(); 448 | 449 | // When the screen is turned off and turned back on, the SurfaceTexture is already 450 | // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open 451 | // a camera and start preview from here (otherwise, we wait until the surface is ready in 452 | // the SurfaceTextureListener). 453 | if (mTextureView.isAvailable()) { 454 | Log.e("onActivityCreated","isAvailable"); 455 | openCamera(mTextureView.getWidth(), mTextureView.getHeight()); 456 | } else { 457 | Log.e("onActivityCreated","isAvailable22222"); 458 | mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); 459 | } 460 | } 461 | 462 | @Override 463 | public void onPause() { 464 | closeCamera(); 465 | stopBackgroundThread(); 466 | super.onPause(); 467 | } 468 | 469 | private void requestCameraPermission() { 470 | if (FragmentCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { 471 | new ConfirmationDialog().show(getChildFragmentManager(), FRAGMENT_DIALOG); 472 | } else { 473 | FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 474 | REQUEST_CAMERA_PERMISSION); 475 | } 476 | } 477 | 478 | @Override 479 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 480 | @NonNull int[] grantResults) { 481 | if (requestCode == REQUEST_CAMERA_PERMISSION) { 482 | if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { 483 | ErrorDialog.newInstance(getString(R.string.request_permission)) 484 | .show(getChildFragmentManager(), FRAGMENT_DIALOG); 485 | } 486 | } else { 487 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 488 | } 489 | } 490 | 491 | /** 492 | * Sets up member variables related to camera. 493 | * 494 | * @param width The width of available size for camera preview 495 | * @param height The height of available size for camera preview 496 | */ 497 | private void setUpCameraOutputs(int width, int height) { 498 | Activity activity = getActivity(); 499 | CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 500 | try { 501 | for (String cameraId : manager.getCameraIdList()) { 502 | CameraCharacteristics characteristics 503 | = manager.getCameraCharacteristics(cameraId); 504 | 505 | // We don't use a front facing camera in this sample. 506 | Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); 507 | if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { 508 | continue; 509 | } 510 | 511 | StreamConfigurationMap map = characteristics.get( 512 | CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 513 | if (map == null) { 514 | continue; 515 | } 516 | 517 | // For still image captures, we use the largest available size. 518 | Size largest = Collections.max( 519 | Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), 520 | new CompareSizesByArea()); 521 | mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), 522 | ImageFormat.JPEG, /*maxImages*/2); 523 | mImageReader.setOnImageAvailableListener( 524 | mOnImageAvailableListener, mBackgroundHandler); 525 | 526 | // Find out if we need to swap dimension to get the preview size relative to sensor 527 | // coordinate. 528 | int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 529 | //noinspection ConstantConditions 530 | mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 531 | boolean swappedDimensions = false; 532 | switch (displayRotation) { 533 | case Surface.ROTATION_0: 534 | case Surface.ROTATION_180: 535 | if (mSensorOrientation == 90 || mSensorOrientation == 270) { 536 | swappedDimensions = true; 537 | } 538 | break; 539 | case Surface.ROTATION_90: 540 | case Surface.ROTATION_270: 541 | if (mSensorOrientation == 0 || mSensorOrientation == 180) { 542 | swappedDimensions = true; 543 | } 544 | break; 545 | default: 546 | Log.e(TAG, "Display rotation is invalid: " + displayRotation); 547 | } 548 | 549 | Point displaySize = new Point(); 550 | activity.getWindowManager().getDefaultDisplay().getSize(displaySize); 551 | int rotatedPreviewWidth = width; 552 | int rotatedPreviewHeight = height; 553 | int maxPreviewWidth = displaySize.x; 554 | int maxPreviewHeight = displaySize.y; 555 | 556 | if (swappedDimensions) { 557 | rotatedPreviewWidth = height; 558 | rotatedPreviewHeight = width; 559 | maxPreviewWidth = displaySize.y; 560 | maxPreviewHeight = displaySize.x; 561 | } 562 | 563 | if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { 564 | maxPreviewWidth = MAX_PREVIEW_WIDTH; 565 | } 566 | 567 | if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { 568 | maxPreviewHeight = MAX_PREVIEW_HEIGHT; 569 | } 570 | 571 | // Danger, W.R.! Attempting to use too large a preview size could exceed the camera 572 | // bus' bandwidth limitation, resulting in gorgeous previews but the storage of 573 | // garbage capture data. 574 | mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), 575 | rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, 576 | maxPreviewHeight, largest); 577 | 578 | // We fit the aspect ratio of TextureView to the size of preview we picked. 579 | int orientation = getResources().getConfiguration().orientation; 580 | if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 581 | mTextureView.setAspectRatio( 582 | mPreviewSize.getWidth(), mPreviewSize.getHeight()); 583 | } else { 584 | mTextureView.setAspectRatio( 585 | mPreviewSize.getHeight(), mPreviewSize.getWidth()); 586 | } 587 | 588 | // Check if the flash is supported. 589 | Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); 590 | mFlashSupported = available == null ? false : available; 591 | 592 | mCameraId = cameraId; 593 | return; 594 | } 595 | } catch (CameraAccessException e) { 596 | e.printStackTrace(); 597 | } catch (NullPointerException e) { 598 | // Currently an NPE is thrown when the Camera2API is used but not supported on the 599 | // device this code runs. 600 | ErrorDialog.newInstance(getString(R.string.camera_error)) 601 | .show(getChildFragmentManager(), FRAGMENT_DIALOG); 602 | } 603 | } 604 | 605 | /** 606 | * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}. 607 | */ 608 | private void openCamera(int width, int height) { 609 | if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) 610 | != PackageManager.PERMISSION_GRANTED) { 611 | requestCameraPermission(); 612 | return; 613 | } 614 | setUpCameraOutputs(width, height); 615 | configureTransform(width, height); 616 | Activity activity = getActivity(); 617 | CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); 618 | try { 619 | if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { 620 | throw new RuntimeException("Time out waiting to lock camera opening."); 621 | } 622 | manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); 623 | } catch (CameraAccessException e) { 624 | e.printStackTrace(); 625 | } catch (InterruptedException e) { 626 | throw new RuntimeException("Interrupted while trying to lock camera opening.", e); 627 | } 628 | } 629 | 630 | /** 631 | * Closes the current {@link CameraDevice}. 632 | */ 633 | private void closeCamera() { 634 | try { 635 | mCameraOpenCloseLock.acquire(); 636 | if (null != mCaptureSession) { 637 | mCaptureSession.close(); 638 | mCaptureSession = null; 639 | } 640 | if (null != mCameraDevice) { 641 | mCameraDevice.close(); 642 | mCameraDevice = null; 643 | } 644 | if (null != mImageReader) { 645 | mImageReader.close(); 646 | mImageReader = null; 647 | } 648 | } catch (InterruptedException e) { 649 | throw new RuntimeException("Interrupted while trying to lock camera closing.", e); 650 | } finally { 651 | mCameraOpenCloseLock.release(); 652 | } 653 | } 654 | 655 | /** 656 | * Starts a background thread and its {@link Handler}. 657 | */ 658 | private void startBackgroundThread() { 659 | mBackgroundThread = new HandlerThread("CameraBackground"); 660 | mBackgroundThread.start(); 661 | mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); 662 | } 663 | 664 | /** 665 | * Stops the background thread and its {@link Handler}. 666 | */ 667 | private void stopBackgroundThread() { 668 | mBackgroundThread.quitSafely(); 669 | try { 670 | mBackgroundThread.join(); 671 | mBackgroundThread = null; 672 | mBackgroundHandler = null; 673 | } catch (InterruptedException e) { 674 | e.printStackTrace(); 675 | } 676 | } 677 | 678 | /** 679 | * Creates a new {@link CameraCaptureSession} for camera preview. 680 | */ 681 | private void createCameraPreviewSession() { 682 | try { 683 | SurfaceTexture texture = mTextureView.getSurfaceTexture(); 684 | assert texture != null; 685 | 686 | // We configure the size of default buffer to be the size of camera preview we want. 687 | texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 688 | 689 | // This is the output Surface we need to start preview. 690 | Surface surface = new Surface(texture); 691 | 692 | // We set up a CaptureRequest.Builder with the output Surface. 693 | mPreviewRequestBuilder 694 | = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 695 | mPreviewRequestBuilder.addTarget(surface); 696 | 697 | // Here, we create a CameraCaptureSession for camera preview. 698 | mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), 699 | new CameraCaptureSession.StateCallback() { 700 | 701 | @Override 702 | public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { 703 | // The camera is already closed 704 | if (null == mCameraDevice) { 705 | return; 706 | } 707 | 708 | // When the session is ready, we start displaying the preview. 709 | mCaptureSession = cameraCaptureSession; 710 | try { 711 | // Auto focus should be continuous for camera preview. 712 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, 713 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 714 | // Flash is automatically enabled when necessary. 715 | setAutoFlash(mPreviewRequestBuilder); 716 | 717 | // Finally, we start displaying the camera preview. 718 | mPreviewRequest = mPreviewRequestBuilder.build(); 719 | mCaptureSession.setRepeatingRequest(mPreviewRequest, 720 | mCaptureCallback, mBackgroundHandler); 721 | } catch (CameraAccessException e) { 722 | e.printStackTrace(); 723 | } 724 | } 725 | 726 | @Override 727 | public void onConfigureFailed( 728 | @NonNull CameraCaptureSession cameraCaptureSession) { 729 | showToast("Failed"); 730 | } 731 | }, null 732 | ); 733 | } catch (CameraAccessException e) { 734 | e.printStackTrace(); 735 | } 736 | } 737 | 738 | /** 739 | * Configures the necessary {@link Matrix} transformation to `mTextureView`. 740 | * This method should be called after the camera preview size is determined in 741 | * setUpCameraOutputs and also the size of `mTextureView` is fixed. 742 | * 743 | * @param viewWidth The width of `mTextureView` 744 | * @param viewHeight The height of `mTextureView` 745 | */ 746 | private void configureTransform(int viewWidth, int viewHeight) { 747 | Activity activity = getActivity(); 748 | if (null == mTextureView || null == mPreviewSize || null == activity) { 749 | return; 750 | } 751 | int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 752 | Matrix matrix = new Matrix(); 753 | RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); 754 | RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); 755 | float centerX = viewRect.centerX(); 756 | float centerY = viewRect.centerY(); 757 | if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { 758 | bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); 759 | matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); 760 | float scale = Math.max( 761 | (float) viewHeight / mPreviewSize.getHeight(), 762 | (float) viewWidth / mPreviewSize.getWidth()); 763 | matrix.postScale(scale, scale, centerX, centerY); 764 | matrix.postRotate(90 * (rotation - 2), centerX, centerY); 765 | } else if (Surface.ROTATION_180 == rotation) { 766 | matrix.postRotate(180, centerX, centerY); 767 | } 768 | mTextureView.setTransform(matrix); 769 | } 770 | 771 | /** 772 | * Initiate a still image capture. 773 | */ 774 | private void takePicture() { 775 | lockFocus(); 776 | } 777 | 778 | /** 779 | * Lock the focus as the first step for a still image capture. 780 | */ 781 | private void lockFocus() { 782 | try { 783 | // This is how to tell the camera to lock focus. 784 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 785 | CameraMetadata.CONTROL_AF_TRIGGER_START); 786 | // Tell #mCaptureCallback to wait for the lock. 787 | mState = STATE_WAITING_LOCK; 788 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 789 | mBackgroundHandler); 790 | } catch (CameraAccessException e) { 791 | e.printStackTrace(); 792 | } 793 | } 794 | 795 | /** 796 | * Run the precapture sequence for capturing a still image. This method should be called when 797 | * we get a response in {@link #mCaptureCallback} from {@link #lockFocus()}. 798 | */ 799 | private void runPrecaptureSequence() { 800 | try { 801 | // This is how to tell the camera to trigger. 802 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 803 | CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 804 | // Tell #mCaptureCallback to wait for the precapture sequence to be set. 805 | mState = STATE_WAITING_PRECAPTURE; 806 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 807 | mBackgroundHandler); 808 | } catch (CameraAccessException e) { 809 | e.printStackTrace(); 810 | } 811 | } 812 | 813 | /** 814 | * Capture a still picture. This method should be called when we get a response in 815 | * {@link #mCaptureCallback} from both {@link #lockFocus()}. 816 | */ 817 | private void captureStillPicture() { 818 | try { 819 | final Activity activity = getActivity(); 820 | if (null == activity || null == mCameraDevice) { 821 | return; 822 | } 823 | // This is the CaptureRequest.Builder that we use to take a picture. 824 | final CaptureRequest.Builder captureBuilder = 825 | mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 826 | captureBuilder.addTarget(mImageReader.getSurface()); 827 | 828 | // Use the same AE and AF modes as the preview. 829 | captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, 830 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 831 | setAutoFlash(captureBuilder); 832 | 833 | // Orientation 834 | int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); 835 | captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation)); 836 | 837 | CameraCaptureSession.CaptureCallback CaptureCallback 838 | = new CameraCaptureSession.CaptureCallback() { 839 | 840 | @Override 841 | public void onCaptureCompleted(@NonNull CameraCaptureSession session, 842 | @NonNull CaptureRequest request, 843 | @NonNull TotalCaptureResult result) { 844 | showToast("Saved: " + mFile); 845 | Log.d(TAG, mFile.toString()); 846 | unlockFocus(); 847 | } 848 | }; 849 | 850 | mCaptureSession.stopRepeating(); 851 | Log.e(TAG,"相机准备就绪,开始捕获图片"); 852 | mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null); 853 | } catch (CameraAccessException e) { 854 | e.printStackTrace(); 855 | } 856 | } 857 | 858 | /** 859 | * Retrieves the JPEG orientation from the specified screen rotation. 860 | * 861 | * @param rotation The screen rotation. 862 | * @return The JPEG orientation (one of 0, 90, 270, and 360) 863 | */ 864 | private int getOrientation(int rotation) { 865 | // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X) 866 | // We have to take that into account and rotate JPEG properly. 867 | // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS. 868 | // For devices with orientation of 270, we need to rotate the JPEG 180 degrees. 869 | return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360; 870 | } 871 | 872 | /** 873 | * Unlock the focus. This method should be called when still image capture sequence is 874 | * finished. 875 | */ 876 | private void unlockFocus() { 877 | try { 878 | // Reset the auto-focus trigger 879 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 880 | CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); 881 | setAutoFlash(mPreviewRequestBuilder); 882 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 883 | mBackgroundHandler); 884 | // After this, the camera will go back to the normal state of preview. 885 | mState = STATE_PREVIEW; 886 | mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, 887 | mBackgroundHandler); 888 | } catch (CameraAccessException e) { 889 | e.printStackTrace(); 890 | } 891 | } 892 | 893 | @Override 894 | public void onClick(View view) { 895 | switch (view.getId()) { 896 | case R.id.picture: { 897 | takePicture(); 898 | break; 899 | } 900 | case R.id.info: { 901 | Activity activity = getActivity(); 902 | if (null != activity) { 903 | new AlertDialog.Builder(activity) 904 | .setMessage("介绍消息") 905 | .setPositiveButton(android.R.string.ok, null) 906 | .show(); 907 | } 908 | break; 909 | } 910 | } 911 | } 912 | 913 | private void setAutoFlash(CaptureRequest.Builder requestBuilder) { 914 | if (mFlashSupported) { 915 | requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, 916 | CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); 917 | } 918 | } 919 | 920 | /** 921 | * Saves a JPEG {@link Image} into the specified {@link File}. 922 | */ 923 | private static class ImageSaver implements Runnable { 924 | 925 | /** 926 | * The JPEG image 927 | */ 928 | private final Image mImage; 929 | /** 930 | * The file we save the image into. 931 | */ 932 | private final File mFile; 933 | 934 | public ImageSaver(Image image, File file) { 935 | mImage = image; 936 | mFile = file; 937 | } 938 | 939 | @Override 940 | public void run() { 941 | ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); 942 | byte[] bytes = new byte[buffer.remaining()]; 943 | buffer.get(bytes); 944 | FileOutputStream output = null; 945 | try { 946 | output = new FileOutputStream(mFile); 947 | output.write(bytes); 948 | } catch (IOException e) { 949 | e.printStackTrace(); 950 | } finally { 951 | mImage.close(); 952 | if (null != output) { 953 | try { 954 | output.close(); 955 | } catch (IOException e) { 956 | e.printStackTrace(); 957 | } 958 | } 959 | } 960 | } 961 | 962 | } 963 | 964 | /** 965 | * Compares two {@code Size}s based on their areas. 966 | */ 967 | static class CompareSizesByArea implements Comparator { 968 | 969 | @Override 970 | public int compare(Size lhs, Size rhs) { 971 | // We cast here to ensure the multiplications won't overflow 972 | return Long.signum((long) lhs.getWidth() * lhs.getHeight() - 973 | (long) rhs.getWidth() * rhs.getHeight()); 974 | } 975 | 976 | } 977 | 978 | /** 979 | * Shows an error message dialog. 980 | */ 981 | public static class ErrorDialog extends DialogFragment { 982 | 983 | private static final String ARG_MESSAGE = "message"; 984 | 985 | public static ErrorDialog newInstance(String message) { 986 | ErrorDialog dialog = new ErrorDialog(); 987 | Bundle args = new Bundle(); 988 | args.putString(ARG_MESSAGE, message); 989 | dialog.setArguments(args); 990 | return dialog; 991 | } 992 | 993 | @Override 994 | public Dialog onCreateDialog(Bundle savedInstanceState) { 995 | final Activity activity = getActivity(); 996 | return new AlertDialog.Builder(activity) 997 | .setMessage(getArguments().getString(ARG_MESSAGE)) 998 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 999 | @Override 1000 | public void onClick(DialogInterface dialogInterface, int i) { 1001 | activity.finish(); 1002 | } 1003 | }) 1004 | .create(); 1005 | } 1006 | 1007 | } 1008 | 1009 | /** 1010 | * Shows OK/Cancel confirmation dialog about camera permission. 1011 | */ 1012 | public static class ConfirmationDialog extends DialogFragment { 1013 | 1014 | @Override 1015 | public Dialog onCreateDialog(Bundle savedInstanceState) { 1016 | final Fragment parent = getParentFragment(); 1017 | return new AlertDialog.Builder(getActivity()) 1018 | .setMessage(R.string.request_permission) 1019 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 1020 | @Override 1021 | public void onClick(DialogInterface dialog, int which) { 1022 | FragmentCompat.requestPermissions(parent, 1023 | new String[]{Manifest.permission.CAMERA}, 1024 | REQUEST_CAMERA_PERMISSION); 1025 | } 1026 | }) 1027 | .setNegativeButton(android.R.string.cancel, 1028 | new DialogInterface.OnClickListener() { 1029 | @Override 1030 | public void onClick(DialogInterface dialog, int which) { 1031 | Activity activity = parent.getActivity(); 1032 | if (activity != null) { 1033 | activity.finish(); 1034 | } 1035 | } 1036 | }) 1037 | .create(); 1038 | } 1039 | } 1040 | 1041 | } 1042 | -------------------------------------------------------------------------------- /app/src/main/java/com/helin/wallpaperdemo/CameraLiveUtil.java: -------------------------------------------------------------------------------- 1 | package com.helin.wallpaperdemo; 2 | 3 | import android.Manifest; 4 | import android.content.Context; 5 | import android.content.pm.PackageManager; 6 | import android.hardware.camera2.CameraAccessException; 7 | import android.hardware.camera2.CameraCaptureSession; 8 | import android.hardware.camera2.CameraCharacteristics; 9 | import android.hardware.camera2.CameraDevice; 10 | import android.hardware.camera2.CameraManager; 11 | import android.hardware.camera2.CaptureRequest; 12 | import android.hardware.camera2.params.StreamConfigurationMap; 13 | import android.os.Handler; 14 | import android.os.HandlerThread; 15 | import android.support.annotation.NonNull; 16 | import android.support.v4.content.ContextCompat; 17 | import android.util.Log; 18 | import android.util.Size; 19 | import android.util.SparseIntArray; 20 | import android.view.Surface; 21 | import android.view.SurfaceHolder; 22 | 23 | import java.util.Arrays; 24 | import java.util.concurrent.Semaphore; 25 | import java.util.concurrent.TimeUnit; 26 | 27 | /** 28 | * Created by qyhl2 on 2017/7/6. 29 | */ 30 | 31 | public class CameraLiveUtil { 32 | Context ctx; 33 | 34 | 35 | private HandlerThread mBackgroundThread; 36 | private Handler mBackgroundHandler; 37 | 38 | 39 | private static final String TAG = "MyCameraActivity"; 40 | /** 41 | * Conversion from screen rotation to JPEG orientation. 42 | */ 43 | private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); 44 | private static final String FRAGMENT_DIALOG = "dialog"; 45 | /** 46 | * 当前相机的ID。 47 | */ 48 | private String mCameraId; 49 | 50 | private Surface mSurface; 51 | 52 | static { 53 | ORIENTATIONS.append(Surface.ROTATION_0, 90); 54 | ORIENTATIONS.append(Surface.ROTATION_90, 0); 55 | ORIENTATIONS.append(Surface.ROTATION_180, 270); 56 | ORIENTATIONS.append(Surface.ROTATION_270, 180); 57 | } 58 | 59 | /** 60 | * 61 | * 一个信号量以防止应用程序在关闭相机之前退出。 62 | */ 63 | private Semaphore mCameraOpenCloseLock = new Semaphore(1); 64 | 65 | 66 | private CameraDevice mCameraDevice; 67 | /** 68 | * CameraDevice状态更改时被调用。 69 | */ 70 | private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { 71 | 72 | @Override 73 | public void onOpened(@NonNull CameraDevice cameraDevice) { 74 | // 打开相机时调用此方法。 在这里开始相机预览。 75 | mCameraOpenCloseLock.release(); 76 | mCameraDevice = cameraDevice; 77 | //创建CameraPreviewSession 78 | createCameraPreviewSession(); 79 | } 80 | 81 | @Override 82 | public void onDisconnected(@NonNull CameraDevice cameraDevice) { 83 | mCameraOpenCloseLock.release(); 84 | cameraDevice.close(); 85 | mCameraDevice = null; 86 | } 87 | 88 | @Override 89 | public void onError(@NonNull CameraDevice cameraDevice, int error) { 90 | mCameraOpenCloseLock.release(); 91 | cameraDevice.close(); 92 | mCameraDevice = null; 93 | } 94 | 95 | }; 96 | 97 | 98 | 99 | 100 | private Integer mSensorOrientation; 101 | private Size mPreviewSize; 102 | private boolean mFlashSupported; 103 | private CaptureRequest.Builder mPreviewRequestBuilder; 104 | private CameraCaptureSession mCaptureSession; 105 | private CaptureRequest mPreviewRequest; 106 | 107 | 108 | 109 | private static CameraLiveUtil cameraLiveUtil; 110 | private SurfaceHolder mSurfaceHolder; 111 | 112 | private CameraLiveUtil(){} 113 | public static CameraLiveUtil getIntace(){ 114 | if( cameraLiveUtil ==null){ 115 | cameraLiveUtil = new CameraLiveUtil(); 116 | } 117 | return cameraLiveUtil; 118 | } 119 | 120 | /** 121 | * 开启预览 122 | * @param ctx 123 | * @param surface 124 | */ 125 | public void startPreview(Context ctx , Surface surface, SurfaceHolder holder){ 126 | Log.e(TAG,"开启预览模式"); 127 | this.ctx=ctx; 128 | this.mSurface =surface; 129 | this.mSurfaceHolder =holder; 130 | startBackgroundThread(); 131 | openCamera(); 132 | } 133 | 134 | /** 135 | * 关闭预览 136 | */ 137 | public void stopPreview(){ 138 | mCameraOpenCloseLock.release(); 139 | if(mCameraDevice!=null){ 140 | mCameraDevice.close(); 141 | mCameraDevice = null; 142 | } 143 | } 144 | 145 | 146 | /** 147 | * 开启摄像头 148 | */ 149 | private void openCamera() { 150 | //检查权限 151 | if (ContextCompat.checkSelfPermission(ctx, Manifest.permission.CAMERA) 152 | != PackageManager.PERMISSION_GRANTED) { 153 | requestCameraPermission(); 154 | return; 155 | } 156 | Log.e(TAG,"设置相机输出"); 157 | //设置相机输出 158 | setUpCameraOutputs(); 159 | CameraManager manager = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE); 160 | try { 161 | if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { 162 | throw new RuntimeException("Time out waiting to lock camera opening."); 163 | } 164 | Log.e(TAG,"打开相机预览"); 165 | //打开相机预览 166 | manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); 167 | } catch (CameraAccessException e) { 168 | e.printStackTrace(); 169 | } catch (InterruptedException e) { 170 | throw new RuntimeException("Interrupted while trying to lock camera opening.", e); 171 | } 172 | } 173 | 174 | /** 175 | * 设置与相机相关的成员变量。 176 | */ 177 | private void setUpCameraOutputs() { 178 | CameraManager manager = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE); 179 | try { 180 | //获取可用摄像头列表 181 | for (String cameraId : manager.getCameraIdList()) { 182 | //获取相机的相关参数 183 | CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); 184 | // 不使用前置摄像头。 185 | Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); 186 | if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { 187 | continue; 188 | } 189 | StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 190 | if (map == null) { 191 | continue; 192 | } 193 | // 检查闪光灯是否支持。 194 | Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); 195 | mFlashSupported = available == null ? false : available; 196 | mCameraId = cameraId; 197 | Log.e(TAG," 相机可用 "); 198 | return; 199 | } 200 | } catch (CameraAccessException e) { 201 | e.printStackTrace(); 202 | } catch (NullPointerException e) { 203 | //不支持Camera2API 204 | } 205 | } 206 | 207 | 208 | 209 | 210 | 211 | private void setAutoFlash(CaptureRequest.Builder requestBuilder) { 212 | if (mFlashSupported) { 213 | requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, 214 | CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); 215 | } 216 | } 217 | 218 | /** 219 | * 为相机预览创建新的CameraCaptureSession 220 | */ 221 | private void createCameraPreviewSession() { 222 | 223 | 224 | try { 225 | Log.e(TAG,"创建PreviewSession"); 226 | //设置了一个具有输出Surface的CaptureRequest.Builder。 227 | mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 228 | Log.e(TAG,"添加mSurface"); 229 | mPreviewRequestBuilder.addTarget(mSurfaceHolder.getSurface()); 230 | Log.e(TAG,"添加完成"); 231 | //创建一个CameraCaptureSession来进行相机预览。 232 | mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface()), 233 | new CameraCaptureSession.StateCallback() { 234 | 235 | @Override 236 | public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { 237 | // 相机已经关闭 238 | if (null == mCameraDevice) { 239 | Log.e(TAG,"相机已经关闭"); 240 | return; 241 | } 242 | // 会话准备好后,我们开始显示预览 243 | mCaptureSession = cameraCaptureSession; 244 | Log.e(TAG,"会话准备好后,我们开始显示预览"); 245 | try { 246 | // 自动对焦应 247 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, 248 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 249 | // 闪光灯 250 | setAutoFlash(mPreviewRequestBuilder); 251 | // 最终开启相机预览并添加事件 252 | mPreviewRequest = mPreviewRequestBuilder.build(); 253 | mCaptureSession.setRepeatingRequest(mPreviewRequest, 254 | null, mBackgroundHandler); 255 | Log.e(TAG," 最终开启相机预览并添加事件"); 256 | } catch (CameraAccessException e) { 257 | e.printStackTrace(); 258 | } 259 | } 260 | 261 | @Override 262 | public void onConfigureFailed( 263 | @NonNull CameraCaptureSession cameraCaptureSession) { 264 | Log.e(TAG," onConfigureFailed 开启预览失败"); 265 | } 266 | }, null); 267 | } catch (CameraAccessException e) { 268 | Log.e(TAG," CameraAccessException 开启预览失败2"); 269 | e.printStackTrace(); 270 | } 271 | } 272 | 273 | /** 274 | * 检查权限 275 | */ 276 | private void requestCameraPermission() { 277 | // if (FragmentCompat.shouldShowRequestPermissionRationale(this.get, Manifest.permission.CAMERA)) { 278 | // new Camera2BasicFragment.ConfirmationDialog().show(getChildFragmentManager(), FRAGMENT_DIALOG); 279 | // } else { 280 | // FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 281 | // REQUEST_CAMERA_PERMISSION); 282 | // } 283 | } 284 | 285 | 286 | /** 287 | * 启动一个HandlerThread 288 | */ 289 | private void startBackgroundThread() { 290 | mBackgroundThread = new HandlerThread("CameraBackground"); 291 | mBackgroundThread.start(); 292 | mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); 293 | } 294 | 295 | } 296 | -------------------------------------------------------------------------------- /app/src/main/java/com/helin/wallpaperdemo/CameraLiveWallpaper.java: -------------------------------------------------------------------------------- 1 | package com.helin.wallpaperdemo; 2 | 3 | import android.service.wallpaper.WallpaperService; 4 | import android.util.Log; 5 | import android.view.MotionEvent; 6 | import android.view.SurfaceHolder; 7 | 8 | /** 9 | * 10 | * Created by helin on 2017/7/6. 11 | */ 12 | public class CameraLiveWallpaper extends WallpaperService { 13 | @Override 14 | public Engine onCreateEngine() { 15 | CameraEngine engin = new CameraEngine(); 16 | return engin; 17 | } 18 | 19 | class CameraEngine extends Engine { 20 | @Override 21 | public void onCreate(SurfaceHolder surfaceHolder) { 22 | super.onCreate(surfaceHolder); 23 | // CameraLiveUtil.getIntace().startPreview (getApplicationContext(),getSurfaceHolder().getSurface(),getSurfaceHolder()); 24 | } 25 | 26 | @Override 27 | public void onSurfaceCreated(SurfaceHolder holder) { 28 | super.onSurfaceCreated(holder); 29 | Log.e("onSurfaceCreated","Surface 准备完毕"); 30 | CameraLiveUtil.getIntace().startPreview(getApplicationContext(),holder.getSurface(),holder); 31 | } 32 | 33 | @Override 34 | public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 35 | super.onSurfaceChanged(holder, format, width, height); 36 | } 37 | @Override 38 | public void onTouchEvent(MotionEvent event) { 39 | super.onTouchEvent(event); 40 | } 41 | @Override 42 | public void onDestroy() { 43 | super.onDestroy(); 44 | CameraLiveUtil.getIntace().stopPreview(); 45 | Log.e("onDestroy","关闭预览111"); 46 | } 47 | @Override 48 | public void onVisibilityChanged(boolean visible) { 49 | 50 | if (visible) { 51 | // CameraLiveUtil.getIntace().startPreview(getApplicationContext(),getSurfaceHolder().getSurface(),getSurfaceHolder()); 52 | //开启预览 53 | } else { 54 | //停止预览 55 | // CameraLiveUtil.getIntace().stopPreview(); 56 | } 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/helin/wallpaperdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.helin.wallpaperdemo; 2 | 3 | import android.Manifest; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.os.Bundle; 8 | import android.support.annotation.NonNull; 9 | import android.support.v4.app.ActivityCompat; 10 | import android.support.v4.content.ContextCompat; 11 | import android.support.v7.app.AppCompatActivity; 12 | import android.view.View; 13 | import android.widget.Toast; 14 | 15 | /** 16 | * 17 | */ 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | private static final int PERMISSIONS_REQUEST_CAMERA = 454; 21 | private Context mContext; 22 | static final String PERMISSION_CAMERA = Manifest.permission.CAMERA; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_main); 28 | 29 | mContext = this; 30 | findViewById(R.id.text) 31 | .setOnClickListener(new View.OnClickListener() { 32 | @Override 33 | public void onClick(View view) { 34 | checkSelfPermission(); 35 | } 36 | }); 37 | findViewById(R.id.btn2).setOnClickListener(new View.OnClickListener() { 38 | @Override 39 | public void onClick(View v) { 40 | startActivity(new Intent(MainActivity.this,MyCameraActivity.class)); 41 | } 42 | }); 43 | } 44 | 45 | 46 | @Override 47 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 48 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 49 | switch (requestCode) { 50 | case PERMISSIONS_REQUEST_CAMERA: { 51 | if (grantResults.length > 0 52 | && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 53 | startWallpaper(); 54 | } else { 55 | Toast.makeText(mContext, getString(R.string._lease_open_permissions), Toast.LENGTH_SHORT).show(); 56 | } 57 | return; 58 | } 59 | } 60 | } 61 | /** 62 | * 检查权限 63 | */ 64 | void checkSelfPermission() { 65 | if (ContextCompat.checkSelfPermission(mContext, PERMISSION_CAMERA) != PackageManager.PERMISSION_GRANTED) { 66 | ActivityCompat.requestPermissions(this, 67 | new String[]{PERMISSION_CAMERA}, 68 | PERMISSIONS_REQUEST_CAMERA); 69 | } else { 70 | startWallpaper(); 71 | } 72 | } 73 | 74 | 75 | /** 76 | * 选择壁纸 77 | */ 78 | void startWallpaper() { 79 | final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); 80 | Intent chooser = Intent.createChooser(pickWallpaper, getString(R.string.choose_wallpaper)); 81 | startActivity(chooser); 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/helin/wallpaperdemo/MyCameraActivity.java: -------------------------------------------------------------------------------- 1 | package com.helin.wallpaperdemo; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.app.AlertDialog; 6 | import android.app.Dialog; 7 | import android.app.DialogFragment; 8 | import android.content.Context; 9 | import android.content.DialogInterface; 10 | import android.content.pm.PackageManager; 11 | import android.content.res.Configuration; 12 | import android.graphics.ImageFormat; 13 | import android.graphics.Matrix; 14 | import android.graphics.Point; 15 | import android.graphics.RectF; 16 | import android.graphics.SurfaceTexture; 17 | import android.hardware.camera2.CameraAccessException; 18 | import android.hardware.camera2.CameraCaptureSession; 19 | import android.hardware.camera2.CameraCharacteristics; 20 | import android.hardware.camera2.CameraDevice; 21 | import android.hardware.camera2.CameraManager; 22 | import android.hardware.camera2.CameraMetadata; 23 | import android.hardware.camera2.CaptureRequest; 24 | import android.hardware.camera2.CaptureResult; 25 | import android.hardware.camera2.TotalCaptureResult; 26 | import android.hardware.camera2.params.StreamConfigurationMap; 27 | import android.media.Image; 28 | import android.media.ImageReader; 29 | import android.os.Bundle; 30 | import android.os.Handler; 31 | import android.os.HandlerThread; 32 | import android.support.annotation.NonNull; 33 | import android.support.v4.content.ContextCompat; 34 | import android.support.v7.app.AppCompatActivity; 35 | import android.util.Log; 36 | import android.util.Size; 37 | import android.util.SparseIntArray; 38 | import android.view.Surface; 39 | import android.view.TextureView; 40 | import android.view.View; 41 | import android.widget.Toast; 42 | 43 | import java.io.File; 44 | import java.io.FileOutputStream; 45 | import java.io.IOException; 46 | import java.nio.ByteBuffer; 47 | import java.util.ArrayList; 48 | import java.util.Arrays; 49 | import java.util.Collections; 50 | import java.util.Comparator; 51 | import java.util.List; 52 | import java.util.concurrent.Semaphore; 53 | import java.util.concurrent.TimeUnit; 54 | 55 | public class MyCameraActivity extends AppCompatActivity implements View.OnClickListener { 56 | 57 | /** 58 | * 相机状态:显示相机预览。 59 | */ 60 | private static final int STATE_PREVIEW = 0; 61 | 62 | /** 63 | * 相机状态:等待焦点被锁定。 64 | */ 65 | private static final int STATE_WAITING_LOCK = 1; 66 | 67 | /** 68 | * 等待曝光被Precapture状态。 69 | */ 70 | private static final int STATE_WAITING_PRECAPTURE = 2; 71 | 72 | /** 73 | * 相机状态:等待曝光的状态是不是Precapture。 74 | */ 75 | private static final int STATE_WAITING_NON_PRECAPTURE = 3; 76 | 77 | /** 78 | * 相机状态:拍照。 79 | */ 80 | private static final int STATE_PICTURE_TAKEN = 4; 81 | 82 | //预览视图 83 | private AutoFitTextureView mTextureView; 84 | //拍照储存文件 85 | private File mFile; 86 | private HandlerThread mBackgroundThread; 87 | private Handler mBackgroundHandler; 88 | 89 | /** 90 | * Max preview width that is guaranteed by Camera2 API 91 | */ 92 | private static final int MAX_PREVIEW_WIDTH = 1920; 93 | 94 | /** 95 | * Max preview height that is guaranteed by Camera2 API 96 | */ 97 | private static final int MAX_PREVIEW_HEIGHT = 1080; 98 | 99 | private static final String TAG = "MyCameraActivity"; 100 | /** 101 | * Conversion from screen rotation to JPEG orientation. 102 | */ 103 | private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); 104 | private static final int REQUEST_CAMERA_PERMISSION = 1; 105 | private static final String FRAGMENT_DIALOG = "dialog"; 106 | private int mState = STATE_PREVIEW; 107 | /** 108 | * 当前相机的ID。 109 | */ 110 | private String mCameraId; 111 | 112 | static { 113 | ORIENTATIONS.append(Surface.ROTATION_0, 90); 114 | ORIENTATIONS.append(Surface.ROTATION_90, 0); 115 | ORIENTATIONS.append(Surface.ROTATION_180, 270); 116 | ORIENTATIONS.append(Surface.ROTATION_270, 180); 117 | } 118 | 119 | /** 120 | * 121 | * 一个信号量以防止应用程序在关闭相机之前退出。 122 | */ 123 | private Semaphore mCameraOpenCloseLock = new Semaphore(1); 124 | 125 | /** 126 | *TextureView 监听 127 | */ 128 | private final TextureView.SurfaceTextureListener mSurfaceTextureListener 129 | = new TextureView.SurfaceTextureListener() { 130 | 131 | @Override 132 | public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { 133 | Log.e("SurfaceTextureListener","onSurfaceTextureAvailable"); 134 | //当TextureView可用的时候 打开预览摄像头 135 | Log.e(TAG,"width:"+width+" height:"+height); 136 | openCamera(width, height); 137 | } 138 | 139 | @Override 140 | public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { 141 | // configureTransform(width, height); 142 | } 143 | 144 | @Override 145 | public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { 146 | return true; 147 | } 148 | 149 | @Override 150 | public void onSurfaceTextureUpdated(SurfaceTexture texture) { 151 | } 152 | 153 | }; 154 | 155 | private CameraDevice mCameraDevice; 156 | /** 157 | * CameraDevice状态更改时被调用。 158 | */ 159 | private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { 160 | 161 | @Override 162 | public void onOpened(@NonNull CameraDevice cameraDevice) { 163 | // 打开相机时调用此方法。 在这里开始相机预览。 164 | mCameraOpenCloseLock.release(); 165 | mCameraDevice = cameraDevice; 166 | //创建CameraPreviewSession 167 | createCameraPreviewSession(); 168 | } 169 | 170 | @Override 171 | public void onDisconnected(@NonNull CameraDevice cameraDevice) { 172 | mCameraOpenCloseLock.release(); 173 | cameraDevice.close(); 174 | mCameraDevice = null; 175 | } 176 | 177 | @Override 178 | public void onError(@NonNull CameraDevice cameraDevice, int error) { 179 | mCameraOpenCloseLock.release(); 180 | cameraDevice.close(); 181 | mCameraDevice = null; 182 | finish(); 183 | } 184 | 185 | }; 186 | 187 | /** 188 | * 处理与JPEG捕获有关的事件 189 | */ 190 | private CameraCaptureSession.CaptureCallback mCaptureCallback 191 | = new CameraCaptureSession.CaptureCallback() { 192 | 193 | //处理 194 | private void process(CaptureResult result) { 195 | switch (mState) { 196 | case STATE_PREVIEW: { 197 | //预览状态 198 | break; 199 | } 200 | 201 | case STATE_WAITING_LOCK: { 202 | //等待对焦 203 | Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); 204 | if (afState == null) { 205 | captureStillPicture(); 206 | } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || 207 | CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { 208 | // CONTROL_AE_STATE can be null on some devices 209 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 210 | if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { 211 | mState = STATE_PICTURE_TAKEN; 212 | captureStillPicture(); 213 | } else { 214 | runPrecaptureSequence(); 215 | } 216 | } 217 | break; 218 | } 219 | case STATE_WAITING_PRECAPTURE: { 220 | // CONTROL_AE_STATE can be null on some devices 221 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 222 | if (aeState == null || 223 | aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || 224 | aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { 225 | mState = STATE_WAITING_NON_PRECAPTURE; 226 | } 227 | break; 228 | } 229 | case STATE_WAITING_NON_PRECAPTURE: { 230 | // CONTROL_AE_STATE can be null on some devices 231 | Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); 232 | if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { 233 | mState = STATE_PICTURE_TAKEN; 234 | captureStillPicture(); 235 | } 236 | break; 237 | } 238 | } 239 | } 240 | 241 | @Override 242 | public void onCaptureProgressed(@NonNull CameraCaptureSession session, 243 | @NonNull CaptureRequest request, 244 | @NonNull CaptureResult partialResult) { 245 | process(partialResult); 246 | } 247 | 248 | @Override 249 | public void onCaptureCompleted(@NonNull CameraCaptureSession session, 250 | @NonNull CaptureRequest request, 251 | @NonNull TotalCaptureResult result) { 252 | process(result); 253 | } 254 | 255 | }; 256 | 257 | 258 | 259 | /** 260 | * This a callback object for the {@link ImageReader}. "onImageAvailable" will be called when a 261 | * still image is ready to be saved. 262 | */ 263 | private final ImageReader.OnImageAvailableListener mOnImageAvailableListener 264 | = new ImageReader.OnImageAvailableListener() { 265 | 266 | @Override 267 | public void onImageAvailable(ImageReader reader) { 268 | //当图片可得到的时候获取图片并保存 269 | mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile)); 270 | } 271 | 272 | }; 273 | 274 | private ImageReader mImageReader; 275 | private Integer mSensorOrientation; 276 | private Size mPreviewSize; 277 | private boolean mFlashSupported; 278 | private CaptureRequest.Builder mPreviewRequestBuilder; 279 | private CameraCaptureSession mCaptureSession; 280 | private CaptureRequest mPreviewRequest; 281 | 282 | @Override 283 | protected void onCreate(Bundle savedInstanceState) { 284 | super.onCreate(savedInstanceState); 285 | setContentView(R.layout.activity_my_camera); 286 | initView(); 287 | } 288 | 289 | /** 290 | * 初始化 291 | */ 292 | private void initView() { 293 | mTextureView = (AutoFitTextureView)findViewById(R.id.texture); 294 | findViewById(R.id.picture).setOnClickListener(this); 295 | findViewById(R.id.info).setOnClickListener(this); 296 | //创建文件 297 | mFile = new File(getExternalFilesDir(null), "pic.jpg"); 298 | } 299 | 300 | @Override 301 | public void onResume() { 302 | super.onResume(); 303 | startBackgroundThread(); 304 | /** 305 | * 当屏幕关闭并重新打开时,SurfaceTexture已经可用,“onSurfaceTextureAvailable”将不被调用。 306 | * 在这种情况下,我们可以打开相机并从这里开始预览(否则,我们等待SurfaceTextureListener中的表面准备就绪)。 307 | */ 308 | if (mTextureView.isAvailable()) { 309 | openCamera(mTextureView.getWidth(), mTextureView.getHeight()); 310 | } else { 311 | mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); 312 | } 313 | } 314 | 315 | 316 | /** 317 | * 开启摄像头 318 | * @param width 319 | * @param height 320 | */ 321 | private void openCamera(int width, int height) { 322 | //检查权限 323 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 324 | != PackageManager.PERMISSION_GRANTED) { 325 | requestCameraPermission(); 326 | return; 327 | } 328 | //设置相机输出 329 | setUpCameraOutputs(width, height); 330 | //配置变换 331 | configureTransform(width, height); 332 | CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE); 333 | try { 334 | if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { 335 | throw new RuntimeException("Time out waiting to lock camera opening."); 336 | } 337 | //打开相机预览 338 | manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler); 339 | } catch (CameraAccessException e) { 340 | e.printStackTrace(); 341 | } catch (InterruptedException e) { 342 | throw new RuntimeException("Interrupted while trying to lock camera opening.", e); 343 | } 344 | } 345 | 346 | 347 | 348 | /** 349 | * 运行捕获静止图像的预捕获序列。 当我们从{@link #lockFocus()}的{@link #mCaptureCallback}中得到响应时,应该调用此方法。 350 | */ 351 | private void runPrecaptureSequence() { 352 | try { 353 | // 这是如何告诉相机触发的。 354 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 355 | CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); 356 | // 告诉 mCaptureCallback 等待preapture序列被设置. 357 | mState = STATE_WAITING_PRECAPTURE; 358 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 359 | mBackgroundHandler); 360 | } catch (CameraAccessException e) { 361 | e.printStackTrace(); 362 | } 363 | } 364 | 365 | 366 | /** 367 | * Configures the necessary {@link Matrix} transformation to `mTextureView`. 368 | * This method should be called after the camera preview size is determined in 369 | * setUpCameraOutputs and also the size of `mTextureView` is fixed. 370 | * 371 | * @param viewWidth The width of `mTextureView` 372 | * @param viewHeight The height of `mTextureView` 373 | */ 374 | private void configureTransform(int viewWidth, int viewHeight) { 375 | if (null == mTextureView || null == mPreviewSize ) { 376 | return; 377 | } 378 | int rotation = this.getWindowManager().getDefaultDisplay().getRotation(); 379 | Matrix matrix = new Matrix(); 380 | RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); 381 | RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth()); 382 | float centerX = viewRect.centerX(); 383 | float centerY = viewRect.centerY(); 384 | if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { 385 | bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); 386 | matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); 387 | float scale = Math.max( 388 | (float) viewHeight / mPreviewSize.getHeight(), 389 | (float) viewWidth / mPreviewSize.getWidth()); 390 | matrix.postScale(scale, scale, centerX, centerY); 391 | matrix.postRotate(90 * (rotation - 2), centerX, centerY); 392 | } else if (Surface.ROTATION_180 == rotation) { 393 | matrix.postRotate(180, centerX, centerY); 394 | } 395 | mTextureView.setTransform(matrix); 396 | } 397 | 398 | /** 399 | * 设置与相机相关的成员变量。 400 | * 401 | * @param width 相机预览的可用尺寸宽度 402 | * @param height 相机预览的可用尺寸的高度 403 | */ 404 | private void setUpCameraOutputs(int width, int height) { 405 | CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE); 406 | try { 407 | //获取可用摄像头列表 408 | for (String cameraId : manager.getCameraIdList()) { 409 | //获取相机的相关参数 410 | CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); 411 | // 不使用前置摄像头。 412 | Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); 413 | if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) { 414 | continue; 415 | } 416 | StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 417 | if (map == null) { 418 | continue; 419 | } 420 | // 对于静态图像拍摄,使用最大的可用尺寸。 421 | Size largest = Collections.max( 422 | Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), 423 | new CompareSizesByArea()); 424 | mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), 425 | ImageFormat.JPEG, /*maxImages*/2); 426 | //添加ImageAvailableListener事件,当图片可得到的时候回到,也就是点击拍照的时候 427 | mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler); 428 | 429 | // Find out if we need to swap dimension to get the preview size relative to sensor 430 | // coordinate. 431 | // 获取手机旋转的角度以调整图片的方向 432 | int displayRotation = this.getWindowManager().getDefaultDisplay().getRotation(); 433 | //noinspection ConstantConditions 434 | mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); 435 | boolean swappedDimensions = false; 436 | switch (displayRotation) { 437 | case Surface.ROTATION_0: 438 | case Surface.ROTATION_180: 439 | // 横屏 440 | if (mSensorOrientation == 90 || mSensorOrientation == 270) { 441 | swappedDimensions = true; 442 | } 443 | break; 444 | case Surface.ROTATION_90: 445 | case Surface.ROTATION_270: 446 | // 竖屏 447 | if (mSensorOrientation == 0 || mSensorOrientation == 180) { 448 | swappedDimensions = true; 449 | } 450 | break; 451 | default: 452 | Log.e(TAG, "Display rotation is invalid: " + displayRotation); 453 | } 454 | 455 | Point displaySize = new Point(); 456 | this.getWindowManager().getDefaultDisplay().getSize(displaySize); 457 | int rotatedPreviewWidth = width; 458 | int rotatedPreviewHeight = height; 459 | int maxPreviewWidth = displaySize.x; 460 | int maxPreviewHeight = displaySize.y; 461 | 462 | if (swappedDimensions) { 463 | rotatedPreviewWidth = height; 464 | rotatedPreviewHeight = width; 465 | maxPreviewWidth = displaySize.y; 466 | maxPreviewHeight = displaySize.x; 467 | } 468 | 469 | if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { 470 | maxPreviewWidth = MAX_PREVIEW_WIDTH; 471 | } 472 | 473 | if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { 474 | maxPreviewHeight = MAX_PREVIEW_HEIGHT; 475 | } 476 | 477 | // Danger, W.R.! Attempting to use too large a preview size could exceed the camera 478 | // bus' bandwidth limitation, resulting in gorgeous previews but the storage of 479 | // garbage capture data. 480 | // 尝试使用太大的预览大小可能会超出摄像头的带宽限制 481 | mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), 482 | rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, 483 | maxPreviewHeight, largest); 484 | 485 | // 我们将TextureView的宽高比与我们选择的预览大小相匹配。 486 | int orientation = getResources().getConfiguration().orientation; 487 | if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 488 | //横屏 489 | mTextureView.setAspectRatio( 490 | mPreviewSize.getWidth(), mPreviewSize.getHeight()); 491 | } else { 492 | // 竖屏 493 | mTextureView.setAspectRatio( 494 | mPreviewSize.getHeight(), mPreviewSize.getWidth()); 495 | } 496 | 497 | // 检查闪光灯是否支持。 498 | Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); 499 | mFlashSupported = available == null ? false : available; 500 | mCameraId = cameraId; 501 | return; 502 | } 503 | } catch (CameraAccessException e) { 504 | e.printStackTrace(); 505 | } catch (NullPointerException e) { 506 | //不支持Camera2API 507 | ErrorDialog.newInstance(getString(R.string.camera_error)) 508 | .show(this.getFragmentManager(), FRAGMENT_DIALOG); 509 | } 510 | } 511 | 512 | 513 | /** 514 | * Capture a still picture. This method should be called when we get a response in 515 | * {@link #mCaptureCallback} from both {@link #lockFocus()}. 516 | * 拍摄静态图片。 517 | */ 518 | private void captureStillPicture() { 519 | try { 520 | if ( null == mCameraDevice) { 521 | return; 522 | } 523 | // 这是用来拍摄照片的CaptureRequest.Builder。 524 | final CaptureRequest.Builder captureBuilder = 525 | mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); 526 | captureBuilder.addTarget(mImageReader.getSurface()); 527 | 528 | // 使用相同的AE和AF模式作为预览。 529 | captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, 530 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 531 | setAutoFlash(captureBuilder); 532 | 533 | // 方向 534 | int rotation = this.getWindowManager().getDefaultDisplay().getRotation(); 535 | captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation)); 536 | 537 | CameraCaptureSession.CaptureCallback CaptureCallback 538 | = new CameraCaptureSession.CaptureCallback() { 539 | 540 | @Override 541 | public void onCaptureCompleted(@NonNull CameraCaptureSession session, 542 | @NonNull CaptureRequest request, 543 | @NonNull TotalCaptureResult result) { 544 | showToast("图片地址: " + mFile); 545 | Log.d(TAG, mFile.toString()); 546 | unlockFocus(); 547 | } 548 | }; 549 | //停止连续取景 550 | mCaptureSession.stopRepeating(); 551 | //捕获图片 552 | mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null); 553 | } catch (CameraAccessException e) { 554 | e.printStackTrace(); 555 | } 556 | } 557 | 558 | 559 | 560 | /** 561 | * Initiate a still image capture. 562 | */ 563 | private void takePicture() { 564 | lockFocus(); 565 | } 566 | 567 | /** 568 | * 将焦点锁定为静态图像捕获的第一步。(对焦) 569 | */ 570 | private void lockFocus() { 571 | try { 572 | // 相机对焦 573 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 574 | CameraMetadata.CONTROL_AF_TRIGGER_START); 575 | // 修改状态 576 | mState = STATE_WAITING_LOCK; 577 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 578 | mBackgroundHandler); 579 | } catch (CameraAccessException e) { 580 | e.printStackTrace(); 581 | } 582 | } 583 | 584 | /** 585 | * 解锁焦点 586 | */ 587 | private void unlockFocus() { 588 | try { 589 | // 重置自动对焦 590 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, 591 | CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); 592 | setAutoFlash(mPreviewRequestBuilder); 593 | mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, 594 | mBackgroundHandler); 595 | // 将相机恢复正常的预览状态。 596 | mState = STATE_PREVIEW; 597 | // 打开连续取景模式 598 | mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, 599 | mBackgroundHandler); 600 | } catch (CameraAccessException e) { 601 | e.printStackTrace(); 602 | } 603 | } 604 | 605 | /** 606 | * Retrieves the JPEG orientation from the specified screen rotation. 607 | * 608 | * @param rotation The screen rotation. 609 | * @return The JPEG orientation (one of 0, 90, 270, and 360) 610 | */ 611 | private int getOrientation(int rotation) { 612 | // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X) 613 | // We have to take that into account and rotate JPEG properly. 614 | // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS. 615 | // For devices with orientation of 270, we need to rotate the JPEG 180 degrees. 616 | return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360; 617 | } 618 | 619 | private void setAutoFlash(CaptureRequest.Builder requestBuilder) { 620 | if (mFlashSupported) { 621 | requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, 622 | CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); 623 | } 624 | } 625 | 626 | /** 627 | * 为相机预览创建新的CameraCaptureSession 628 | */ 629 | private void createCameraPreviewSession() { 630 | try { 631 | SurfaceTexture texture = mTextureView.getSurfaceTexture(); 632 | assert texture != null; 633 | 634 | 635 | // 将默认缓冲区的大小配置为我们想要的相机预览的大小。 设置分辨率 636 | texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); 637 | 638 | // 预览的输出Surface。 639 | Surface surface = new Surface(texture); 640 | 641 | //设置了一个具有输出Surface的CaptureRequest.Builder。 642 | mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 643 | mPreviewRequestBuilder.addTarget(surface); 644 | 645 | //创建一个CameraCaptureSession来进行相机预览。 646 | mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), 647 | new CameraCaptureSession.StateCallback() { 648 | 649 | @Override 650 | public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { 651 | // 相机已经关闭 652 | if (null == mCameraDevice) { 653 | return; 654 | } 655 | 656 | // 会话准备好后,我们开始显示预览 657 | mCaptureSession = cameraCaptureSession; 658 | try { 659 | // 自动对焦应 660 | mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, 661 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); 662 | // 闪光灯 663 | setAutoFlash(mPreviewRequestBuilder); 664 | // 最终开启相机预览并添加事件 665 | mPreviewRequest = mPreviewRequestBuilder.build(); 666 | mCaptureSession.setRepeatingRequest(mPreviewRequest, 667 | mCaptureCallback, mBackgroundHandler); 668 | } catch (CameraAccessException e) { 669 | e.printStackTrace(); 670 | } 671 | } 672 | 673 | @Override 674 | public void onConfigureFailed( 675 | @NonNull CameraCaptureSession cameraCaptureSession) { 676 | showToast("Failed"); 677 | } 678 | }, null 679 | ); 680 | } catch (CameraAccessException e) { 681 | e.printStackTrace(); 682 | } 683 | } 684 | 685 | /** 686 | * Shows a {@link Toast} on the UI thread. 687 | * 688 | * @param text The message to show 689 | */ 690 | private void showToast(final String text) { 691 | runOnUiThread(new Runnable() { 692 | @Override 693 | public void run() { 694 | Toast.makeText(MyCameraActivity.this, text, Toast.LENGTH_SHORT).show(); 695 | } 696 | }); 697 | } 698 | 699 | private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, 700 | int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { 701 | 702 | // Collect the supported resolutions that are at least as big as the preview Surface 703 | List bigEnough = new ArrayList<>(); 704 | // Collect the supported resolutions that are smaller than the preview Surface 705 | List notBigEnough = new ArrayList<>(); 706 | int w = aspectRatio.getWidth(); 707 | int h = aspectRatio.getHeight(); 708 | for (Size option : choices) { 709 | if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && 710 | option.getHeight() == option.getWidth() * h / w) { 711 | if (option.getWidth() >= textureViewWidth && 712 | option.getHeight() >= textureViewHeight) { 713 | bigEnough.add(option); 714 | } else { 715 | notBigEnough.add(option); 716 | } 717 | } 718 | } 719 | 720 | // Pick the smallest of those big enough. If there is no one big enough, pick the 721 | // largest of those not big enough. 722 | if (bigEnough.size() > 0) { 723 | return Collections.min(bigEnough, new CompareSizesByArea()); 724 | } else if (notBigEnough.size() > 0) { 725 | return Collections.max(notBigEnough, new CompareSizesByArea()); 726 | } else { 727 | Log.e(TAG, "Couldn't find any suitable preview size"); 728 | return choices[0]; 729 | } 730 | } 731 | 732 | /** 733 | * 检查权限 734 | */ 735 | private void requestCameraPermission() { 736 | // if (FragmentCompat.shouldShowRequestPermissionRationale(this.get, Manifest.permission.CAMERA)) { 737 | // new Camera2BasicFragment.ConfirmationDialog().show(getChildFragmentManager(), FRAGMENT_DIALOG); 738 | // } else { 739 | // FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 740 | // REQUEST_CAMERA_PERMISSION); 741 | // } 742 | } 743 | 744 | /** 745 | * 启动一个HandlerThread 746 | */ 747 | private void startBackgroundThread() { 748 | mBackgroundThread = new HandlerThread("CameraBackground"); 749 | mBackgroundThread.start(); 750 | mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); 751 | } 752 | 753 | /** 754 | * 点击事件 755 | * @param v 756 | */ 757 | @Override 758 | public void onClick(View v) { 759 | switch (v.getId()) { 760 | case R.id.picture: { 761 | takePicture(); 762 | break; 763 | } 764 | } 765 | } 766 | 767 | 768 | /** 769 | * 770 | *根据他们的区域比较两个Size 771 | * 772 | */ 773 | static class CompareSizesByArea implements Comparator { 774 | 775 | @Override 776 | public int compare(Size lhs, Size rhs) { 777 | // 我们在这里投放,以确保乘法不会溢出 778 | return Long.signum((long) lhs.getWidth() * lhs.getHeight() - 779 | (long) rhs.getWidth() * rhs.getHeight()); 780 | } 781 | 782 | } 783 | 784 | 785 | /** 786 | * 显示错误消息对话框。 787 | */ 788 | public static class ErrorDialog extends DialogFragment { 789 | 790 | private static final String ARG_MESSAGE = "message"; 791 | 792 | public static ErrorDialog newInstance(String message) { 793 | ErrorDialog dialog = new ErrorDialog(); 794 | Bundle args = new Bundle(); 795 | args.putString(ARG_MESSAGE, message); 796 | dialog.setArguments(args); 797 | return dialog; 798 | } 799 | 800 | @Override 801 | public Dialog onCreateDialog(Bundle savedInstanceState) { 802 | final Activity activity = getActivity(); 803 | return new AlertDialog.Builder(activity) 804 | .setMessage(getArguments().getString(ARG_MESSAGE)) 805 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 806 | @Override 807 | public void onClick(DialogInterface dialogInterface, int i) { 808 | activity.finish(); 809 | } 810 | }) 811 | .create(); 812 | } 813 | 814 | } 815 | 816 | 817 | /** 818 | * 将JPG保存到指定的文件中。 819 | */ 820 | private static class ImageSaver implements Runnable { 821 | 822 | /** 823 | * JPEG图像 824 | */ 825 | private final Image mImage; 826 | /** 827 | * 保存图像的文件 828 | */ 829 | private final File mFile; 830 | 831 | public ImageSaver(Image image, File file) { 832 | mImage = image; 833 | mFile = file; 834 | } 835 | 836 | @Override 837 | public void run() { 838 | ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); 839 | byte[] bytes = new byte[buffer.remaining()]; 840 | buffer.get(bytes); 841 | FileOutputStream output = null; 842 | try { 843 | output = new FileOutputStream(mFile); 844 | output.write(bytes); 845 | } catch (IOException e) { 846 | e.printStackTrace(); 847 | } finally { 848 | mImage.close(); 849 | if (null != output) { 850 | try { 851 | output.close(); 852 | } catch (IOException e) { 853 | e.printStackTrace(); 854 | } 855 | } 856 | } 857 | } 858 | 859 | } 860 | } 861 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/drawable-xhdpi/ic_action_info.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_camera2.xml: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 |