├── .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' --------------------------------------------------------------------------------