+
+
+
\ 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 @@
-