├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ ├── CMakeLists.txt
│ ├── include
│ │ ├── ImageUtil.h
│ │ └── com_afei_camera2getpreview_util_NativeLibrary.h
│ └── src
│ │ ├── ImageUtil.cpp
│ │ └── com_afei_camera2getpreview_util_NativeLibrary.cpp
│ ├── java
│ └── com
│ │ └── afei
│ │ └── camera2getpreview
│ │ ├── Camera2View.java
│ │ ├── CameraFragment.java
│ │ ├── MainActivity.java
│ │ └── util
│ │ ├── Camera2Proxy.java
│ │ ├── ColorConvertUtil.java
│ │ ├── FileUtil.java
│ │ ├── NativeLibrary.java
│ │ ├── Permission.java
│ │ └── PermissionDialog.java
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ ├── click_button_selector.xml
│ └── ic_launcher_background.xml
│ ├── layout
│ ├── activity_main.xml
│ └── fragment_camera.xml
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_camera_switch.png
│ ├── ic_close.png
│ ├── ic_launcher.png
│ ├── ic_launcher_round.png
│ ├── icon_button_click.png
│ └── icon_button_click_down.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # IntelliJ
36 | *.iml
37 | .idea/
38 |
39 | # Keystore files
40 | # Uncomment the following line if you do not want to check your keystore files in.
41 | #*.jks
42 |
43 | # External native build folder generated in Android Studio 2.2 and later
44 | .externalNativeBuild
45 |
46 | # Google Services (e.g. APIs or Firebase)
47 | google-services.json
48 |
49 | # Freeline
50 | freeline.py
51 | freeline/
52 | freeline_project_description.json
53 |
54 | # fastlane
55 | fastlane/report.xml
56 | fastlane/Preview.html
57 | fastlane/screenshots
58 | fastlane/test_output
59 | fastlane/readme.md
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Camera2GetPreview
2 | 在 Android Camera2 中获取和处理预览帧的示例工程
3 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /.cxx/
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdk 32
5 |
6 | defaultConfig {
7 | applicationId "com.afei.camera2getpreview"
8 | minSdkVersion 21
9 | targetSdkVersion 32
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | externalNativeBuild {
14 | cmake {
15 | cppFlags "-std=c++11"
16 | abiFilters "armeabi-v7a" , "arm64-v8a"
17 | }
18 | }
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 |
28 | externalNativeBuild {
29 | cmake {
30 | path 'src/main/cpp/CMakeLists.txt'
31 | version "3.18.1"
32 | }
33 | }
34 |
35 | compileOptions {
36 | sourceCompatibility JavaVersion.VERSION_1_8
37 | targetCompatibility JavaVersion.VERSION_1_8
38 | }
39 | }
40 |
41 | dependencies {
42 | implementation fileTree(dir: 'libs', include: ['*.jar'])
43 | implementation 'androidx.appcompat:appcompat:1.5.0'
44 | }
45 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.18.1)
2 | project("nativelib")
3 |
4 | # 指定包含的头文件路径
5 | include_directories(
6 | ${CMAKE_SOURCE_DIR}/include
7 | )
8 |
9 | # 搜索自动目录下的所有源代码文件,并存储在 SRC_LIST 变量中
10 | file(GLOB_RECURSE SRC_LIST ${CMAKE_SOURCE_DIR}/src/*.cpp)
11 |
12 | add_library( native-lib
13 | SHARED
14 | ${SRC_LIST})
15 |
16 | find_library( log-lib log )
17 |
18 | target_link_libraries( native-lib
19 | # Links the target library to the log library
20 | # included in the NDK.
21 | ${log-lib}
22 | )
23 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/ImageUtil.h:
--------------------------------------------------------------------------------
1 | #ifndef FACEUNLOCK_IMAGEUTIL_H
2 | #define FACEUNLOCK_IMAGEUTIL_H
3 |
4 | extern "C" void i420torgba(const unsigned char *src,
5 | const int width,
6 | const int height,
7 | unsigned char *dst);
8 |
9 | #endif //FACEUNLOCK_IMAGEUTIL_H
10 |
--------------------------------------------------------------------------------
/app/src/main/cpp/include/com_afei_camera2getpreview_util_NativeLibrary.h:
--------------------------------------------------------------------------------
1 | /* DO NOT EDIT THIS FILE - it is machine generated */
2 | #include
3 | /* Header for class com_afei_camera2getpreview_util_NativeLibrary */
4 |
5 | #ifndef _Included_com_afei_camera2getpreview_util_NativeLibrary
6 | #define _Included_com_afei_camera2getpreview_util_NativeLibrary
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 | /*
11 | * Class: com_afei_camera2getpreview_util_NativeLibrary
12 | * Method: yuv420p2rgba
13 | * Signature: ([BII[B)V
14 | */
15 | JNIEXPORT void JNICALL Java_com_afei_camera2getpreview_util_NativeLibrary_yuv420p2rgba
16 | (JNIEnv *, jclass, jbyteArray, jint, jint, jbyteArray);
17 |
18 | #ifdef __cplusplus
19 | }
20 | #endif
21 | #endif
22 |
--------------------------------------------------------------------------------
/app/src/main/cpp/src/ImageUtil.cpp:
--------------------------------------------------------------------------------
1 | #include "ImageUtil.h"
2 |
3 | #define MAX(a, b) ((a > b) ? a : b)
4 | #define MIN(a, b) ((a < b) ? a : b)
5 | #define CLAP(a) (MAX((MIN(a, 0xff)), 0x00))
6 |
7 | void i420torgba(const unsigned char *imgY,
8 | const int width,
9 | const int height,
10 | unsigned char *imgDst) {
11 | int w, h;
12 | int shift = 14, offset = 8192;
13 | int C0 = 22987, C1 = -11698, C2 = -5636, C3 = 29049;
14 |
15 | int y1, y2, u1, v1;
16 |
17 | const unsigned char *pY1 = imgY;
18 | const unsigned char *pY2 = imgY + width;
19 | const unsigned char *pU = imgY + width * height;
20 | const unsigned char *pV = imgY + (int) (width * height * 1.25);
21 |
22 | unsigned char *pD1 = imgDst;
23 | unsigned char *pD2 = imgDst + width * 4;
24 |
25 | for (h = 0; h < height; h += 2) {
26 | for (w = 0; w < width; w += 2) {
27 | v1 = *pV - 128;
28 | pV++;
29 | u1 = *pU - 128;
30 | pU++;
31 |
32 | y1 = *pY1;
33 | y2 = *pY2;
34 |
35 | *pD1++ = CLAP(y1 + ((v1 * C0 + offset) >> shift)); // r
36 | *pD1++ = CLAP(y1 + ((u1 * C2 + v1 * C1 + offset) >> shift)); // g
37 | *pD1++ = CLAP(y1 + ((u1 * C3 + offset) >> shift)); // b
38 | *pD1++ = 0xff; // a
39 | *pD2++ = CLAP(y2 + ((v1 * C0 + offset) >> shift)); // r
40 | *pD2++ = CLAP(y2 + ((u1 * C2 + v1 * C1 + offset) >> shift)); // g
41 | *pD2++ = CLAP(y2 + ((u1 * C3 + offset) >> shift)); // b
42 | *pD2++ = 0xff; // a
43 |
44 | pY1++;
45 | pY2++;
46 | y1 = *pY1;
47 | y2 = *pY2;
48 |
49 | *pD1++ = CLAP(y1 + ((v1 * C0 + offset) >> shift)); // r
50 | *pD1++ = CLAP(y1 + ((u1 * C2 + v1 * C1 + offset) >> shift)); // g
51 | *pD1++ = CLAP(y1 + ((u1 * C3 + offset) >> shift)); // b
52 | *pD1++ = 0xff; // a
53 | *pD2++ = CLAP(y2 + ((v1 * C0 + offset) >> shift)); // r
54 | *pD2++ = CLAP(y2 + ((u1 * C2 + v1 * C1 + offset) >> shift)); // g
55 | *pD2++ = CLAP(y2 + ((u1 * C3 + offset) >> shift)); // b
56 | *pD2++ = 0xff; // a
57 | pY1++;
58 | pY2++;
59 | }
60 | pY1 += width;
61 | pY2 += width;
62 | pD1 += 4 * width;
63 | pD2 += 4 * width;
64 | }
65 | }
--------------------------------------------------------------------------------
/app/src/main/cpp/src/com_afei_camera2getpreview_util_NativeLibrary.cpp:
--------------------------------------------------------------------------------
1 | #include "com_afei_camera2getpreview_util_NativeLibrary.h"
2 | #include "ImageUtil.h"
3 |
4 | JNIEXPORT void JNICALL Java_com_afei_camera2getpreview_util_NativeLibrary_yuv420p2rgba
5 | (JNIEnv *env, jclass type, jbyteArray yuv420p_, jint width, jint height, jbyteArray rgba_) {
6 | jbyte *yuv420p = env->GetByteArrayElements(yuv420p_, NULL);
7 | jbyte *rgba = env->GetByteArrayElements(rgba_, NULL);
8 |
9 | i420torgba(reinterpret_cast(yuv420p), width, height, reinterpret_cast(rgba));
10 |
11 | env->ReleaseByteArrayElements(yuv420p_, yuv420p, 0);
12 | env->ReleaseByteArrayElements(rgba_, rgba, 0);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/afei/camera2getpreview/Camera2View.java:
--------------------------------------------------------------------------------
1 | package com.afei.camera2getpreview;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.util.AttributeSet;
6 | import android.view.TextureView;
7 |
8 | import com.afei.camera2getpreview.util.Camera2Proxy;
9 |
10 | public class Camera2View extends TextureView {
11 |
12 | private static final String TAG = "Camera2View";
13 | private Camera2Proxy mCameraProxy;
14 | private int mRatioWidth = 0;
15 | private int mRatioHeight = 0;
16 |
17 | public Camera2View(Context context) {
18 | this(context, null);
19 | }
20 |
21 | public Camera2View(Context context, AttributeSet attrs) {
22 | this(context, attrs, 0);
23 | }
24 |
25 | public Camera2View(Context context, AttributeSet attrs, int defStyleAttr) {
26 | this(context, attrs, defStyleAttr, 0);
27 | }
28 |
29 | public Camera2View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
30 | super(context, attrs, defStyleAttr, defStyleRes);
31 | init(context);
32 | }
33 |
34 | private void init(Context context) {
35 | mCameraProxy = new Camera2Proxy((Activity) context);
36 | }
37 |
38 | public void setAspectRatio(int width, int height) {
39 | if (width < 0 || height < 0) {
40 | throw new IllegalArgumentException("Size cannot be negative.");
41 | }
42 | mRatioWidth = width;
43 | mRatioHeight = height;
44 | requestLayout();
45 | }
46 |
47 | public Camera2Proxy getCameraProxy() {
48 | return mCameraProxy;
49 | }
50 |
51 | @Override
52 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
53 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
54 | int width = MeasureSpec.getSize(widthMeasureSpec);
55 | int height = MeasureSpec.getSize(heightMeasureSpec);
56 | if (0 == mRatioWidth || 0 == mRatioHeight) {
57 | setMeasuredDimension(width, height);
58 | } else {
59 | if (width < height * mRatioWidth / mRatioHeight) {
60 | setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
61 | } else {
62 | setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
63 | }
64 | }
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/afei/camera2getpreview/CameraFragment.java:
--------------------------------------------------------------------------------
1 | package com.afei.camera2getpreview;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.SurfaceTexture;
5 | import android.media.Image;
6 | import android.media.ImageReader;
7 | import android.os.Bundle;
8 | import android.util.Size;
9 | import android.view.LayoutInflater;
10 | import android.view.TextureView;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.ImageView;
14 |
15 | import androidx.annotation.NonNull;
16 | import androidx.annotation.Nullable;
17 | import androidx.fragment.app.Fragment;
18 |
19 | import com.afei.camera2getpreview.util.Camera2Proxy;
20 | import com.afei.camera2getpreview.util.ColorConvertUtil;
21 | import com.afei.camera2getpreview.util.FileUtil;
22 |
23 | import java.nio.ByteBuffer;
24 |
25 | public class CameraFragment extends Fragment implements View.OnClickListener {
26 |
27 | private static final String TAG = "CameraFragment";
28 |
29 | private ImageView mCloseIv;
30 | private ImageView mSwitchCameraIv;
31 | private ImageView mTakePictureIv;
32 | private Camera2View mCameraView;
33 |
34 | private Camera2Proxy mCameraProxy;
35 |
36 | private byte[] mYuvBytes;
37 | private boolean mIsShutter;
38 |
39 | private final TextureView.SurfaceTextureListener mSurfaceTextureListener
40 | = new TextureView.SurfaceTextureListener() {
41 |
42 | @Override
43 | public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
44 | mCameraProxy.openCamera();
45 | mCameraProxy.setPreviewSurface(texture);
46 | // 根据相机预览设置View大小,避免显示变形
47 | Size previewSize = mCameraProxy.getPreviewSize();
48 | mCameraView.setAspectRatio(previewSize.getHeight(), previewSize.getWidth());
49 | }
50 |
51 | @Override
52 | public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
53 | }
54 |
55 | @Override
56 | public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
57 | return true;
58 | }
59 |
60 | @Override
61 | public void onSurfaceTextureUpdated(SurfaceTexture texture) {
62 | }
63 |
64 | };
65 |
66 | private ImageReader.OnImageAvailableListener mOnImageAvailableListener
67 | = reader -> {
68 | Image image = reader.acquireLatestImage();
69 | if (image == null) {
70 | return;
71 | }
72 | processImage(image);
73 | image.close(); // 一定不能忘记close
74 | };
75 |
76 | @Nullable
77 | @Override
78 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
79 | @Nullable Bundle savedInstanceState) {
80 | View rootView = inflater.inflate(R.layout.fragment_camera, null);
81 | initView(rootView);
82 | return rootView;
83 | }
84 |
85 | private void initView(View rootView) {
86 | mCloseIv = rootView.findViewById(R.id.toolbar_close_iv);
87 | mSwitchCameraIv = rootView.findViewById(R.id.toolbar_switch_iv);
88 | mTakePictureIv = rootView.findViewById(R.id.take_picture_iv);
89 | mCameraView = rootView.findViewById(R.id.camera_view);
90 | mCameraProxy = mCameraView.getCameraProxy();
91 |
92 | mCloseIv.setOnClickListener(this);
93 | mSwitchCameraIv.setOnClickListener(this);
94 | mTakePictureIv.setOnClickListener(this);
95 | mCameraProxy.setImageAvailableListener(mOnImageAvailableListener);
96 | }
97 |
98 | @Override
99 | public void onResume() {
100 | super.onResume();
101 | if (mCameraView.isAvailable()) {
102 | mCameraProxy.openCamera();
103 | } else {
104 | mCameraView.setSurfaceTextureListener(mSurfaceTextureListener);
105 | }
106 | }
107 |
108 | @Override
109 | public void onPause() {
110 | super.onPause();
111 | mCameraProxy.releaseCamera();
112 | }
113 |
114 | @Override
115 | public void onClick(View v) {
116 | switch (v.getId()) {
117 | case R.id.toolbar_close_iv:
118 | getActivity().finish();
119 | break;
120 | case R.id.toolbar_switch_iv:
121 | mCameraProxy.switchCamera();
122 | break;
123 | case R.id.take_picture_iv:
124 | mIsShutter = true;
125 | break;
126 | }
127 | }
128 |
129 |
130 | private void processImage(Image image) {
131 | int width = mCameraProxy.getPreviewSize().getWidth();
132 | int height = mCameraProxy.getPreviewSize().getHeight();
133 | if (mYuvBytes == null) {
134 | // YUV420 大小总是 width * height * 3 / 2
135 | mYuvBytes = new byte[width * height * 3 / 2];
136 | }
137 |
138 | ColorConvertUtil.getI420FromImage(image, mYuvBytes);
139 |
140 | if (mIsShutter) {
141 | mIsShutter = false;
142 | // save yuv data
143 | String yuvPath = FileUtil.SAVE_DIR + System.currentTimeMillis() + ".yuv";
144 | FileUtil.saveBytes(mYuvBytes, yuvPath);
145 | // save bitmap data
146 | String jpgPath = yuvPath.replace(".yuv", ".jpg");
147 | Bitmap bitmap = ColorConvertUtil.yuv420pToBitmap(mYuvBytes, width, height);
148 | FileUtil.saveBitmap(bitmap, jpgPath);
149 | bitmap.recycle();
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/app/src/main/java/com/afei/camera2getpreview/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.afei.camera2getpreview;
2 |
3 | import android.content.pm.PackageManager;
4 | import android.os.Bundle;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.appcompat.app.AppCompatActivity;
8 |
9 | import com.afei.camera2getpreview.util.Permission;
10 | import com.afei.camera2getpreview.util.PermissionDialog;
11 |
12 | public class MainActivity extends AppCompatActivity {
13 |
14 | private CameraFragment mCameraFragment;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_main);
20 | }
21 |
22 | @Override
23 | protected void onStart() {
24 | super.onStart();
25 | Permission.checkPermission(this);
26 | }
27 |
28 | @Override
29 | protected void onResume() {
30 | super.onResume();
31 | if (Permission.isPermissionGranted(this)) {
32 | initCameraFragment();
33 | }
34 | }
35 |
36 | private void initCameraFragment() {
37 | if (mCameraFragment == null) {
38 | mCameraFragment = new CameraFragment();
39 | getSupportFragmentManager()
40 | .beginTransaction()
41 | .replace(R.id.fragment_container, mCameraFragment)
42 | .commit();
43 | }
44 | }
45 |
46 | @Override
47 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
48 | @NonNull int[] grantResults) {
49 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
50 | if (requestCode == Permission.REQUEST_CODE) {
51 | for (int grantResult : grantResults) {
52 | if (grantResult != PackageManager.PERMISSION_GRANTED) {
53 | showPermissionDenyDialog();
54 | return;
55 | }
56 | }
57 | }
58 | }
59 |
60 | private void showPermissionDenyDialog() {
61 | PermissionDialog dialog = new PermissionDialog();
62 | dialog.show(getSupportFragmentManager(), "PermissionDeny");
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/afei/camera2getpreview/util/Camera2Proxy.java:
--------------------------------------------------------------------------------
1 | package com.afei.camera2getpreview.util;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.annotation.TargetApi;
5 | import android.app.Activity;
6 | import android.content.Context;
7 | import android.graphics.ImageFormat;
8 | import android.graphics.SurfaceTexture;
9 | import android.hardware.camera2.CameraAccessException;
10 | import android.hardware.camera2.CameraCaptureSession;
11 | import android.hardware.camera2.CameraCharacteristics;
12 | import android.hardware.camera2.CameraDevice;
13 | import android.hardware.camera2.CameraManager;
14 | import android.hardware.camera2.CaptureRequest;
15 | import android.media.ImageReader;
16 | import android.os.Build;
17 | import android.os.Handler;
18 | import android.os.HandlerThread;
19 | import android.util.Log;
20 | import android.util.Size;
21 | import android.view.Surface;
22 |
23 | import androidx.annotation.NonNull;
24 |
25 | import java.util.Arrays;
26 |
27 | public class Camera2Proxy {
28 |
29 | private static final String TAG = "Camera2Proxy";
30 |
31 | private Activity mActivity;
32 |
33 | // camera
34 | private int mCameraId = CameraCharacteristics.LENS_FACING_FRONT; // 要打开的摄像头ID
35 | private Size mPreviewSize = new Size(640, 480); // 固定640*480演示
36 | private CameraDevice mCameraDevice; // 相机对象
37 | private CameraCaptureSession mCaptureSession;
38 |
39 | // handler
40 | private Handler mBackgroundHandler;
41 | private HandlerThread mBackgroundThread;
42 |
43 | // output
44 | private Surface mPreviewSurface; // 输出到屏幕的预览
45 | private ImageReader mImageReader; // 预览回调的接收者
46 | private ImageReader.OnImageAvailableListener mOnImageAvailableListener;
47 |
48 | /**
49 | * 打开摄像头的回调
50 | */
51 | private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
52 | @Override
53 | public void onOpened(@NonNull CameraDevice camera) {
54 | Log.d(TAG, "onOpened");
55 | mCameraDevice = camera;
56 | initPreviewRequest();
57 | }
58 |
59 | @Override
60 | public void onDisconnected(@NonNull CameraDevice camera) {
61 | Log.d(TAG, "onDisconnected");
62 | releaseCamera();
63 | }
64 |
65 | @Override
66 | public void onError(@NonNull CameraDevice camera, int error) {
67 | Log.e(TAG, "Camera Open failed, error: " + error);
68 | releaseCamera();
69 | }
70 | };
71 |
72 | @TargetApi(Build.VERSION_CODES.M)
73 | public Camera2Proxy(Activity activity) {
74 | mActivity = activity;
75 |
76 | }
77 |
78 | @SuppressLint("MissingPermission")
79 | public void openCamera() {
80 | Log.v(TAG, "openCamera");
81 | startBackgroundThread(); // 对应 releaseCamera() 方法中的 stopBackgroundThread()
82 | try {
83 | CameraManager cameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
84 | Log.d(TAG, "preview size: " + mPreviewSize.getWidth() + "*" + mPreviewSize.getHeight());
85 | mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(),
86 | ImageFormat.YUV_420_888, 2);
87 | mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
88 | // 打开摄像头
89 | cameraManager.openCamera(Integer.toString(mCameraId), mStateCallback, mBackgroundHandler);
90 | } catch (CameraAccessException e) {
91 | e.printStackTrace();
92 | }
93 | }
94 |
95 | public void releaseCamera() {
96 | Log.v(TAG, "releaseCamera");
97 | if (mImageReader != null) {
98 | mImageReader.close();
99 | mImageReader = null;
100 | }
101 | if (mCaptureSession != null) {
102 | mCaptureSession.close();
103 | mCaptureSession = null;
104 | }
105 | if (mCameraDevice != null) {
106 | mCameraDevice.close();
107 | mCameraDevice = null;
108 | }
109 | stopBackgroundThread(); // 对应 openCamera() 方法中的 startBackgroundThread()
110 | }
111 |
112 | public void setImageAvailableListener(ImageReader.OnImageAvailableListener onImageAvailableListener) {
113 | mOnImageAvailableListener = onImageAvailableListener;
114 | }
115 |
116 | public void setPreviewSurface(SurfaceTexture surfaceTexture) {
117 | // mPreviewSize必须先初始化完成
118 | surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
119 | mPreviewSurface = new Surface(surfaceTexture);
120 | }
121 |
122 | private void initPreviewRequest() {
123 | try {
124 | final CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
125 | // 添加输出到屏幕的surface
126 | builder.addTarget(mPreviewSurface);
127 | // 添加输出到ImageReader的surface。然后我们就可以从ImageReader中获取预览数据了
128 | builder.addTarget(mImageReader.getSurface());
129 | mCameraDevice.createCaptureSession(Arrays.asList(mPreviewSurface, mImageReader.getSurface()),
130 | new CameraCaptureSession.StateCallback() {
131 |
132 | @Override
133 | public void onConfigured(@NonNull CameraCaptureSession session) {
134 | mCaptureSession = session;
135 | // 设置连续自动对焦和自动曝光
136 | builder.set(CaptureRequest.CONTROL_AF_MODE,
137 | CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
138 | builder.set(CaptureRequest.CONTROL_AE_MODE,
139 | CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
140 | CaptureRequest captureRequest = builder.build();
141 | try {
142 | // 一直发送预览请求
143 | mCaptureSession.setRepeatingRequest(captureRequest, null, mBackgroundHandler);
144 | } catch (CameraAccessException e) {
145 | e.printStackTrace();
146 | }
147 | }
148 |
149 | @Override
150 | public void onConfigureFailed(@NonNull CameraCaptureSession session) {
151 | Log.e(TAG, "ConfigureFailed. session: mCaptureSession");
152 | }
153 | }, mBackgroundHandler); // handle 传入 null 表示使用当前线程的 Looper
154 | } catch (CameraAccessException e) {
155 | e.printStackTrace();
156 | }
157 | }
158 |
159 | public Size getPreviewSize() {
160 | return mPreviewSize;
161 | }
162 |
163 | public void switchCamera() {
164 | mCameraId ^= 1;
165 | Log.d(TAG, "switchCamera: mCameraId: " + mCameraId);
166 | releaseCamera();
167 | openCamera();
168 | }
169 |
170 | private void startBackgroundThread() {
171 | if (mBackgroundThread == null || mBackgroundHandler == null) {
172 | Log.v(TAG, "startBackgroundThread");
173 | mBackgroundThread = new HandlerThread("CameraBackground");
174 | mBackgroundThread.start();
175 | mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
176 | }
177 | }
178 |
179 | private void stopBackgroundThread() {
180 | Log.v(TAG, "stopBackgroundThread");
181 | mBackgroundThread.quitSafely();
182 | try {
183 | mBackgroundThread.join();
184 | mBackgroundThread = null;
185 | mBackgroundHandler = null;
186 | } catch (InterruptedException e) {
187 | e.printStackTrace();
188 | }
189 | }
190 |
191 | }
192 |
--------------------------------------------------------------------------------
/app/src/main/java/com/afei/camera2getpreview/util/ColorConvertUtil.java:
--------------------------------------------------------------------------------
1 | package com.afei.camera2getpreview.util;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Matrix;
5 | import android.graphics.Rect;
6 | import android.media.Image;
7 | import android.util.Log;
8 |
9 | import java.nio.ByteBuffer;
10 |
11 | public class ColorConvertUtil {
12 |
13 | private static final String TAG = "ColorConvertUtil";
14 |
15 | public static Bitmap yuv420pToBitmap(byte[] yuv420p, int width, int height) {
16 | if (yuv420p == null || width < 0 || height < 0) {
17 | Log.e(TAG, "cropNv21ToBitmap failed: illegal para !");
18 | return null;
19 | }
20 | byte[] rgba = new byte[width * height * 4];
21 | ColorConvertUtil.yuv420pToRGBA(yuv420p, width, height, rgba);
22 | Bitmap bitmap = byteArrayToBitmap(rgba, width, height);
23 | return bitmap;
24 | }
25 |
26 | public static void yuv420pToRGBA(byte[] yuv420p, int width, int height, byte[] rgba) {
27 | if (yuv420p == null || rgba == null) {
28 | Log.e(TAG, "yuv420pToRGBA failed: yuv420p or rgba is null ");
29 | return;
30 | }
31 | if (yuv420p.length != width * height * 3 / 2) {
32 | Log.e(TAG, "yuv420p length: " + yuv420p.length);
33 | Log.e(TAG, "yuv420pToRGBA failed: yuv420p length error!");
34 | return;
35 | }
36 | NativeLibrary.yuv420p2rgba(yuv420p, width, height, rgba);
37 | }
38 |
39 | /**
40 | * 将 rgba 的 byte[] 数据转换成 bitmap
41 | *
42 | * @param rgba 输入的 rgba 数据
43 | * @param width 图片宽度
44 | * @param height 图片高度
45 | * @return 得到的 bitmap
46 | */
47 | public static Bitmap byteArrayToBitmap(byte[] rgba, int width, int height) {
48 | ByteBuffer buffer = ByteBuffer.wrap(rgba);
49 | Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
50 | bitmap.copyPixelsFromBuffer(buffer);
51 | return bitmap;
52 | }
53 |
54 | public static Bitmap rotateBitmap(Bitmap bitmap, int rotate, boolean mirrorX) {
55 | Matrix matrix = new Matrix();
56 | matrix.postRotate(rotate);
57 | if (mirrorX) {
58 | matrix.postScale(-1f, 1f);
59 | }
60 | Bitmap rotateBitmap = null;
61 | if (bitmap != null) {
62 | rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
63 | bitmap.recycle(); // 回收旧Bitmap
64 | }
65 | return rotateBitmap;
66 | }
67 |
68 | public static void getI420FromImage(Image image, byte[] outBuffer) {
69 | Rect crop = image.getCropRect();
70 | int width = crop.width();
71 | int height = crop.height();
72 | Log.d(TAG, "getI420FromImage crop width: " + crop.width() + ", height: " + crop.height());
73 |
74 | int yLength = width * height;
75 | if (outBuffer == null || outBuffer.length != yLength * 3 / 2) {
76 | Log.e(TAG, "outBuffer size error");
77 | return;
78 | }
79 | long time = System.currentTimeMillis();
80 | // YUV_420_888
81 | Image.Plane[] planes = image.getPlanes();
82 | ByteBuffer yBuffer = planes[0].getBuffer();
83 | ByteBuffer uBuffer = planes[1].getBuffer();
84 | ByteBuffer vBuffer = planes[2].getBuffer();
85 |
86 | int stride = planes[0].getRowStride();
87 | Log.d(TAG, "stride y: " + stride);
88 | int pixelStrideUV = planes[1].getPixelStride(); // pixelStride = 2
89 |
90 | synchronized (outBuffer) {
91 | if (stride == width) {
92 | yBuffer.get(outBuffer, 0, yLength);
93 | int index = yLength;
94 | for (int i = 0; i < yLength / 2; i += pixelStrideUV) {
95 | outBuffer[index++] = uBuffer.get(i);
96 | }
97 | for (int i = 0; i < yLength / 2; i += pixelStrideUV) {
98 | outBuffer[index++] = vBuffer.get(i);
99 | }
100 | } else {
101 | for (int i = 0; i < height; i++) {
102 | yBuffer.position(i * stride);
103 | yBuffer.get(outBuffer, i * width, width);
104 | }
105 | int index = yLength;
106 | for (int i = 0; i < height / 2; i++) {
107 | int offset = i * stride;
108 | for (int j = 0; j < width; j += pixelStrideUV) {
109 | outBuffer[index++] = uBuffer.get(offset + j);
110 | }
111 | }
112 | for (int i = 0; i < height / 2; i++) {
113 | int offset = i * stride;
114 | for (int j = 0; j < width; j += pixelStrideUV) {
115 | outBuffer[index++] = vBuffer.get(offset + j);
116 | }
117 | }
118 | }
119 | }
120 | Log.d(TAG, "getI420FromImage time: " + (System.currentTimeMillis() - time));
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/app/src/main/java/com/afei/camera2getpreview/util/FileUtil.java:
--------------------------------------------------------------------------------
1 | package com.afei.camera2getpreview.util;
2 |
3 | import android.graphics.Bitmap;
4 |
5 | import java.io.File;
6 | import java.io.FileNotFoundException;
7 | import java.io.FileOutputStream;
8 | import java.io.IOException;
9 |
10 | public class FileUtil {
11 |
12 | // Android新的版本对SDCARD的权限管理更严格了,直接在该目录存bin文件可能没有权限
13 | public static final String SAVE_DIR = "/sdcard/DCIM/Camera2GetPreview/";
14 |
15 | public static boolean saveBytes(byte[] bytes, String imagePath) {
16 | File file = new File(imagePath);
17 | File parentFile = file.getParentFile();
18 | if (!parentFile.exists()) {
19 | parentFile.mkdirs();
20 | }
21 | try {
22 | FileOutputStream fos = new FileOutputStream(file);
23 | fos.write(bytes);
24 | fos.flush();
25 | fos.close();
26 | return true;
27 | } catch (FileNotFoundException e) {
28 | e.printStackTrace();
29 | } catch (IOException e) {
30 | e.printStackTrace();
31 | }
32 | return false;
33 | }
34 |
35 | public static boolean saveBitmap(Bitmap bitmap, String imagePath) {
36 | if (bitmap == null) {
37 | return false;
38 | }
39 | File file = new File(imagePath);
40 | File parentFile = file.getParentFile();
41 | if (!parentFile.exists()) {
42 | parentFile.mkdirs();
43 | }
44 | try {
45 | FileOutputStream fos = new FileOutputStream(file);
46 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
47 | fos.flush();
48 | fos.close();
49 | return true;
50 | } catch (FileNotFoundException e) {
51 | e.printStackTrace();
52 | } catch (IOException e) {
53 | e.printStackTrace();
54 | }
55 | return false;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/com/afei/camera2getpreview/util/NativeLibrary.java:
--------------------------------------------------------------------------------
1 | package com.afei.camera2getpreview.util;
2 |
3 | public class NativeLibrary {
4 |
5 | static {
6 | System.loadLibrary("native-lib");
7 | }
8 |
9 | public static native void yuv420p2rgba(byte[] yuv420p,
10 | int width,
11 | int height,
12 | byte[] rgba);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/afei/camera2getpreview/util/Permission.java:
--------------------------------------------------------------------------------
1 | package com.afei.camera2getpreview.util;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.pm.PackageManager;
6 | import android.os.Build;
7 |
8 | import androidx.core.app.ActivityCompat;
9 | import androidx.core.content.ContextCompat;
10 |
11 | public class Permission {
12 |
13 | public static final int REQUEST_CODE = 5;
14 |
15 | private static final String[] PERMISSIONS = new String[]{
16 | Manifest.permission.CAMERA,
17 | Manifest.permission.WRITE_EXTERNAL_STORAGE,
18 | };
19 |
20 | public static boolean checkPermission(Activity activity) {
21 | if (isPermissionGranted(activity)) {
22 | return true;
23 | } else {
24 | ActivityCompat.requestPermissions(activity, PERMISSIONS, REQUEST_CODE);
25 | return false;
26 | }
27 | }
28 |
29 | public static boolean isPermissionGranted(Activity activity) {
30 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
31 | for (int i = 0; i < PERMISSIONS.length; i++) {
32 | int checkPermission = ContextCompat.checkSelfPermission(activity, PERMISSIONS[i]);
33 | if (checkPermission != PackageManager.PERMISSION_GRANTED) {
34 | return false;
35 | }
36 | }
37 | }
38 | return true;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/afei/camera2getpreview/util/PermissionDialog.java:
--------------------------------------------------------------------------------
1 | package com.afei.camera2getpreview.util;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.Dialog;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.net.Uri;
8 | import android.os.Bundle;
9 | import android.provider.Settings;
10 |
11 | import androidx.fragment.app.DialogFragment;
12 |
13 | import com.afei.camera2getpreview.R;
14 |
15 | public class PermissionDialog extends DialogFragment implements DialogInterface.OnClickListener {
16 |
17 | @Override
18 | public Dialog onCreateDialog(Bundle savedInstanceState) {
19 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
20 | builder.setTitle(R.string.permission_tip);
21 | builder.setMessage(R.string.permission_msg);
22 | builder.setCancelable(false);
23 | builder.setPositiveButton(R.string.permission_btn_setting, this);
24 | builder.setNegativeButton(R.string.permission_btn_quit, this);
25 | return builder.create();
26 | }
27 |
28 | @Override
29 | public void onClick(DialogInterface dialog, int which) {
30 | dismiss();
31 | if (which == DialogInterface.BUTTON_POSITIVE) {
32 | //setting detail intent
33 | final Intent intent = new Intent();
34 | intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
35 | Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
36 | intent.setData(uri);
37 | startActivity(intent);
38 | } else {
39 | getActivity().finish();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/click_button_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
76 |
81 |
86 |
91 |
96 |
101 |
106 |
111 |
116 |
121 |
126 |
131 |
136 |
141 |
146 |
151 |
156 |
161 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
20 |
21 |
28 |
29 |
33 |
34 |
41 |
42 |
43 |
44 |
49 |
50 |
51 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_camera_switch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/app/src/main/res/mipmap-xxhdpi/ic_camera_switch.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/app/src/main/res/mipmap-xxhdpi/ic_close.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/icon_button_click.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/app/src/main/res/mipmap-xxhdpi/icon_button_click.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/icon_button_click_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/app/src/main/res/mipmap-xxhdpi/icon_button_click_down.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #000000
4 | #000000
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Camera2GetPreview
3 |
4 | Tip
5 | request permission failed! \n
6 | you can grant permission in setting interface -> Permissions
7 | go to setting
8 | quit
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id 'com.android.application' version '7.1.2' apply false
4 | id 'com.android.library' version '7.1.2' apply false
5 | }
6 |
7 | task clean(type: Delete) {
8 | delete rootProject.buildDir
9 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Enables namespacing of each library's R class so that its R class includes only the
19 | # resources declared in the library itself and none from the library's dependencies,
20 | # thereby reducing the size of the R class for that library
21 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afei-cn/Camera2GetPreview/daf52e5c74470af428f699d5718dae7ac31c4887/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Sep 06 15:22:32 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 | rootProject.name = "Camera2GetPreview"
16 | include ':app'
--------------------------------------------------------------------------------