Skip to content

Commit 0c8fce1

Browse files
fix(*): 解决无法播放音频的问题
1 parent 133e70b commit 0c8fce1

File tree

4 files changed

+189
-89
lines changed

4 files changed

+189
-89
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
34
package="com.pengxh.kt.lib">
45

56
<uses-permission android:name="android.permission.RECORD_AUDIO" />
7+
<!-- Android 10以下 -->
8+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
9+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
10+
<!-- Android 11 ~ Android 12 -->
11+
<uses-permission
12+
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
13+
tools:ignore="ScopedStorage" />
14+
<!-- Android 13+ -->
15+
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
616

717
<application
818
android:allowBackup="true"
Lines changed: 94 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,55 @@
11
package com.pengxh.kt.lib
22

33
import android.Manifest
4+
import android.annotation.SuppressLint
5+
import android.os.Build
46
import android.os.Bundle
7+
import android.view.Gravity
8+
import android.view.MotionEvent
9+
import android.widget.ImageView
10+
import android.widget.PopupWindow
11+
import android.widget.TextView
512
import com.pengxh.kt.lib.databinding.ActivityMainBinding
613
import com.pengxh.kt.lite.base.KotlinBaseActivity
7-
import com.pengxh.kt.lite.widget.SteeringWheelView
14+
import com.pengxh.kt.lite.extensions.createAudioFile
15+
import com.pengxh.kt.lite.extensions.millsToTime
16+
import com.pengxh.kt.lite.widget.audio.AudioPopupWindow
17+
import com.pengxh.kt.lite.widget.audio.AudioRecordHub
818
import pub.devrel.easypermissions.EasyPermissions
19+
import java.io.File
920

1021
class MainActivity : KotlinBaseActivity<ActivityMainBinding>(),
1122
EasyPermissions.PermissionCallbacks {
1223

1324
private val kTag = "MainActivity"
1425
private val context = this@MainActivity
1526
private val permissionCode = 20231211
16-
private val userPermissions = arrayOf(
17-
Manifest.permission.RECORD_AUDIO
18-
)
27+
private val userPermissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
28+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
29+
arrayOf(
30+
Manifest.permission.RECORD_AUDIO,
31+
Manifest.permission.WRITE_EXTERNAL_STORAGE,
32+
Manifest.permission.READ_EXTERNAL_STORAGE,
33+
Manifest.permission.MANAGE_EXTERNAL_STORAGE,
34+
Manifest.permission.READ_MEDIA_AUDIO
35+
)
36+
} else {
37+
arrayOf(
38+
Manifest.permission.RECORD_AUDIO,
39+
Manifest.permission.WRITE_EXTERNAL_STORAGE,
40+
Manifest.permission.READ_EXTERNAL_STORAGE,
41+
Manifest.permission.MANAGE_EXTERNAL_STORAGE
42+
)
43+
}
44+
} else {
45+
arrayOf(
46+
Manifest.permission.RECORD_AUDIO,
47+
Manifest.permission.WRITE_EXTERNAL_STORAGE,
48+
Manifest.permission.READ_EXTERNAL_STORAGE
49+
)
50+
}
51+
52+
private var recordHub: AudioRecordHub? = null
1953

2054
override fun initViewBinding(): ActivityMainBinding {
2155
return ActivityMainBinding.inflate(layoutInflater)
@@ -26,34 +60,10 @@ class MainActivity : KotlinBaseActivity<ActivityMainBinding>(),
2660

2761
}
2862

63+
@SuppressLint("ClickableViewAccessibility")
2964
override fun initOnCreate(savedInstanceState: Bundle?) {
3065
if (EasyPermissions.hasPermissions(this, *userPermissions)) {
31-
binding.steeringWheelView.setOnWheelTouchListener(object :
32-
SteeringWheelView.OnWheelTouchListener {
33-
override fun onCenterClicked() {
34-
35-
}
36-
37-
override fun onLeftTurn() {
38-
39-
}
40-
41-
override fun onTopTurn() {
42-
43-
}
44-
45-
override fun onRightTurn() {
46-
47-
}
48-
49-
override fun onBottomTurn() {
50-
51-
}
52-
53-
override fun onActionTurnUp(dir: SteeringWheelView.Direction) {
54-
55-
}
56-
})
66+
initAudioRecord()
5767
} else {
5868
EasyPermissions.requestPermissions(
5969
this@MainActivity,
@@ -64,6 +74,54 @@ class MainActivity : KotlinBaseActivity<ActivityMainBinding>(),
6474
}
6575
}
6676

77+
@SuppressLint("ClickableViewAccessibility")
78+
private fun initAudioRecord() {
79+
recordHub = AudioRecordHub.get(this)
80+
81+
AudioPopupWindow.get(this).create(object : AudioPopupWindow.IAudioPopupCallback {
82+
override fun onViewCreated(
83+
window: PopupWindow, imageView: ImageView, textView: TextView
84+
) {
85+
binding.recodeAudioButton.setOnTouchListener { v, event ->
86+
when (event.action) {
87+
MotionEvent.ACTION_DOWN -> {
88+
binding.recodeAudioButton.animate()
89+
.scaleX(0.75f).scaleY(0.75f)
90+
.setDuration(100).start()
91+
window.showAtLocation(binding.rootView, Gravity.CENTER, 0, 0)
92+
recordHub?.apply {
93+
initHub(createAudioFile())
94+
startRecord(object : AudioRecordHub.OnAudioStatusUpdateListener {
95+
override fun onUpdate(db: Double, time: Long) {
96+
imageView.drawable.level = (3000 + 6000 * db / 100).toInt()
97+
textView.text = time.millsToTime()
98+
}
99+
100+
override fun onStop(file: File?) {
101+
if (file != null) {
102+
binding.audioPathView.text =
103+
"录音文件路径:\r\n${file.absolutePath}"
104+
binding.audioPlayView.setAudioSource(file)
105+
}
106+
}
107+
})
108+
}
109+
}
110+
111+
MotionEvent.ACTION_UP -> {
112+
recordHub?.stopRecord() //结束录音(保存录音文件)
113+
window.dismiss()
114+
binding.recodeAudioButton.animate()
115+
.scaleX(1.0f).scaleY(1.0f)
116+
.setDuration(100).start()
117+
}
118+
}
119+
true
120+
}
121+
}
122+
})
123+
}
124+
67125
override fun observeRequestState() {
68126

69127
}
@@ -81,10 +139,15 @@ class MainActivity : KotlinBaseActivity<ActivityMainBinding>(),
81139
}
82140

83141
override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
84-
142+
initAudioRecord()
85143
}
86144

87145
override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
88146

89147
}
148+
149+
override fun onDestroy() {
150+
super.onDestroy()
151+
binding.audioPlayView.stop()
152+
}
90153
}
Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,35 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3-
xmlns:app="http://schemas.android.com/apk/res-auto"
43
android:id="@+id/rootView"
54
android:layout_width="match_parent"
65
android:layout_height="match_parent">
76

8-
<com.pengxh.kt.lite.widget.SteeringWheelView
9-
android:id="@+id/steeringWheelView"
7+
<com.pengxh.kt.lite.widget.audio.AudioPlayerView
8+
android:id="@+id/audioPlayView"
9+
style="@style/AudioPlayerViewBaseStyle"
10+
android:layout_above="@id/audioPathView"
11+
android:layout_margin="@dimen/dp_10"
12+
android:background="@drawable/bg_audio_view"
13+
android:text="00:00"
14+
android:textColor="@color/white"
15+
android:textSize="16sp" />
16+
17+
<TextView
18+
android:id="@+id/audioPathView"
19+
android:layout_width="match_parent"
20+
android:layout_height="wrap_content"
21+
android:layout_above="@id/recodeAudioButton"
22+
android:padding="@dimen/dp_20"
23+
android:text="@string/app_name"
24+
android:textSize="16sp" />
25+
26+
<ImageButton
27+
android:id="@+id/recodeAudioButton"
1028
android:layout_width="wrap_content"
1129
android:layout_height="wrap_content"
12-
android:layout_centerInParent="true"
13-
android:background="#F7F7F7"
14-
android:padding="@dimen/dp_5"
15-
app:ctrl_backgroundColor="@color/white"
16-
app:ctrl_borderColor="#64BFEB"
17-
app:ctrl_borderStroke="@dimen/dp_5"
18-
app:ctrl_diameter="@dimen/dp_200" />
30+
android:layout_alignParentBottom="true"
31+
android:layout_centerHorizontal="true"
32+
android:layout_margin="@dimen/dp_10"
33+
android:padding="@dimen/dp_20"
34+
android:src="@drawable/ic_voice" />
1935
</RelativeLayout>

lite/src/main/java/com/pengxh/kt/lite/widget/audio/AudioPlayerView.kt

Lines changed: 59 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,33 @@
11
package com.pengxh.kt.lite.widget.audio
22

33
import android.content.Context
4-
import android.media.AudioAttributes
54
import android.media.MediaPlayer
65
import android.os.Handler
76
import android.os.Message
87
import android.util.AttributeSet
8+
import android.view.View
99
import androidx.annotation.DrawableRes
1010
import androidx.appcompat.widget.AppCompatTextView
1111
import androidx.core.content.res.ResourcesCompat
1212
import com.pengxh.kt.lite.utils.Constant
1313
import com.pengxh.kt.lite.utils.WeakReferenceHandler
14+
import java.io.File
15+
import java.io.FileInputStream
16+
import java.io.IOException
1417

1518
class AudioPlayerView(context: Context, attrs: AttributeSet) : AppCompatTextView(context, attrs),
16-
Handler.Callback {
19+
Handler.Callback, View.OnClickListener {
1720

1821
private val kTag = "AudioPlayerView"
19-
private val mediaPlayer by lazy { MediaPlayer() }
20-
private val audioAttributes = AudioAttributes.Builder()
21-
.setUsage(AudioAttributes.USAGE_UNKNOWN)
22-
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
23-
.build()
24-
25-
/**
26-
* 在非初始化状态下调用setDataSource 会抛出IllegalStateException异常
27-
*/
22+
private lateinit var audioSource: File
23+
private var mediaPlayer: MediaPlayer? = null
2824
private var hasPrepared = false
2925
private var index = 0
3026
private var weakReferenceHandler: WeakReferenceHandler
3127
private var animationRunnable: Runnable
3228

3329
init {
34-
mediaPlayer.setAudioAttributes(audioAttributes)
35-
mediaPlayer.setOnPreparedListener {
36-
hasPrepared = true
37-
text = audioDuration
38-
}
39-
mediaPlayer.setOnErrorListener { mp, _, _ ->
40-
mp.reset()
41-
false
42-
}
43-
mediaPlayer.setOnCompletionListener { stopAnimation() }
44-
45-
setOnClickListener {
46-
if (mediaPlayer.isPlaying) {
47-
mediaPlayer.pause()
48-
stopAnimation()
49-
} else {
50-
mediaPlayer.seekTo(0)
51-
startAnimation()
52-
mediaPlayer.start()
53-
}
54-
}
30+
setOnClickListener(this)
5531

5632
weakReferenceHandler = WeakReferenceHandler(this)
5733
animationRunnable = object : Runnable {
@@ -63,22 +39,54 @@ class AudioPlayerView(context: Context, attrs: AttributeSet) : AppCompatTextView
6339
}
6440
}
6541

66-
private val audioDuration: String
67-
get() {
68-
val duration = mediaPlayer.duration
69-
return if (duration == -1) {
70-
""
71-
} else {
72-
val sec = duration / 1000
73-
val m = sec / 60
74-
val s = sec % 60
75-
"$m:$s"
42+
fun setAudioSource(file: File) {
43+
audioSource = file
44+
initMediaPlayer()
45+
}
46+
47+
private fun initMediaPlayer() {
48+
mediaPlayer = MediaPlayer()
49+
mediaPlayer?.apply {
50+
val inputStream = FileInputStream(audioSource)
51+
setDataSource(inputStream.fd)
52+
try {
53+
prepare()
54+
} catch (e: IOException) {
55+
e.printStackTrace()
56+
}
57+
58+
setOnPreparedListener {
59+
hasPrepared = true
60+
61+
text = if (duration == -1) {
62+
""
63+
} else {
64+
val sec = duration / 1000
65+
val m = sec / 60
66+
val s = sec % 60
67+
"$m:$s"
68+
}
69+
}
70+
71+
setOnCompletionListener {
72+
stopAnimation()
7673
}
7774
}
75+
}
7876

79-
fun setAudioSource(audioSource: String) {
80-
mediaPlayer.setDataSource(audioSource)
81-
mediaPlayer.prepare()
77+
override fun onClick(v: View?) {
78+
mediaPlayer?.apply {
79+
if (isPlaying) {
80+
stop()
81+
release()
82+
stopAnimation()
83+
mediaPlayer = null
84+
} else {
85+
initMediaPlayer()
86+
start()
87+
startAnimation()
88+
}
89+
}
8290
}
8391

8492
private fun startAnimation() {
@@ -98,10 +106,13 @@ class AudioPlayerView(context: Context, attrs: AttributeSet) : AppCompatTextView
98106
setCompoundDrawables(drawable, null, null, null)
99107
}
100108

101-
fun release() {
102-
mediaPlayer.stop()
103-
mediaPlayer.release()
109+
fun stop() {
110+
mediaPlayer?.apply {
111+
stop()
112+
release()
113+
}
104114
weakReferenceHandler.removeCallbacks(animationRunnable)
115+
mediaPlayer = null
105116
}
106117

107118
override fun handleMessage(msg: Message): Boolean {

0 commit comments

Comments
 (0)