Skip to content

Commit 1316621

Browse files
fix: more fixes
1 parent 7e6f1d7 commit 1316621

File tree

12 files changed

+558
-404
lines changed

12 files changed

+558
-404
lines changed

android/src/main/java/com/musicplayer/AudioModule.kt

Lines changed: 119 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import com.google.android.exoplayer2.ui.PlayerNotificationManager
1717

1818
@ReactModule(name = AudioModule.NAME)
1919
class AudioModule(private val reactContext: ReactApplicationContext) :
20-
NativeAudioModuleSpec(reactContext) {
20+
NativeAudioModuleSpec(reactContext) {
2121

2222
private var exoPlayer: ExoPlayer? = null
2323
private var progressHandler: Handler? = Handler(Looper.getMainLooper())
@@ -31,65 +31,84 @@ class AudioModule(private val reactContext: ReactApplicationContext) :
3131

3232
init {
3333
reactContext.runOnUiQueueThread {
34-
initializePlayer()
3534
initializeProgressRunnable()
3635
createNotificationChannel()
3736
}
3837
}
3938

4039
private fun createNotificationChannel() {
41-
val channel = NotificationChannel(
42-
CHANNEL_ID,
43-
"Audio Player",
44-
NotificationManager.IMPORTANCE_LOW
45-
)
46-
val notificationManager = reactContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
40+
val channel =
41+
NotificationChannel(CHANNEL_ID, "Audio Player", NotificationManager.IMPORTANCE_LOW)
42+
val notificationManager =
43+
reactContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
4744
notificationManager.createNotificationChannel(channel)
4845
}
4946

50-
private fun initializePlayer() {
47+
private fun ensurePlayerReady() {
5148
if (exoPlayer == null) {
52-
exoPlayer = ExoPlayer.Builder(reactContext).build().apply {
53-
addListener(object : Player.Listener {
54-
override fun onPlaybackStateChanged(state: Int) {
55-
when (state) {
56-
Player.STATE_BUFFERING -> emitAudioStateChange("BUFFERING")
57-
Player.STATE_READY -> emitAudioStateChange("LOADED")
58-
Player.STATE_ENDED -> emitAudioStateChange("COMPLETED")
59-
}
60-
}
49+
exoPlayer =
50+
ExoPlayer.Builder(reactContext).build().apply {
51+
addListener(
52+
object : Player.Listener {
53+
override fun onPlaybackStateChanged(state: Int) {
54+
when (state) {
55+
Player.STATE_BUFFERING ->
56+
emitAudioStateChange("BUFFERING")
57+
Player.STATE_READY -> emitAudioStateChange("LOADED")
58+
Player.STATE_ENDED -> emitAudioStateChange("COMPLETED")
59+
}
60+
}
6161

62-
override fun onPlayerError(error: PlaybackException) {
63-
Log.e(TAG, "ExoPlayer Error: ${error.message}")
64-
emitAudioStateChange("ERROR", error.message)
62+
override fun onPlayerError(error: PlaybackException) {
63+
Log.e(TAG, "ExoPlayer Error: ${error.message}")
64+
emitAudioStateChange("ERROR", error.message)
65+
}
66+
}
67+
)
6568
}
66-
})
67-
}
6869
}
6970
}
7071

71-
@ReactMethod
72-
override fun addListener(eventName: String) {
73-
// Required for RN event emitter
74-
}
72+
@ReactMethod override fun addListener(eventName: String) {}
7573

76-
@ReactMethod
77-
override fun removeListeners(count: Double) {
78-
// Required for RN event emitter
79-
}
74+
@ReactMethod override fun removeListeners(count: Double) {}
8075

8176
@ReactMethod
82-
override fun setMediaPlayerInfo(title: String?, artist: String?, album: String?, artwork: String?, promise: Promise) {
83-
try {
84-
currentTitle = title ?: "Unknown"
85-
currentArtist = artist ?: "Unknown"
86-
currentAlbum = album ?: "Unknown"
87-
currentArtwork = artwork ?: ""
88-
89-
updateNotification()
90-
promise.resolve(null)
91-
} catch (e: Exception) {
92-
promise.reject("ERROR", "Error setting media player info: ${e.message}")
77+
override fun setMediaPlayerInfo(
78+
title: String?,
79+
artist: String?,
80+
album: String?,
81+
artwork: String?,
82+
promise: Promise
83+
) {
84+
reactContext.runOnUiQueueThread {
85+
try {
86+
if (exoPlayer == null) {
87+
ensurePlayerReady()
88+
}
89+
90+
currentTitle = title ?: "Unknown"
91+
currentArtist = artist ?: "Unknown"
92+
currentAlbum = album ?: "Unknown"
93+
currentArtwork = artwork ?: ""
94+
95+
// Update media session metadata
96+
try {
97+
updateNotification()
98+
} catch (e: Exception) {
99+
Log.e(TAG, "Error updating notification: ${e.message}")
100+
// Don't fail the whole operation if notification update fails
101+
}
102+
103+
promise.resolve(null)
104+
} catch (e: Exception) {
105+
Log.e(TAG, "Error setting media info: ${e.message}", e)
106+
promise.reject(
107+
"ERROR",
108+
"Error setting media player info: ${e.message ?: "Unknown error"}",
109+
e
110+
)
111+
}
93112
}
94113
}
95114

@@ -98,7 +117,7 @@ class AudioModule(private val reactContext: ReactApplicationContext) :
98117
reactContext.runOnUiQueueThread {
99118
try {
100119
if (url.isBlank()) throw IllegalArgumentException("URL cannot be empty")
101-
initializePlayer()
120+
ensurePlayerReady()
102121
val mediaItem = MediaItem.fromUri(Uri.parse(url))
103122
exoPlayer?.setMediaItem(mediaItem)
104123
exoPlayer?.prepare()
@@ -112,6 +131,7 @@ class AudioModule(private val reactContext: ReactApplicationContext) :
112131
@ReactMethod
113132
override fun playAudio() {
114133
reactContext.runOnUiQueueThread {
134+
ensurePlayerReady()
115135
exoPlayer?.playWhenReady = true
116136
emitAudioStateChange("PLAYING")
117137
progressRunnable?.let { progressHandler?.post(it) }
@@ -153,33 +173,41 @@ class AudioModule(private val reactContext: ReactApplicationContext) :
153173
}
154174

155175
private fun emitAudioStateChange(state: String, message: String? = null) {
156-
val params = Arguments.createMap().apply {
157-
putString("state", state)
158-
message?.let { putString("message", it) }
159-
}
160-
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
161-
?.emit("onAudioStateChange", params)
176+
val params =
177+
Arguments.createMap().apply {
178+
putString("state", state)
179+
message?.let { putString("message", it) }
180+
}
181+
reactContext
182+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
183+
?.emit("onAudioStateChange", params)
162184
}
163185

164186
private fun initializeProgressRunnable() {
165-
progressRunnable = object : Runnable {
166-
override fun run() {
167-
if (exoPlayer?.isPlaying == true) {
168-
val currentPosition = exoPlayer?.currentPosition?.toDouble() ?: 0.0
169-
val duration = exoPlayer?.duration?.toDouble() ?: 1.0
170-
171-
val params = Arguments.createMap().apply {
172-
putDouble("progress", currentPosition / duration)
173-
putDouble("currentTime", currentPosition / 1000)
174-
putDouble("totalDuration", duration / 1000)
175-
}
187+
progressRunnable =
188+
object : Runnable {
189+
override fun run() {
190+
if (exoPlayer?.isPlaying == true) {
191+
val currentPosition = exoPlayer?.currentPosition?.toDouble() ?: 0.0
192+
val duration = exoPlayer?.duration?.toDouble() ?: 1.0
193+
194+
val params =
195+
Arguments.createMap().apply {
196+
putDouble("progress", currentPosition / duration)
197+
putDouble("currentTime", currentPosition / 1000)
198+
putDouble("totalDuration", duration / 1000)
199+
}
176200

177-
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
178-
?.emit("onAudioProgress", params)
179-
progressHandler?.postDelayed(this, 1000)
201+
reactContext
202+
.getJSModule(
203+
DeviceEventManagerModule.RCTDeviceEventEmitter::class
204+
.java
205+
)
206+
?.emit("onAudioProgress", params)
207+
progressHandler?.postDelayed(this, 1000)
208+
}
209+
}
180210
}
181-
}
182-
}
183211
}
184212

185213
override fun onCatalystInstanceDestroy() {
@@ -193,17 +221,32 @@ class AudioModule(private val reactContext: ReactApplicationContext) :
193221

194222
private fun updateNotification() {
195223
if (playerNotificationManager == null) {
196-
playerNotificationManager = PlayerNotificationManager.Builder(reactContext, NOTIFICATION_ID, CHANNEL_ID)
197-
.setMediaDescriptionAdapter(object : PlayerNotificationManager.MediaDescriptionAdapter {
198-
override fun getCurrentContentTitle(player: Player) = currentTitle
199-
override fun createCurrentContentIntent(player: Player): PendingIntent? = null
200-
override fun getCurrentContentText(player: Player) = currentArtist
201-
override fun getCurrentSubText(player: Player) = currentAlbum
202-
override fun getCurrentLargeIcon(player: Player, callback: PlayerNotificationManager.BitmapCallback): Bitmap? = null
203-
})
204-
.build()
224+
playerNotificationManager =
225+
PlayerNotificationManager.Builder(reactContext, NOTIFICATION_ID, CHANNEL_ID)
226+
.setMediaDescriptionAdapter(
227+
object : PlayerNotificationManager.MediaDescriptionAdapter {
228+
override fun getCurrentContentTitle(player: Player) =
229+
currentTitle
230+
override fun createCurrentContentIntent(
231+
player: Player
232+
): PendingIntent? = null
233+
override fun getCurrentContentText(player: Player) =
234+
currentArtist
235+
override fun getCurrentSubText(player: Player) =
236+
currentAlbum
237+
override fun getCurrentLargeIcon(
238+
player: Player,
239+
callback: PlayerNotificationManager.BitmapCallback
240+
): Bitmap? = null
241+
}
242+
)
243+
.build()
244+
.apply {
245+
setPlayer(exoPlayer)
246+
}
247+
} else {
248+
playerNotificationManager?.setPlayer(exoPlayer)
205249
}
206-
playerNotificationManager?.setPlayer(exoPlayer)
207250
}
208251

209252
companion object {
@@ -212,4 +255,4 @@ class AudioModule(private val reactContext: ReactApplicationContext) :
212255
private const val CHANNEL_ID = "audio_player_channel"
213256
private const val NOTIFICATION_ID = 1001
214257
}
215-
}
258+
}

example/android/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ buildscript {
66
targetSdkVersion = 34
77
ndkVersion = "27.1.12297006"
88
kotlinVersion = "2.0.21"
9+
nodeExecutable = System.getenv("NODE_BINARY") ?: "node"
910
}
1011
repositories {
1112
google()

example/react-native.config.js

Lines changed: 0 additions & 21 deletions
This file was deleted.

example/src/App.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
PlayerProvider,
77
usePlayerContext,
88
PlayerState,
9+
usePlayerHook,
910
} from '@liftoffllc/rn-audio-player';
1011
import { mockAudioContent } from '../data/mockData.native';
1112
import Icon from 'react-native-vector-icons/FontAwesome6';
@@ -82,7 +83,8 @@ export default function App() {
8283
}
8384

8485
function PlayerContent() {
85-
const { playerControls, playerState } = usePlayerContext();
86+
const { playerState } = usePlayerContext();
87+
const { play, pause, loadContent } = usePlayerHook();
8688

8789
const MockContent1 = (
8890
<View style={styles.mockContent}>
@@ -99,8 +101,8 @@ function PlayerContent() {
99101
);
100102

101103
const handlePlay = () => {
102-
playerControls?.loadContent?.();
103-
playerControls?.play?.();
104+
loadContent?.();
105+
play?.();
104106
};
105107

106108
return (
@@ -135,7 +137,7 @@ function PlayerContent() {
135137
/>
136138
</View>
137139
<View style={styles.mainContainer}>
138-
{CustomPlayer(playerState.state, handlePlay, playerControls?.pause!)}
140+
{CustomPlayer(playerState.state, handlePlay, pause!)}
139141
</View>
140142
</>
141143
);

0 commit comments

Comments
 (0)