@javascriptcommon/react-native-track-player 1.2.24 → 1.2.26
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.
|
@@ -40,7 +40,6 @@ import com.guichaguri.trackplayer.model.Track
|
|
|
40
40
|
import kotlinx.coroutines.MainScope
|
|
41
41
|
import kotlinx.coroutines.delay
|
|
42
42
|
import kotlinx.coroutines.launch
|
|
43
|
-
import timber.log.Timber
|
|
44
43
|
import java.util.*
|
|
45
44
|
import java.util.concurrent.Callable
|
|
46
45
|
import java.util.concurrent.ExecutorService
|
|
@@ -125,30 +124,37 @@ class MusicModule(reactContext: ReactApplicationContext?) :
|
|
|
125
124
|
|
|
126
125
|
override fun onServiceConnected(name: ComponentName, service: IBinder) {
|
|
127
126
|
scope.launch {
|
|
128
|
-
|
|
127
|
+
try {
|
|
128
|
+
binder = service as? MusicBinder ?: return@launch
|
|
129
129
|
connecting = false
|
|
130
|
+
|
|
130
131
|
if (musicService == null) {
|
|
131
|
-
|
|
132
|
-
musicService
|
|
133
|
-
musicService?.setupPlayer(playerOptions);
|
|
132
|
+
musicService = binder?.service
|
|
133
|
+
musicService?.setupPlayer(playerOptions)
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
isServiceBound = true
|
|
137
137
|
|
|
138
138
|
// Reapply options that user set before with updateOptions
|
|
139
|
-
if (options != null) {
|
|
139
|
+
if (options != null && binder != null) {
|
|
140
140
|
binder!!.updateOptions(options!!)
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// Triggers all callbacks
|
|
144
|
-
while (!initCallbacks.isEmpty()) {
|
|
144
|
+
while (!initCallbacks.isEmpty() && binder != null) {
|
|
145
145
|
binder!!.post(initCallbacks.remove())
|
|
146
146
|
}
|
|
147
|
+
} catch (ex: Exception) {
|
|
148
|
+
connecting = false
|
|
149
|
+
isServiceBound = false
|
|
150
|
+
binder = null
|
|
151
|
+
}
|
|
147
152
|
}
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
override fun onServiceDisconnected(name: ComponentName) {
|
|
151
156
|
binder = null
|
|
157
|
+
musicService = null
|
|
152
158
|
connecting = false
|
|
153
159
|
isServiceBound = false
|
|
154
160
|
}
|
|
@@ -175,7 +181,9 @@ class MusicModule(reactContext: ReactApplicationContext?) :
|
|
|
175
181
|
} else {
|
|
176
182
|
initCallbacks.add(r)
|
|
177
183
|
}
|
|
178
|
-
if (connecting) return
|
|
184
|
+
if (connecting || isServiceBound) return
|
|
185
|
+
connecting = true
|
|
186
|
+
|
|
179
187
|
val context = reactApplicationContext
|
|
180
188
|
|
|
181
189
|
// Binds the service to get a MediaWrapper instance
|
|
@@ -204,8 +212,16 @@ class MusicModule(reactContext: ReactApplicationContext?) :
|
|
|
204
212
|
}
|
|
205
213
|
}
|
|
206
214
|
intent.action = Utils.CONNECT_INTENT
|
|
207
|
-
|
|
208
|
-
|
|
215
|
+
val bindResult = try {
|
|
216
|
+
context.bindService(intent, this, Context.BIND_AUTO_CREATE)
|
|
217
|
+
} catch (ex: Exception) {
|
|
218
|
+
connecting = false
|
|
219
|
+
false
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!bindResult) {
|
|
223
|
+
connecting = false
|
|
224
|
+
}
|
|
209
225
|
}
|
|
210
226
|
|
|
211
227
|
/* ****************************** API ****************************** */
|
|
@@ -545,6 +561,14 @@ class MusicModule(reactContext: ReactApplicationContext?) :
|
|
|
545
561
|
)
|
|
546
562
|
return
|
|
547
563
|
}
|
|
564
|
+
|
|
565
|
+
if (connecting) {
|
|
566
|
+
promise.reject(
|
|
567
|
+
"player_already_connecting",
|
|
568
|
+
"The player is already connecting to the service."
|
|
569
|
+
)
|
|
570
|
+
return
|
|
571
|
+
}
|
|
548
572
|
|
|
549
573
|
// prevent crash Fatal Exception: android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException
|
|
550
574
|
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && AppForegroundTracker.backgrounded) {
|
|
@@ -612,27 +636,34 @@ class MusicModule(reactContext: ReactApplicationContext?) :
|
|
|
612
636
|
)
|
|
613
637
|
|
|
614
638
|
val musicModule = this
|
|
639
|
+
connecting = true
|
|
615
640
|
scope.launch {
|
|
616
641
|
var retries = 0
|
|
617
|
-
|
|
642
|
+
var bindSuccess = false
|
|
643
|
+
while (retries <= 10 && !bindSuccess) {
|
|
618
644
|
try {
|
|
619
|
-
Intent(reactApplicationContext, MusicService::class.java)
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
645
|
+
val intent = Intent(reactApplicationContext, MusicService::class.java)
|
|
646
|
+
intent.action = Utils.CONNECT_INTENT
|
|
647
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
648
|
+
reactApplicationContext.startForegroundService(intent)
|
|
649
|
+
} else {
|
|
650
|
+
reactApplicationContext.startService(intent)
|
|
651
|
+
}
|
|
652
|
+
bindSuccess = reactApplicationContext.bindService(intent, musicModule, Context.BIND_AUTO_CREATE)
|
|
653
|
+
if (bindSuccess) {
|
|
654
|
+
break
|
|
626
655
|
}
|
|
627
|
-
break
|
|
628
656
|
} catch (exception: Exception) {
|
|
629
657
|
retries += 1
|
|
630
658
|
if (retries > 10) {
|
|
631
|
-
|
|
659
|
+
break
|
|
632
660
|
}
|
|
633
661
|
delay(500)
|
|
634
662
|
}
|
|
635
663
|
}
|
|
664
|
+
if (!bindSuccess) {
|
|
665
|
+
connecting = false
|
|
666
|
+
}
|
|
636
667
|
}
|
|
637
668
|
} catch (_: Exception) {}
|
|
638
669
|
}
|
|
@@ -726,13 +757,13 @@ class MusicModule(reactContext: ReactApplicationContext?) :
|
|
|
726
757
|
fun destroy() {
|
|
727
758
|
val serviceConnection = this
|
|
728
759
|
scope.launch {
|
|
760
|
+
if (binder == null && !connecting) return@launch
|
|
761
|
+
|
|
729
762
|
musicService = null
|
|
730
763
|
isServiceBound = false
|
|
731
764
|
connecting = false
|
|
732
765
|
firstPlayDone = false
|
|
733
766
|
|
|
734
|
-
// Ignore if it was already destroyed
|
|
735
|
-
if (binder == null && !connecting) return@launch
|
|
736
767
|
try {
|
|
737
768
|
if (binder != null) {
|
|
738
769
|
binder!!.manager.metadata.removeNotifications()
|
|
@@ -742,10 +773,7 @@ class MusicModule(reactContext: ReactApplicationContext?) :
|
|
|
742
773
|
}
|
|
743
774
|
val context: ReactContext? = reactApplicationContext
|
|
744
775
|
context?.unbindService(serviceConnection)
|
|
745
|
-
} catch (ex: Exception) {
|
|
746
|
-
// This method shouldn't be throwing unhandled errors even if something goes wrong.
|
|
747
|
-
Timber.tag(Utils.LOG).e(ex, "An error occurred while destroying the service")
|
|
748
|
-
}
|
|
776
|
+
} catch (ex: Exception) {}
|
|
749
777
|
}
|
|
750
778
|
}
|
|
751
779
|
|
|
@@ -22,6 +22,7 @@ import android.view.KeyEvent
|
|
|
22
22
|
import android.view.KeyEvent.KEYCODE_MEDIA_STOP
|
|
23
23
|
import androidx.annotation.MainThread
|
|
24
24
|
import androidx.core.app.NotificationCompat
|
|
25
|
+
import androidx.media.MediaBrowserServiceCompat
|
|
25
26
|
import androidx.media.session.MediaButtonReceiver
|
|
26
27
|
import androidx.media.utils.MediaConstants
|
|
27
28
|
import com.facebook.react.ReactHost
|
|
@@ -61,7 +62,6 @@ import com.guichaguri.trackplayer.kotlinaudio.models.RepeatMode
|
|
|
61
62
|
import com.guichaguri.trackplayer.kotlinaudio.players.QueuedAudioPlayer
|
|
62
63
|
import com.guichaguri.trackplayer.model.Track
|
|
63
64
|
import com.guichaguri.trackplayer.model.TrackAudioItem
|
|
64
|
-
import com.guichaguri.trackplayer.module.AutoConnectionDetector
|
|
65
65
|
import com.guichaguri.trackplayer.module.MusicEvents
|
|
66
66
|
import com.guichaguri.trackplayer.module.MusicModule
|
|
67
67
|
import com.guichaguri.trackplayer.module.MusicModule.Companion.autoConnectionDetector
|
|
@@ -121,7 +121,7 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
121
121
|
private var mediaSession: MediaSessionCompat? = null
|
|
122
122
|
private var stateBuilder: PlaybackStateCompat.Builder? = null
|
|
123
123
|
|
|
124
|
-
override fun getTaskConfig(intent: Intent): HeadlessJsTaskConfig {
|
|
124
|
+
override fun getTaskConfig(intent: Intent?): HeadlessJsTaskConfig {
|
|
125
125
|
return HeadlessJsTaskConfig("TrackPlayer", Arguments.createMap(), 0, true)
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -155,46 +155,22 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
155
155
|
} catch (_: Exception) {}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
@
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
// The session is only active when the service is on foreground
|
|
163
|
-
serviceForeground = manager!!.metadata.session.isActive
|
|
158
|
+
@MainThread
|
|
159
|
+
override fun onBind(intent: Intent?): IBinder? {
|
|
160
|
+
if (intent == null) {
|
|
161
|
+
return super.onBind(intent)
|
|
164
162
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// Checks whether there is a React activity
|
|
170
|
-
if (currentReactContext == null || !currentReactContext.hasCurrentActivity()) {
|
|
171
|
-
val channel = Utils.getNotificationChannel(this as Context)
|
|
172
|
-
|
|
173
|
-
// Sets the service to foreground with an empty notification
|
|
174
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
175
|
-
startForeground(
|
|
176
|
-
EMPTY_NOTIFICATION_ID,
|
|
177
|
-
NotificationCompat.Builder(this, channel).build(),
|
|
178
|
-
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
|
179
|
-
)
|
|
180
|
-
} else {
|
|
181
|
-
startForeground(EMPTY_NOTIFICATION_ID, NotificationCompat.Builder(this, channel).build())
|
|
182
|
-
}
|
|
183
|
-
// Stops the service right after
|
|
184
|
-
stopSelf()
|
|
185
|
-
}
|
|
163
|
+
|
|
164
|
+
val intentAction = intent.action
|
|
165
|
+
if (intentAction == MediaBrowserServiceCompat.SERVICE_INTERFACE) {
|
|
166
|
+
return super.onBind(intent)
|
|
186
167
|
}
|
|
187
|
-
}
|
|
188
168
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return new MusicBinder(this, manager);
|
|
169
|
+
if (manager == null) {
|
|
170
|
+
manager = MusicManager(this)
|
|
192
171
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return if (SERVICE_INTERFACE == intent.action) {
|
|
196
|
-
super.onBind(intent)
|
|
197
|
-
} else MusicBinder(this, manager!!)
|
|
172
|
+
|
|
173
|
+
return MusicBinder(this, manager!!)
|
|
198
174
|
}
|
|
199
175
|
|
|
200
176
|
fun invokeStartTask(reactContext: ReactContext, restart: Boolean = false) {
|
|
@@ -664,7 +640,7 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
664
640
|
* information see https://github.com/doublesymmetry/react-native-track-player/issues/1666
|
|
665
641
|
*/
|
|
666
642
|
private fun startAndStopEmptyNotificationToAvoidANR() {
|
|
667
|
-
val notificationManager = this.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
|
643
|
+
val notificationManager = this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
668
644
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
669
645
|
notificationManager.createNotificationChannel(
|
|
670
646
|
NotificationChannel("Playback", "Playback", NotificationManager.IMPORTANCE_LOW)
|
|
@@ -673,14 +649,12 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
673
649
|
|
|
674
650
|
val resId = applicationContext.resources.getIdentifier("track_player_logo", "drawable", applicationContext.packageName)
|
|
675
651
|
val logo = if (resId != 0) resId else R.drawable.ic_play
|
|
676
|
-
val notificationBuilder
|
|
677
|
-
this, "Playback"
|
|
678
|
-
)
|
|
652
|
+
val notificationBuilder = NotificationCompat.Builder(this, "Playback")
|
|
679
653
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
|
680
|
-
.setCategory(
|
|
654
|
+
.setCategory(Notification.CATEGORY_SERVICE)
|
|
681
655
|
.setSmallIcon(logo)
|
|
682
656
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
683
|
-
notificationBuilder.
|
|
657
|
+
notificationBuilder.foregroundServiceBehavior = NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE
|
|
684
658
|
}
|
|
685
659
|
val notification = notificationBuilder.build()
|
|
686
660
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
@@ -697,20 +671,11 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
697
671
|
}
|
|
698
672
|
|
|
699
673
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
700
|
-
|
|
701
674
|
if (intent == null) {
|
|
702
675
|
return START_NOT_STICKY
|
|
703
676
|
}
|
|
704
677
|
|
|
705
678
|
if (Intent.ACTION_MEDIA_BUTTON == intent.action) {
|
|
706
|
-
onStartForeground()
|
|
707
|
-
|
|
708
|
-
if (Build.VERSION.SDK_INT >= 33) {
|
|
709
|
-
try {
|
|
710
|
-
startAndStopEmptyNotificationToAvoidANR()
|
|
711
|
-
} catch (_: java.lang.Exception) {}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
679
|
@Suppress("DEPRECATION")
|
|
715
680
|
val intentExtra: KeyEvent? = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)
|
|
716
681
|
if (intentExtra!!.keyCode == KEYCODE_MEDIA_STOP) {
|
|
@@ -732,8 +697,9 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
732
697
|
@Suppress("DEPRECATION")
|
|
733
698
|
if (handler == null) handler = Handler()
|
|
734
699
|
|
|
735
|
-
|
|
736
|
-
|
|
700
|
+
startTask(getTaskConfig(intent))
|
|
701
|
+
startAndStopEmptyNotificationToAvoidANR()
|
|
702
|
+
return START_STICKY
|
|
737
703
|
}
|
|
738
704
|
|
|
739
705
|
private fun startServiceOreoAndAbove() {
|