LuckSiege / PictureSelector

Picture Selector Library for Android or 图片选择器
Apache License 2.0
13.23k stars 3k forks source link

获取不到录音机录音的文件 #2847

Open badboy-tian opened 6 months ago

badboy-tian commented 6 months ago

Current use version?

当前使用的版本是多少?

3.11.2

Will this problem occur in demo?

Demo能否复现这问题?

Describe the problem or provide an error log?

描述问题或提供错误log?

没有获取语音列表....demo里面没.  

 val model = openGallery(SelectMimeType.ofAudio())
    model.setSelectionMode(if (isSingle) SelectModeConfig.SINGLE else SelectModeConfig.MULTIPLE)
        .setMaxSelectNum(maxSelectNum)
        .setMaxVideoSelectNum(maxSelectNum)
        .setImageEngine(MyGlideEngine.create())
        .setVideoPlayerEngine(MyExoPlayerEngine())
        .setSandboxFileEngine(MyUriToFileTransformEngine())
        .setRecordAudioInterceptListener { fragment, requestCode ->
            val recordAudioIntent = Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
            if (recordAudioIntent.resolveActivity(fragment!!.requireActivity().packageManager) != null) {
                fragment.startActivityForResult(recordAudioIntent, requestCode)
            } else {
                IAPP.getApp().toastError("没有发现录音APP")
            }
        }

然后发现没有列出系统录音机文件列表, 用系统语音录音后 也没有显示

adongs commented 6 months ago

我使用自己编写的录音方案,用于记录

1.创建AudioViewModel.kt

`package com.yb.app.webview.ui.audio

import android.media.MediaRecorder import android.net.Uri import android.os.Build import android.os.Environment import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.yb.app.webview.App import com.yb.app.webview.R import com.yb.app.webview.expand.R_string import io.reactivex.Observable import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import java.io.File import java.util.concurrent.TimeUnit

class AudioViewModel : ViewModel() {

companion object {
    private const val DIRECTORY_MOVIES = "Recordings"
}

val timerText = MutableLiveData(R.string.view_audio_time_default.R_string())

private var disposable: Disposable? = null

private var startNumber: Long = 0L

private var mediaRecorder: MediaRecorder? = null

private val folder = File(Environment.getExternalStorageDirectory(), DIRECTORY_MOVIES).apply {
    if (!exists()) {
        mkdirs()
    }
}

private var outputFile: File? = null

/**
 * 开始或暂停录音
 * @return true 开始录音  false 暂停录音
 */
fun startOrPause(): Boolean {
    val startOrPauseTimer = startOrPauseTimer()
    if (startOrPauseTimer) {
        startRecording()
    } else {
        pauseRecording()
    }
    return startOrPauseTimer
}

/**
 * 重置
 */
fun reset() {
    resetTimer()
    resetRecording()
}

/**
 * 停止录音
 */
fun stop(): Uri {
    resetTimer()
    return stopRecording()
}

/**
 * 开始或者暂停计时器
 */
private fun startOrPauseTimer(): Boolean {
    return if (disposable == null) {
        disposable = Observable.intervalRange(
            startNumber,
            Long.MAX_VALUE - startNumber,
            0L,
            1L,
            TimeUnit.MILLISECONDS
        )
            .subscribeOn(Schedulers.io())
            .subscribe {
                startNumber = it
                val milliseconds = "%02d".format(it/10  % 100)
                val seconds = "%02d".format(it / 1000 % 60)
                val minute = "%02d".format((it / 1000 / 60) % 60)
                val hour = "%02d".format(it / 1000 / 3600)
                timerText.postValue("$hour:$minute:$seconds:$milliseconds")
            }
        true
    } else {
        disposable?.let {
            if (!it.isDisposed) {
                it.dispose()
                disposable = null
            }
        }
        false
    }
}

/**
 * 重置计时器
 */
private fun resetTimer() {
    disposable?.let {
        if (!it.isDisposed) {
            it.dispose()
            disposable = null
        }
    }
    startNumber = 0L
    timerText.postValue(R.string.view_audio_time_default.R_string())
}

/**
 * 开始录音
 */
private fun startRecording() {
    if (mediaRecorder == null) {
        mediaRecorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            MediaRecorder(App.getApplication())
        } else {
            MediaRecorder()
        }
        mediaRecorder?.let {
            outputFile =
                File(folder.absoluteFile, "灵云-${System.currentTimeMillis()}.wav").apply {
                    if (!exists()) {
                        createNewFile()
                    }
                }
            it.setAudioSource(MediaRecorder.AudioSource.MIC)
            it.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
            it.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
            it.setOutputFile(outputFile)
            it.prepare()
            it.start()
        }
    } else {
        mediaRecorder?.resume()
    }
}

/**
 * 暂停录音
 */
private fun pauseRecording() {
    mediaRecorder?.pause()
}

/**
 * 停止录音
 */
private fun stopRecording(): Uri {
    mediaRecorder?.let {
        it.stop()
        it.reset()
        it.release()
    }
    mediaRecorder = null
    val uri = Uri.parse(outputFile!!.absolutePath)
    outputFile = null
    return uri
}

/**
 * 重置录音
 */
private fun resetRecording() {
    mediaRecorder?.let {
        it.stop()
        it.reset()
        it.release()
    }
    mediaRecorder = null
    outputFile?.delete()
    outputFile = null
}

} 2.创建AudioViewModelFactory.kt文件 import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider

class AudioViewModelFactory : ViewModelProvider.Factory {

@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
    if (modelClass.isAssignableFrom(AudioViewModel::class.java)) {
        return AudioViewModel() as T
    }
    throw IllegalArgumentException("Unknown ViewModel class")
}

} `

3.创建AudioActivity.kt

` package com.yb.app.webview.ui.audio

import android.app.Activity import android.content.Intent import android.os.Bundle import android.provider.MediaStore import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider import com.yb.app.webview.R import com.yb.app.webview.databinding.ActivityAudioBinding

class AudioActivity : AppCompatActivity() {

private lateinit var binding:ActivityAudioBinding
private lateinit var audioViewModel:AudioViewModel
private val backPressedCallback: OnBackPressedCallback =  object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
        setResult(Activity.RESULT_CANCELED)
        finish()
    }
}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityAudioBinding.inflate(layoutInflater)
    setContentView(binding.root)
    binding.lifecycleOwner = this
    audioViewModel = ViewModelProvider(this,AudioViewModelFactory())[AudioViewModel::class.java]
    binding.audioData = audioViewModel
    binding.recording.setOnClickListener {
        val startOrStopTimer = audioViewModel.startOrPause()
        if(startOrStopTimer){
            it.setBackgroundResource(R.drawable.ic_audio_stop_recording)
        }else{
            it.setBackgroundResource(R.drawable.ic_audio_recording)
        }
    }
    binding.reset.setOnClickListener {
        audioViewModel.reset()
        binding.recording.setBackgroundResource(R.drawable.ic_audio_recording)
    }
    binding.confirm.setOnClickListener {
        val uri = audioViewModel.stop()
        binding.recording.setBackgroundResource(R.drawable.ic_audio_recording)
        val resultIntent = Intent()
        resultIntent.data = uri
        resultIntent.putExtra(MediaStore.EXTRA_OUTPUT,uri)
        setResult(Activity.RESULT_OK, resultIntent)
        finish()
    }
   onBackPressedDispatcher.addCallback(this, backPressedCallback)
}

} ` 4.创建activity_audio.xml文件和AudioActivity对应

`

<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools">

<data>
    <variable
        name="audioData"
        type="com.yb.app.webview.ui.audio.AudioViewModel" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.audio.AudioActivity">

    <TextView
        android:id="@+id/timer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{audioData.timerText}"
        android:textSize="40sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.4" />

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="@color/black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHeight_percent="0.3"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1">

        <ImageButton
            android:id="@+id/reset"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:background="@drawable/ic_audio_reset"
            android:contentDescription="@string/view_audio_reset"
            android:text="@string/view_audio_reset"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/recording"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.5" />

        <ImageButton
            android:id="@+id/recording"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:background="@drawable/ic_audio_recording"
            android:contentDescription="@string/view_audio_recording"
            android:text="@string/view_audio_recording"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/reset"
            app:layout_constraintRight_toLeftOf="@+id/confirm"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.5" />

        <ImageButton
            android:id="@+id/confirm"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:background="@drawable/ic_audio_confirm"
            android:contentDescription="@string/view_audio_recording"
            android:text="@string/view_audio_confirm"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/recording"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.5" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

` 6.在你的values-> strings.xml中加入

`

00:00:00:00
<string name="view_audio_reset">重新录音</string>
<string name="view_audio_recording">录音</string>
<string name="view_audio_confirm">确认</string>

`

5.在AndroidManifest.xml文件中的application标签中写入上面创建的Activity

<activity android:name=".ui.audio.AudioActivity" android:exported="false" />

6.写完上述后你会发现缺少如下文件,这些都是图标,你自己去找相应的图标代替即可 (ic_audio_confirm.xml 确认图标 ic_audio_recording.xml 录音图标 ic_audio_reset.xml 重新录音 ic_audio_stop_recording.xml 停止录音)

image

7.在你的setRecordAudioInterceptListener 监听中如下调用

fragment.startActivityForResult(Intent(fragment.requireActivity(), AudioActivity::class.java),requestCode)

8.完整调用如下

image

9.录音界面

image