Skip to content

Commit 49ad08a

Browse files
committed
fix: fix all leaking promises
chore: add ignore for vs cod plugin + comment annoying log fix: continue better promise handling chore: use err.toString() instead of err.message fix: more better handling of promises fix: forget to toString the duration fix: remove deprecation + clean code of logd fix: fix bad handling of onError and remove useless resolve/reject chore: better detection of leaking promise with reject fix: add wrong path case + fix rebase broken code fix: unify error code in preparePlayer chore: code simplification for player & player key usage and more chore: final code review
1 parent 6464e2d commit 49ad08a

File tree

9 files changed

+289
-283
lines changed

9 files changed

+289
-283
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,6 @@ buck-out/
6262

6363
# generated
6464
lib
65+
66+
# Local history vs code plugin
67+
.history/

android/src/main/java/com/audiowaveform/AudioPlayer.kt

Lines changed: 48 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,24 @@ package com.audiowaveform
22

33
import android.net.Uri
44
import android.os.CountDownTimer
5+
import androidx.media3.common.MediaItem
6+
import androidx.media3.common.PlaybackException
7+
import androidx.media3.common.Player
8+
import androidx.media3.exoplayer.ExoPlayer
59
import com.facebook.react.bridge.Arguments
610
import com.facebook.react.bridge.Promise
711
import com.facebook.react.bridge.ReactApplicationContext
812
import com.facebook.react.bridge.WritableMap
913
import com.facebook.react.common.JavascriptException
1014
import com.facebook.react.modules.core.DeviceEventManagerModule
11-
import androidx.media3.exoplayer.ExoPlayer
12-
import androidx.media3.common.MediaItem
13-
import androidx.media3.common.Player
1415

1516
class AudioPlayer(
1617
context: ReactApplicationContext,
1718
playerKey: String,
1819
) {
1920
private val appContext = context
2021
private lateinit var player: ExoPlayer
21-
private var playerListener: Player.Listener? = null
22+
private lateinit var playerListener: Player.Listener
2223
private var isPlayerPrepared: Boolean = false
2324
private var finishMode = FinishMode.Stop
2425
private val key = playerKey
@@ -49,8 +50,7 @@ class AudioPlayer(
4950
player.prepare()
5051
playerListener = object : Player.Listener {
5152

52-
@Deprecated("Deprecated in Java")
53-
override fun onPlayerStateChanged(isReady: Boolean, state: Int) {
53+
override fun onPlaybackStateChanged(state: Int) {
5454
if (!isPlayerPrepared) {
5555
if (state == Player.STATE_READY) {
5656
player.volume = (volume ?: 1).toFloat()
@@ -59,6 +59,10 @@ class AudioPlayer(
5959
val duration = player.duration
6060
promise.resolve(duration.toString())
6161
}
62+
else if (state == Player.STATE_IDLE) {
63+
// Fix leaking promise when path is incorrect
64+
promise.reject("preparePlayer-onPlayerStateChanged-error-idle", "Player stayed in idle state, unable to load $path")
65+
}
6266
}
6367
if (state == Player.STATE_ENDED) {
6468
val args: WritableMap = Arguments.createMap()
@@ -87,30 +91,27 @@ class AudioPlayer(
8791
}
8892
}
8993
}
94+
override fun onPlayerError(error: PlaybackException) {
95+
promise.reject("preparePlayer-onPlayerError", error.message)
96+
}
9097
}
91-
player.addListener(playerListener!!)
98+
99+
player.addListener(playerListener)
92100
} else {
93-
promise.reject("preparePlayer Error", "path to audio file or unique key can't be null")
101+
promise.reject("preparePlayer-error", "path to audio file or unique key can't be null")
94102
}
95103
}
96104

97-
fun seekToPosition(progress: Long?, promise: Promise) {
105+
fun seekToPosition(progress: Long?): Boolean {
98106
if (progress != null) {
99107
player.seekTo(progress)
100-
promise.resolve(true)
101-
} else {
102-
promise.resolve(false)
108+
return true;
103109
}
110+
return false;
104111
}
105112

106-
fun getDuration(durationType: DurationType, promise: Promise) {
107-
if (durationType == DurationType.Current) {
108-
val duration = player.currentPosition
109-
promise.resolve(duration.toString())
110-
} else {
111-
val duration = player.duration
112-
promise.resolve(duration.toString())
113-
}
113+
fun getDuration(durationType: DurationType): Long {
114+
return if (durationType == DurationType.Current) player.currentPosition else player.duration
114115
}
115116

116117
private fun validateAndSetPlaybackSpeed(player: Player, speed: Float?): Boolean {
@@ -124,75 +125,48 @@ class AudioPlayer(
124125
return true // Indicate success
125126
}
126127

127-
fun start(finishMode: Int?, speed: Float?, promise: Promise) {
128-
try {
129-
if (finishMode != null && finishMode == 0) {
130-
this.finishMode = FinishMode.Loop
131-
} else if (finishMode != null && finishMode == 1) {
132-
this.finishMode = FinishMode.Pause
133-
} else {
134-
this.finishMode = FinishMode.Stop
135-
}
128+
fun start(finishMode: Int, speed: Float): Boolean {
129+
this.finishMode = when (finishMode) {
130+
0 -> FinishMode.Loop
131+
1 -> FinishMode.Pause
132+
else -> FinishMode.Stop
133+
}
136134

137-
validateAndSetPlaybackSpeed(player, speed)
135+
validateAndSetPlaybackSpeed(player, speed)
138136

139-
player.playWhenReady = true
140-
player.play()
141-
promise.resolve(true)
142-
startListening(promise)
143-
} catch (e: Exception) {
144-
promise.reject("Can not start the player", e.toString())
145-
}
137+
player.playWhenReady = true
138+
player.play()
139+
startListening()
140+
return true
146141
}
147142

148-
fun stop(promise: Promise) {
143+
fun stop() {
149144
stopListening()
150-
if (playerListener != null) {
151-
player.removeListener(playerListener!!)
152-
}
145+
player.removeListener(playerListener)
153146
isPlayerPrepared = false
154-
player.stop()
147+
if(player.isPlaying) player.stop()
155148
player.release()
156-
promise.resolve(true)
157149
}
158150

159-
fun pause(promise: Promise) {
160-
try {
161-
stopListening()
162-
player.pause()
163-
promise.resolve(true)
164-
} catch (e: Exception) {
165-
promise.reject("Failed to pause the player", e.toString())
166-
}
167-
151+
fun pause(): Boolean {
152+
stopListening()
153+
player.pause()
154+
return true
168155
}
169156

170-
fun setVolume(volume: Float?, promise: Promise) {
157+
fun setVolume(volume: Float): Boolean {
171158
try {
172-
if (volume != null) {
173-
player.volume = volume
174-
promise.resolve(true)
175-
} else {
176-
promise.resolve(false)
177-
}
178-
} catch (e: Exception) {
179-
promise.resolve(false)
159+
player.volume = volume
160+
return true;
161+
} catch (_: Exception) {
162+
// Noop
180163
}
164+
return false
181165
}
182166

183-
fun setPlaybackSpeed(speed: Float?, promise: Promise) {
184-
try {
185-
// Call the custom function to validate and set the playback speed
186-
val success = validateAndSetPlaybackSpeed(player, speed)
187-
promise.resolve(success) // Resolve the promise with success
188-
189-
} catch (e: Exception) {
190-
// Handle any exceptions and reject the promise
191-
promise.reject("setPlaybackSpeed Error", e.toString())
192-
}
193-
}
167+
fun setPlaybackSpeed(speed: Float?) = validateAndSetPlaybackSpeed(player, speed)
194168

195-
private fun startListening(promise: Promise) {
169+
private fun startListening() {
196170
try {
197171
audioPlaybackListener = object : CountDownTimer(player.duration, UpdateFrequency.Low.value) {
198172
override fun onTick(millisUntilFinished: Long) {
@@ -207,7 +181,7 @@ class AudioPlayer(
207181
override fun onFinish() {}
208182
}.start()
209183
} catch(err: JavascriptException) {
210-
promise.reject("startListening Error", err)
184+
throw Exception("startListening-error", err)
211185
}
212186
}
213187

android/src/main/java/com/audiowaveform/AudioRecorder.kt

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,15 @@ class AudioRecorder {
2222
private var useLegacyNormalization = false
2323
private var isRecording = false
2424

25-
private fun isPermissionGranted(activity: Activity?): Int? {
26-
return activity?.let { ActivityCompat.checkSelfPermission(it, permissions[0]) }
27-
}
25+
private fun isPermissionGranted(activity: Activity?): Int? = activity?.let { ActivityCompat.checkSelfPermission(it, permissions[0]) }
2826

29-
fun checkPermission(activity: Activity?, promise: Promise): String {
30-
val permissionResponse = isPermissionGranted(activity)
31-
if (permissionResponse === PackageManager.PERMISSION_GRANTED) {
32-
promise.resolve("granted")
33-
return "granted"
34-
} else {
35-
promise.resolve("denied")
36-
return "denied"
37-
}
38-
}
27+
fun checkPermission(activity: Activity?): String = if (isPermissionGranted(activity) == PackageManager.PERMISSION_GRANTED) "granted" else "denied"
3928

40-
fun getPermission(activity: Activity?, promise: Promise): String {
41-
val permissionResponse = isPermissionGranted(activity)
42-
if (permissionResponse === PackageManager.PERMISSION_GRANTED) {
43-
promise.resolve("granted");
44-
return "granted"
45-
} else {
46-
activity?.let {
47-
ActivityCompat.requestPermissions(
48-
it, permissions,
49-
RECORD_AUDIO_REQUEST_CODE
50-
)
51-
}
52-
return "denied"
53-
}
29+
fun getPermission(activity: Activity?): String {
30+
if (isPermissionGranted(activity) == PackageManager.PERMISSION_GRANTED) return "granted"
31+
32+
activity?.let { ActivityCompat.requestPermissions(it, permissions, RECORD_AUDIO_REQUEST_CODE) }
33+
return "denied"
5434
}
5535

5636
fun getDecibel(recorder: MediaRecorder?): Double? {
@@ -133,7 +113,7 @@ class AudioRecorder {
133113
val tempArrayForCommunication : MutableList<String> = mutableListOf()
134114
val duration = getDuration(path)
135115
tempArrayForCommunication.add(path)
136-
tempArrayForCommunication.add(duration.toString())
116+
tempArrayForCommunication.add(duration)
137117
promise.resolve(Arguments.fromList(tempArrayForCommunication))
138118
} else {
139119
promise.reject("Error", "Recorder is not recording or has already been stopped")

0 commit comments

Comments
 (0)