From a39793707ed2f975bf03f20c1553aa6a08259e61 Mon Sep 17 00:00:00 2001 From: luguian Date: Wed, 2 Sep 2020 17:59:25 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/knight/cameraone/CameraPresenter.java | 11 +--------- .../com/knight/cameraone/Configuration.java | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/knight/cameraone/CameraPresenter.java b/app/src/main/java/com/knight/cameraone/CameraPresenter.java index 9def36f..c5faa85 100644 --- a/app/src/main/java/com/knight/cameraone/CameraPresenter.java +++ b/app/src/main/java/com/knight/cameraone/CameraPresenter.java @@ -1,32 +1,22 @@ 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.MediaRecorder; 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.SystemUtil; import com.knight.cameraone.utils.ThreadPoolUtil; @@ -787,6 +777,7 @@ public void releaseCamera() { * 创建拍照照片文件夹 */ private void setUpFile() { + //这里是app的内部存储 这里要注意 不是外部私有目录 详情请看 Configuration这个类 photosFile = new File(Configuration.insidePath); if (!photosFile.exists() || !photosFile.isDirectory()) { boolean isSuccess = false; diff --git a/app/src/main/java/com/knight/cameraone/Configuration.java b/app/src/main/java/com/knight/cameraone/Configuration.java index ea09a61..6685e65 100644 --- a/app/src/main/java/com/knight/cameraone/Configuration.java +++ b/app/src/main/java/com/knight/cameraone/Configuration.java @@ -11,8 +11,26 @@ public class Configuration { - //这是app内部存储 格式如下 /data/data/包名/xxx/ + //这是app内部存储 格式如下 /data/data/包名/xxx/ 内部存储在Android系统对应的根目录是 /data/data/,这个目录普通用户是无权访问的,用户需要root权限才可以查看 public static String insidePath = "/data/data/com.knight.cameraone/pic/"; + //这是app外部存储的私有存储目录 沙盒模式(Android 10) + //context.getExternalFilesDir(String type) + /** + * 1.如果type为"",那么获取到的目录是 /storage/emulated/0/Android/data/package_name/files + * 2.如果type不为空,则会在/storage/emulated/0/Android/data/package_name/files目录下创建一个以传入的type值为名称的目录,例如你将type设为了test,那么就会创建/storage/emulated/0/Android/data/package_name/files/test目录,这个其实有点类似于内部存储getDir方法传入的name参数。但是android官方推荐使用以下的type类型 + * public static String DIRECTORY_MUSIC = "Music"; + * public static String DIRECTORY_PODCASTS = "Podcasts"; + * public static String DIRECTORY_RINGTONES = "Ringtones"; + * public static String DIRECTORY_ALARMS = "Alarms"; + * public static String DIRECTORY_NOTIFICATIONS = "Notifications"; + * public static String DIRECTORY_PICTURES = "Pictures"; + * public static String DIRECTORY_MOVIES = "Movies"; + * public static String DIRECTORY_DOWNLOADS = "Download"; + * public static String DIRECTORY_DCIM = "DCIM"; + * public static String DIRECTORY_DOCUMENTS = "Documents"; + * + * + */ //外部路径 public static String OUTPATH = Environment.getExternalStorageDirectory() + "/拍照-相册/"; } From 19b78b677a233f4add55d6ececbdc16110ea8450 Mon Sep 17 00:00:00 2001 From: luguian Date: Thu, 3 Sep 2020 20:19:36 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=8A=A0=E4=BC=A0?= =?UTF-8?q?=E6=84=9F=E5=99=A8=E3=80=91=E9=80=82=E9=85=8D=E6=A8=AA=E7=AB=96?= =?UTF-8?q?=E5=B1=8F=E5=88=87=E6=8D=A2=E5=9B=BE=E7=89=87=E8=BE=93=E5=87=BA?= =?UTF-8?q?=E6=97=8B=E8=BD=AC=E8=A7=92=E5=BA=A6=E9=97=AE=E9=A2=98=20?= =?UTF-8?q?=E3=80=90=E9=80=82=E9=85=8DAndroid10=E3=80=91=E9=80=82=E9=85=8D?= =?UTF-8?q?Android10=E5=9B=BE=E7=89=87/=E6=96=87=E4=BB=B6=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 +- .../com/knight/cameraone/CameraPresenter.java | 55 ++++++++++----- .../activity/CustomCameraActivity.java | 69 +++++++++++++++++-- .../com/knight/cameraone/utils/ImageUtil.java | 67 ++++++++++++++++++ .../knight/cameraone/utils/SystemUtil.java | 2 +- 5 files changed, 167 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/knight/cameraone/utils/ImageUtil.java 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 c5faa85..84b5356 100644 --- a/app/src/main/java/com/knight/cameraone/CameraPresenter.java +++ b/app/src/main/java/com/knight/cameraone/CameraPresenter.java @@ -8,6 +8,7 @@ import android.graphics.RectF; import android.hardware.Camera; import android.media.MediaRecorder; +import android.os.Environment; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; @@ -18,6 +19,7 @@ import android.view.SurfaceView; import android.view.View; +import com.knight.cameraone.utils.ImageUtil; import com.knight.cameraone.utils.SystemUtil; import com.knight.cameraone.utils.ThreadPoolUtil; import com.knight.cameraone.utils.ToastUtil; @@ -152,7 +154,7 @@ public void setFrontOrBack(int mCameraId) { /** * 拍照 */ - public void takePicture() { + public void takePicture(final int takePhotoOrientation) { if (mCamera != null) { //拍照回调 点击拍照时回调 写一个空实现 mCamera.takePicture(new Camera.ShutterCallback() { @@ -175,7 +177,7 @@ public void onPictureTaken(byte[] data, Camera camera) { //回调 mCameraCallBack.onTakePicture(data, camera); //保存图片 - getPhotoPath(data); + getPhotoPath(data,takePhotoOrientation); } }); @@ -777,8 +779,10 @@ public void releaseCamera() { * 创建拍照照片文件夹 */ private void setUpFile() { - //这里是app的内部存储 这里要注意 不是外部私有目录 详情请看 Configuration这个类 - 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 { @@ -799,7 +803,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() { @@ -808,7 +812,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()) { @@ -835,14 +839,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(); @@ -861,18 +871,25 @@ public void run() { 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){ +// //0是后置 +// if(cameraId == 0){ +// if(orientation == 90){ +// matrix.postRotate(90); +// } +// } +// //1是前置 +// if(cameraId == 1){ +// matrix.postRotate(270); +// } + + if(cameraId == 1){ if(orientation == 90){ - matrix.postRotate(90); + matrix.postRotate(180f); } } - //1是前置 - if(cameraId == 1){ - matrix.postRotate(270); - } // 创建新的图片 resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); diff --git a/app/src/main/java/com/knight/cameraone/activity/CustomCameraActivity.java b/app/src/main/java/com/knight/cameraone/activity/CustomCameraActivity.java index 8d919b0..249c3a5 100644 --- a/app/src/main/java/com/knight/cameraone/activity/CustomCameraActivity.java +++ b/app/src/main/java/com/knight/cameraone/activity/CustomCameraActivity.java @@ -1,6 +1,5 @@ package com.knight.cameraone.activity; -import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Intent; import android.graphics.RectF; @@ -12,17 +11,15 @@ import android.support.v7.widget.OrientationHelper; import android.support.v7.widget.RecyclerView; import android.util.DisplayMetrics; -import android.util.Log; import android.view.MotionEvent; +import android.view.OrientationEventListener; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; -import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.TextView; import com.bumptech.glide.Glide; @@ -111,11 +108,23 @@ public class CustomCameraActivity extends AppCompatActivity implements View.OnCl private int aftersufaceViewWidth; private int aftersurfaceViewHeight; + //增加传感器 + private OrientationEventListener mOrientationEventListener; + //当前角度 + private float mCurrentOrientation = 0f; + //拍照时的传感器方向 + private int takePhotoOrientation = 0; + + + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); supportRequestWindowFeature(Window.FEATURE_NO_TITLE); + // 去掉通知栏 + // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + // WindowManager.LayoutParams.FLAG_FULLSCREEN);//去掉信息栏 setContentView(R.layout.activity_customcamera); @@ -131,7 +140,7 @@ protected void onCreate(Bundle savedInstanceState) { //添加点击,触摸事件等监听 initListener(); - + initOrientate(); //初始化CameraPresenter mCameraPresenter = new CameraPresenter(this, sf_camera); //设置后置摄像头 @@ -143,6 +152,8 @@ protected void onCreate(Bundle savedInstanceState) { //默认关闭人脸检测 mCameraPresenter.turnFaceDetect(false); + // ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(screenHeight, ConstraintLayout.LayoutParams.WRAP_CONTENT); + // sf_camera.setLayoutParams(layoutParams); photoList = new ArrayList<>(); mPhotosAdapter = new PhotosAdapter(photoList); mPhotosAdapter.setOnItemClickListener(this); @@ -269,7 +280,7 @@ private void initListener() { @Override public void onClick() { //拍照的调用方法 - mCameraPresenter.takePicture(); + mCameraPresenter.takePicture(takePhotoOrientation); } }); @@ -306,6 +317,10 @@ protected void onDestroy() { if (mCameraPresenter != null) { mCameraPresenter.releaseCamera(); } + + if(mOrientationEventListener != null){ + mOrientationEventListener.disable(); + } } /** @@ -505,7 +520,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 +533,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/utils/ImageUtil.java b/app/src/main/java/com/knight/cameraone/utils/ImageUtil.java new file mode 100644 index 0000000..a7569fd --- /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; From f8f00263180681ae894dfe8545aa0058897ce829 Mon Sep 17 00:00:00 2001 From: luguian Date: Tue, 27 Oct 2020 10:41:29 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E3=80=90=E4=BF=AE=E5=A4=8DBug=E3=80=91?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BD=95=E5=88=B6=E8=A7=86=E9=A2=91=E7=94=9F?= =?UTF-8?q?=E6=88=90=E6=96=87=E4=BB=B6=E5=90=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/knight/cameraone/CameraPresenter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/knight/cameraone/CameraPresenter.java b/app/src/main/java/com/knight/cameraone/CameraPresenter.java index 84b5356..0c16230 100644 --- a/app/src/main/java/com/knight/cameraone/CameraPresenter.java +++ b/app/src/main/java/com/knight/cameraone/CameraPresenter.java @@ -1075,8 +1075,8 @@ public void startRecord(String path,String name){ file.mkdirs(); } //设置输出文件名字 - mediaRecorder.setOutputFile(path + File.separator + name + "mp4"); - File file1 = new File(path + File.separator + name + "mp4"); + mediaRecorder.setOutputFile(path + File.separator + name + ".mp4"); + File file1 = new File(path + File.separator + name + ".mp4"); if(file1.exists()){ file1.delete(); } From fe95125e8f4f9c168cc5e0262835b2f288beada0 Mon Sep 17 00:00:00 2001 From: luguian Date: Thu, 29 Oct 2020 16:03:17 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E3=80=90=E4=BF=AE=E5=A4=8DBug=E3=80=91?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BD=95=E5=88=B6=E8=A7=86=E9=A2=91=E5=9C=A8?= =?UTF-8?q?=E4=B8=80=E5=8A=A0=E6=89=8B=E6=9C=BA=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 32 +++ .../com/knight/cameraone/CameraPresenter.java | 237 ++++++++---------- .../com/knight/cameraone/Configuration.java | 5 +- .../activity/CustomCameraActivity.java | 27 +- .../cameraone/activity/PlayAudioActivity.java | 8 +- .../knight/cameraone/utils/CameraHelper.java | 177 +++++++++++++ cameraone.jks | Bin 0 -> 2533 bytes 7 files changed, 330 insertions(+), 156 deletions(-) create mode 100644 app/src/main/java/com/knight/cameraone/utils/CameraHelper.java create mode 100644 cameraone.jks 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/java/com/knight/cameraone/CameraPresenter.java b/app/src/main/java/com/knight/cameraone/CameraPresenter.java index 0c16230..f07468b 100644 --- a/app/src/main/java/com/knight/cameraone/CameraPresenter.java +++ b/app/src/main/java/com/knight/cameraone/CameraPresenter.java @@ -7,6 +7,7 @@ import android.graphics.Matrix; import android.graphics.RectF; import android.hardware.Camera; +import android.media.CamcorderProfile; import android.media.MediaRecorder; import android.os.Environment; import android.os.Handler; @@ -19,6 +20,7 @@ import android.view.SurfaceView; import android.view.View; +import com.knight.cameraone.utils.CameraHelper; import com.knight.cameraone.utils.ImageUtil; import com.knight.cameraone.utils.SystemUtil; import com.knight.cameraone.utils.ThreadPoolUtil; @@ -78,7 +80,10 @@ public class CameraPresenter implements Camera.PreviewCallback { //检测头像的FaceView private FaceDeteView mFaceView; + //视频连接 + private String videoFilePath; + CamcorderProfile profile; private boolean isFull =false; @@ -93,6 +98,9 @@ public void setFull(boolean full) { + + + //自定义回调 public interface CameraCallBack { //预览帧回调 @@ -106,6 +114,11 @@ public interface CameraCallBack { //拍照路径返回 void getPhotoFile(String imagePath); + + //返回视频路径 + void getVideoFile(String videoFilePath); + + } public void setCameraCallBack(CameraCallBack mCameraCallBack) { @@ -348,15 +361,11 @@ public void setPreviewSize(int width,int height) { Camera.Size targetSiz2 = null;// 没有屏幕分辨率就取跟屏幕分辨率相近(小)的size if (localSizes != null) { int cameraSizeLength = localSizes.size(); - + // 如果是 预览窗口宽:高 == 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); break; } @@ -364,9 +373,9 @@ public void setPreviewSize(int width,int height) { } } 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; @@ -400,17 +409,13 @@ 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); } + } - - - } } @@ -456,49 +461,6 @@ private void getOpyimalPreviewSize(){ 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 缩放系数 @@ -729,32 +691,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); -// -// } - - /** * 释放相机资源 */ @@ -874,16 +810,6 @@ private void rotateImageView(int cameraId,int orientation,String path){ matrix.postRotate(Float.valueOf(orientation)); // 创建新的图片 Bitmap resizedBitmap; -// //0是后置 -// if(cameraId == 0){ -// if(orientation == 90){ -// matrix.postRotate(90); -// } -// } -// //1是前置 -// if(cameraId == 1){ -// matrix.postRotate(270); -// } if(cameraId == 1){ if(orientation == 90){ @@ -968,46 +894,19 @@ public void getVideoSize(){ 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; - } - - 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; + List mSupportedPreviewSizes = parameters.getSupportedPreviewSizes(); - }else if(w < screenHeight || h < screenWidth){ - fitSize_widthBig = w; - fitSize_heightBig = h; - - } - } - } - if(width == 0 && height == 0){ - width = fitSize_width; - height = fitSize_height; - } + //使用官方demo + Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(videoSize, + mSupportedPreviewSizes, mSurfaceView.getWidth(), mSurfaceView.getHeight()); - if(width == 0 && height == 0){ - width = fitSize_widthBig; - height = fitSize_heightBig; - } + // 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 = biggest_width; - height = biggest_height; - } } @@ -1020,6 +919,9 @@ public void stopRecord(){ mediaRecorder.release(); mediaRecorder = null; } + mCameraCallBack.getVideoFile(videoFilePath); + + if(mCamera != null){ mCamera.release(); @@ -1045,18 +947,16 @@ public void startRecord(String path,String name){ 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); //调整视频旋转角度 如果不设置 后置和前置都会被旋转播放 if(mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) { if(orientation == 270 || orientation == 90 || orientation == 180){ @@ -1070,6 +970,7 @@ public void startRecord(String path,String name){ } } + //路径问题 可以到Configuration 这个类看 File file = new File(path); if(!file.exists()){ file.mkdirs(); @@ -1080,6 +981,9 @@ public void startRecord(String path,String name){ if(file1.exists()){ file1.delete(); } + + //赋值视频文件 + videoFilePath = path + File.separator + name + ".mp4"; //设置预览 mediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface()); try { @@ -1133,6 +1037,65 @@ public void update(SurfaceView mSurfaceView){ } + /** + * + * 获取视频文件链接 + * @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/Configuration.java b/app/src/main/java/com/knight/cameraone/Configuration.java index 6685e65..a4896eb 100644 --- a/app/src/main/java/com/knight/cameraone/Configuration.java +++ b/app/src/main/java/com/knight/cameraone/Configuration.java @@ -13,6 +13,7 @@ public class Configuration { //这是app内部存储 格式如下 /data/data/包名/xxx/ 内部存储在Android系统对应的根目录是 /data/data/,这个目录普通用户是无权访问的,用户需要root权限才可以查看 public static String insidePath = "/data/data/com.knight.cameraone/pic/"; + //这是app外部存储的私有存储目录 沙盒模式(Android 10) //context.getExternalFilesDir(String type) /** @@ -31,6 +32,8 @@ public class Configuration { * * */ - //外部路径 + + //例子: context.getExternalFilesDir(Environment.DIRECTORY_MOVIES) 就会在/storage/emulated/0/Android/data/com.knight.cameraone/files/Movies + //外部路径 Android10以上Environment.getExternalStorageDirectory() 不能用 public static String OUTPATH = Environment.getExternalStorageDirectory() + "/拍照-相册/"; } diff --git a/app/src/main/java/com/knight/cameraone/activity/CustomCameraActivity.java b/app/src/main/java/com/knight/cameraone/activity/CustomCameraActivity.java index 249c3a5..ec5b435 100644 --- a/app/src/main/java/com/knight/cameraone/activity/CustomCameraActivity.java +++ b/app/src/main/java/com/knight/cameraone/activity/CustomCameraActivity.java @@ -5,6 +5,7 @@ import android.graphics.RectF; import android.hardware.Camera; import android.os.Bundle; +import android.os.Environment; import android.support.constraint.ConstraintLayout; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; @@ -27,7 +28,6 @@ import com.bumptech.glide.request.RequestOptions; import com.knight.cameraone.CameraPresenter; import com.knight.cameraone.CircleButtonView; -import com.knight.cameraone.Configuration; import com.knight.cameraone.R; import com.knight.cameraone.adapter.PhotosAdapter; import com.knight.cameraone.utils.SystemUtil; @@ -208,18 +208,6 @@ public void onClick(View v) { 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; @@ -288,7 +276,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"); } @@ -300,7 +291,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); @@ -370,6 +362,11 @@ public void getPhotoFile(String imagePath) { mPhotosAdapter.notifyDataSetChanged(); } + @Override + public void getVideoFile(String videoFilePath) { + + } + /** * 触摸回调 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..c557d61 100644 --- a/app/src/main/java/com/knight/cameraone/activity/PlayAudioActivity.java +++ b/app/src/main/java/com/knight/cameraone/activity/PlayAudioActivity.java @@ -3,11 +3,10 @@ import android.media.MediaPlayer; import android.os.Bundle; -import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.view.SurfaceHolder; import android.view.SurfaceView; -import com.knight.cameraone.Configuration; + import com.knight.cameraone.R; @@ -29,7 +28,10 @@ protected void onCreate(Bundle savedInstanceState) { player.setOnCompletionListener(this); player.setOnPreparedListener(this); //设置数据源,也就是播放文件地址,可以是网络地址 - String dataPath = Configuration.OUTPATH + "/videomp4"; + + + String dataPath = getIntent().getStringExtra("videoPath"); + // String dataPath = Configuration.OUTPATH + "/videomp4"; try { player.setDataSource(dataPath); 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..1c22a23 --- /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/cameraone.jks b/cameraone.jks new file mode 100644 index 0000000000000000000000000000000000000000..434048c7a6b18198855db9e5c0cd6c5ec36f5aa1 GIT binary patch literal 2533 zcmY+EX*d*$8pmgt8DkmBGQ=Ru*l7l5vSu3`ONqA-wzxD$0R#$qQfgo9UQTcItBqHI!{cT3XG zYl*}7<>Ce7Ma!`m>LG*WC16#h#)|3s!}-^Pi@n)a_=8)udg4S^ahFgLbO40WuQ_SD z8Nr*}?&M=z6G0opI5b^eGfO6>KC9ScRB${MRrljGwXrU)Pb%5mRX9A~CioUQkIc&2r7LHl8rN4B5=TmcPQ z45>dC_9W;H%cJC}5iE*@GcVdo8G%Le` zrs;EP{Na|1UXyZrj&QTvM6_EumQT5f3nMQ0JWY5;1 zyXKy%l#KIn7hdKAtYj=qO6yC7rKWj3FR&$QyGsxRr(AD?8~13J!9;E-3U$dn=R-lCuh*e_dX&Me|?C&TbiY8*&R?enO@i=yWaVbCJpA- zo&E9LL|F*kvZN_Y?w6uG9%HuG|BR|K?XNrS2$w=9BR3=JI!7R}@TLo1>9eo0KDMYH z9(`Jk#M~bwGZ7V%veW(-SinO<|l3gb$_-p zxWN+wwx!=sBzhA>O$v3D+`}L5C)x4RQyjGUxuaok3!4;c^eKO46VlF4uWQDPdbjD~ zvRXf=AAHRooLIHS=cbJivOs3z>*Ti!h;k6e<=oXP(oBCmj5d@(h9zWhC&|~mZ$zlH z`R>Lc8se8h6(wC}&i7v?Y{%S5ov5iX>7guWJ@V_6qe=^uA9~3OuNIvyO zp`c=|`la7;nJF#(=q3q*ZAHR7_b zbaPLf_6X;8x07TB%4P#bX`j_r!fqm*D*QCY_Oq?7A+WLGoz3QkL;DKVe2VAa{1qY# z{cevb?NfZOT3XfiUDb7{$8o0O_1Z~Y#-u`b&+LbYTbg*64z6McbX*m{Dz!??NVVIZ zQEsdnpmx^qx%5~BE-&+L6JOEY;fa)6XEewk2@MPkl`-Y%RS+zA4cPVFDh6$VlKESa z+`?#KDM7#$fCnHDKm>#y^7B4}Zdpwrbr;z=|HrKN+x=;&x`qjiteDg5^x zF36Eh!AA(l0R*6^oCp6+fdA)L$Upr`T?BL3jX_o5aP@r1f)~NlaXI5(e!WTM?3jPn zUo>*g1PxIIk9H>N1=H7Nn)AQ&O3?hQ>(N=z6q0FD%0tbxk0-2!(JWK>9p@QU}biL5=?S)Fbya^U?Q$n8_d{LvWcjK0# zkv2*2tAK})a~bl@c*M()@ciOA#)!p>u9$aap}+OlvX`NAMjP?VxUiqdNu)!c)`5wu z;I+F}LD)f;g$g${$7$cG2UkMJoZ(9#+#KnYbzy+3FL^P&X)NAliff3ijPy`XH5HNh zb`EOv`mZm_$ELAhq6(9m@=iS@~n5hQRNmybtB)L;;N_;m` zHZID@XE>;vT(bQX7s!1zghH*lSJO>3hgs^a0vjJ*Ik-w|ML1vGw~$+uC)@KBd$68- z4qYhSLMxIgS#nlJeF^)CRi7evCCpoVzkE2a6Nc>xpK7wGjg$dsQdQW5$*>??1n zJ?=E#sU#7$x<;TwUD?=S+aUS*Ey)M81HQdiCOk-GB&V2@ED3V*q~!S1wel^`rb*t* z8DaYFuhU@z+D)4B1oy850!SRA$6J*!m*{;Na|cql1T*#`~gX)CKd%V~)Ro zzGXY7pQ{`fxkU2(&`T*|dnd>HJe}mODLlw!T{bnm{~iH;QHfb4jC!lENQ`;LLYe6? z0}0`{Fc@w{0^&|8ELLI#-b+%kFm`t^H8@Xoh@64u7ox3hEC~%>>3NL3Ua6w(%SIGL zDR^XuVF7O^XN(-^cN1&y_-)}`BYw0|(ObeMc+WOcrCnN4vAS49!>(cYlE))M^4Mdq zGMS=g+(pj16Rowe%a3iQk~T$yHHJjR=i*+=22w7_(4iviS-Kx{7r>AMSYf0R8~BSO z=6QW5=~4|Hx>`T?T0>60X*$r9R`H{Qy>j2~6Ot1&60{@@++PsIE0ZK8qf5uUF0J(9 z`A^yKT&wy*_WB;XnIw`|z3Dvnp|6MRH2p1)S>!(=S<(^k1w%ZtvfQ5iJe49b5pn Date: Thu, 29 Oct 2020 16:05:10 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E3=80=90=E4=BF=AE=E5=A4=8DBug=E3=80=91?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BD=95=E5=88=B6=E8=A7=86=E9=A2=91=E5=9C=A8?= =?UTF-8?q?=E4=B8=80=E5=8A=A0=E6=89=8B=E6=9C=BA=E5=B4=A9=E6=BA=83=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E6=B7=BB=E5=8A=A0=E5=9C=A8=E7=94=9F=E4=BA=A7=E4=B8=8A?= =?UTF-8?q?=E6=85=8E=E7=94=A8=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f20b735..0c97592 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ ## 一、前言 +**此项目仅是demo,若用在生产环境需谨慎** 现在很多`app`都会有拍照功能,一般调用系统进行拍照裁剪就能满足平时的需求,但有些场景或者特殊情况下如:持续不间断拍多张照片或者是进行人脸识别的时候,这时候之间调用系统原生相机拍照时不能满足自己的开发需求,就需要使用原生`Camera`来进行自定义开发,本文会采用`android.hardware.Camera`API来进行开发。在`Android`生态中,`Camera`是碎片化较为严重的一块,因为现在`Android`本身有三套API: * Camera:Android 5.0以下 * Camera2:Android 5.0以上 From e260d02aae6b1d7d2e94659d74723bb55c1d1082 Mon Sep 17 00:00:00 2001 From: luguian Date: Thu, 29 Oct 2020 16:16:48 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E3=80=90=E6=8F=90=E9=86=92=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E6=AD=A4demo=E7=94=A8=E5=9C=A8=E7=94=9F?= =?UTF-8?q?=E4=BA=A7=E8=B0=A8=E6=85=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++- app/src/main/java/com/knight/cameraone/Configuration.java | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0c97592..37b4291 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ## 一、前言 -**此项目仅是demo,若用在生产环境需谨慎** +**此项目仅是demo,代码质量较差,若用在生产环境需谨慎** + + 现在很多`app`都会有拍照功能,一般调用系统进行拍照裁剪就能满足平时的需求,但有些场景或者特殊情况下如:持续不间断拍多张照片或者是进行人脸识别的时候,这时候之间调用系统原生相机拍照时不能满足自己的开发需求,就需要使用原生`Camera`来进行自定义开发,本文会采用`android.hardware.Camera`API来进行开发。在`Android`生态中,`Camera`是碎片化较为严重的一块,因为现在`Android`本身有三套API: * Camera:Android 5.0以下 * Camera2:Android 5.0以上 diff --git a/app/src/main/java/com/knight/cameraone/Configuration.java b/app/src/main/java/com/knight/cameraone/Configuration.java index a4896eb..f68d213 100644 --- a/app/src/main/java/com/knight/cameraone/Configuration.java +++ b/app/src/main/java/com/knight/cameraone/Configuration.java @@ -1,7 +1,5 @@ package com.knight.cameraone; -import android.os.Environment; - /** * @author created by knight * @organize @@ -35,5 +33,5 @@ public class Configuration { //例子: context.getExternalFilesDir(Environment.DIRECTORY_MOVIES) 就会在/storage/emulated/0/Android/data/com.knight.cameraone/files/Movies //外部路径 Android10以上Environment.getExternalStorageDirectory() 不能用 - public static String OUTPATH = Environment.getExternalStorageDirectory() + "/拍照-相册/"; + //public static String OUTPATH = Environment.getExternalStorageDirectory() + "/拍照-相册/"; } From 609adf07183b84e3e5a43834cf5af29c1b9b229b Mon Sep 17 00:00:00 2001 From: luguian Date: Thu, 29 Oct 2020 16:19:36 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E3=80=90=E6=8F=90=E9=86=92=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E6=AD=A4demo=E7=94=A8=E5=9C=A8=E7=94=9F?= =?UTF-8?q?=E4=BA=A7=E8=B0=A8=E6=85=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 37b4291..300295b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ## 一、前言 -**此项目仅是demo,代码质量较差,若用在生产环境需谨慎** +**此项目仅是demo,宗旨在于学习,若用在生产环境需谨慎** 现在很多`app`都会有拍照功能,一般调用系统进行拍照裁剪就能满足平时的需求,但有些场景或者特殊情况下如:持续不间断拍多张照片或者是进行人脸识别的时候,这时候之间调用系统原生相机拍照时不能满足自己的开发需求,就需要使用原生`Camera`来进行自定义开发,本文会采用`android.hardware.Camera`API来进行开发。在`Android`生态中,`Camera`是碎片化较为严重的一块,因为现在`Android`本身有三套API: From 0d0662f1c50e3f9380b06b05ba1482ba3d66f2c3 Mon Sep 17 00:00:00 2001 From: luguian Date: Fri, 17 Sep 2021 14:56:44 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E3=80=90=E4=BF=AE=E6=94=B9bug=E3=80=91?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E4=BF=AE=E6=94=B9=EF=BC=9A=E5=BD=95=E5=88=B6?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E4=B8=8B=E9=97=AA=E5=85=89=E7=81=AF=E5=BF=85?= =?UTF-8?q?=E9=A1=BB=E5=85=B3=E9=97=AD=20=E3=80=90=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=94=B9=E5=8A=A8=E3=80=91=E5=8D=8A=E5=B1=8F=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E6=94=B9=E4=B8=BA9=EF=BC=9A16=20=E3=80=90=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=94=B9=E5=8A=A8=E3=80=91AGP=E5=8D=87=E7=BA=A7=E5=88=B06.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/caches/build_file_checksums.ser | Bin 0 -> 536 bytes .idea/codeStyles/Project.xml | 116 ++++++++ .idea/compiler.xml | 1 + .idea/gradle.xml | 3 + .idea/inspectionProfiles/Project_Default.xml | 36 +++ .idea/jarRepositories.xml | 25 ++ .idea/misc.xml | 21 +- .idea/modules.xml | 4 +- .../8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d | 0 .../f07866736216be0ee2aba49e392191aeae700a35 | 0 .idea/sonarlint/issuestore/index.pb | 5 + .idea/vcs.xml | 6 + .../com/knight/cameraone/CameraPresenter.java | 259 +++++++++--------- .../knight/cameraone/CircleButtonView.java | 42 ++- .../com/knight/cameraone/Configuration.java | 1 + .../activity/CustomCameraActivity.java | 12 +- .../cameraone/activity/PlayAudioActivity.java | 48 +++- .../knight/cameraone/utils/CameraHelper.java | 2 +- .../knight/cameraone/utils/CameraUtils.java | 22 ++ .../com/knight/cameraone/utils/ImageUtil.java | 2 +- .../main/res/layout/activity_customcamera.xml | 75 +++-- .../main/res/layout/activity_playaudio.xml | 9 +- app/src/main/res/values/styles.xml | 2 +- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 25 files changed, 482 insertions(+), 213 deletions(-) create mode 100644 .idea/caches/build_file_checksums.ser create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/sonarlint/issuestore/8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d create mode 100644 .idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35 create mode 100644 .idea/sonarlint/issuestore/index.pb create mode 100644 .idea/vcs.xml create mode 100644 app/src/main/java/com/knight/cameraone/utils/CameraUtils.java diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser new file mode 100644 index 0000000000000000000000000000000000000000..593bd429c732401571152c92f9616507186948eb GIT binary patch literal 536 zcmZ4UmVvdnh`~NNKUXg?FQq6yGexf?KR>5fFEb@IQ7^qHF(oHeub?PDD>b=9F91S2 zm1gFoxMk*~I%lLNXBU^|7Q2L-Ts|(GuF1r}l-a(W(##Th_( zR`y#54~r#SWM*J;W8likPfT%3OfJbU@?_vF$tX%K&dAS6sVJ~_U;qK0at2z_zyOYkOxc*3?Qo+1YSK^JoBYtXkhiL?8^BYyLEt0 zEKV&c$;?YHhG{HhsAFIQshxK-VV})?_B`DLJ*jY~!wX9o)SazjLW@(2iepNFWDHbC z3?d*4Qj1D5Q;SRCW?Yf&x!KRKiREPj=ZrT$oa#y#cu-skS7>)FP1ZKd%);vA(tKON zp8r5E<>V(P=76FH!&J$Ci~cOnJpD29!Jz|-ejVRh!XSvp4f_QC94PV6sP*2O(OKBI HD5wGe3Yfpc literal 0 HcmV?d00001 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/app/src/main/java/com/knight/cameraone/CameraPresenter.java b/app/src/main/java/com/knight/cameraone/CameraPresenter.java index f07468b..f58d8b8 100644 --- a/app/src/main/java/com/knight/cameraone/CameraPresenter.java +++ b/app/src/main/java/com/knight/cameraone/CameraPresenter.java @@ -75,7 +75,7 @@ public class CameraPresenter implements Camera.PreviewCallback { //视频录制 private MediaRecorder mediaRecorder; //录制视频的videoSize - private int height,width; + private int height, width; //检测头像的FaceView private FaceDeteView mFaceView; @@ -86,7 +86,7 @@ public class CameraPresenter implements Camera.PreviewCallback { CamcorderProfile profile; - private boolean isFull =false; + private boolean isFull = false; public boolean isFull() { return isFull; @@ -97,10 +97,6 @@ public void setFull(boolean full) { } - - - - //自定义回调 public interface CameraCallBack { //预览帧回调 @@ -129,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; } - - /** * 设置前置还是后置 * @@ -190,7 +184,7 @@ public void onPictureTaken(byte[] data, Camera camera) { //回调 mCameraCallBack.onTakePicture(data, camera); //保存图片 - getPhotoPath(data,takePhotoOrientation); + getPhotoPath(data, takePhotoOrientation); } }); @@ -205,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); @@ -248,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; @@ -273,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(); @@ -302,7 +296,6 @@ private void initParameters(Camera camera) { /** - * * 设置保存图片的尺寸 */ private void setPictureSize() { @@ -352,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;//最大分辨率 @@ -362,11 +355,11 @@ public void setPreviewSize(int width,int height) { if (localSizes != null) { int cameraSizeLength = localSizes.size(); // 如果是 预览窗口宽:高 == 3:4的话 - if(Float.valueOf(width) / height == 3.0f / 4){ + if (Float.valueOf(width) / height == 3.0f / 4) { for (int n = 0; n < cameraSizeLength; n++) { Camera.Size size = localSizes.get(n); - if(Float.valueOf(size.width) / size.height == 4.0f / 3){ - mParameters.setPreviewSize(size.width,size.height); + if (Float.valueOf(size.width) / size.height == 4.0f / 3) { + mParameters.setPreviewSize(size.width, size.height); break; } @@ -410,7 +403,7 @@ public void setPreviewSize(int width,int height) { fitSize = biggestSize; } mParameters.setPreviewSize(fitSize.width, fitSize.height); - fixScreenSize(fitSize.height,fitSize.width); + fixScreenSize(fitSize.height, fitSize.width); } } @@ -421,25 +414,26 @@ public void setPreviewSize(int width,int height) { /** * 解决预览变形问题 - * - * */ - 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); } @@ -447,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); } @@ -458,53 +452,55 @@ private void getOpyimalPreviewSize(){ } - mParameters.setPreviewSize(optimalSize.width,optimalSize.height); + mParameters.setPreviewSize(optimalSize.width, optimalSize.height); } /** * 变焦 + * * @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(); @@ -564,7 +560,7 @@ private void startPreview() { //根据所传入的SurfaceHolder对象来设置实时预览 mCamera.setPreviewDisplay(mSurfaceHolder); //调整预览角度 - setCameraDisplayOrientation(mAppCompatActivity,mCameraId,mCamera); + setCameraDisplayOrientation(mAppCompatActivity, mCameraId, mCamera); mCamera.startPreview(); //开启人脸检测 startFaceDetect(); @@ -578,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); + // } + // } + // }); } /** @@ -616,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<>(); @@ -704,7 +700,7 @@ public void releaseCamera() { mHandler.removeMessages(1); } - if(mediaRecorder != null){ + if (mediaRecorder != null) { mediaRecorder.release(); mediaRecorder = null; @@ -716,7 +712,7 @@ public void releaseCamera() { */ private void setUpFile() { //方式1 这里是app的内部存储 这里要注意 不是外部私有目录 详情请看 Configuration这个类 - // photosFile = new File(Configuration.insidePath); + // 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()) { @@ -775,15 +771,15 @@ public void run() { } //将图片旋转 - // rotateImageView(mCameraId,orientation,Configuration.insidePath + file.getName()); - rotateImageView(mCameraId,takePhotoOrientation,file.getAbsolutePath()); + // 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); + // SystemUtil.saveAlbum(file.getAbsolutePath(), file.getName(), mAppCompatActivity); //将图片保存到手机相册 方式2 - ImageUtil.saveAlbum(mAppCompatActivity,file); + ImageUtil.saveAlbum(mAppCompatActivity, file); Message message = new Message(); @@ -800,19 +796,20 @@ 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; - if(cameraId == 1){ - if(orientation == 90){ + if (cameraId == 1) { + if (orientation == 90) { matrix.postRotate(180f); } } @@ -821,9 +818,9 @@ private void rotateImageView(int cameraId,int orientation,String path){ 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); @@ -832,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); @@ -841,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; } @@ -849,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) { @@ -871,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) { @@ -885,12 +882,11 @@ 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(); @@ -911,19 +907,17 @@ public void getVideoSize(){ /** - * * 停止录制 */ - 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); @@ -933,15 +927,16 @@ 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); @@ -950,35 +945,37 @@ public void startRecord(String path,String name){ //设置输出文件格式 录制过程中产生的输出文件的格式 mediaRecorder.setOutputFormat(profile.fileFormat); //设置录制的视频编码比特率 会影响清晰 - mediaRecorder.setVideoEncodingBitRate(5* 1024 * 1024); + mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024); //设置视频编码器 用于录制 mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); //设置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()){ + if (file1.exists()) { file1.delete(); } @@ -994,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; } @@ -1019,43 +1016,38 @@ 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(){ + public String getVideoFilePath() { return videoFilePath; } /** - * * 适配 华为p40 小米mix 一些不规则尺寸 * * @param fitSizeHeight 获取系统支持最适合的高(现实系统的屏幕宽度) - * @param fitSizeWidth 获取系统支持最适合的宽(现实系统的屏幕高度) - * + * @param fitSizeWidth 获取系统支持最适合的宽(现实系统的屏幕高度) */ - private void fixScreenSize(int fitSizeHeight,int fitSizeWidth){ + private void fixScreenSize(int fitSizeHeight, int fitSizeWidth) { // 预览 View 的大小,比如 SurfaceView int viewHeight = screenHeight; @@ -1093,9 +1085,4 @@ private void fixScreenSize(int fitSizeHeight,int fitSizeWidth){ 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视频可以在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); + } + /** * 释放资源 * @@ -99,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 index 1c22a23..6239ad4 100644 --- a/app/src/main/java/com/knight/cameraone/utils/CameraHelper.java +++ b/app/src/main/java/com/knight/cameraone/utils/CameraHelper.java @@ -14,7 +14,7 @@ /** * @author created by luguian - * @organize 车童网 + * @organize * @Date 2020/10/28 15:11 * @descript: */ 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 index a7569fd..31622f2 100644 --- a/app/src/main/java/com/knight/cameraone/utils/ImageUtil.java +++ b/app/src/main/java/com/knight/cameraone/utils/ImageUtil.java @@ -15,7 +15,7 @@ /** * @author created by luguian - * @organize 车童网 + * @organize * @Date 2020/9/3 19:45 * @descript:照片处理 */ 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 @@ -