diff --git a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java index 59b6341..1445603 100644 --- a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java +++ b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java @@ -7,14 +7,16 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ import com.casic.dcms.mvp.view.ICaseSmallClassView; import com.casic.dcms.mvp.view.ICaseSubmitCaseView; import com.casic.dcms.mvp.view.IUploadFileView; +import com.casic.dcms.utils.AudioRecodeHelper; import com.casic.dcms.utils.ColorHelper; import com.casic.dcms.utils.Constant; import com.casic.dcms.utils.FileUtils; @@ -47,12 +50,14 @@ import com.casic.dcms.utils.ItemDecorationSpace; import com.casic.dcms.utils.OtherUtils; import com.casic.dcms.utils.StringHelper; +import com.casic.dcms.utils.TimeOrDateUtil; import com.casic.dcms.utils.ToastHelper; import com.iceteck.silicompressorr.SiliCompressor; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; import com.qmuiteam.qmui.widget.dialog.QMUIDialog; import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; @@ -73,6 +78,8 @@ ICaseLargeClassView, ICaseSmallClassView, ICaseSubmitCaseView, IUploadFileView { private Context context = this; + @BindView(R.id.caseLayout) + LinearLayout caseLayout; @BindView(R.id.caseClassView) TextView caseClassView; @BindView(R.id.caseLargeClassView) @@ -104,8 +111,6 @@ @BindView(R.id.submitButton) QMUIRoundButton submitButton; - private MediaRecorder mediaRecorder; - private File outputFile; private CaseLargeClassPresenterImpl caseLargeClassPresenter; private CaseSmallClassPresenterImpl caseSmallClassPresenter; private CaseSubmitPresenterImpl caseSubmitPresenter; @@ -125,6 +130,8 @@ private String gridId; private UploadVideoPresenterImpl uploadVideoPresenter; private String audioUrl, videoUrl; + private AudioRecodeHelper audioRecodeHelper; + private PopupWindow popWindow; @Override public int initLayoutView() { @@ -138,7 +145,6 @@ @Override public void initData() { - initMediaRecorder(); largeClassBeans = new ArrayList<>(); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java index 59b6341..1445603 100644 --- a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java +++ b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java @@ -7,14 +7,16 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ import com.casic.dcms.mvp.view.ICaseSmallClassView; import com.casic.dcms.mvp.view.ICaseSubmitCaseView; import com.casic.dcms.mvp.view.IUploadFileView; +import com.casic.dcms.utils.AudioRecodeHelper; import com.casic.dcms.utils.ColorHelper; import com.casic.dcms.utils.Constant; import com.casic.dcms.utils.FileUtils; @@ -47,12 +50,14 @@ import com.casic.dcms.utils.ItemDecorationSpace; import com.casic.dcms.utils.OtherUtils; import com.casic.dcms.utils.StringHelper; +import com.casic.dcms.utils.TimeOrDateUtil; import com.casic.dcms.utils.ToastHelper; import com.iceteck.silicompressorr.SiliCompressor; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; import com.qmuiteam.qmui.widget.dialog.QMUIDialog; import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; @@ -73,6 +78,8 @@ ICaseLargeClassView, ICaseSmallClassView, ICaseSubmitCaseView, IUploadFileView { private Context context = this; + @BindView(R.id.caseLayout) + LinearLayout caseLayout; @BindView(R.id.caseClassView) TextView caseClassView; @BindView(R.id.caseLargeClassView) @@ -104,8 +111,6 @@ @BindView(R.id.submitButton) QMUIRoundButton submitButton; - private MediaRecorder mediaRecorder; - private File outputFile; private CaseLargeClassPresenterImpl caseLargeClassPresenter; private CaseSmallClassPresenterImpl caseSmallClassPresenter; private CaseSubmitPresenterImpl caseSubmitPresenter; @@ -125,6 +130,8 @@ private String gridId; private UploadVideoPresenterImpl uploadVideoPresenter; private String audioUrl, videoUrl; + private AudioRecodeHelper audioRecodeHelper; + private PopupWindow popWindow; @Override public int initLayoutView() { @@ -138,7 +145,6 @@ @Override public void initData() { - initMediaRecorder(); largeClassBeans = new ArrayList<>(); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java new file mode 100644 index 0000000..3213591 --- /dev/null +++ b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java @@ -0,0 +1,121 @@ +package com.casic.dcms.utils; + +import android.media.MediaRecorder; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +public class AudioRecodeHelper { + + private static final String TAG = "AudioRecodeHelper"; + private static final int MAX_LENGTH = 1000 * 60 * 5;// 最大录音时长5分钟; + private MediaRecorder mMediaRecorder; + private String filePath; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private long startTime; + + /** + * 开始录音 使用m4a格式 + * + * @return + */ + public void startRecordAudio() { + if (mMediaRecorder == null) + mMediaRecorder = new MediaRecorder(); + try { + /* ②setAudioSource/setVideoSource */ + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风 + /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + /* + * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 + * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) + */ + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + + filePath = FileUtils.getOutputAudioFile().toString(); + /* ③准备 */ + mMediaRecorder.setOutputFile(filePath); + mMediaRecorder.setMaxDuration(MAX_LENGTH); + mMediaRecorder.prepare(); + /* ④开始 */ + mMediaRecorder.start(); + // AudioRecord audioRecord. + /* 获取开始时间* */ + startTime = System.currentTimeMillis(); + updateMicStatus(); + } catch (IllegalStateException | IOException e) { + Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); + } + } + + /** + * 停止录音 + */ + public void stopRecordAudio() { + if (mMediaRecorder == null) + return; + long endTime = System.currentTimeMillis(); + try { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + audioStatusUpdateListener.onStop(filePath); + filePath = ""; + } catch (RuntimeException e) { + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + File file = new File(filePath); + if (file.exists()) + file.delete(); + filePath = ""; + } + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener statusUpdateListener) { + this.audioStatusUpdateListener = statusUpdateListener; + } + + /** + * 更新麦克状态 + */ + private void updateMicStatus() { + if (mMediaRecorder != null) { + double ratio = mMediaRecorder.getMaxAmplitude(); + double db;// 分贝 + if (ratio > 1) { + db = 20 * Math.log10(ratio); + if (null != audioStatusUpdateListener) { + audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime); + } + } + mHandler.postDelayed(mUpdateMicStatusTimer, 100); + } + } + + private Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = this::updateMicStatus; + + public interface OnAudioStatusUpdateListener { + /** + * 录音中... + * + * @param db 当前声音分贝 + * @param time 录音时长 + */ + void onUpdate(double db, long time); + + /** + * 停止录音 + * + * @param filePath 保存路径 + */ + void onStop(String filePath); + } +} diff --git a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java index 59b6341..1445603 100644 --- a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java +++ b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java @@ -7,14 +7,16 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ import com.casic.dcms.mvp.view.ICaseSmallClassView; import com.casic.dcms.mvp.view.ICaseSubmitCaseView; import com.casic.dcms.mvp.view.IUploadFileView; +import com.casic.dcms.utils.AudioRecodeHelper; import com.casic.dcms.utils.ColorHelper; import com.casic.dcms.utils.Constant; import com.casic.dcms.utils.FileUtils; @@ -47,12 +50,14 @@ import com.casic.dcms.utils.ItemDecorationSpace; import com.casic.dcms.utils.OtherUtils; import com.casic.dcms.utils.StringHelper; +import com.casic.dcms.utils.TimeOrDateUtil; import com.casic.dcms.utils.ToastHelper; import com.iceteck.silicompressorr.SiliCompressor; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; import com.qmuiteam.qmui.widget.dialog.QMUIDialog; import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; @@ -73,6 +78,8 @@ ICaseLargeClassView, ICaseSmallClassView, ICaseSubmitCaseView, IUploadFileView { private Context context = this; + @BindView(R.id.caseLayout) + LinearLayout caseLayout; @BindView(R.id.caseClassView) TextView caseClassView; @BindView(R.id.caseLargeClassView) @@ -104,8 +111,6 @@ @BindView(R.id.submitButton) QMUIRoundButton submitButton; - private MediaRecorder mediaRecorder; - private File outputFile; private CaseLargeClassPresenterImpl caseLargeClassPresenter; private CaseSmallClassPresenterImpl caseSmallClassPresenter; private CaseSubmitPresenterImpl caseSubmitPresenter; @@ -125,6 +130,8 @@ private String gridId; private UploadVideoPresenterImpl uploadVideoPresenter; private String audioUrl, videoUrl; + private AudioRecodeHelper audioRecodeHelper; + private PopupWindow popWindow; @Override public int initLayoutView() { @@ -138,7 +145,6 @@ @Override public void initData() { - initMediaRecorder(); largeClassBeans = new ArrayList<>(); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java new file mode 100644 index 0000000..3213591 --- /dev/null +++ b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java @@ -0,0 +1,121 @@ +package com.casic.dcms.utils; + +import android.media.MediaRecorder; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +public class AudioRecodeHelper { + + private static final String TAG = "AudioRecodeHelper"; + private static final int MAX_LENGTH = 1000 * 60 * 5;// 最大录音时长5分钟; + private MediaRecorder mMediaRecorder; + private String filePath; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private long startTime; + + /** + * 开始录音 使用m4a格式 + * + * @return + */ + public void startRecordAudio() { + if (mMediaRecorder == null) + mMediaRecorder = new MediaRecorder(); + try { + /* ②setAudioSource/setVideoSource */ + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风 + /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + /* + * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 + * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) + */ + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + + filePath = FileUtils.getOutputAudioFile().toString(); + /* ③准备 */ + mMediaRecorder.setOutputFile(filePath); + mMediaRecorder.setMaxDuration(MAX_LENGTH); + mMediaRecorder.prepare(); + /* ④开始 */ + mMediaRecorder.start(); + // AudioRecord audioRecord. + /* 获取开始时间* */ + startTime = System.currentTimeMillis(); + updateMicStatus(); + } catch (IllegalStateException | IOException e) { + Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); + } + } + + /** + * 停止录音 + */ + public void stopRecordAudio() { + if (mMediaRecorder == null) + return; + long endTime = System.currentTimeMillis(); + try { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + audioStatusUpdateListener.onStop(filePath); + filePath = ""; + } catch (RuntimeException e) { + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + File file = new File(filePath); + if (file.exists()) + file.delete(); + filePath = ""; + } + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener statusUpdateListener) { + this.audioStatusUpdateListener = statusUpdateListener; + } + + /** + * 更新麦克状态 + */ + private void updateMicStatus() { + if (mMediaRecorder != null) { + double ratio = mMediaRecorder.getMaxAmplitude(); + double db;// 分贝 + if (ratio > 1) { + db = 20 * Math.log10(ratio); + if (null != audioStatusUpdateListener) { + audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime); + } + } + mHandler.postDelayed(mUpdateMicStatusTimer, 100); + } + } + + private Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = this::updateMicStatus; + + public interface OnAudioStatusUpdateListener { + /** + * 录音中... + * + * @param db 当前声音分贝 + * @param time 录音时长 + */ + void onUpdate(double db, long time); + + /** + * 停止录音 + * + * @param filePath 保存路径 + */ + void onStop(String filePath); + } +} diff --git a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java index 61aa9ec..fa6b3be 100644 --- a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java +++ b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java @@ -15,6 +15,14 @@ private static final SimpleDateFormat allDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + private static final SimpleDateFormat millsFormat = new SimpleDateFormat("mm:ss"); + + /** + * 时间戳转时间 + */ + public static String millsToTime(long millSeconds) { + return millsFormat.format(new Date(millSeconds)); + } /** * 时间戳转日期 diff --git a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java index 59b6341..1445603 100644 --- a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java +++ b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java @@ -7,14 +7,16 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ import com.casic.dcms.mvp.view.ICaseSmallClassView; import com.casic.dcms.mvp.view.ICaseSubmitCaseView; import com.casic.dcms.mvp.view.IUploadFileView; +import com.casic.dcms.utils.AudioRecodeHelper; import com.casic.dcms.utils.ColorHelper; import com.casic.dcms.utils.Constant; import com.casic.dcms.utils.FileUtils; @@ -47,12 +50,14 @@ import com.casic.dcms.utils.ItemDecorationSpace; import com.casic.dcms.utils.OtherUtils; import com.casic.dcms.utils.StringHelper; +import com.casic.dcms.utils.TimeOrDateUtil; import com.casic.dcms.utils.ToastHelper; import com.iceteck.silicompressorr.SiliCompressor; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; import com.qmuiteam.qmui.widget.dialog.QMUIDialog; import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; @@ -73,6 +78,8 @@ ICaseLargeClassView, ICaseSmallClassView, ICaseSubmitCaseView, IUploadFileView { private Context context = this; + @BindView(R.id.caseLayout) + LinearLayout caseLayout; @BindView(R.id.caseClassView) TextView caseClassView; @BindView(R.id.caseLargeClassView) @@ -104,8 +111,6 @@ @BindView(R.id.submitButton) QMUIRoundButton submitButton; - private MediaRecorder mediaRecorder; - private File outputFile; private CaseLargeClassPresenterImpl caseLargeClassPresenter; private CaseSmallClassPresenterImpl caseSmallClassPresenter; private CaseSubmitPresenterImpl caseSubmitPresenter; @@ -125,6 +130,8 @@ private String gridId; private UploadVideoPresenterImpl uploadVideoPresenter; private String audioUrl, videoUrl; + private AudioRecodeHelper audioRecodeHelper; + private PopupWindow popWindow; @Override public int initLayoutView() { @@ -138,7 +145,6 @@ @Override public void initData() { - initMediaRecorder(); largeClassBeans = new ArrayList<>(); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java new file mode 100644 index 0000000..3213591 --- /dev/null +++ b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java @@ -0,0 +1,121 @@ +package com.casic.dcms.utils; + +import android.media.MediaRecorder; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +public class AudioRecodeHelper { + + private static final String TAG = "AudioRecodeHelper"; + private static final int MAX_LENGTH = 1000 * 60 * 5;// 最大录音时长5分钟; + private MediaRecorder mMediaRecorder; + private String filePath; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private long startTime; + + /** + * 开始录音 使用m4a格式 + * + * @return + */ + public void startRecordAudio() { + if (mMediaRecorder == null) + mMediaRecorder = new MediaRecorder(); + try { + /* ②setAudioSource/setVideoSource */ + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风 + /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + /* + * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 + * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) + */ + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + + filePath = FileUtils.getOutputAudioFile().toString(); + /* ③准备 */ + mMediaRecorder.setOutputFile(filePath); + mMediaRecorder.setMaxDuration(MAX_LENGTH); + mMediaRecorder.prepare(); + /* ④开始 */ + mMediaRecorder.start(); + // AudioRecord audioRecord. + /* 获取开始时间* */ + startTime = System.currentTimeMillis(); + updateMicStatus(); + } catch (IllegalStateException | IOException e) { + Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); + } + } + + /** + * 停止录音 + */ + public void stopRecordAudio() { + if (mMediaRecorder == null) + return; + long endTime = System.currentTimeMillis(); + try { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + audioStatusUpdateListener.onStop(filePath); + filePath = ""; + } catch (RuntimeException e) { + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + File file = new File(filePath); + if (file.exists()) + file.delete(); + filePath = ""; + } + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener statusUpdateListener) { + this.audioStatusUpdateListener = statusUpdateListener; + } + + /** + * 更新麦克状态 + */ + private void updateMicStatus() { + if (mMediaRecorder != null) { + double ratio = mMediaRecorder.getMaxAmplitude(); + double db;// 分贝 + if (ratio > 1) { + db = 20 * Math.log10(ratio); + if (null != audioStatusUpdateListener) { + audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime); + } + } + mHandler.postDelayed(mUpdateMicStatusTimer, 100); + } + } + + private Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = this::updateMicStatus; + + public interface OnAudioStatusUpdateListener { + /** + * 录音中... + * + * @param db 当前声音分贝 + * @param time 录音时长 + */ + void onUpdate(double db, long time); + + /** + * 停止录音 + * + * @param filePath 保存路径 + */ + void onStop(String filePath); + } +} diff --git a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java index 61aa9ec..fa6b3be 100644 --- a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java +++ b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java @@ -15,6 +15,14 @@ private static final SimpleDateFormat allDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + private static final SimpleDateFormat millsFormat = new SimpleDateFormat("mm:ss"); + + /** + * 时间戳转时间 + */ + public static String millsToTime(long millSeconds) { + return millsFormat.format(new Date(millSeconds)); + } /** * 时间戳转日期 diff --git a/app/src/main/res/anim/popup_hide.xml b/app/src/main/res/anim/popup_hide.xml new file mode 100644 index 0000000..8a98f85 --- /dev/null +++ b/app/src/main/res/anim/popup_hide.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java index 59b6341..1445603 100644 --- a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java +++ b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java @@ -7,14 +7,16 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ import com.casic.dcms.mvp.view.ICaseSmallClassView; import com.casic.dcms.mvp.view.ICaseSubmitCaseView; import com.casic.dcms.mvp.view.IUploadFileView; +import com.casic.dcms.utils.AudioRecodeHelper; import com.casic.dcms.utils.ColorHelper; import com.casic.dcms.utils.Constant; import com.casic.dcms.utils.FileUtils; @@ -47,12 +50,14 @@ import com.casic.dcms.utils.ItemDecorationSpace; import com.casic.dcms.utils.OtherUtils; import com.casic.dcms.utils.StringHelper; +import com.casic.dcms.utils.TimeOrDateUtil; import com.casic.dcms.utils.ToastHelper; import com.iceteck.silicompressorr.SiliCompressor; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; import com.qmuiteam.qmui.widget.dialog.QMUIDialog; import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; @@ -73,6 +78,8 @@ ICaseLargeClassView, ICaseSmallClassView, ICaseSubmitCaseView, IUploadFileView { private Context context = this; + @BindView(R.id.caseLayout) + LinearLayout caseLayout; @BindView(R.id.caseClassView) TextView caseClassView; @BindView(R.id.caseLargeClassView) @@ -104,8 +111,6 @@ @BindView(R.id.submitButton) QMUIRoundButton submitButton; - private MediaRecorder mediaRecorder; - private File outputFile; private CaseLargeClassPresenterImpl caseLargeClassPresenter; private CaseSmallClassPresenterImpl caseSmallClassPresenter; private CaseSubmitPresenterImpl caseSubmitPresenter; @@ -125,6 +130,8 @@ private String gridId; private UploadVideoPresenterImpl uploadVideoPresenter; private String audioUrl, videoUrl; + private AudioRecodeHelper audioRecodeHelper; + private PopupWindow popWindow; @Override public int initLayoutView() { @@ -138,7 +145,6 @@ @Override public void initData() { - initMediaRecorder(); largeClassBeans = new ArrayList<>(); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java new file mode 100644 index 0000000..3213591 --- /dev/null +++ b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java @@ -0,0 +1,121 @@ +package com.casic.dcms.utils; + +import android.media.MediaRecorder; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +public class AudioRecodeHelper { + + private static final String TAG = "AudioRecodeHelper"; + private static final int MAX_LENGTH = 1000 * 60 * 5;// 最大录音时长5分钟; + private MediaRecorder mMediaRecorder; + private String filePath; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private long startTime; + + /** + * 开始录音 使用m4a格式 + * + * @return + */ + public void startRecordAudio() { + if (mMediaRecorder == null) + mMediaRecorder = new MediaRecorder(); + try { + /* ②setAudioSource/setVideoSource */ + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风 + /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + /* + * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 + * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) + */ + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + + filePath = FileUtils.getOutputAudioFile().toString(); + /* ③准备 */ + mMediaRecorder.setOutputFile(filePath); + mMediaRecorder.setMaxDuration(MAX_LENGTH); + mMediaRecorder.prepare(); + /* ④开始 */ + mMediaRecorder.start(); + // AudioRecord audioRecord. + /* 获取开始时间* */ + startTime = System.currentTimeMillis(); + updateMicStatus(); + } catch (IllegalStateException | IOException e) { + Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); + } + } + + /** + * 停止录音 + */ + public void stopRecordAudio() { + if (mMediaRecorder == null) + return; + long endTime = System.currentTimeMillis(); + try { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + audioStatusUpdateListener.onStop(filePath); + filePath = ""; + } catch (RuntimeException e) { + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + File file = new File(filePath); + if (file.exists()) + file.delete(); + filePath = ""; + } + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener statusUpdateListener) { + this.audioStatusUpdateListener = statusUpdateListener; + } + + /** + * 更新麦克状态 + */ + private void updateMicStatus() { + if (mMediaRecorder != null) { + double ratio = mMediaRecorder.getMaxAmplitude(); + double db;// 分贝 + if (ratio > 1) { + db = 20 * Math.log10(ratio); + if (null != audioStatusUpdateListener) { + audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime); + } + } + mHandler.postDelayed(mUpdateMicStatusTimer, 100); + } + } + + private Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = this::updateMicStatus; + + public interface OnAudioStatusUpdateListener { + /** + * 录音中... + * + * @param db 当前声音分贝 + * @param time 录音时长 + */ + void onUpdate(double db, long time); + + /** + * 停止录音 + * + * @param filePath 保存路径 + */ + void onStop(String filePath); + } +} diff --git a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java index 61aa9ec..fa6b3be 100644 --- a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java +++ b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java @@ -15,6 +15,14 @@ private static final SimpleDateFormat allDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + private static final SimpleDateFormat millsFormat = new SimpleDateFormat("mm:ss"); + + /** + * 时间戳转时间 + */ + public static String millsToTime(long millSeconds) { + return millsFormat.format(new Date(millSeconds)); + } /** * 时间戳转日期 diff --git a/app/src/main/res/anim/popup_hide.xml b/app/src/main/res/anim/popup_hide.xml new file mode 100644 index 0000000..8a98f85 --- /dev/null +++ b/app/src/main/res/anim/popup_hide.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/popup_show.xml b/app/src/main/res/anim/popup_show.xml new file mode 100644 index 0000000..5cb9603 --- /dev/null +++ b/app/src/main/res/anim/popup_show.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java index 59b6341..1445603 100644 --- a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java +++ b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java @@ -7,14 +7,16 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ import com.casic.dcms.mvp.view.ICaseSmallClassView; import com.casic.dcms.mvp.view.ICaseSubmitCaseView; import com.casic.dcms.mvp.view.IUploadFileView; +import com.casic.dcms.utils.AudioRecodeHelper; import com.casic.dcms.utils.ColorHelper; import com.casic.dcms.utils.Constant; import com.casic.dcms.utils.FileUtils; @@ -47,12 +50,14 @@ import com.casic.dcms.utils.ItemDecorationSpace; import com.casic.dcms.utils.OtherUtils; import com.casic.dcms.utils.StringHelper; +import com.casic.dcms.utils.TimeOrDateUtil; import com.casic.dcms.utils.ToastHelper; import com.iceteck.silicompressorr.SiliCompressor; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; import com.qmuiteam.qmui.widget.dialog.QMUIDialog; import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; @@ -73,6 +78,8 @@ ICaseLargeClassView, ICaseSmallClassView, ICaseSubmitCaseView, IUploadFileView { private Context context = this; + @BindView(R.id.caseLayout) + LinearLayout caseLayout; @BindView(R.id.caseClassView) TextView caseClassView; @BindView(R.id.caseLargeClassView) @@ -104,8 +111,6 @@ @BindView(R.id.submitButton) QMUIRoundButton submitButton; - private MediaRecorder mediaRecorder; - private File outputFile; private CaseLargeClassPresenterImpl caseLargeClassPresenter; private CaseSmallClassPresenterImpl caseSmallClassPresenter; private CaseSubmitPresenterImpl caseSubmitPresenter; @@ -125,6 +130,8 @@ private String gridId; private UploadVideoPresenterImpl uploadVideoPresenter; private String audioUrl, videoUrl; + private AudioRecodeHelper audioRecodeHelper; + private PopupWindow popWindow; @Override public int initLayoutView() { @@ -138,7 +145,6 @@ @Override public void initData() { - initMediaRecorder(); largeClassBeans = new ArrayList<>(); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java new file mode 100644 index 0000000..3213591 --- /dev/null +++ b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java @@ -0,0 +1,121 @@ +package com.casic.dcms.utils; + +import android.media.MediaRecorder; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +public class AudioRecodeHelper { + + private static final String TAG = "AudioRecodeHelper"; + private static final int MAX_LENGTH = 1000 * 60 * 5;// 最大录音时长5分钟; + private MediaRecorder mMediaRecorder; + private String filePath; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private long startTime; + + /** + * 开始录音 使用m4a格式 + * + * @return + */ + public void startRecordAudio() { + if (mMediaRecorder == null) + mMediaRecorder = new MediaRecorder(); + try { + /* ②setAudioSource/setVideoSource */ + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风 + /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + /* + * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 + * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) + */ + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + + filePath = FileUtils.getOutputAudioFile().toString(); + /* ③准备 */ + mMediaRecorder.setOutputFile(filePath); + mMediaRecorder.setMaxDuration(MAX_LENGTH); + mMediaRecorder.prepare(); + /* ④开始 */ + mMediaRecorder.start(); + // AudioRecord audioRecord. + /* 获取开始时间* */ + startTime = System.currentTimeMillis(); + updateMicStatus(); + } catch (IllegalStateException | IOException e) { + Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); + } + } + + /** + * 停止录音 + */ + public void stopRecordAudio() { + if (mMediaRecorder == null) + return; + long endTime = System.currentTimeMillis(); + try { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + audioStatusUpdateListener.onStop(filePath); + filePath = ""; + } catch (RuntimeException e) { + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + File file = new File(filePath); + if (file.exists()) + file.delete(); + filePath = ""; + } + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener statusUpdateListener) { + this.audioStatusUpdateListener = statusUpdateListener; + } + + /** + * 更新麦克状态 + */ + private void updateMicStatus() { + if (mMediaRecorder != null) { + double ratio = mMediaRecorder.getMaxAmplitude(); + double db;// 分贝 + if (ratio > 1) { + db = 20 * Math.log10(ratio); + if (null != audioStatusUpdateListener) { + audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime); + } + } + mHandler.postDelayed(mUpdateMicStatusTimer, 100); + } + } + + private Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = this::updateMicStatus; + + public interface OnAudioStatusUpdateListener { + /** + * 录音中... + * + * @param db 当前声音分贝 + * @param time 录音时长 + */ + void onUpdate(double db, long time); + + /** + * 停止录音 + * + * @param filePath 保存路径 + */ + void onStop(String filePath); + } +} diff --git a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java index 61aa9ec..fa6b3be 100644 --- a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java +++ b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java @@ -15,6 +15,14 @@ private static final SimpleDateFormat allDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + private static final SimpleDateFormat millsFormat = new SimpleDateFormat("mm:ss"); + + /** + * 时间戳转时间 + */ + public static String millsToTime(long millSeconds) { + return millsFormat.format(new Date(millSeconds)); + } /** * 时间戳转日期 diff --git a/app/src/main/res/anim/popup_hide.xml b/app/src/main/res/anim/popup_hide.xml new file mode 100644 index 0000000..8a98f85 --- /dev/null +++ b/app/src/main/res/anim/popup_hide.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/popup_show.xml b/app/src/main/res/anim/popup_show.xml new file mode 100644 index 0000000..5cb9603 --- /dev/null +++ b/app/src/main/res/anim/popup_show.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_recode.xml b/app/src/main/res/drawable/ic_recode.xml new file mode 100644 index 0000000..49697d8 --- /dev/null +++ b/app/src/main/res/drawable/ic_recode.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java index 59b6341..1445603 100644 --- a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java +++ b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java @@ -7,14 +7,16 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ import com.casic.dcms.mvp.view.ICaseSmallClassView; import com.casic.dcms.mvp.view.ICaseSubmitCaseView; import com.casic.dcms.mvp.view.IUploadFileView; +import com.casic.dcms.utils.AudioRecodeHelper; import com.casic.dcms.utils.ColorHelper; import com.casic.dcms.utils.Constant; import com.casic.dcms.utils.FileUtils; @@ -47,12 +50,14 @@ import com.casic.dcms.utils.ItemDecorationSpace; import com.casic.dcms.utils.OtherUtils; import com.casic.dcms.utils.StringHelper; +import com.casic.dcms.utils.TimeOrDateUtil; import com.casic.dcms.utils.ToastHelper; import com.iceteck.silicompressorr.SiliCompressor; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; import com.qmuiteam.qmui.widget.dialog.QMUIDialog; import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; @@ -73,6 +78,8 @@ ICaseLargeClassView, ICaseSmallClassView, ICaseSubmitCaseView, IUploadFileView { private Context context = this; + @BindView(R.id.caseLayout) + LinearLayout caseLayout; @BindView(R.id.caseClassView) TextView caseClassView; @BindView(R.id.caseLargeClassView) @@ -104,8 +111,6 @@ @BindView(R.id.submitButton) QMUIRoundButton submitButton; - private MediaRecorder mediaRecorder; - private File outputFile; private CaseLargeClassPresenterImpl caseLargeClassPresenter; private CaseSmallClassPresenterImpl caseSmallClassPresenter; private CaseSubmitPresenterImpl caseSubmitPresenter; @@ -125,6 +130,8 @@ private String gridId; private UploadVideoPresenterImpl uploadVideoPresenter; private String audioUrl, videoUrl; + private AudioRecodeHelper audioRecodeHelper; + private PopupWindow popWindow; @Override public int initLayoutView() { @@ -138,7 +145,6 @@ @Override public void initData() { - initMediaRecorder(); largeClassBeans = new ArrayList<>(); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java new file mode 100644 index 0000000..3213591 --- /dev/null +++ b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java @@ -0,0 +1,121 @@ +package com.casic.dcms.utils; + +import android.media.MediaRecorder; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +public class AudioRecodeHelper { + + private static final String TAG = "AudioRecodeHelper"; + private static final int MAX_LENGTH = 1000 * 60 * 5;// 最大录音时长5分钟; + private MediaRecorder mMediaRecorder; + private String filePath; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private long startTime; + + /** + * 开始录音 使用m4a格式 + * + * @return + */ + public void startRecordAudio() { + if (mMediaRecorder == null) + mMediaRecorder = new MediaRecorder(); + try { + /* ②setAudioSource/setVideoSource */ + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风 + /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + /* + * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 + * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) + */ + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + + filePath = FileUtils.getOutputAudioFile().toString(); + /* ③准备 */ + mMediaRecorder.setOutputFile(filePath); + mMediaRecorder.setMaxDuration(MAX_LENGTH); + mMediaRecorder.prepare(); + /* ④开始 */ + mMediaRecorder.start(); + // AudioRecord audioRecord. + /* 获取开始时间* */ + startTime = System.currentTimeMillis(); + updateMicStatus(); + } catch (IllegalStateException | IOException e) { + Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); + } + } + + /** + * 停止录音 + */ + public void stopRecordAudio() { + if (mMediaRecorder == null) + return; + long endTime = System.currentTimeMillis(); + try { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + audioStatusUpdateListener.onStop(filePath); + filePath = ""; + } catch (RuntimeException e) { + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + File file = new File(filePath); + if (file.exists()) + file.delete(); + filePath = ""; + } + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener statusUpdateListener) { + this.audioStatusUpdateListener = statusUpdateListener; + } + + /** + * 更新麦克状态 + */ + private void updateMicStatus() { + if (mMediaRecorder != null) { + double ratio = mMediaRecorder.getMaxAmplitude(); + double db;// 分贝 + if (ratio > 1) { + db = 20 * Math.log10(ratio); + if (null != audioStatusUpdateListener) { + audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime); + } + } + mHandler.postDelayed(mUpdateMicStatusTimer, 100); + } + } + + private Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = this::updateMicStatus; + + public interface OnAudioStatusUpdateListener { + /** + * 录音中... + * + * @param db 当前声音分贝 + * @param time 录音时长 + */ + void onUpdate(double db, long time); + + /** + * 停止录音 + * + * @param filePath 保存路径 + */ + void onStop(String filePath); + } +} diff --git a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java index 61aa9ec..fa6b3be 100644 --- a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java +++ b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java @@ -15,6 +15,14 @@ private static final SimpleDateFormat allDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + private static final SimpleDateFormat millsFormat = new SimpleDateFormat("mm:ss"); + + /** + * 时间戳转时间 + */ + public static String millsToTime(long millSeconds) { + return millsFormat.format(new Date(millSeconds)); + } /** * 时间戳转日期 diff --git a/app/src/main/res/anim/popup_hide.xml b/app/src/main/res/anim/popup_hide.xml new file mode 100644 index 0000000..8a98f85 --- /dev/null +++ b/app/src/main/res/anim/popup_hide.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/popup_show.xml b/app/src/main/res/anim/popup_show.xml new file mode 100644 index 0000000..5cb9603 --- /dev/null +++ b/app/src/main/res/anim/popup_show.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_recode.xml b/app/src/main/res/drawable/ic_recode.xml new file mode 100644 index 0000000..49697d8 --- /dev/null +++ b/app/src/main/res/drawable/ic_recode.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_recoding.xml b/app/src/main/res/drawable/ic_recoding.xml new file mode 100644 index 0000000..4f819bf --- /dev/null +++ b/app/src/main/res/drawable/ic_recoding.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java index 59b6341..1445603 100644 --- a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java +++ b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java @@ -7,14 +7,16 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ import com.casic.dcms.mvp.view.ICaseSmallClassView; import com.casic.dcms.mvp.view.ICaseSubmitCaseView; import com.casic.dcms.mvp.view.IUploadFileView; +import com.casic.dcms.utils.AudioRecodeHelper; import com.casic.dcms.utils.ColorHelper; import com.casic.dcms.utils.Constant; import com.casic.dcms.utils.FileUtils; @@ -47,12 +50,14 @@ import com.casic.dcms.utils.ItemDecorationSpace; import com.casic.dcms.utils.OtherUtils; import com.casic.dcms.utils.StringHelper; +import com.casic.dcms.utils.TimeOrDateUtil; import com.casic.dcms.utils.ToastHelper; import com.iceteck.silicompressorr.SiliCompressor; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; import com.qmuiteam.qmui.widget.dialog.QMUIDialog; import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; @@ -73,6 +78,8 @@ ICaseLargeClassView, ICaseSmallClassView, ICaseSubmitCaseView, IUploadFileView { private Context context = this; + @BindView(R.id.caseLayout) + LinearLayout caseLayout; @BindView(R.id.caseClassView) TextView caseClassView; @BindView(R.id.caseLargeClassView) @@ -104,8 +111,6 @@ @BindView(R.id.submitButton) QMUIRoundButton submitButton; - private MediaRecorder mediaRecorder; - private File outputFile; private CaseLargeClassPresenterImpl caseLargeClassPresenter; private CaseSmallClassPresenterImpl caseSmallClassPresenter; private CaseSubmitPresenterImpl caseSubmitPresenter; @@ -125,6 +130,8 @@ private String gridId; private UploadVideoPresenterImpl uploadVideoPresenter; private String audioUrl, videoUrl; + private AudioRecodeHelper audioRecodeHelper; + private PopupWindow popWindow; @Override public int initLayoutView() { @@ -138,7 +145,6 @@ @Override public void initData() { - initMediaRecorder(); largeClassBeans = new ArrayList<>(); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java new file mode 100644 index 0000000..3213591 --- /dev/null +++ b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java @@ -0,0 +1,121 @@ +package com.casic.dcms.utils; + +import android.media.MediaRecorder; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +public class AudioRecodeHelper { + + private static final String TAG = "AudioRecodeHelper"; + private static final int MAX_LENGTH = 1000 * 60 * 5;// 最大录音时长5分钟; + private MediaRecorder mMediaRecorder; + private String filePath; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private long startTime; + + /** + * 开始录音 使用m4a格式 + * + * @return + */ + public void startRecordAudio() { + if (mMediaRecorder == null) + mMediaRecorder = new MediaRecorder(); + try { + /* ②setAudioSource/setVideoSource */ + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风 + /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + /* + * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 + * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) + */ + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + + filePath = FileUtils.getOutputAudioFile().toString(); + /* ③准备 */ + mMediaRecorder.setOutputFile(filePath); + mMediaRecorder.setMaxDuration(MAX_LENGTH); + mMediaRecorder.prepare(); + /* ④开始 */ + mMediaRecorder.start(); + // AudioRecord audioRecord. + /* 获取开始时间* */ + startTime = System.currentTimeMillis(); + updateMicStatus(); + } catch (IllegalStateException | IOException e) { + Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); + } + } + + /** + * 停止录音 + */ + public void stopRecordAudio() { + if (mMediaRecorder == null) + return; + long endTime = System.currentTimeMillis(); + try { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + audioStatusUpdateListener.onStop(filePath); + filePath = ""; + } catch (RuntimeException e) { + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + File file = new File(filePath); + if (file.exists()) + file.delete(); + filePath = ""; + } + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener statusUpdateListener) { + this.audioStatusUpdateListener = statusUpdateListener; + } + + /** + * 更新麦克状态 + */ + private void updateMicStatus() { + if (mMediaRecorder != null) { + double ratio = mMediaRecorder.getMaxAmplitude(); + double db;// 分贝 + if (ratio > 1) { + db = 20 * Math.log10(ratio); + if (null != audioStatusUpdateListener) { + audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime); + } + } + mHandler.postDelayed(mUpdateMicStatusTimer, 100); + } + } + + private Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = this::updateMicStatus; + + public interface OnAudioStatusUpdateListener { + /** + * 录音中... + * + * @param db 当前声音分贝 + * @param time 录音时长 + */ + void onUpdate(double db, long time); + + /** + * 停止录音 + * + * @param filePath 保存路径 + */ + void onStop(String filePath); + } +} diff --git a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java index 61aa9ec..fa6b3be 100644 --- a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java +++ b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java @@ -15,6 +15,14 @@ private static final SimpleDateFormat allDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + private static final SimpleDateFormat millsFormat = new SimpleDateFormat("mm:ss"); + + /** + * 时间戳转时间 + */ + public static String millsToTime(long millSeconds) { + return millsFormat.format(new Date(millSeconds)); + } /** * 时间戳转日期 diff --git a/app/src/main/res/anim/popup_hide.xml b/app/src/main/res/anim/popup_hide.xml new file mode 100644 index 0000000..8a98f85 --- /dev/null +++ b/app/src/main/res/anim/popup_hide.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/popup_show.xml b/app/src/main/res/anim/popup_show.xml new file mode 100644 index 0000000..5cb9603 --- /dev/null +++ b/app/src/main/res/anim/popup_show.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_recode.xml b/app/src/main/res/drawable/ic_recode.xml new file mode 100644 index 0000000..49697d8 --- /dev/null +++ b/app/src/main/res/drawable/ic_recode.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_recoding.xml b/app/src/main/res/drawable/ic_recoding.xml new file mode 100644 index 0000000..4f819bf --- /dev/null +++ b/app/src/main/res/drawable/ic_recoding.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/layer_microphone.xml b/app/src/main/res/drawable/layer_microphone.xml new file mode 100644 index 0000000..dceaea2 --- /dev/null +++ b/app/src/main/res/drawable/layer_microphone.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java index 59b6341..1445603 100644 --- a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java +++ b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java @@ -7,14 +7,16 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ import com.casic.dcms.mvp.view.ICaseSmallClassView; import com.casic.dcms.mvp.view.ICaseSubmitCaseView; import com.casic.dcms.mvp.view.IUploadFileView; +import com.casic.dcms.utils.AudioRecodeHelper; import com.casic.dcms.utils.ColorHelper; import com.casic.dcms.utils.Constant; import com.casic.dcms.utils.FileUtils; @@ -47,12 +50,14 @@ import com.casic.dcms.utils.ItemDecorationSpace; import com.casic.dcms.utils.OtherUtils; import com.casic.dcms.utils.StringHelper; +import com.casic.dcms.utils.TimeOrDateUtil; import com.casic.dcms.utils.ToastHelper; import com.iceteck.silicompressorr.SiliCompressor; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; import com.qmuiteam.qmui.widget.dialog.QMUIDialog; import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; @@ -73,6 +78,8 @@ ICaseLargeClassView, ICaseSmallClassView, ICaseSubmitCaseView, IUploadFileView { private Context context = this; + @BindView(R.id.caseLayout) + LinearLayout caseLayout; @BindView(R.id.caseClassView) TextView caseClassView; @BindView(R.id.caseLargeClassView) @@ -104,8 +111,6 @@ @BindView(R.id.submitButton) QMUIRoundButton submitButton; - private MediaRecorder mediaRecorder; - private File outputFile; private CaseLargeClassPresenterImpl caseLargeClassPresenter; private CaseSmallClassPresenterImpl caseSmallClassPresenter; private CaseSubmitPresenterImpl caseSubmitPresenter; @@ -125,6 +130,8 @@ private String gridId; private UploadVideoPresenterImpl uploadVideoPresenter; private String audioUrl, videoUrl; + private AudioRecodeHelper audioRecodeHelper; + private PopupWindow popWindow; @Override public int initLayoutView() { @@ -138,7 +145,6 @@ @Override public void initData() { - initMediaRecorder(); largeClassBeans = new ArrayList<>(); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java new file mode 100644 index 0000000..3213591 --- /dev/null +++ b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java @@ -0,0 +1,121 @@ +package com.casic.dcms.utils; + +import android.media.MediaRecorder; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +public class AudioRecodeHelper { + + private static final String TAG = "AudioRecodeHelper"; + private static final int MAX_LENGTH = 1000 * 60 * 5;// 最大录音时长5分钟; + private MediaRecorder mMediaRecorder; + private String filePath; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private long startTime; + + /** + * 开始录音 使用m4a格式 + * + * @return + */ + public void startRecordAudio() { + if (mMediaRecorder == null) + mMediaRecorder = new MediaRecorder(); + try { + /* ②setAudioSource/setVideoSource */ + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风 + /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + /* + * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 + * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) + */ + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + + filePath = FileUtils.getOutputAudioFile().toString(); + /* ③准备 */ + mMediaRecorder.setOutputFile(filePath); + mMediaRecorder.setMaxDuration(MAX_LENGTH); + mMediaRecorder.prepare(); + /* ④开始 */ + mMediaRecorder.start(); + // AudioRecord audioRecord. + /* 获取开始时间* */ + startTime = System.currentTimeMillis(); + updateMicStatus(); + } catch (IllegalStateException | IOException e) { + Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); + } + } + + /** + * 停止录音 + */ + public void stopRecordAudio() { + if (mMediaRecorder == null) + return; + long endTime = System.currentTimeMillis(); + try { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + audioStatusUpdateListener.onStop(filePath); + filePath = ""; + } catch (RuntimeException e) { + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + File file = new File(filePath); + if (file.exists()) + file.delete(); + filePath = ""; + } + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener statusUpdateListener) { + this.audioStatusUpdateListener = statusUpdateListener; + } + + /** + * 更新麦克状态 + */ + private void updateMicStatus() { + if (mMediaRecorder != null) { + double ratio = mMediaRecorder.getMaxAmplitude(); + double db;// 分贝 + if (ratio > 1) { + db = 20 * Math.log10(ratio); + if (null != audioStatusUpdateListener) { + audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime); + } + } + mHandler.postDelayed(mUpdateMicStatusTimer, 100); + } + } + + private Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = this::updateMicStatus; + + public interface OnAudioStatusUpdateListener { + /** + * 录音中... + * + * @param db 当前声音分贝 + * @param time 录音时长 + */ + void onUpdate(double db, long time); + + /** + * 停止录音 + * + * @param filePath 保存路径 + */ + void onStop(String filePath); + } +} diff --git a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java index 61aa9ec..fa6b3be 100644 --- a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java +++ b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java @@ -15,6 +15,14 @@ private static final SimpleDateFormat allDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + private static final SimpleDateFormat millsFormat = new SimpleDateFormat("mm:ss"); + + /** + * 时间戳转时间 + */ + public static String millsToTime(long millSeconds) { + return millsFormat.format(new Date(millSeconds)); + } /** * 时间戳转日期 diff --git a/app/src/main/res/anim/popup_hide.xml b/app/src/main/res/anim/popup_hide.xml new file mode 100644 index 0000000..8a98f85 --- /dev/null +++ b/app/src/main/res/anim/popup_hide.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/popup_show.xml b/app/src/main/res/anim/popup_show.xml new file mode 100644 index 0000000..5cb9603 --- /dev/null +++ b/app/src/main/res/anim/popup_show.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_recode.xml b/app/src/main/res/drawable/ic_recode.xml new file mode 100644 index 0000000..49697d8 --- /dev/null +++ b/app/src/main/res/drawable/ic_recode.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_recoding.xml b/app/src/main/res/drawable/ic_recoding.xml new file mode 100644 index 0000000..4f819bf --- /dev/null +++ b/app/src/main/res/drawable/ic_recoding.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/layer_microphone.xml b/app/src/main/res/drawable/layer_microphone.xml new file mode 100644 index 0000000..dceaea2 --- /dev/null +++ b/app/src/main/res/drawable/layer_microphone.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_case_upload.xml b/app/src/main/res/layout/activity_case_upload.xml index 1b94cc1..79f5967 100644 --- a/app/src/main/res/layout/activity_case_upload.xml +++ b/app/src/main/res/layout/activity_case_upload.xml @@ -1,6 +1,7 @@ (); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java new file mode 100644 index 0000000..3213591 --- /dev/null +++ b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java @@ -0,0 +1,121 @@ +package com.casic.dcms.utils; + +import android.media.MediaRecorder; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +public class AudioRecodeHelper { + + private static final String TAG = "AudioRecodeHelper"; + private static final int MAX_LENGTH = 1000 * 60 * 5;// 最大录音时长5分钟; + private MediaRecorder mMediaRecorder; + private String filePath; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private long startTime; + + /** + * 开始录音 使用m4a格式 + * + * @return + */ + public void startRecordAudio() { + if (mMediaRecorder == null) + mMediaRecorder = new MediaRecorder(); + try { + /* ②setAudioSource/setVideoSource */ + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风 + /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + /* + * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 + * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) + */ + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + + filePath = FileUtils.getOutputAudioFile().toString(); + /* ③准备 */ + mMediaRecorder.setOutputFile(filePath); + mMediaRecorder.setMaxDuration(MAX_LENGTH); + mMediaRecorder.prepare(); + /* ④开始 */ + mMediaRecorder.start(); + // AudioRecord audioRecord. + /* 获取开始时间* */ + startTime = System.currentTimeMillis(); + updateMicStatus(); + } catch (IllegalStateException | IOException e) { + Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); + } + } + + /** + * 停止录音 + */ + public void stopRecordAudio() { + if (mMediaRecorder == null) + return; + long endTime = System.currentTimeMillis(); + try { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + audioStatusUpdateListener.onStop(filePath); + filePath = ""; + } catch (RuntimeException e) { + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + File file = new File(filePath); + if (file.exists()) + file.delete(); + filePath = ""; + } + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener statusUpdateListener) { + this.audioStatusUpdateListener = statusUpdateListener; + } + + /** + * 更新麦克状态 + */ + private void updateMicStatus() { + if (mMediaRecorder != null) { + double ratio = mMediaRecorder.getMaxAmplitude(); + double db;// 分贝 + if (ratio > 1) { + db = 20 * Math.log10(ratio); + if (null != audioStatusUpdateListener) { + audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime); + } + } + mHandler.postDelayed(mUpdateMicStatusTimer, 100); + } + } + + private Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = this::updateMicStatus; + + public interface OnAudioStatusUpdateListener { + /** + * 录音中... + * + * @param db 当前声音分贝 + * @param time 录音时长 + */ + void onUpdate(double db, long time); + + /** + * 停止录音 + * + * @param filePath 保存路径 + */ + void onStop(String filePath); + } +} diff --git a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java index 61aa9ec..fa6b3be 100644 --- a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java +++ b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java @@ -15,6 +15,14 @@ private static final SimpleDateFormat allDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + private static final SimpleDateFormat millsFormat = new SimpleDateFormat("mm:ss"); + + /** + * 时间戳转时间 + */ + public static String millsToTime(long millSeconds) { + return millsFormat.format(new Date(millSeconds)); + } /** * 时间戳转日期 diff --git a/app/src/main/res/anim/popup_hide.xml b/app/src/main/res/anim/popup_hide.xml new file mode 100644 index 0000000..8a98f85 --- /dev/null +++ b/app/src/main/res/anim/popup_hide.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/popup_show.xml b/app/src/main/res/anim/popup_show.xml new file mode 100644 index 0000000..5cb9603 --- /dev/null +++ b/app/src/main/res/anim/popup_show.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_recode.xml b/app/src/main/res/drawable/ic_recode.xml new file mode 100644 index 0000000..49697d8 --- /dev/null +++ b/app/src/main/res/drawable/ic_recode.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_recoding.xml b/app/src/main/res/drawable/ic_recoding.xml new file mode 100644 index 0000000..4f819bf --- /dev/null +++ b/app/src/main/res/drawable/ic_recoding.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/layer_microphone.xml b/app/src/main/res/drawable/layer_microphone.xml new file mode 100644 index 0000000..dceaea2 --- /dev/null +++ b/app/src/main/res/drawable/layer_microphone.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_case_upload.xml b/app/src/main/res/layout/activity_case_upload.xml index 1b94cc1..79f5967 100644 --- a/app/src/main/res/layout/activity_case_upload.xml +++ b/app/src/main/res/layout/activity_case_upload.xml @@ -1,6 +1,7 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java index 59b6341..1445603 100644 --- a/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java +++ b/app/src/main/java/com/casic/dcms/ui/CaseUploadActivity.java @@ -7,14 +7,16 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.EditText; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.TextView; import androidx.annotation.NonNull; @@ -40,6 +42,7 @@ import com.casic.dcms.mvp.view.ICaseSmallClassView; import com.casic.dcms.mvp.view.ICaseSubmitCaseView; import com.casic.dcms.mvp.view.IUploadFileView; +import com.casic.dcms.utils.AudioRecodeHelper; import com.casic.dcms.utils.ColorHelper; import com.casic.dcms.utils.Constant; import com.casic.dcms.utils.FileUtils; @@ -47,12 +50,14 @@ import com.casic.dcms.utils.ItemDecorationSpace; import com.casic.dcms.utils.OtherUtils; import com.casic.dcms.utils.StringHelper; +import com.casic.dcms.utils.TimeOrDateUtil; import com.casic.dcms.utils.ToastHelper; import com.iceteck.silicompressorr.SiliCompressor; import com.luck.picture.lib.PictureSelector; import com.luck.picture.lib.config.PictureConfig; import com.luck.picture.lib.config.PictureMimeType; import com.luck.picture.lib.entity.LocalMedia; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; import com.qmuiteam.qmui.widget.dialog.QMUIDialog; import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; @@ -73,6 +78,8 @@ ICaseLargeClassView, ICaseSmallClassView, ICaseSubmitCaseView, IUploadFileView { private Context context = this; + @BindView(R.id.caseLayout) + LinearLayout caseLayout; @BindView(R.id.caseClassView) TextView caseClassView; @BindView(R.id.caseLargeClassView) @@ -104,8 +111,6 @@ @BindView(R.id.submitButton) QMUIRoundButton submitButton; - private MediaRecorder mediaRecorder; - private File outputFile; private CaseLargeClassPresenterImpl caseLargeClassPresenter; private CaseSmallClassPresenterImpl caseSmallClassPresenter; private CaseSubmitPresenterImpl caseSubmitPresenter; @@ -125,6 +130,8 @@ private String gridId; private UploadVideoPresenterImpl uploadVideoPresenter; private String audioUrl, videoUrl; + private AudioRecodeHelper audioRecodeHelper; + private PopupWindow popWindow; @Override public int initLayoutView() { @@ -138,7 +145,6 @@ @Override public void initData() { - initMediaRecorder(); largeClassBeans = new ArrayList<>(); smallClassBeans = new ArrayList<>(); caseLargeClassPresenter = new CaseLargeClassPresenterImpl(this); @@ -159,26 +165,28 @@ selectedResultView.setLayoutManager(new GridLayoutManager(this, 2)); selectedResultView.addItemDecoration(new ItemDecorationSpace()); selectedResultView.setAdapter(imageAdapter); - } - /** - * 初始化MediaRecorder - */ - private void initMediaRecorder() { - mediaRecorder = new MediaRecorder(); - mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//麦克风 - mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); - mediaRecorder.setAudioSamplingRate(44100); - //设置编码 - mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); - mediaRecorder.setAudioEncodingBitRate(96000); - outputFile = FileUtils.getOutputAudioFile(); - mediaRecorder.setOutputFile(outputFile.getAbsolutePath()); - try { - mediaRecorder.prepare(); - } catch (IllegalStateException | IOException e) { - releaseMediaRecorder(); - } + //录音相关 + View view = View.inflate(this, R.layout.popu_microphone, null); + int popWidth = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.35); + int popHeight = (int) (QMUIDisplayHelper.getScreenWidth(this) * 0.30); + popWindow = new PopupWindow(view, popWidth, popHeight, true); + popWindow.setAnimationStyle(R.style.PopupAnimation); + ImageView recodeImageView = view.findViewById(R.id.recodeImageView); + TextView recodeTextView = view.findViewById(R.id.recodeTextView); + audioRecodeHelper = new AudioRecodeHelper(); + audioRecodeHelper.setOnAudioStatusUpdateListener(new AudioRecodeHelper.OnAudioStatusUpdateListener() { + @Override + public void onUpdate(double db, long time) { + recodeImageView.getDrawable().setLevel((int) (3000 + 6000 * db / 100)); + recodeTextView.setText(TimeOrDateUtil.millsToTime(time)); + } + + @Override + public void onStop(String filePath) { + uploadAudioPresenter.onReadyRetrofitRequest(new File(filePath)); + } + }); } @SuppressLint("ClickableViewAccessibility") @@ -428,37 +436,19 @@ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - //按下动画 voiceView.animate().scaleX(0.75f).scaleY(0.75f).setDuration(100).start(); - if (mediaRecorder != null) { - mediaRecorder.start(); - playAudioView.setText("正在录音..."); - } + popWindow.showAtLocation(caseLayout, Gravity.CENTER, 0, 0); + audioRecodeHelper.startRecordAudio(); break; case MotionEvent.ACTION_UP: - releaseMediaRecorder(); - if (outputFile.exists()) { - String audioPath = outputFile.getAbsolutePath(); - uploadAudioPresenter.onReadyRetrofitRequest(new File(audioPath)); - } else { - playAudioView.setText("录音上传失败"); - playAudioView.setTextColor(Color.RED); - } - //松开动画 voiceView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(100).start(); + audioRecodeHelper.stopRecordAudio();//结束录音(保存录音文件) + popWindow.dismiss(); break; } return true; } - private void releaseMediaRecorder() { - if (mediaRecorder != null) { - mediaRecorder.reset(); - mediaRecorder.release(); - mediaRecorder = null; - } - } - @Override public void obtainLargeClassData(CaseLargeClassBean largeClassBean) { // Log.d(TAG, "obtainLargeClassData: " + gson.toJson(largeClassBean)); diff --git a/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java new file mode 100644 index 0000000..3213591 --- /dev/null +++ b/app/src/main/java/com/casic/dcms/utils/AudioRecodeHelper.java @@ -0,0 +1,121 @@ +package com.casic.dcms.utils; + +import android.media.MediaRecorder; +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.io.IOException; + +public class AudioRecodeHelper { + + private static final String TAG = "AudioRecodeHelper"; + private static final int MAX_LENGTH = 1000 * 60 * 5;// 最大录音时长5分钟; + private MediaRecorder mMediaRecorder; + private String filePath; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private long startTime; + + /** + * 开始录音 使用m4a格式 + * + * @return + */ + public void startRecordAudio() { + if (mMediaRecorder == null) + mMediaRecorder = new MediaRecorder(); + try { + /* ②setAudioSource/setVideoSource */ + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风 + /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */ + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + /* + * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式 + * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB) + */ + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + + filePath = FileUtils.getOutputAudioFile().toString(); + /* ③准备 */ + mMediaRecorder.setOutputFile(filePath); + mMediaRecorder.setMaxDuration(MAX_LENGTH); + mMediaRecorder.prepare(); + /* ④开始 */ + mMediaRecorder.start(); + // AudioRecord audioRecord. + /* 获取开始时间* */ + startTime = System.currentTimeMillis(); + updateMicStatus(); + } catch (IllegalStateException | IOException e) { + Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage()); + } + } + + /** + * 停止录音 + */ + public void stopRecordAudio() { + if (mMediaRecorder == null) + return; + long endTime = System.currentTimeMillis(); + try { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + audioStatusUpdateListener.onStop(filePath); + filePath = ""; + } catch (RuntimeException e) { + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + File file = new File(filePath); + if (file.exists()) + file.delete(); + filePath = ""; + } + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener statusUpdateListener) { + this.audioStatusUpdateListener = statusUpdateListener; + } + + /** + * 更新麦克状态 + */ + private void updateMicStatus() { + if (mMediaRecorder != null) { + double ratio = mMediaRecorder.getMaxAmplitude(); + double db;// 分贝 + if (ratio > 1) { + db = 20 * Math.log10(ratio); + if (null != audioStatusUpdateListener) { + audioStatusUpdateListener.onUpdate(db, System.currentTimeMillis() - startTime); + } + } + mHandler.postDelayed(mUpdateMicStatusTimer, 100); + } + } + + private Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = this::updateMicStatus; + + public interface OnAudioStatusUpdateListener { + /** + * 录音中... + * + * @param db 当前声音分贝 + * @param time 录音时长 + */ + void onUpdate(double db, long time); + + /** + * 停止录音 + * + * @param filePath 保存路径 + */ + void onStop(String filePath); + } +} diff --git a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java index 61aa9ec..fa6b3be 100644 --- a/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java +++ b/app/src/main/java/com/casic/dcms/utils/TimeOrDateUtil.java @@ -15,6 +15,14 @@ private static final SimpleDateFormat allDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + private static final SimpleDateFormat millsFormat = new SimpleDateFormat("mm:ss"); + + /** + * 时间戳转时间 + */ + public static String millsToTime(long millSeconds) { + return millsFormat.format(new Date(millSeconds)); + } /** * 时间戳转日期 diff --git a/app/src/main/res/anim/popup_hide.xml b/app/src/main/res/anim/popup_hide.xml new file mode 100644 index 0000000..8a98f85 --- /dev/null +++ b/app/src/main/res/anim/popup_hide.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/popup_show.xml b/app/src/main/res/anim/popup_show.xml new file mode 100644 index 0000000..5cb9603 --- /dev/null +++ b/app/src/main/res/anim/popup_show.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_recode.xml b/app/src/main/res/drawable/ic_recode.xml new file mode 100644 index 0000000..49697d8 --- /dev/null +++ b/app/src/main/res/drawable/ic_recode.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_recoding.xml b/app/src/main/res/drawable/ic_recoding.xml new file mode 100644 index 0000000..4f819bf --- /dev/null +++ b/app/src/main/res/drawable/ic_recoding.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/layer_microphone.xml b/app/src/main/res/drawable/layer_microphone.xml new file mode 100644 index 0000000..dceaea2 --- /dev/null +++ b/app/src/main/res/drawable/layer_microphone.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_case_upload.xml b/app/src/main/res/layout/activity_case_upload.xml index 1b94cc1..79f5967 100644 --- a/app/src/main/res/layout/activity_case_upload.xml +++ b/app/src/main/res/layout/activity_case_upload.xml @@ -1,6 +1,7 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 02181b6..dee8e2e 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -49,4 +49,11 @@ stateAlwaysHidden + +