diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser new file mode 100644 index 0000000..593bd42 Binary files /dev/null and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..681f41a --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 40ed937..8144c3c 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -11,5 +11,6 @@ + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 7ac24c7..9bba60d 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,8 +1,10 @@ + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..6560a98 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,36 @@ + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..a5f05cd --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 214e8dc..ecb3119 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,26 +12,41 @@ - + diff --git a/.idea/modules.xml b/.idea/modules.xml index 9c44462..b0f928c 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,8 +2,8 @@ - - + + \ No newline at end of file diff --git a/.idea/sonarlint/issuestore/8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d b/.idea/sonarlint/issuestore/8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d new file mode 100644 index 0000000..e69de29 diff --git a/.idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35 b/.idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35 new file mode 100644 index 0000000..e69de29 diff --git a/.idea/sonarlint/issuestore/index.pb b/.idea/sonarlint/issuestore/index.pb new file mode 100644 index 0000000..ce0bfe4 --- /dev/null +++ b/.idea/sonarlint/issuestore/index.pb @@ -0,0 +1,5 @@ + +< + build.gradle,f\0\f07866736216be0ee2aba49e392191aeae700a35 +9 + README.md,8\e\8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index f20b735..300295b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ ## 一、前言 +**此项目仅是demo,宗旨在于学习,若用在生产环境需谨慎** + + 现在很多`app`都会有拍照功能,一般调用系统进行拍照裁剪就能满足平时的需求,但有些场景或者特殊情况下如:持续不间断拍多张照片或者是进行人脸识别的时候,这时候之间调用系统原生相机拍照时不能满足自己的开发需求,就需要使用原生`Camera`来进行自定义开发,本文会采用`android.hardware.Camera`API来进行开发。在`Android`生态中,`Camera`是碎片化较为严重的一块,因为现在`Android`本身有三套API: * Camera:Android 5.0以下 * Camera2:Android 5.0以上 diff --git a/app/build.gradle b/app/build.gradle index d30ee54..65f32e2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,6 +15,38 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + + + signingConfigs { + debug { + // storeFile file("D:\\keystore\\debug.keystore") + } + release { + //这样写就得把jks文件放在项目目录 + storeFile file('../cameraone.jks')//密钥文件路径 + storePassword "123456"//密钥文件密码 + keyAlias "cameraone"//key别名 + keyPassword "123456"//key密码 + } + } + + + buildTypes { + debug { + minifyEnabled false + shrinkResources false + zipAlignEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + + release { + minifyEnabled true//开启混淆 + shrinkResources true + zipAlignEnabled true//去除无用资源 + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release + } + } } dependencies { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1444971..b0f3b9d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -46,7 +46,7 @@ + android:screenOrientation="portrait"/> diff --git a/app/src/main/java/com/knight/cameraone/CameraPresenter.java b/app/src/main/java/com/knight/cameraone/CameraPresenter.java index 9def36f..f58d8b8 100644 --- a/app/src/main/java/com/knight/cameraone/CameraPresenter.java +++ b/app/src/main/java/com/knight/cameraone/CameraPresenter.java @@ -1,33 +1,27 @@ package com.knight.cameraone; import android.annotation.SuppressLint; -import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.ImageFormat; import android.graphics.Matrix; -import android.graphics.Point; import android.graphics.RectF; -import android.graphics.SurfaceTexture; import android.hardware.Camera; -import android.hardware.camera2.CameraAccessException; -import android.hardware.camera2.CameraCharacteristics; -import android.hardware.camera2.CameraManager; -import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.CamcorderProfile; import android.media.MediaRecorder; +import android.os.Environment; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.util.DisplayMetrics; import android.util.Log; -import android.util.Size; -import android.view.Display; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; -import android.view.WindowManager; +import com.knight.cameraone.utils.CameraHelper; +import com.knight.cameraone.utils.ImageUtil; import com.knight.cameraone.utils.SystemUtil; import com.knight.cameraone.utils.ThreadPoolUtil; import com.knight.cameraone.utils.ToastUtil; @@ -81,15 +75,18 @@ public class CameraPresenter implements Camera.PreviewCallback { //视频录制 private MediaRecorder mediaRecorder; //录制视频的videoSize - private int height,width; + private int height, width; //检测头像的FaceView private FaceDeteView mFaceView; + //视频连接 + private String videoFilePath; + CamcorderProfile profile; - private boolean isFull =false; + private boolean isFull = false; public boolean isFull() { return isFull; @@ -100,7 +97,6 @@ public void setFull(boolean full) { } - //自定义回调 public interface CameraCallBack { //预览帧回调 @@ -114,6 +110,11 @@ public interface CameraCallBack { //拍照路径返回 void getPhotoFile(String imagePath); + + //返回视频路径 + void getVideoFile(String videoFilePath); + + } public void setCameraCallBack(CameraCallBack mCameraCallBack) { @@ -124,31 +125,29 @@ public void setCameraCallBack(CameraCallBack mCameraCallBack) { public CameraPresenter(AppCompatActivity mAppCompatActivity, SurfaceView mSurfaceView) { this.mAppCompatActivity = mAppCompatActivity; this.mSurfaceView = mSurfaceView; - // mSurfaceView.getHolder().setKeepScreenOn(true); + // mSurfaceView.getHolder().setKeepScreenOn(true); mSurfaceHolder = mSurfaceView.getHolder(); DisplayMetrics dm = new DisplayMetrics(); mAppCompatActivity.getWindowManager().getDefaultDisplay().getMetrics(dm); //获取宽高像素 screenWidth = dm.widthPixels; screenHeight = dm.heightPixels; - Log.d("sssd-手机宽高尺寸:",screenWidth +"*"+screenHeight); + Log.d("sssd-手机宽高尺寸:", screenWidth + "*" + screenHeight); //创建文件夹目录 setUpFile(); init(); } /** - * * 人脸检测设置检测的View 矩形框 + * * @param mFaceView */ - public void setFaceView(FaceDeteView mFaceView){ + public void setFaceView(FaceDeteView mFaceView) { this.mFaceView = mFaceView; } - - /** * 设置前置还是后置 * @@ -162,7 +161,7 @@ public void setFrontOrBack(int mCameraId) { /** * 拍照 */ - public void takePicture() { + public void takePicture(final int takePhotoOrientation) { if (mCamera != null) { //拍照回调 点击拍照时回调 写一个空实现 mCamera.takePicture(new Camera.ShutterCallback() { @@ -185,7 +184,7 @@ public void onPictureTaken(byte[] data, Camera camera) { //回调 mCameraCallBack.onTakePicture(data, camera); //保存图片 - getPhotoPath(data); + getPhotoPath(data, takePhotoOrientation); } }); @@ -200,7 +199,7 @@ public void init() { mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { - Log.d("sssd-宽",mSurfaceView.getMeasuredWidth() + "*" +mSurfaceView.getMeasuredHeight()); + Log.d("sssd-宽", mSurfaceView.getMeasuredWidth() + "*" + mSurfaceView.getMeasuredHeight()); //surface创建时执行 if (mCamera == null) { openCamera(mCameraId); @@ -243,7 +242,7 @@ private boolean openCamera(int FaceOrBack) { if (mCamera != null) { mCamera.setPreviewCallback(this); } - } catch (Exception e) { + } catch (Exception e) { e.printStackTrace(); ToastUtil.showShortToast(mAppCompatActivity, "打开相机失败~"); return false; @@ -268,10 +267,10 @@ private void initParameters(Camera camera) { mParameters.setPreviewFormat(ImageFormat.NV21); //mParameters.setExposureCompensation(2); - if(isFull){ - setPreviewSize(screenWidth,screenHeight); + if (isFull) { + setPreviewSize(screenWidth, screenHeight); } else { - setPreviewSize(mSurfaceView.getMeasuredWidth(),mSurfaceView.getMeasuredHeight()); + setPreviewSize(mSurfaceView.getMeasuredWidth(), mSurfaceView.getMeasuredHeight()); } //getOpyimalPreviewSize(); @@ -297,7 +296,6 @@ private void initParameters(Camera camera) { /** - * * 设置保存图片的尺寸 */ private void setPictureSize() { @@ -347,7 +345,7 @@ private void setPictureSize() { /** * 设置预览界面尺寸 */ - public void setPreviewSize(int width,int height) { + public void setPreviewSize(int width, int height) { //获取系统支持预览大小 List localSizes = mParameters.getSupportedPreviewSizes(); Camera.Size biggestSize = null;//最大分辨率 @@ -356,25 +354,21 @@ public void setPreviewSize(int width,int height) { Camera.Size targetSiz2 = null;// 没有屏幕分辨率就取跟屏幕分辨率相近(小)的size if (localSizes != null) { int cameraSizeLength = localSizes.size(); - - if(Float.valueOf(width) / height == 3.0f / 4){ + // 如果是 预览窗口宽:高 == 3:4的话 + if (Float.valueOf(width) / height == 3.0f / 4) { for (int n = 0; n < cameraSizeLength; n++) { Camera.Size size = localSizes.get(n); - // Log.d("sssd-系统支持的尺寸size.width:",size.width + "*" +size.height); - // Log.d("sssd-系统",1440f / 1080+""); - // Log.d("sssd-系统支持的尺寸比:",Double.valueOf(size.width) / size.height+""); - if(Float.valueOf(size.width) / size.height == 4.0f / 3){ - Log.d("sssd-系统支持的尺寸:","进入"); - mParameters.setPreviewSize(size.width,size.height); + if (Float.valueOf(size.width) / size.height == 4.0f / 3) { + mParameters.setPreviewSize(size.width, size.height); break; } } } else { + //全屏幕预览 for (int n = 0; n < cameraSizeLength; n++) { Camera.Size size = localSizes.get(n); - Log.d("sssd-系统支持的尺寸:",size.width + "*" +size.height); if (biggestSize == null || (size.width >= biggestSize.width && size.height >= biggestSize.height)) { biggestSize = size; @@ -408,41 +402,38 @@ public void setPreviewSize(int width,int height) { if (fitSize == null) { fitSize = biggestSize; } - Log.d("sssd-最佳预览尺寸:",fitSize.width + "*" + fitSize.height); - - //mParameters.setPreviewSize(640,480); mParameters.setPreviewSize(fitSize.width, fitSize.height); + fixScreenSize(fitSize.height, fitSize.width); } + } - - - } } /** * 解决预览变形问题 - * - * */ - private void getOpyimalPreviewSize(){ + private void getOpyimalPreviewSize() { List sizes = mParameters.getSupportedPreviewSizes(); int w = screenWidth; int h = screenHeight; final double ASPECT_TOLERANCE = 0.1; double targetRatio = (double) w / h; - if (sizes == null) return; + if (sizes == null) + return; Camera.Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; - for(Camera.Size size : sizes){ + for (Camera.Size size : sizes) { double ratio = (double) size.width / size.height; - if(Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;; - if(Math.abs(size.height - targetHeight) < minDiff){ + if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) + continue; + ; + if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } @@ -450,10 +441,10 @@ private void getOpyimalPreviewSize(){ } - if(optimalSize == null){ + if (optimalSize == null) { minDiff = Double.MAX_VALUE; - for(Camera.Size size : sizes){ - if(Math.abs(size.height - targetHeight) < minDiff){ + for (Camera.Size size : sizes) { + if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } @@ -461,96 +452,55 @@ private void getOpyimalPreviewSize(){ } - mParameters.setPreviewSize(optimalSize.width,optimalSize.height); + mParameters.setPreviewSize(optimalSize.width, optimalSize.height); } - - - -// private Point screenResolution; -// private int mCameraPreviewWidth; -// private int mCameraPreviewHeight; -// //从底层拿camera支持的previewsize,完了和屏幕分辨率做差,diff最小的就是最佳预览分辨率 -// private void getPreviewSize(int mCameraId) { -// try { -// int diffs = Integer.MAX_VALUE; -// WindowManager windowManager = (WindowManager) mAppCompatActivity.getSystemService(Context.WINDOW_SERVICE); -// Display display = windowManager.getDefaultDisplay(); -// screenResolution = new Point(display.getWidth(), display.getHeight()); -// -// CameraCharacteristics props = mCameraManager.getCameraCharacteristics(mCameraId); -// StreamConfigurationMap configurationMap = props.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); -// Size[] availablePreviewSizes = configurationMap.getOutputSizes(SurfaceTexture.class); -// -// for (Size previewSize : availablePreviewSizes) { -// // Log.v(TAG, " PreviewSizes = " + previewSize); -// mCameraPreviewWidth = previewSize.getWidth(); -// mCameraPreviewHeight = previewSize.getHeight(); -// int newDiffs = Math.abs(mCameraPreviewWidth - screenResolution.x) + Math.abs(mCameraPreviewHeight - screenResolution.y); -// // Log.v(TAG, "newDiffs = " + newDiffs); -// -// if (newDiffs == 0) { -// bestPreviewWidth = mCameraPreviewWidth; -// bestPreviewHeight = mCameraPreviewHeight; -// break; -// } -// if (diffs > newDiffs) { -// bestPreviewWidth = mCameraPreviewWidth; -// bestPreviewHeight = mCameraPreviewHeight; -// diffs = newDiffs; -// } -// } -// } catch (CameraAccessException cae) { -// -// } -// } - - - /** * 变焦 + * * @param zoom 缩放系数 */ - public void setZoom(int zoom){ - if(mCamera == null){ - return; - } - //获取Paramters对象 - Camera.Parameters parameters; - parameters = mCamera.getParameters(); - //如果不支持变焦 - if(!parameters.isZoomSupported()){ - return; - } - // - parameters.setZoom(zoom); - //Camera对象重新设置Paramters对象参数 - mCamera.setParameters(parameters); - mZoom = zoom; + public void setZoom(int zoom) { + if (mCamera == null) { + return; + } + //获取Paramters对象 + Camera.Parameters parameters; + parameters = mCamera.getParameters(); + //如果不支持变焦 + if (!parameters.isZoomSupported()) { + return; + } + // + parameters.setZoom(zoom); + //Camera对象重新设置Paramters对象参数 + mCamera.setParameters(parameters); + mZoom = zoom; } /** - * * 返回缩放值 + * * @return 返回缩放值 */ - public int getZoom(){ + public int getZoom() { return mZoom; } /** * 获取最大Zoom值 + * * @return zoom */ - public int getMaxZoom(){ - if(mCamera == null){ + public int getMaxZoom() { + if (mCamera == null) { return -1; } Camera.Parameters parameters = mCamera.getParameters(); - if(!parameters.isZoomSupported()){ + if (!parameters.isZoomSupported()) { return -1; } return parameters.getMaxZoom() > 50 ? 50 : parameters.getMaxZoom(); @@ -610,7 +560,7 @@ private void startPreview() { //根据所传入的SurfaceHolder对象来设置实时预览 mCamera.setPreviewDisplay(mSurfaceHolder); //调整预览角度 - setCameraDisplayOrientation(mAppCompatActivity,mCameraId,mCamera); + setCameraDisplayOrientation(mAppCompatActivity, mCameraId, mCamera); mCamera.startPreview(); //开启人脸检测 startFaceDetect(); @@ -624,19 +574,19 @@ private void startPreview() { */ private void startFaceDetect() { //开始人脸检测,这个要调用startPreview之后调用 -// mCamera.startFaceDetection(); -// //添加回调 -// mCamera.setFaceDetectionListener(new Camera.FaceDetectionListener() { -// @Override -// public void onFaceDetection(Camera.Face[] faces, Camera camera) { -// // mCameraCallBack.onFaceDetect(transForm(faces), camera); -// mFaceView.setFace(transForm(faces)); -// Log.d("sssd", "检测到" + faces.length + "人脸"); -// for(int i = 0;i < faces.length;i++){ -// Log.d("第"+(i+1)+"张人脸","分数"+faces[i].score+"左眼"+faces[i].leftEye+"右眼"+faces[i].rightEye+"嘴巴"+faces[i].mouth); -// } -// } -// }); + // mCamera.startFaceDetection(); + // //添加回调 + // mCamera.setFaceDetectionListener(new Camera.FaceDetectionListener() { + // @Override + // public void onFaceDetection(Camera.Face[] faces, Camera camera) { + // // mCameraCallBack.onFaceDetect(transForm(faces), camera); + // mFaceView.setFace(transForm(faces)); + // Log.d("sssd", "检测到" + faces.length + "人脸"); + // for(int i = 0;i < faces.length;i++){ + // Log.d("第"+(i+1)+"张人脸","分数"+faces[i].score+"左眼"+faces[i].leftEye+"右眼"+faces[i].rightEye+"嘴巴"+faces[i].mouth); + // } + // } + // }); } /** @@ -662,7 +612,7 @@ private ArrayList transForm(Camera.Face[] faces) { //后乘旋转角度 matrix.postRotate(Float.valueOf(orientation)); //后乘缩放 - matrix.postScale(mSurfaceView.getWidth() / 2000f,mSurfaceView.getHeight() / 2000f); + matrix.postScale(mSurfaceView.getWidth() / 2000f, mSurfaceView.getHeight() / 2000f); //再进行位移 matrix.postTranslate(mSurfaceView.getWidth() / 2f, mSurfaceView.getHeight() / 2f); ArrayList arrayList = new ArrayList<>(); @@ -737,32 +687,6 @@ public void switchCamera() { startPreview(); } - -// 这里可以找准前后摄像头的id -// int frontIndex = -1; -// int backIndex = -1; -// int cameraCount = Camera.getNumberOfCameras(); -// Camera.CameraInfo info = new Camera.CameraInfo(); -// for(int cameraIndex = 0;cameraIndex < cameraCount;cameraIndex ++){ -// Camera.getCameraInfo(cameraIndex,info); -// if(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){ -// frontIndex = cameraIndex; -// }else if(info.facing == Camera.CameraInfo.CAMERA_FACING_BACK){ -// backIndex = cameraIndex; -// } -// -// } -// -// //跟据传入的type来判断 -// if(type == FRONT && frontIndex != -1){ -// -// openCamera(frontIndex); -// } else if(type == BACK && backIndex != -1){ -// openCamera(backIndex); -// -// } - - /** * 释放相机资源 */ @@ -776,7 +700,7 @@ public void releaseCamera() { mHandler.removeMessages(1); } - if(mediaRecorder != null){ + if (mediaRecorder != null) { mediaRecorder.release(); mediaRecorder = null; @@ -787,7 +711,10 @@ public void releaseCamera() { * 创建拍照照片文件夹 */ private void setUpFile() { - photosFile = new File(Configuration.insidePath); + //方式1 这里是app的内部存储 这里要注意 不是外部私有目录 详情请看 Configuration这个类 + // photosFile = new File(Configuration.insidePath); + //方式2 这里改为app的外部存储 私有存储目录 /storage/emulated/0/Android/data/com.knight.cameraone/Pictures + photosFile = mAppCompatActivity.getExternalFilesDir(Environment.DIRECTORY_PICTURES); if (!photosFile.exists() || !photosFile.isDirectory()) { boolean isSuccess = false; try { @@ -808,7 +735,7 @@ private void setUpFile() { /** * @return 返回路径 */ - private void getPhotoPath(final byte[] data) { + private void getPhotoPath(final byte[] data, final int takePhotoOrientation) { ThreadPoolUtil.execute(new Runnable() { @Override public void run() { @@ -817,7 +744,7 @@ public void run() { //拍照数量+1 photoNum++; //图片名字 - String name = SystemUtil.formatTime(timeMillis, SystemUtil.formatTime(photoNum) + ".jpg"); + String name = SystemUtil.formatTime(timeMillis, SystemUtil.formatRandom(photoNum) + ".jpg"); //创建具体文件 File file = new File(photosFile, name); if (!file.exists()) { @@ -844,14 +771,20 @@ public void run() { } //将图片旋转 - rotateImageView(mCameraId,orientation,Configuration.insidePath + file.getName()); - //将图片保存到手机相册 - SystemUtil.saveAlbum(Configuration.insidePath + file.getName(), file.getName(), mAppCompatActivity); - //将图片复制到外部 - SystemUtil.copyPicture(Configuration.insidePath + file.getName(),Configuration.OUTPATH,file.getName()); + // rotateImageView(mCameraId,orientation,Configuration.insidePath + file.getName()); + rotateImageView(mCameraId, takePhotoOrientation, file.getAbsolutePath()); + //将图片复制到外部 target SDK 设置 Android 10以下 + //SystemUtil.copyPicture(Configuration.insidePath + file.getName(),Configuration.OUTPATH,file.getName()); + + //将图片保存到手机相册 方式1 + // SystemUtil.saveAlbum(file.getAbsolutePath(), file.getName(), mAppCompatActivity); + //将图片保存到手机相册 方式2 + ImageUtil.saveAlbum(mAppCompatActivity, file); + + Message message = new Message(); message.what = 1; - message.obj = Configuration.insidePath + file.getName(); + message.obj = file.getAbsolutePath(); mHandler.sendMessage(message); } catch (FileNotFoundException e) { e.printStackTrace(); @@ -863,33 +796,31 @@ public void run() { /** * 旋转图片 - * @param cameraId 前置还是后置 + * + * @param cameraId 前置还是后置 * @param orientation 拍照时传感器方向 - * @param path 图片路径 + * @param path 图片路径 */ - private void rotateImageView(int cameraId,int orientation,String path){ + private void rotateImageView(int cameraId, int orientation, String path) { Bitmap bitmap = BitmapFactory.decodeFile(path); Matrix matrix = new Matrix(); + matrix.postRotate(Float.valueOf(orientation)); // 创建新的图片 Bitmap resizedBitmap; - //0是后置 - if(cameraId == 0){ - if(orientation == 90){ - matrix.postRotate(90); + + if (cameraId == 1) { + if (orientation == 90) { + matrix.postRotate(180f); } } - //1是前置 - if(cameraId == 1){ - matrix.postRotate(270); - } // 创建新的图片 resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); //新增 如果是前置 需要镜面翻转处理 - if(cameraId == 1){ + if (cameraId == 1) { Matrix matrix1 = new Matrix(); - matrix1.postScale(-1f,1f); + matrix1.postScale(-1f, 1f); resizedBitmap = Bitmap.createBitmap(resizedBitmap, 0, 0, resizedBitmap.getWidth(), resizedBitmap.getHeight(), matrix1, true); @@ -898,7 +829,7 @@ private void rotateImageView(int cameraId,int orientation,String path){ File file = new File(path); //重新写入文件 - try{ + try { // 写入文件 FileOutputStream fos; fos = new FileOutputStream(file); @@ -907,7 +838,7 @@ private void rotateImageView(int cameraId,int orientation,String path){ fos.flush(); fos.close(); resizedBitmap.recycle(); - }catch (Exception e){ + } catch (Exception e) { e.printStackTrace(); return; } @@ -915,12 +846,12 @@ private void rotateImageView(int cameraId,int orientation,String path){ } // if ("png".equals(imageType)) { -// bitmap.compress(Bitmap.CompressFormat.PNG, 70, fos); -// } else { -// bitmap.compress(Bitmap.CompressFormat.JPEG, 70, fos); -// } + // bitmap.compress(Bitmap.CompressFormat.PNG, 70, fos); + // } else { + // bitmap.compress(Bitmap.CompressFormat.JPEG, 70, fos); + // } @SuppressLint("HandlerLeak") - Handler mHandler = new Handler(){ + Handler mHandler = new Handler() { @SuppressLint("NewApi") @Override public void handleMessage(Message msg) { @@ -937,8 +868,8 @@ public void handleMessage(Message msg) { /** * 自动变焦 */ - public void autoFoucus(){ - if(mCamera == null){ + public void autoFoucus() { + if (mCamera == null) { mCamera.autoFocus(new Camera.AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { @@ -951,69 +882,42 @@ public void onAutoFocus(boolean success, Camera camera) { /** * 获取输出视频的width和height - * */ - public void getVideoSize(){ - int biggest_width=0 ,biggest_height=0;//最大分辨率 - int fitSize_width=0,fitSize_height=0; - int fitSize_widthBig=0,fitSize_heightBig=0; + public void getVideoSize() { + int biggest_width = 0, biggest_height = 0;//最大分辨率 + int fitSize_width = 0, fitSize_height = 0; + int fitSize_widthBig = 0, fitSize_heightBig = 0; Camera.Parameters parameters = mCamera.getParameters(); //得到系统支持视频尺寸 List videoSize = parameters.getSupportedVideoSizes(); - for(int i = 0;i < videoSize.size();i++){ - int w = videoSize.get(i).width; - int h = videoSize.get(i).height; - if ((biggest_width == 0 && biggest_height == 0)|| - (w >= biggest_height && h >= biggest_width)) { - biggest_width = w; - biggest_height = h; - } + List mSupportedPreviewSizes = parameters.getSupportedPreviewSizes(); - if(w == screenHeight && h == screenWidth){ - width = w; - height = h; - }else if(w == screenHeight || h == screenWidth){ - if(width == 0 || height == 0){ - fitSize_width = w; - fitSize_height = h; - }else if(w < screenHeight || h < screenWidth){ - fitSize_widthBig = w; - fitSize_heightBig = h; + //使用官方demo + Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(videoSize, + mSupportedPreviewSizes, mSurfaceView.getWidth(), mSurfaceView.getHeight()); - } - } - } + // Use the same size for recording profile. + profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH); + profile.videoFrameWidth = optimalSize.width; + profile.videoFrameHeight = optimalSize.height; - if(width == 0 && height == 0){ - width = fitSize_width; - height = fitSize_height; - } - - if(width == 0 && height == 0){ - width = fitSize_widthBig; - height = fitSize_heightBig; - } - if(width == 0 && height == 0){ - width = biggest_width; - height = biggest_height; - - } } /** - * * 停止录制 */ - public void stopRecord(){ - if(mediaRecorder != null){ + public void stopRecord() { + if (mediaRecorder != null) { mediaRecorder.release(); mediaRecorder = null; } + mCameraCallBack.getVideoFile(videoFilePath); + - if(mCamera != null){ + if (mCamera != null) { mCamera.release(); } openCamera(mCameraId); @@ -1023,55 +927,60 @@ public void stopRecord(){ /** - * * 录制方法 */ - public void startRecord(String path,String name){ + public void startRecord(String path, String name) { + //解锁Camera硬件 mCamera.unlock(); - if(mediaRecorder == null){ + if (mediaRecorder == null) { mediaRecorder = new MediaRecorder(); } + mediaRecorder.setCamera(mCamera); //音频源 麦克风 mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); //视频源 camera mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); - //输出格式 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - //音频编码 - mediaRecorder.setAudioEncoder(MediaRecorder.VideoEncoder.DEFAULT); - //视频编码 + //设置输出文件格式 录制过程中产生的输出文件的格式 + mediaRecorder.setOutputFormat(profile.fileFormat); + //设置录制的视频编码比特率 会影响清晰 + mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024); + //设置视频编码器 用于录制 mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); - //设置帧频率 - mediaRecorder.setVideoEncodingBitRate(1 * 1024 * 1024 * 100); - Log.d("sssd视频宽高:","宽"+width+"高"+height+""); - mediaRecorder.setVideoSize(width,height); - //每秒的帧数 - mediaRecorder.setVideoFrameRate(20); + //设置Audio编码格式 + mediaRecorder.setAudioEncoder(MediaRecorder.VideoEncoder.DEFAULT); + //设置捕获的视频宽度和高度 + mediaRecorder.setVideoSize(profile.videoFrameWidth,profile.videoFrameHeight); + + // mediaRecorder.setVideoSize(1920,1080); //调整视频旋转角度 如果不设置 后置和前置都会被旋转播放 - if(mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) { - if(orientation == 270 || orientation == 90 || orientation == 180){ + if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) { + if (orientation == 270 || orientation == 90 || orientation == 180) { mediaRecorder.setOrientationHint(180); - }else{ + } else { mediaRecorder.setOrientationHint(0); } - }else{ - if(orientation == 90){ + } else { + if (orientation == 90) { mediaRecorder.setOrientationHint(90); } } + //路径问题 可以到Configuration 这个类看 File file = new File(path); - if(!file.exists()){ + if (!file.exists()) { file.mkdirs(); } //设置输出文件名字 - mediaRecorder.setOutputFile(path + File.separator + name + "mp4"); - File file1 = new File(path + File.separator + name + "mp4"); - if(file1.exists()){ + mediaRecorder.setOutputFile(path + File.separator + name + ".mp4"); + File file1 = new File(path + File.separator + name + ".mp4"); + if (file1.exists()) { file1.delete(); } + + //赋值视频文件 + videoFilePath = path + File.separator + name + ".mp4"; //设置预览 mediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface()); try { @@ -1082,21 +991,21 @@ public void startRecord(String path,String name){ } catch (IOException e) { e.printStackTrace(); } - + // } /** - * * 闪光灯 + * * @param turnSwitch true 为开启 false 为关闭 */ - public void turnLight(boolean turnSwitch){ - if(mCamera == null){ + public void turnLight(boolean turnSwitch) { + if (mCamera == null) { return; } Camera.Parameters parameters = mCamera.getParameters(); - if(parameters == null){ + if (parameters == null) { return; } @@ -1107,24 +1016,73 @@ public void turnLight(boolean turnSwitch){ /** * 开启人脸检测 - * */ - public void turnFaceDetect(boolean isDetect){ - mFaceView.setVisibility(isDetect ? View.VISIBLE : View.GONE); + public void turnFaceDetect(boolean isDetect) { + mFaceView.setVisibility(isDetect ? View.VISIBLE : View.GONE); } /** - * - * * @param mSurfaceView */ - public void update(SurfaceView mSurfaceView){ + public void update(SurfaceView mSurfaceView) { mSurfaceHolder = mSurfaceView.getHolder(); } + /** + * 获取视频文件链接 + * + * @return + */ + public String getVideoFilePath() { + return videoFilePath; + } + + + /** + * 适配 华为p40 小米mix 一些不规则尺寸 + * + * @param fitSizeHeight 获取系统支持最适合的高(现实系统的屏幕宽度) + * @param fitSizeWidth 获取系统支持最适合的宽(现实系统的屏幕高度) + */ + private void fixScreenSize(int fitSizeHeight, int fitSizeWidth) { + + // 预览 View 的大小,比如 SurfaceView + int viewHeight = screenHeight; + int viewWidth = screenWidth; + // 相机选择的预览尺寸 + int cameraHeight = fitSizeWidth; + int cameraWidth = fitSizeHeight; + // 计算出将相机的尺寸 => View 的尺寸需要的缩放倍数 + float ratioPreview = (float) cameraWidth / cameraHeight; + float ratioView = (float) viewWidth / viewHeight; + float scaleX, scaleY; + if (ratioView < ratioPreview) { + scaleX = ratioPreview / ratioView; + scaleY = 1; + } else { + scaleX = 1; + scaleY = ratioView / ratioPreview; + } + // 计算出 View 的偏移量 + float scaledWidth = viewWidth * scaleX; + float scaledHeight = viewHeight * scaleY; + float dx = (viewWidth - scaledWidth) / 2; + float dy = (viewHeight - scaledHeight) / 2; + Matrix matrix = new Matrix(); + matrix.postScale(scaleX, scaleY); + matrix.postTranslate(dx, dy); + + float[] values = new float[9]; + matrix.getValues(values); + mSurfaceView.setTranslationX(values[Matrix.MTRANS_X]); + mSurfaceView.setTranslationY(values[Matrix.MTRANS_Y]); + mSurfaceView.setScaleX(values[Matrix.MSCALE_X]); + mSurfaceView.setScaleY(values[Matrix.MSCALE_Y]); + mSurfaceView.invalidate(); + } } diff --git a/app/src/main/java/com/knight/cameraone/CircleButtonView.java b/app/src/main/java/com/knight/cameraone/CircleButtonView.java index 074a5ea..04d63e6 100644 --- a/app/src/main/java/com/knight/cameraone/CircleButtonView.java +++ b/app/src/main/java/com/knight/cameraone/CircleButtonView.java @@ -14,6 +14,9 @@ import android.view.MotionEvent; import android.view.View; +import com.knight.cameraone.utils.CameraUtils; +import com.knight.cameraone.utils.ToastUtil; + public class CircleButtonView extends View{ private static final int WHAT_LONG_CLICK = 1; private Paint mBigCirclePaint; @@ -121,11 +124,14 @@ public void handleMessage(Message msg) { switch (msg.what){ case WHAT_LONG_CLICK: //长按事件触发 - if(onLongClickListener!=null) { + if(onLongClickListener!=null && !CameraUtils.flashState()) { onLongClickListener.onLongClick(); + //内外圆动画,内圆缩小,外圆放大 + startAnimation(mBigRadius,mBigRadius*1.33f,mSmallRadius,mSmallRadius*0.7f); + } else { + ToastUtil.showShortToast(mContext,"请关闭闪光灯在录制"); } - //内外圆动画,内圆缩小,外圆放大 - startAnimation(mBigRadius,mBigRadius*1.33f,mSmallRadius,mSmallRadius*0.7f); + break; } } @@ -150,20 +156,25 @@ public boolean onTouchEvent(MotionEvent event) { if(onClickListener!=null) onClickListener.onClick(); }else{ - startAnimation(mBigRadius,mInitBitRadius,mSmallRadius,mInitSmallRadius);//手指离开时动画复原 - if(mProgressAni!=null&&mProgressAni.getCurrentPlayTime()/1000(); mPhotosAdapter = new PhotosAdapter(photoList); mPhotosAdapter.setOnItemClickListener(this); @@ -169,6 +178,7 @@ public void onClick(View v) { case R.id.tv_flash: mCameraPresenter.turnLight(isTurn); tv_flash.setBackgroundResource(isTurn ? R.drawable.icon_turnon : R.drawable.icon_turnoff); + Configuration.flaseState = isTurn; isTurn = !isTurn; break; //开启人脸检测 @@ -186,29 +196,20 @@ public void onClick(View v) { if(isFull){ //是全屏 切换成4:3 layoutParams.width = (int) (screen[0]); - layoutParams.height = (int) (screen[0] * 4/3); + layoutParams.height = (int) (screen[0] * 16/9); + tv_matchorwrap.setText("全屏模式"); } else { //不是全屏 //是全屏 切换成4:3 layoutParams.width = (int) (screen[0]); layoutParams.height = (int) (screen[1]); + tv_matchorwrap.setText("半屏模式"); } sf_camera.setLayoutParams(layoutParams); isFull = !isFull; mCameraPresenter.setFull(isFull); cl_parent.addView(sf_camera,0,layoutParams); -// scaleSurfaceView(sf_camera, isFull); -// if(isFull){ -// mCameraPresenter.setFull(false); -// int s = screen[0] * 4/ 3; -// Log.d("sss进入",screen[1]+ "sdsd" +s+""); -// changeViewHeightAnimatorStart(sf_camera,screen[1],screen[0] * 4/3); -// } else { -// mCameraPresenter.setFull(true); -// changeViewHeightAnimatorStart(sf_camera,screen[0] * 4/3,screen[1]); -// } -// isFull = !isFull; break; default: break; @@ -269,7 +270,7 @@ private void initListener() { @Override public void onClick() { //拍照的调用方法 - mCameraPresenter.takePicture(); + mCameraPresenter.takePicture(takePhotoOrientation); } }); @@ -277,7 +278,10 @@ public void onClick() { tv_takephoto.setOnLongClickListener(new CircleButtonView.OnLongClickListener() { @Override public void onLongClick() { - mCameraPresenter.startRecord(Configuration.OUTPATH, "video"); + + + // mCameraPresenter.startRecord(Configuration.OUTPATH, "video"); + mCameraPresenter.startRecord(getExternalFilesDir(Environment.DIRECTORY_MOVIES).getPath(),"video"); } @@ -289,7 +293,8 @@ public void onNoMinRecord(int currentTime) { @Override public void onRecordFinishedListener() { mCameraPresenter.stopRecord(); - startActivity(new Intent(CustomCameraActivity.this, PlayAudioActivity.class)); + startActivity(new Intent(CustomCameraActivity.this, PlayAudioActivity.class) + .putExtra("videoPath",mCameraPresenter.getVideoFilePath())); } }); tv_facedetect.setOnClickListener(this); @@ -306,6 +311,10 @@ protected void onDestroy() { if (mCameraPresenter != null) { mCameraPresenter.releaseCamera(); } + + if(mOrientationEventListener != null){ + mOrientationEventListener.disable(); + } } /** @@ -355,6 +364,11 @@ public void getPhotoFile(String imagePath) { mPhotosAdapter.notifyDataSetChanged(); } + @Override + public void getVideoFile(String videoFilePath) { + + } + /** * 触摸回调 @@ -505,7 +519,7 @@ public void onAnimationUpdate(ValueAnimator animation) { String s = String.valueOf(animation.getAnimatedValue()); // params.width = (int) (Float.valueOf(s) / 4.0f * 3); view.setLayoutParams(params); - Log.d("sssd-伸缩后的宽高",view.getMeasuredWidth() + "111"+ params.height +""); + } @@ -518,4 +532,44 @@ public void onAnimationUpdate(ValueAnimator animation) { } + /** + * 初始化传感器方向 + * + * + */ + private void initOrientate(){ + if(mOrientationEventListener == null){ + mOrientationEventListener = new OrientationEventListener(CustomCameraActivity.this) { + @Override + public void onOrientationChanged(int orientation) { + // i的范围是0-359 + // 屏幕左边在顶部的时候 i = 90; + // 屏幕顶部在底部的时候 i = 180; + // 屏幕右边在底部的时候 i = 270; + // 正常的情况默认i = 0; + if(45 <= orientation && orientation < 135){ + takePhotoOrientation = 180; + mCurrentOrientation = -180; + } else if(135 <= orientation && orientation < 225){ + takePhotoOrientation = 270; + mCurrentOrientation = 90; + } else if(225 <= orientation && orientation < 315){ + takePhotoOrientation = 0; + mCurrentOrientation = 0; + } else { + takePhotoOrientation = 90; + mCurrentOrientation = -90; + } + + + + } + }; + + } + mOrientationEventListener.enable(); + + } + + } diff --git a/app/src/main/java/com/knight/cameraone/activity/PlayAudioActivity.java b/app/src/main/java/com/knight/cameraone/activity/PlayAudioActivity.java index 126f406..169d501 100644 --- a/app/src/main/java/com/knight/cameraone/activity/PlayAudioActivity.java +++ b/app/src/main/java/com/knight/cameraone/activity/PlayAudioActivity.java @@ -1,13 +1,14 @@ package com.knight.cameraone.activity; +import android.content.pm.ActivityInfo; import android.media.MediaPlayer; import android.os.Bundle; -import android.os.Environment; +import android.support.constraint.ConstraintLayout; import android.support.v7.app.AppCompatActivity; import android.view.SurfaceHolder; import android.view.SurfaceView; -import com.knight.cameraone.Configuration; + import com.knight.cameraone.R; @@ -17,19 +18,28 @@ public class PlayAudioActivity extends AppCompatActivity implements MediaPlayer. private SurfaceView sf_play; private MediaPlayer player; - + private int surfaceWidth; + private int surfaceHeight; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_playaudio); sf_play = findViewById(R.id.sf_play); - //下面开始实例化MediaPlayer对象 player = new MediaPlayer(); player.setOnCompletionListener(this); player.setOnPreparedListener(this); //设置数据源,也就是播放文件地址,可以是网络地址 - String dataPath = Configuration.OUTPATH + "/videomp4"; + sf_play.post(new Runnable() { + @Override + public void run() { + surfaceWidth = sf_play.getWidth(); + surfaceHeight = sf_play.getHeight(); + } + }); + + String dataPath = getIntent().getStringExtra("videoPath"); + // String dataPath = Configuration.OUTPATH + "/videomp4"; try { player.setDataSource(dataPath); @@ -58,7 +68,12 @@ public void surfaceDestroyed(SurfaceHolder holder) { } }); - + player.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() { + @Override + public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { + changeVideoSize(); + } + }); } @@ -83,6 +98,31 @@ public void onPrepared(MediaPlayer mp) { } + public void changeVideoSize() { + int videoWidth = player.getVideoWidth(); + int videoHeight = player.getVideoHeight(); + + //根据视频尺寸去计算->视频可以在sufaceView中放大的最大倍数。 + float max; + if (getResources().getConfiguration().orientation== ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { + //竖屏模式下按视频宽度计算放大倍数值 + max = Math.max((float) videoWidth / (float) surfaceWidth,(float) videoHeight / (float) surfaceHeight); + } else{ + //横屏模式下按视频高度计算放大倍数值 + max = Math.max(((float) videoWidth/(float) surfaceHeight),(float) videoHeight/(float) surfaceWidth); + } + + //视频宽高分别/最大倍数值 计算出放大后的视频尺寸 + videoWidth = (int) Math.ceil((float) videoWidth / max); + videoHeight = (int) Math.ceil((float) videoHeight / max); + + //无法直接设置视频尺寸,将计算出的视频尺寸设置到surfaceView 让视频自动填充。 + ConstraintLayout.LayoutParams sfPlayLayoutParams = (ConstraintLayout.LayoutParams) sf_play.getLayoutParams(); + sfPlayLayoutParams.height = videoHeight; + sfPlayLayoutParams.width = videoWidth; + sf_play.setLayoutParams(sfPlayLayoutParams); + } + /** * 释放资源 * @@ -97,4 +137,6 @@ protected void onDestroy(){ } } + + } diff --git a/app/src/main/java/com/knight/cameraone/utils/CameraHelper.java b/app/src/main/java/com/knight/cameraone/utils/CameraHelper.java new file mode 100644 index 0000000..6239ad4 --- /dev/null +++ b/app/src/main/java/com/knight/cameraone/utils/CameraHelper.java @@ -0,0 +1,177 @@ +package com.knight.cameraone.utils; + +import android.annotation.TargetApi; +import android.hardware.Camera; +import android.os.Build; +import android.os.Environment; +import android.util.Log; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +/** + * @author created by luguian + * @organize + * @Date 2020/10/28 15:11 + * @descript: + */ +public class CameraHelper { + + public static final int MEDIA_TYPE_IMAGE = 1; + public static final int MEDIA_TYPE_VIDEO = 2; + + /** + * Iterate over supported camera video sizes to see which one best fits the + * dimensions of the given view while maintaining the aspect ratio. If none can, + * be lenient with the aspect ratio. + * + * @param supportedVideoSizes Supported camera video sizes. + * @param previewSizes Supported camera preview sizes. + * @param w The width of the view. + * @param h The height of the view. + * @return Best match camera video size to fit in the view. + */ + public static Camera.Size getOptimalVideoSize(List supportedVideoSizes, + List previewSizes, int w, int h) { + // Use a very small tolerance because we want an exact match. + final double ASPECT_TOLERANCE = 0.1; + double targetRatio = (double) w / h; + + // Supported video sizes list might be null, it means that we are allowed to use the preview + // sizes + List videoSizes; + if (supportedVideoSizes != null) { + videoSizes = supportedVideoSizes; + } else { + videoSizes = previewSizes; + } + Camera.Size optimalSize = null; + + // Start with max value and refine as we iterate over available video sizes. This is the + // minimum difference between view and camera height. + double minDiff = Double.MAX_VALUE; + + // Target view height + int targetHeight = h; + + // Try to find a video size that matches aspect ratio and the target view size. + // Iterate over all available sizes and pick the largest size that can fit in the view and + // still maintain the aspect ratio. + for (Camera.Size size : videoSizes) { + double ratio = (double) size.width / size.height; + if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) + continue; + if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) { + optimalSize = size; + minDiff = Math.abs(size.height - targetHeight); + } + } + + // Cannot find video size that matches the aspect ratio, ignore the requirement + if (optimalSize == null) { + minDiff = Double.MAX_VALUE; + for (Camera.Size size : videoSizes) { + if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) { + optimalSize = size; + minDiff = Math.abs(size.height - targetHeight); + } + } + } + return optimalSize; + } + + /** + * @return the default camera on the device. Return null if there is no camera on the device. + */ + public static Camera getDefaultCameraInstance() { + return Camera.open(); + } + + + /** + * @return the default rear/back facing camera on the device. Returns null if camera is not + * available. + */ + public static Camera getDefaultBackFacingCameraInstance() { + return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_BACK); + } + + /** + * @return the default front facing camera on the device. Returns null if camera is not + * available. + */ + public static Camera getDefaultFrontFacingCameraInstance() { + return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_FRONT); + } + + + /** + * + * @param position Physical position of the camera i.e Camera.CameraInfo.CAMERA_FACING_FRONT + * or Camera.CameraInfo.CAMERA_FACING_BACK. + * @return the default camera on the device. Returns null if camera is not available. + */ + @TargetApi(Build.VERSION_CODES.GINGERBREAD) + private static Camera getDefaultCamera(int position) { + // Find the total number of cameras available + int mNumberOfCameras = Camera.getNumberOfCameras(); + + // Find the ID of the back-facing ("default") camera + Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); + for (int i = 0; i < mNumberOfCameras; i++) { + Camera.getCameraInfo(i, cameraInfo); + if (cameraInfo.facing == position) { + return Camera.open(i); + + } + } + + return null; + } + + /** + * Creates a media file in the {@code Environment.DIRECTORY_PICTURES} directory. The directory + * is persistent and available to other applications like gallery. + * + * @param type Media type. Can be video or image. + * @return A file object pointing to the newly created file. + */ + public static File getOutputMediaFile(int type){ + // To be safe, you should check that the SDCard is mounted + // using Environment.getExternalStorageState() before doing this. + if (!Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED)) { + return null; + } + + File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES), "CameraSample"); + // This location works best if you want the created images to be shared + // between applications and persist after your app has been uninstalled. + + // Create the storage directory if it does not exist + if (! mediaStorageDir.exists()){ + if (! mediaStorageDir.mkdirs()) { + Log.d("CameraSample", "failed to create directory"); + return null; + } + } + + // Create a media file name + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()); + File mediaFile; + if (type == MEDIA_TYPE_IMAGE){ + mediaFile = new File(mediaStorageDir.getPath() + File.separator + + "IMG_"+ timeStamp + ".jpg"); + } else if(type == MEDIA_TYPE_VIDEO) { + mediaFile = new File(mediaStorageDir.getPath() + File.separator + + "VID_"+ timeStamp + ".mp4"); + } else { + return null; + } + + return mediaFile; + } +} diff --git a/app/src/main/java/com/knight/cameraone/utils/CameraUtils.java b/app/src/main/java/com/knight/cameraone/utils/CameraUtils.java new file mode 100644 index 0000000..3aeae7a --- /dev/null +++ b/app/src/main/java/com/knight/cameraone/utils/CameraUtils.java @@ -0,0 +1,22 @@ +package com.knight.cameraone.utils; + +import com.knight.cameraone.Configuration; + +/** + * @author created by knight + * @organize + * @Date 2021/9/16 20:08 + * @descript: + */ +public class CameraUtils { + + + /** + * + * 判断闪光灯状态 + * @return + */ + public static boolean flashState() { + return Configuration.flaseState; + } +} diff --git a/app/src/main/java/com/knight/cameraone/utils/ImageUtil.java b/app/src/main/java/com/knight/cameraone/utils/ImageUtil.java new file mode 100644 index 0000000..31622f2 --- /dev/null +++ b/app/src/main/java/com/knight/cameraone/utils/ImageUtil.java @@ -0,0 +1,67 @@ +package com.knight.cameraone.utils; + +import android.content.ContentValues; +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.MediaStore; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * @author created by luguian + * @organize + * @Date 2020/9/3 19:45 + * @descript:照片处理 + */ +public class ImageUtil { + + public static void saveAlbum(Context context, File targetFile){ + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ + ContentValues contentValues = new ContentValues(); + //设置文件名 + contentValues.put(MediaStore.Images.Media.DISPLAY_NAME,targetFile.getName()); + //设置文件类型 + contentValues.put(MediaStore.Images.Media.MIME_TYPE,"image/jpeg"); + //方式1 会在Pictures / Camera 文件夹下生成图片 + //contentValues.put(MediaStore.Images.Media.RELATIVE_PATH,"Pictures/Camera"); + //方式2 直接在Pictures生成 + contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); + Uri insertUri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); + BufferedInputStream inputStream; + OutputStream outputStream; + + try { + inputStream = new BufferedInputStream(new FileInputStream(targetFile)); + if(insertUri != null){ + outputStream = context.getContentResolver().openOutputStream(insertUri); + if(outputStream != null){ + byte[] buffer = new byte[1024]; + int length = -1; + while ((length = inputStream.read(buffer)) != -1){ + outputStream.write(buffer,0,length); + } + outputStream.close(); + inputStream.close(); + } + + } + }catch (IOException e){ + return; + } + + } else { + SystemUtil.saveAlbum(targetFile.getAbsolutePath(), targetFile.getName(), context); + } + + + } + + + +} diff --git a/app/src/main/java/com/knight/cameraone/utils/SystemUtil.java b/app/src/main/java/com/knight/cameraone/utils/SystemUtil.java index 7391fe0..3c580f5 100644 --- a/app/src/main/java/com/knight/cameraone/utils/SystemUtil.java +++ b/app/src/main/java/com/knight/cameraone/utils/SystemUtil.java @@ -72,7 +72,7 @@ public static String formatTime(long time,String file){ * @param i 传入的数字 * @return */ - public String formatRandom(int i){ + public static String formatRandom(int i){ String s = i + ""; if(s.length() == 1){ return "000" + s; diff --git a/app/src/main/res/layout/activity_customcamera.xml b/app/src/main/res/layout/activity_customcamera.xml index a9f917f..6d4efc7 100644 --- a/app/src/main/res/layout/activity_customcamera.xml +++ b/app/src/main/res/layout/activity_customcamera.xml @@ -1,19 +1,18 @@ - + android:layout_height="match_parent"> @@ -23,64 +22,61 @@ android:id="@+id/iv_test" android:layout_width="0dp" android:layout_height="0dp" - app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toRightOf="parent" - app:layout_constraintDimensionRatio="3:4" - app:layout_constraintTop_toTopOf="parent" android:background="@drawable/test" android:visibility="gone" - /> + app:layout_constraintDimensionRatio="3:4" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + android:layout_height="match_parent" /> - + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toBottomOf="@id/tv_change_camera" /> + app:layout_constraintTop_toTopOf="parent" /> @@ -92,8 +88,7 @@ android:layout_marginLeft="15dp" android:maxHeight="120dp" app:layout_constraintBottom_toTopOf="@+id/cl_bottom" - app:layout_constraintLeft_toLeftOf="parent" - > + app:layout_constraintLeft_toLeftOf="parent"> @@ -101,8 +96,7 @@ android:id="@+id/cl_bottom" android:layout_width="match_parent" android:layout_height="120dp" - app:layout_constraintBottom_toBottomOf="parent" - > + app:layout_constraintBottom_toBottomOf="parent"> + app:layout_constraintTop_toTopOf="parent" /> - - - - - - - - - + + + + + + + + + - - + app:layout_constraintTop_toTopOf="parent" /> diff --git a/app/src/main/res/layout/activity_playaudio.xml b/app/src/main/res/layout/activity_playaudio.xml index a48cf18..5404df8 100644 --- a/app/src/main/res/layout/activity_playaudio.xml +++ b/app/src/main/res/layout/activity_playaudio.xml @@ -3,13 +3,18 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:id="@+id/test"> + android:layout_height="0dp" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent"/> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5885930..28aab42 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ -