@javascriptcommon/react-native-track-player 1.2.23 → 1.2.25
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.
|
@@ -111,7 +111,7 @@ public abstract class HeadlessJsMediaService extends MediaBrowserServiceCompat i
|
|
|
111
111
|
*/
|
|
112
112
|
protected void startTask(final HeadlessJsTaskConfig taskConfig) {
|
|
113
113
|
UiThreadUtil.assertOnUiThread();
|
|
114
|
-
|
|
114
|
+
acquireWakeLockNow(this);
|
|
115
115
|
ReactContext reactContext = getReactContext();
|
|
116
116
|
if (reactContext == null) {
|
|
117
117
|
createReactContextAndScheduleTask(taskConfig);
|
|
@@ -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) {
|
|
@@ -611,13 +635,35 @@ class MusicModule(reactContext: ReactApplicationContext?) :
|
|
|
611
635
|
IntentFilter(EVENT_INTENT)
|
|
612
636
|
)
|
|
613
637
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
638
|
+
val musicModule = this
|
|
639
|
+
connecting = true
|
|
640
|
+
scope.launch {
|
|
641
|
+
var retries = 0
|
|
642
|
+
var bindSuccess = false
|
|
643
|
+
while (retries <= 10 && !bindSuccess) {
|
|
644
|
+
try {
|
|
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
|
|
655
|
+
}
|
|
656
|
+
} catch (exception: Exception) {
|
|
657
|
+
retries += 1
|
|
658
|
+
if (retries > 10) {
|
|
659
|
+
break
|
|
660
|
+
}
|
|
661
|
+
delay(500)
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (!bindSuccess) {
|
|
665
|
+
connecting = false
|
|
619
666
|
}
|
|
620
|
-
reactApplicationContext.bindService(intent, this, Context.BIND_AUTO_CREATE)
|
|
621
667
|
}
|
|
622
668
|
} catch (_: Exception) {}
|
|
623
669
|
}
|
|
@@ -711,13 +757,13 @@ class MusicModule(reactContext: ReactApplicationContext?) :
|
|
|
711
757
|
fun destroy() {
|
|
712
758
|
val serviceConnection = this
|
|
713
759
|
scope.launch {
|
|
760
|
+
if (binder == null && !connecting) return@launch
|
|
761
|
+
|
|
714
762
|
musicService = null
|
|
715
763
|
isServiceBound = false
|
|
716
764
|
connecting = false
|
|
717
765
|
firstPlayDone = false
|
|
718
766
|
|
|
719
|
-
// Ignore if it was already destroyed
|
|
720
|
-
if (binder == null && !connecting) return@launch
|
|
721
767
|
try {
|
|
722
768
|
if (binder != null) {
|
|
723
769
|
binder!!.manager.metadata.removeNotifications()
|
|
@@ -727,10 +773,7 @@ class MusicModule(reactContext: ReactApplicationContext?) :
|
|
|
727
773
|
}
|
|
728
774
|
val context: ReactContext? = reactApplicationContext
|
|
729
775
|
context?.unbindService(serviceConnection)
|
|
730
|
-
} catch (ex: Exception) {
|
|
731
|
-
// This method shouldn't be throwing unhandled errors even if something goes wrong.
|
|
732
|
-
Timber.tag(Utils.LOG).e(ex, "An error occurred while destroying the service")
|
|
733
|
-
}
|
|
776
|
+
} catch (ex: Exception) {}
|
|
734
777
|
}
|
|
735
778
|
}
|
|
736
779
|
|
|
@@ -18,11 +18,9 @@ import android.support.v4.media.MediaBrowserCompat.MediaItem
|
|
|
18
18
|
import android.support.v4.media.RatingCompat
|
|
19
19
|
import android.support.v4.media.session.MediaSessionCompat
|
|
20
20
|
import android.support.v4.media.session.PlaybackStateCompat
|
|
21
|
-
import android.view.KeyEvent
|
|
22
|
-
import android.view.KeyEvent.KEYCODE_MEDIA_STOP
|
|
23
21
|
import androidx.annotation.MainThread
|
|
24
22
|
import androidx.core.app.NotificationCompat
|
|
25
|
-
import androidx.media.
|
|
23
|
+
import androidx.media.MediaBrowserServiceCompat
|
|
26
24
|
import androidx.media.utils.MediaConstants
|
|
27
25
|
import com.facebook.react.ReactHost
|
|
28
26
|
import com.facebook.react.ReactInstanceEventListener
|
|
@@ -61,7 +59,6 @@ import com.guichaguri.trackplayer.kotlinaudio.models.RepeatMode
|
|
|
61
59
|
import com.guichaguri.trackplayer.kotlinaudio.players.QueuedAudioPlayer
|
|
62
60
|
import com.guichaguri.trackplayer.model.Track
|
|
63
61
|
import com.guichaguri.trackplayer.model.TrackAudioItem
|
|
64
|
-
import com.guichaguri.trackplayer.module.AutoConnectionDetector
|
|
65
62
|
import com.guichaguri.trackplayer.module.MusicEvents
|
|
66
63
|
import com.guichaguri.trackplayer.module.MusicModule
|
|
67
64
|
import com.guichaguri.trackplayer.module.MusicModule.Companion.autoConnectionDetector
|
|
@@ -121,7 +118,7 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
121
118
|
private var mediaSession: MediaSessionCompat? = null
|
|
122
119
|
private var stateBuilder: PlaybackStateCompat.Builder? = null
|
|
123
120
|
|
|
124
|
-
override fun getTaskConfig(intent: Intent): HeadlessJsTaskConfig {
|
|
121
|
+
override fun getTaskConfig(intent: Intent?): HeadlessJsTaskConfig {
|
|
125
122
|
return HeadlessJsTaskConfig("TrackPlayer", Arguments.createMap(), 0, true)
|
|
126
123
|
}
|
|
127
124
|
|
|
@@ -155,46 +152,22 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
155
152
|
} catch (_: Exception) {}
|
|
156
153
|
}
|
|
157
154
|
|
|
158
|
-
@
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
// The session is only active when the service is on foreground
|
|
163
|
-
serviceForeground = manager!!.metadata.session.isActive
|
|
155
|
+
@MainThread
|
|
156
|
+
override fun onBind(intent: Intent?): IBinder? {
|
|
157
|
+
if (intent == null) {
|
|
158
|
+
return super.onBind(intent)
|
|
164
159
|
}
|
|
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
|
-
1,
|
|
177
|
-
NotificationCompat.Builder(this, channel).build(),
|
|
178
|
-
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
|
179
|
-
)
|
|
180
|
-
} else {
|
|
181
|
-
startForeground(1, NotificationCompat.Builder(this, channel).build())
|
|
182
|
-
}
|
|
183
|
-
// Stops the service right after
|
|
184
|
-
stopSelf()
|
|
185
|
-
}
|
|
160
|
+
|
|
161
|
+
val intentAction = intent.action
|
|
162
|
+
if (intentAction == MediaBrowserServiceCompat.SERVICE_INTERFACE) {
|
|
163
|
+
return super.onBind(intent)
|
|
186
164
|
}
|
|
187
|
-
}
|
|
188
165
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return new MusicBinder(this, manager);
|
|
166
|
+
if (manager == null) {
|
|
167
|
+
manager = MusicManager(this)
|
|
192
168
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return if (SERVICE_INTERFACE == intent.action) {
|
|
196
|
-
super.onBind(intent)
|
|
197
|
-
} else MusicBinder(this, manager!!)
|
|
169
|
+
|
|
170
|
+
return MusicBinder(this, manager!!)
|
|
198
171
|
}
|
|
199
172
|
|
|
200
173
|
fun invokeStartTask(reactContext: ReactContext, restart: Boolean = false) {
|
|
@@ -646,12 +619,12 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
646
619
|
try {
|
|
647
620
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
648
621
|
startForeground(
|
|
649
|
-
|
|
622
|
+
EMPTY_NOTIFICATION_ID,
|
|
650
623
|
NotificationCompat.Builder(this, channel).build(),
|
|
651
624
|
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
|
652
625
|
)
|
|
653
626
|
} else {
|
|
654
|
-
startForeground(
|
|
627
|
+
startForeground(EMPTY_NOTIFICATION_ID, NotificationCompat.Builder(this, channel).build())
|
|
655
628
|
}
|
|
656
629
|
} catch (_: Exception) { }
|
|
657
630
|
}
|
|
@@ -664,7 +637,7 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
664
637
|
* information see https://github.com/doublesymmetry/react-native-track-player/issues/1666
|
|
665
638
|
*/
|
|
666
639
|
private fun startAndStopEmptyNotificationToAvoidANR() {
|
|
667
|
-
val notificationManager = this.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
|
640
|
+
val notificationManager = this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
668
641
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
669
642
|
notificationManager.createNotificationChannel(
|
|
670
643
|
NotificationChannel("Playback", "Playback", NotificationManager.IMPORTANCE_LOW)
|
|
@@ -673,59 +646,31 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
673
646
|
|
|
674
647
|
val resId = applicationContext.resources.getIdentifier("track_player_logo", "drawable", applicationContext.packageName)
|
|
675
648
|
val logo = if (resId != 0) resId else R.drawable.ic_play
|
|
676
|
-
val notificationBuilder
|
|
677
|
-
this, "Playback"
|
|
678
|
-
)
|
|
649
|
+
val notificationBuilder = NotificationCompat.Builder(this, "Playback")
|
|
679
650
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
|
680
|
-
.setCategory(
|
|
651
|
+
.setCategory(Notification.CATEGORY_SERVICE)
|
|
681
652
|
.setSmallIcon(logo)
|
|
682
653
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
683
|
-
notificationBuilder.
|
|
654
|
+
notificationBuilder.foregroundServiceBehavior = NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE
|
|
684
655
|
}
|
|
685
656
|
val notification = notificationBuilder.build()
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
692
|
-
|
|
693
|
-
if (intent == null) {
|
|
694
|
-
return START_NOT_STICKY
|
|
657
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
658
|
+
startForeground(EMPTY_NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
|
|
659
|
+
} else {
|
|
660
|
+
startForeground(EMPTY_NOTIFICATION_ID, notification)
|
|
695
661
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
if (Build.VERSION.SDK_INT >= 33) {
|
|
701
|
-
try {
|
|
702
|
-
startAndStopEmptyNotificationToAvoidANR()
|
|
703
|
-
} catch (_: java.lang.Exception) {}
|
|
704
|
-
}
|
|
705
|
-
|
|
662
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
663
|
+
stopForeground(STOP_FOREGROUND_REMOVE)
|
|
664
|
+
} else {
|
|
706
665
|
@Suppress("DEPRECATION")
|
|
707
|
-
|
|
708
|
-
if (intentExtra!!.keyCode == KEYCODE_MEDIA_STOP) {
|
|
709
|
-
intentToStop = true
|
|
710
|
-
startServiceOreoAndAbove()
|
|
711
|
-
stopSelf()
|
|
712
|
-
} else {
|
|
713
|
-
intentToStop = false
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
if (manager != null) {
|
|
717
|
-
MediaButtonReceiver.handleIntent(manager!!.metadata.session, intent)
|
|
718
|
-
return START_NOT_STICKY
|
|
719
|
-
}
|
|
666
|
+
stopForeground(true)
|
|
720
667
|
}
|
|
668
|
+
}
|
|
721
669
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
super.onStartCommand(intent, flags, startId)
|
|
728
|
-
return START_NOT_STICKY
|
|
670
|
+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
671
|
+
startTask(getTaskConfig(intent))
|
|
672
|
+
if (intent != null) startAndStopEmptyNotificationToAvoidANR()
|
|
673
|
+
return START_STICKY
|
|
729
674
|
}
|
|
730
675
|
|
|
731
676
|
private fun startServiceOreoAndAbove() {
|
|
@@ -750,9 +695,9 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
750
695
|
NotificationCompat.PRIORITY_MIN
|
|
751
696
|
).build()
|
|
752
697
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
753
|
-
startForeground(
|
|
698
|
+
startForeground(EMPTY_NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
|
|
754
699
|
} else {
|
|
755
|
-
startForeground(
|
|
700
|
+
startForeground(EMPTY_NOTIFICATION_ID, notification)
|
|
756
701
|
}
|
|
757
702
|
}
|
|
758
703
|
}
|
|
@@ -1185,6 +1130,9 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
1185
1130
|
notification = it.notification;
|
|
1186
1131
|
if (it.ongoing) {
|
|
1187
1132
|
if (player?.playWhenReady == true) {
|
|
1133
|
+
if (sWakeLock?.isHeld != true) {
|
|
1134
|
+
sWakeLock?.acquire()
|
|
1135
|
+
}
|
|
1188
1136
|
startForegroundIfNecessary()
|
|
1189
1137
|
}
|
|
1190
1138
|
} else if (shouldStopForeground()) {
|
|
@@ -1194,7 +1142,10 @@ class MusicService : HeadlessJsMediaService() {
|
|
|
1194
1142
|
// user's queue is complete. This prevents the service from potentially
|
|
1195
1143
|
// being immediately destroyed once the player finishes playing media.
|
|
1196
1144
|
scope.launch {
|
|
1197
|
-
|
|
1145
|
+
if (sWakeLock?.isHeld == true) {
|
|
1146
|
+
sWakeLock?.release()
|
|
1147
|
+
}
|
|
1148
|
+
// delay(stopForegroundGracePeriod.toLong() * 1000)
|
|
1198
1149
|
// if (shouldStopForeground()) {
|
|
1199
1150
|
// @Suppress("DEPRECATION")
|
|
1200
1151
|
// stopForeground(removeNotificationWhenNotOngoing)
|