|
|
@@ -1,32 +1,242 @@
|
|
|
package com.adealink.weparty.profile.edit.dialog.fragment
|
|
|
|
|
|
+import android.Manifest
|
|
|
+import android.annotation.SuppressLint
|
|
|
+import androidx.fragment.app.FragmentActivity
|
|
|
+import androidx.lifecycle.lifecycleScope
|
|
|
+import com.adealink.frame.aab.util.getCompatString
|
|
|
+import com.adealink.frame.ext.isViewBindingValid
|
|
|
+import com.adealink.frame.log.Log
|
|
|
import com.adealink.frame.mvvm.view.viewBinding
|
|
|
import com.adealink.frame.util.onClick
|
|
|
+import com.adealink.frame.util.runOnUiThread
|
|
|
import com.adealink.weparty.commonui.BaseFragment
|
|
|
+import com.adealink.weparty.commonui.ext.gone
|
|
|
+import com.adealink.weparty.commonui.ext.show
|
|
|
+import com.adealink.weparty.commonui.toast.util.debugToast
|
|
|
+import com.adealink.weparty.commonui.toast.util.showToast
|
|
|
+import com.adealink.weparty.permission.PermissionUtils
|
|
|
import com.adealink.weparty.profile.R
|
|
|
import com.adealink.weparty.profile.databinding.FragmentTalentVoiceEditBinding
|
|
|
import com.adealink.weparty.profile.edit.dialog.EditTalentVoiceDialog
|
|
|
import com.adealink.weparty.profile.edit.dialog.viewmodel.EditVoiceViewModel
|
|
|
+import com.adealink.weparty.util.formatMissTime
|
|
|
import com.adealink.weparty.viewmodel.parentFragmentViewModels
|
|
|
+import com.tencent.qcloud.tuicore.TUIConstants.TUICalling
|
|
|
+import com.tencent.qcloud.tuikit.tuichat.component.audio.AudioPlayer
|
|
|
+import com.tencent.qcloud.tuikit.tuichat.component.audio.AudioRecorder
|
|
|
+import com.tencent.qcloud.tuikit.tuichat.component.audio.AudioRecorder.AudioRecorderCallback
|
|
|
+import kotlinx.coroutines.launch
|
|
|
+import kotlinx.coroutines.suspendCancellableCoroutine
|
|
|
+import java.util.Timer
|
|
|
+import java.util.TimerTask
|
|
|
+import kotlin.coroutines.resume
|
|
|
+import com.adealink.weparty.R as APP_R
|
|
|
|
|
|
class VoiceEditFragment : BaseFragment(R.layout.fragment_talent_voice_edit) {
|
|
|
|
|
|
+ companion object {
|
|
|
+ private const val TAG = "VoiceEditFragment"
|
|
|
+ }
|
|
|
+
|
|
|
private val binding by viewBinding(FragmentTalentVoiceEditBinding::bind)
|
|
|
|
|
|
- private val viewModel by parentFragmentViewModels<EditVoiceViewModel, EditTalentVoiceDialog>(
|
|
|
+ private val editViewModel by parentFragmentViewModels<EditVoiceViewModel, EditTalentVoiceDialog>(
|
|
|
EditTalentVoiceDialog::class.java
|
|
|
)
|
|
|
|
|
|
+ private var mTimer: Timer? = null
|
|
|
+ private var times = 0
|
|
|
+
|
|
|
+ private var isRecording: Boolean = false
|
|
|
+
|
|
|
override fun initViews() {
|
|
|
super.initViews()
|
|
|
- //binding.vSound.setSoundUrl()
|
|
|
binding.btnRecord.onClick {
|
|
|
+ clickRecord()
|
|
|
+ }
|
|
|
+ binding.btnReRecord.onClick {
|
|
|
+ clickReRecord()
|
|
|
+ }
|
|
|
+ binding.btnPreview.onClick {
|
|
|
+ audioPreview()
|
|
|
+ }
|
|
|
+ binding.btnSubmit.onClick {
|
|
|
+ debugToast("录音审核功能未实现,目前仅支持录音过程")
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ override fun observeViewModel() {
|
|
|
+ super.observeViewModel()
|
|
|
+ editViewModel.voiceDataLD.observeWithoutCache(viewLifecycleOwner) {
|
|
|
+ if (it.path.isNullOrEmpty()) {
|
|
|
+ binding.clRecord.show()
|
|
|
+ binding.clPreview.gone()
|
|
|
+ binding.btnSubmit.isEnabled = false
|
|
|
+ } else {
|
|
|
+ binding.clRecord.gone()
|
|
|
+ binding.clPreview.show()
|
|
|
+ binding.btnSubmit.isEnabled = true
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
override fun loadData() {
|
|
|
super.loadData()
|
|
|
+ isRecording = false
|
|
|
+ editViewModel.setVoiceData(null)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun clickRecord() {
|
|
|
+ if (!isRecording) {
|
|
|
+ //启动录制
|
|
|
+ startRecord()
|
|
|
+ } else {
|
|
|
+ stopRecord()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun clickReRecord() {
|
|
|
+ editViewModel.setVoiceData(null)
|
|
|
+ resetRecord()
|
|
|
+ startRecord()
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun audioPreview() {
|
|
|
+ val voicePath = editViewModel.voiceDataLD.value?.path
|
|
|
+ if (voicePath.isNullOrEmpty()) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ AudioPlayer.getInstance().stopPlay()
|
|
|
+ AudioPlayer.getInstance().startPlay(voicePath) { }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun startRecord() {
|
|
|
+ //录音前关闭音频播放
|
|
|
+ AudioPlayer.getInstance().stopPlay()
|
|
|
+
|
|
|
+ Log.d(TAG, "startAudioRecord")
|
|
|
+ AudioRecorder.startRecord(object : AudioRecorderCallback {
|
|
|
+ override fun onStarted() {
|
|
|
+ Log.d(TAG, "startAudioRecord, onStarted")
|
|
|
+ isRecording = true
|
|
|
+ if (mTimer == null) {
|
|
|
+ mTimer = Timer()
|
|
|
+ }
|
|
|
+ mTimer?.schedule(object : TimerTask() {
|
|
|
+ override fun run() {
|
|
|
+ runOnUiThread {
|
|
|
+ times++
|
|
|
+ if (isViewBindingValid()) {
|
|
|
+ binding.tvDuration.text = formatMissTime(times)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 0, 1000)
|
|
|
+ binding.tvDurationTips.text =
|
|
|
+ getCompatString(R.string.profile_edit_talent_voice_stop)
|
|
|
+ binding.btnRecord.setImageResource(R.drawable.profile_edit_talent_voice_stop_ic)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onFinished(outputPath: String?) {
|
|
|
+ Log.d(TAG, "startAudioRecord, onFinished:$outputPath")
|
|
|
+ resetRecord()
|
|
|
+ val duration = AudioRecorder.getDuration(outputPath)
|
|
|
+ if (duration < 3000) {
|
|
|
+ showToast(R.string.profile_record_voice_less_than_3_seconds)
|
|
|
+ editViewModel.setVoiceData(null)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ editViewModel.setVoiceData(outputPath)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onFailed(errorCode: Int, errorMessage: String?) {
|
|
|
+ Log.d(
|
|
|
+ TAG,
|
|
|
+ "startAudioRecord, onFailed, errorCode:$errorCode, errorMessage:$errorMessage"
|
|
|
+ )
|
|
|
+ resetRecord()
|
|
|
+ if (errorCode == TUICalling.ERROR_MIC_PERMISSION_REFUSED) {
|
|
|
+ //没有授予权限
|
|
|
+ requestPermission()
|
|
|
+ } else if (errorCode == AudioRecorder.ERROR_CODE_MIC_IS_BEING_USED || errorCode == TUICalling.ERROR_STATUS_IN_CALL) {
|
|
|
+ showToast(APP_R.string.common_mic_is_being_used_cant_record)
|
|
|
+ } else {
|
|
|
+ showToast(APP_R.string.common_record_audio_failed)
|
|
|
+ }
|
|
|
+ Log.e(
|
|
|
+ TAG,
|
|
|
+ "record audio failed, errorCode $errorCode, errorMessage $errorMessage"
|
|
|
+ )
|
|
|
+ binding.btnRecord.setImageResource(R.drawable.profile_edit_talent_voice_record_ic)
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onCanceled() {
|
|
|
+ Log.d(TAG, "startAudioRecord, onCanceled")
|
|
|
+ resetRecord()
|
|
|
+ binding.btnRecord.setImageResource(R.drawable.profile_edit_talent_voice_record_ic)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun requestPermission() {
|
|
|
+ val act = activity ?: return
|
|
|
+ viewLifecycleOwner.lifecycleScope.launch {
|
|
|
+ val granted = requestPermission(act)
|
|
|
+ if (granted) {
|
|
|
+ startRecord()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private val permissions = arrayOf(Manifest.permission.RECORD_AUDIO)
|
|
|
+ private suspend fun requestPermission(activity: FragmentActivity): Boolean {
|
|
|
+ return suspendCancellableCoroutine { coroutine ->
|
|
|
+ if (PermissionUtils.hasPermissions(activity, *permissions)) {
|
|
|
+ if (coroutine.isActive) {
|
|
|
+ coroutine.resume(true)
|
|
|
+ }
|
|
|
+ return@suspendCancellableCoroutine
|
|
|
+ }
|
|
|
+ PermissionUtils.requestPermissions(
|
|
|
+ activity,
|
|
|
+ permissions.toList(),
|
|
|
+ getCompatString(R.string.profile_record_voice_permission_request_tips),
|
|
|
+ onGranted = {
|
|
|
+ if (coroutine.isActive) {
|
|
|
+ coroutine.resume(true)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onDenied = {
|
|
|
+ if (coroutine.isActive) {
|
|
|
+ coroutine.resume(false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun stopRecord() {
|
|
|
+ AudioRecorder.stopRecord()
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressLint("SetTextI18n")
|
|
|
+ private fun resetRecord() {
|
|
|
+ isRecording = false
|
|
|
+ mTimer?.cancel()
|
|
|
+ mTimer = null
|
|
|
+ times = 0
|
|
|
+ if (isViewBindingValid()) {
|
|
|
+ binding.tvDuration.text = "00:00"
|
|
|
+ binding.tvDurationTips.text = getCompatString(R.string.profile_edit_talent_voice_record)
|
|
|
+ binding.btnRecord.setImageResource(R.drawable.profile_edit_talent_voice_record_ic)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onDestroyView() {
|
|
|
+ super.onDestroyView()
|
|
|
+ AudioPlayer.getInstance().stopPlay()
|
|
|
+ AudioRecorder.stopRecord()
|
|
|
}
|
|
|
|
|
|
}
|