├── .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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
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 |
21 |
22 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_my_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
26 |
27 |
28 |
45 |
46 |
52 |
53 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_camera2_basic.xml:
--------------------------------------------------------------------------------
1 |
16 |
19 |
20 |
27 |
28 |
36 |
37 |
43 |
44 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #cc4285f4
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WallpaperDemo
3 | Please open permissions
4 | Choose wallpaper
5 | Info
6 | This sample needs camera permission.
7 | This device doesn\'t support Camera2 API.
8 | 拍摄
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/livewallpaper.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/test/java/com/helin/wallpaperdemo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.helin.wallpaperdemo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hemumu/WallpaperDemo/c374f835074c4ef4fab9d493ed676c2a6c33dcec/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jul 05 11:16:30 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------