react-native-mp3 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/android/build.gradle +111 -0
  2. package/android/src/main/AndroidManifest.xml +44 -0
  3. package/android/src/main/java/com/reactnativemp3/Mp3Package.kt +23 -0
  4. package/android/src/main/java/com/reactnativemp3/Mp3TurboModule.kt +43 -0
  5. package/android/src/main/java/com/reactnativemp3/database/MusicDatabase.kt +48 -0
  6. package/android/src/main/java/com/reactnativemp3/database/dao/SongDao.kt +72 -0
  7. package/android/src/main/java/com/reactnativemp3/database/entities/PlaylistEntity.kt +58 -0
  8. package/android/src/main/java/com/reactnativemp3/database/entities/SongEntity.kt +104 -0
  9. package/android/src/main/java/com/reactnativemp3/database/entities/ThumbnailCacheEntity.kt +43 -0
  10. package/android/src/main/java/com/reactnativemp3/managers/CacheManager.kt +0 -0
  11. package/android/src/main/java/com/reactnativemp3/managers/EqualizerManager.kt +0 -0
  12. package/android/src/main/java/com/reactnativemp3/modules/MetadataModule.kt +330 -0
  13. package/android/src/main/java/com/reactnativemp3/modules/NotificationModule.kt +236 -0
  14. package/android/src/main/java/com/reactnativemp3/modules/PlayerModule.kt +710 -0
  15. package/android/src/main/java/com/reactnativemp3/modules/ScannerModule.kt +640 -0
  16. package/android/src/main/java/com/reactnativemp3/services/AudioFocusService.kt +0 -0
  17. package/android/src/main/java/com/reactnativemp3/services/FileObserverService.kt +0 -0
  18. package/android/src/main/java/com/reactnativemp3/services/MusicService.kt +309 -0
  19. package/android/src/main/java/com/reactnativemp3/utils/MediaStoreUtils.kt +0 -0
  20. package/android/src/main/java/com/reactnativemp3/utils/PermissionUtils.kt +0 -0
  21. package/android/src/main/jni/Mp3TurboModule.cpp +29 -0
  22. package/android/src/main/res/drawable/ic_music_note.xml +11 -0
  23. package/android/src/main/res/drawable/ic_pause.xml +11 -0
  24. package/android/src/main/res/drawable/ic_play.xml +11 -0
  25. package/android/src/main/res/drawable/ic_skip_next.xml +11 -0
  26. package/android/src/main/res/drawable/ic_skip_previous.xml +11 -0
  27. package/android/src/main/res/drawable/ic_stop.xml +11 -0
  28. package/lib/components/MusicList.d.ts +0 -0
  29. package/lib/components/MusicList.js +1 -0
  30. package/lib/components/MusicPlayerUI.d.ts +0 -0
  31. package/lib/components/MusicPlayerUI.js +1 -0
  32. package/lib/hooks/useMusicPlayer.d.ts +38 -0
  33. package/lib/hooks/useMusicPlayer.js +242 -0
  34. package/lib/hooks/useMusicScanner.d.ts +27 -0
  35. package/lib/hooks/useMusicScanner.js +217 -0
  36. package/lib/hooks/usePermissions.d.ts +9 -0
  37. package/lib/hooks/usePermissions.js +55 -0
  38. package/lib/index.d.ts +144 -0
  39. package/lib/index.js +148 -0
  40. package/lib/types/common.types.d.ts +79 -0
  41. package/lib/types/common.types.js +2 -0
  42. package/lib/types/index.d.ts +3 -0
  43. package/lib/types/index.js +2 -0
  44. package/lib/types/player.types.d.ts +35 -0
  45. package/lib/types/player.types.js +2 -0
  46. package/lib/types/scanner.types.d.ts +29 -0
  47. package/lib/types/scanner.types.js +2 -0
  48. package/lib/utils/constants.d.ts +31 -0
  49. package/lib/utils/constants.js +55 -0
  50. package/lib/utils/events.d.ts +0 -0
  51. package/lib/utils/events.js +1 -0
  52. package/package.json +62 -0
  53. package/src/components/MusicList.tsx +0 -0
  54. package/src/components/MusicPlayerUI.tsx +0 -0
  55. package/src/hooks/useMusicPlayer.ts +358 -0
  56. package/src/hooks/useMusicScanner.ts +286 -0
  57. package/src/hooks/usePermissions.ts +64 -0
  58. package/src/index.ts +214 -0
  59. package/src/types/common.types.ts +86 -0
  60. package/src/types/index.ts +4 -0
  61. package/src/types/player.types.ts +37 -0
  62. package/src/types/scanner.types.ts +31 -0
  63. package/src/utils/constants.ts +56 -0
  64. package/src/utils/events.ts +0 -0
@@ -0,0 +1,309 @@
1
+ package com.reactnativemp3.services
2
+
3
+ import android.app.Notification
4
+ import android.app.NotificationChannel
5
+ import android.app.NotificationManager
6
+ import android.app.PendingIntent
7
+ import android.app.Service
8
+ import android.content.Context
9
+ import android.content.Intent
10
+ import android.graphics.Bitmap
11
+ import android.graphics.BitmapFactory
12
+ import android.media.AudioManager
13
+ import android.media.MediaPlayer
14
+ import android.os.Binder
15
+ import android.os.Build
16
+ import android.os.IBinder
17
+ import android.support.v4.media.session.MediaSessionCompat
18
+ import androidx.core.app.NotificationCompat
19
+ import androidx.media.app.NotificationCompat as MediaNotificationCompat
20
+ import com.reactnativemp3.R
21
+ import kotlinx.coroutines.CoroutineScope
22
+ import kotlinx.coroutines.Dispatchers
23
+ import kotlinx.coroutines.SupervisorJob
24
+ import kotlinx.coroutines.launch
25
+ import java.lang.ref.WeakReference
26
+
27
+ class MusicService : Service(), AudioManager.OnAudioFocusChangeListener {
28
+
29
+ companion object {
30
+ private const val NOTIFICATION_CHANNEL_ID = "music_service_channel"
31
+ private const val NOTIFICATION_ID = 1002
32
+ const val ACTION_PLAY = "com.reactnativemp3.ACTION_PLAY"
33
+ const val ACTION_PAUSE = "com.reactnativemp3.ACTION_PAUSE"
34
+ const val ACTION_NEXT = "com.reactnativemp3.ACTION_NEXT"
35
+ const val ACTION_PREVIOUS = "com.reactnativemp3.ACTION_PREVIOUS"
36
+ const val ACTION_STOP = "com.reactnativemp3.ACTION_STOP"
37
+
38
+ fun createIntent(context: Context): Intent {
39
+ return Intent(context, MusicService::class.java)
40
+ }
41
+ }
42
+
43
+ private val binder = MusicBinder()
44
+ private lateinit var notificationManager: NotificationManager
45
+ private lateinit var audioManager: AudioManager
46
+ private var mediaSession: MediaSessionCompat? = null
47
+
48
+ private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
49
+
50
+ // Media player state
51
+ private var mediaPlayer: MediaPlayer? = null
52
+ private var isPlaying: Boolean = false
53
+ private var currentTitle: String = ""
54
+ private var currentArtist: String = ""
55
+ private var currentAlbum: String = ""
56
+ private var albumArtBitmap: Bitmap? = null
57
+
58
+ inner class MusicBinder : Binder() {
59
+ fun getService(): MusicService = this@MusicService
60
+ }
61
+
62
+ override fun onCreate() {
63
+ super.onCreate()
64
+ notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
65
+ audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
66
+ createNotificationChannel()
67
+ setupMediaSession()
68
+ }
69
+
70
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
71
+ intent?.action?.let { handleAction(it) }
72
+ return START_STICKY
73
+ }
74
+
75
+ override fun onBind(intent: Intent?): IBinder {
76
+ return binder
77
+ }
78
+
79
+ override fun onUnbind(intent: Intent?): Boolean {
80
+ stopForeground(true)
81
+ stopSelf()
82
+ return super.onUnbind(intent)
83
+ }
84
+
85
+ override fun onDestroy() {
86
+ cleanup()
87
+ super.onDestroy()
88
+ }
89
+
90
+ override fun onAudioFocusChange(focusChange: Int) {
91
+ when (focusChange) {
92
+ AudioManager.AUDIOFOCUS_LOSS -> {
93
+ // Lost audio focus for an indefinite period
94
+ pausePlayback()
95
+ }
96
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
97
+ // Lost audio focus for a short time
98
+ pausePlayback()
99
+ }
100
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
101
+ // Lost focus but can play at a lower volume
102
+ mediaPlayer?.setVolume(0.2f, 0.2f)
103
+ }
104
+ AudioManager.AUDIOFOCUS_GAIN -> {
105
+ // Gained audio focus
106
+ mediaPlayer?.setVolume(1.0f, 1.0f)
107
+ if (isPlaying) {
108
+ startPlayback()
109
+ }
110
+ }
111
+ }
112
+ }
113
+
114
+ fun setPlaybackMetadata(title: String, artist: String, album: String, albumArt: Bitmap?) {
115
+ currentTitle = title
116
+ currentArtist = artist
117
+ currentAlbum = album
118
+ albumArtBitmap = albumArt
119
+ updateNotification()
120
+ }
121
+
122
+ fun startPlayback() {
123
+ isPlaying = true
124
+ requestAudioFocus()
125
+ mediaPlayer?.start()
126
+ updateNotification()
127
+ }
128
+
129
+ fun pausePlayback() {
130
+ isPlaying = false
131
+ mediaPlayer?.pause()
132
+ updateNotification()
133
+ }
134
+
135
+ fun stopPlayback() {
136
+ isPlaying = false
137
+ mediaPlayer?.stop()
138
+ mediaPlayer?.release()
139
+ mediaPlayer = null
140
+ updateNotification()
141
+ }
142
+
143
+ fun setMediaPlayer(player: MediaPlayer) {
144
+ mediaPlayer?.release()
145
+ mediaPlayer = player
146
+ isPlaying = player.isPlaying
147
+ }
148
+
149
+ private fun handleAction(action: String) {
150
+ when (action) {
151
+ ACTION_PLAY -> startPlayback()
152
+ ACTION_PAUSE -> pausePlayback()
153
+ ACTION_NEXT -> {
154
+ // Forward to activity/receiver
155
+ sendBroadcast(Intent(ACTION_NEXT))
156
+ }
157
+ ACTION_PREVIOUS -> {
158
+ // Forward to activity/receiver
159
+ sendBroadcast(Intent(ACTION_PREVIOUS))
160
+ }
161
+ ACTION_STOP -> {
162
+ stopPlayback()
163
+ stopForeground(true)
164
+ stopSelf()
165
+ }
166
+ }
167
+ }
168
+
169
+ private fun requestAudioFocus(): Boolean {
170
+ val result = audioManager.requestAudioFocus(
171
+ this,
172
+ AudioManager.STREAM_MUSIC,
173
+ AudioManager.AUDIOFOCUS_GAIN
174
+ )
175
+ return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
176
+ }
177
+
178
+ private fun createNotificationChannel() {
179
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
180
+ val channel = NotificationChannel(
181
+ NOTIFICATION_CHANNEL_ID,
182
+ "Music Playback",
183
+ NotificationManager.IMPORTANCE_LOW
184
+ ).apply {
185
+ description = "Shows music playback controls"
186
+ setShowBadge(false)
187
+ lockscreenVisibility = Notification.VISIBILITY_PUBLIC
188
+ setSound(null, null)
189
+ }
190
+
191
+ notificationManager.createNotificationChannel(channel)
192
+ }
193
+ }
194
+
195
+ private fun setupMediaSession() {
196
+ mediaSession = MediaSessionCompat(this, "MusicService").apply {
197
+ setFlags(
198
+ MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
199
+ MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
200
+ )
201
+ setCallback(object : MediaSessionCompat.Callback() {
202
+ override fun onPlay() {
203
+ startPlayback()
204
+ }
205
+
206
+ override fun onPause() {
207
+ pausePlayback()
208
+ }
209
+
210
+ override fun onSkipToNext() {
211
+ sendBroadcast(Intent(ACTION_NEXT))
212
+ }
213
+
214
+ override fun onSkipToPrevious() {
215
+ sendBroadcast(Intent(ACTION_PREVIOUS))
216
+ }
217
+
218
+ override fun onStop() {
219
+ stopPlayback()
220
+ }
221
+ })
222
+ isActive = true
223
+ }
224
+ }
225
+
226
+ private fun updateNotification() {
227
+ scope.launch {
228
+ val notification = buildNotification()
229
+ startForeground(NOTIFICATION_ID, notification)
230
+ }
231
+ }
232
+
233
+ private suspend fun buildNotification(): Notification {
234
+ // Create pending intents
235
+ val playIntent = createPendingIntent(ACTION_PLAY)
236
+ val pauseIntent = createPendingIntent(ACTION_PAUSE)
237
+ val nextIntent = createPendingIntent(ACTION_NEXT)
238
+ val previousIntent = createPendingIntent(ACTION_PREVIOUS)
239
+ val stopIntent = createPendingIntent(ACTION_STOP)
240
+
241
+ val builder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
242
+ .setSmallIcon(R.drawable.ic_music_note)
243
+ .setContentTitle(currentTitle.ifEmpty { "Music Player" })
244
+ .setContentText(currentArtist.ifEmpty { "Unknown Artist" })
245
+ .setSubText(currentAlbum.ifEmpty { "Unknown Album" })
246
+ .setLargeIcon(albumArtBitmap)
247
+ .setStyle(
248
+ MediaNotificationCompat.MediaStyle()
249
+ .setMediaSession(mediaSession?.sessionToken)
250
+ .setShowActionsInCompactView(0, 1, 2)
251
+ )
252
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
253
+ .setPriority(NotificationCompat.PRIORITY_LOW)
254
+ .setOngoing(isPlaying)
255
+ .setShowWhen(false)
256
+ .setAutoCancel(false)
257
+ .setDeleteIntent(stopIntent)
258
+
259
+ // Add actions
260
+ if (isPlaying) {
261
+ builder.addAction(
262
+ R.drawable.ic_pause,
263
+ "Pause",
264
+ pauseIntent
265
+ )
266
+ } else {
267
+ builder.addAction(
268
+ R.drawable.ic_play,
269
+ "Play",
270
+ playIntent
271
+ )
272
+ }
273
+
274
+ builder.addAction(
275
+ R.drawable.ic_skip_previous,
276
+ "Previous",
277
+ previousIntent
278
+ ).addAction(
279
+ R.drawable.ic_skip_next,
280
+ "Next",
281
+ nextIntent
282
+ )
283
+
284
+ return builder.build()
285
+ }
286
+
287
+ private fun createPendingIntent(action: String): PendingIntent {
288
+ val intent = Intent(action).apply {
289
+ setPackage(packageName)
290
+ }
291
+
292
+ val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
293
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
294
+ } else {
295
+ PendingIntent.FLAG_UPDATE_CURRENT
296
+ }
297
+
298
+ return PendingIntent.getBroadcast(this, action.hashCode(), intent, flags)
299
+ }
300
+
301
+ private fun cleanup() {
302
+ mediaPlayer?.release()
303
+ mediaPlayer = null
304
+ mediaSession?.release()
305
+ mediaSession = null
306
+ audioManager.abandonAudioFocus(this)
307
+ scope.cancel()
308
+ }
309
+ }
@@ -0,0 +1,29 @@
1
+ #include <jni.h>
2
+ #include <string>
3
+ #include <fbjni/fbjni.h>
4
+ #include <ReactCommon/TurboModuleManagerDelegate.h>
5
+ #include <ReactCommon/CallInvokerHolder.h>
6
+
7
+ #include "Mp3TurboModule.h"
8
+
9
+ using namespace facebook;
10
+ using namespace facebook::react;
11
+
12
+ // JNI Methods
13
+ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
14
+ return jni::initialize(vm, [] {
15
+ Mp3TurboModule::registerNatives();
16
+ });
17
+ }
18
+
19
+ // Module registration
20
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
21
+ JNIEnv* env;
22
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
23
+ return JNI_ERR;
24
+ }
25
+
26
+ // Register your native methods here
27
+
28
+ return JNI_VERSION_1_6;
29
+ }
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+
8
+ <path
9
+ android:fillColor="#FFFFFF"
10
+ android:pathData="M12,3L12,12.26C11.5,12.09 11,12 10.5,12C8,12 6,14 6,16.5S8,21 10.5,21 15,19 15,16.5V6h4V3H12z"/>
11
+ </vector>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+
8
+ <path
9
+ android:fillColor="#FFFFFF"
10
+ android:pathData="M6,19h4V5H6V19zM14,5v14h4V5H14z"/>
11
+ </vector>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+
8
+ <path
9
+ android:fillColor="#FFFFFF"
10
+ android:pathData="M8,5.14V19.14L19,12.14L8,5.14Z"/>
11
+ </vector>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+
8
+ <path
9
+ android:fillColor="#FFFFFF"
10
+ android:pathData="M6,18l8.5-6L6,6v12zM16,6v12h2V6h-2z"/>
11
+ </vector>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+
8
+ <path
9
+ android:fillColor="#FFFFFF"
10
+ android:pathData="M6,6h2v12H6zm3.5,6l8.5,6V6z"/>
11
+ </vector>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="24dp"
4
+ android:height="24dp"
5
+ android:viewportWidth="24"
6
+ android:viewportHeight="24">
7
+
8
+ <path
9
+ android:fillColor="#FFFFFF"
10
+ android:pathData="M6,6h12v12H6z"/>
11
+ </vector>
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,38 @@
1
+ import type { Song, PlaybackState, PlayerOptions } from '../types/common.types';
2
+ interface UseMusicPlayerProps {
3
+ autoSetup?: boolean;
4
+ playerOptions?: PlayerOptions;
5
+ }
6
+ interface UseMusicPlayerReturn {
7
+ playbackState: PlaybackState;
8
+ currentSong: Song | null;
9
+ queue: Song[];
10
+ isPlaying: boolean;
11
+ isLoading: boolean;
12
+ error: string | null;
13
+ play: (uri: string) => Promise<void>;
14
+ playSong: (song: Song) => Promise<void>;
15
+ playFromQueue: (index: number) => void;
16
+ pause: () => void;
17
+ stop: () => void;
18
+ seekTo: (position: number) => void;
19
+ skipToNext: () => void;
20
+ skipToPrevious: () => void;
21
+ setVolume: (volume: number) => void;
22
+ setPlaybackRate: (rate: number) => void;
23
+ setQueue: (songs: Song[]) => void;
24
+ addToQueue: (songs: Song[]) => void;
25
+ clearQueue: () => void;
26
+ getQueue: () => Promise<Song[]>;
27
+ setRepeatMode: (mode: 'none' | 'one' | 'all') => void;
28
+ setShuffleMode: (enabled: boolean) => void;
29
+ setEqualizerPreset: (preset: string) => void;
30
+ setEqualizerBands: (bands: number[]) => void;
31
+ enableEqualizer: (enabled: boolean) => void;
32
+ setupBackgroundPlayback: (config?: any) => void;
33
+ stopBackgroundPlayback: () => void;
34
+ setSleepTimer: (minutes: number) => void;
35
+ cancelSleepTimer: () => void;
36
+ }
37
+ export declare const useMusicPlayer: ({ autoSetup, playerOptions, }?: UseMusicPlayerProps) => UseMusicPlayerReturn;
38
+ export {};