@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
- binder = service as MusicBinder
127
+ try {
128
+ binder = service as? MusicBinder ?: return@launch
129
129
  connecting = false
130
+
130
131
  if (musicService == null) {
131
- val binder: MusicBinder = service
132
- musicService = binder.service
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
- context.bindService(intent, this, 0)
208
- connecting = true
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
- while (true) {
642
+ var bindSuccess = false
643
+ while (retries <= 10 && !bindSuccess) {
618
644
  try {
619
- Intent(reactApplicationContext, MusicService::class.java).also { intent ->
620
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
621
- reactApplicationContext.startForegroundService(intent)
622
- } else {
623
- reactApplicationContext.startService(intent)
624
- }
625
- reactApplicationContext.bindService(intent, musicModule, Context.BIND_AUTO_CREATE)
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
- throw exception
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
- @SuppressLint("VisibleForTests")
159
- private fun onStartForeground() {
160
- var serviceForeground = false
161
- if (manager != null) {
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
- if (!serviceForeground) {
167
- val currentReactContext = if (bridgelessEnabled) reactHost.currentReactContext else reactNativeHost.reactInstanceManager.currentReactContext
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
- override fun onBind(intent: Intent): IBinder? {
190
- /* if(Utils.CONNECT_INTENT.equals(intent.getAction())) {
191
- return new MusicBinder(this, manager);
169
+ if (manager == null) {
170
+ manager = MusicManager(this)
192
171
  }
193
- return super.onBind(intent); */
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: NotificationCompat.Builder = NotificationCompat.Builder(
677
- this, "Playback"
678
- )
652
+ val notificationBuilder = NotificationCompat.Builder(this, "Playback")
679
653
  .setPriority(NotificationCompat.PRIORITY_LOW)
680
- .setCategory(NotificationCompat.CATEGORY_SERVICE)
654
+ .setCategory(Notification.CATEGORY_SERVICE)
681
655
  .setSmallIcon(logo)
682
656
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
683
- notificationBuilder.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
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
- super.onStartCommand(intent, flags, startId)
736
- return START_NOT_STICKY
700
+ startTask(getTaskConfig(intent))
701
+ startAndStopEmptyNotificationToAvoidANR()
702
+ return START_STICKY
737
703
  }
738
704
 
739
705
  private fun startServiceOreoAndAbove() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@javascriptcommon/react-native-track-player",
3
- "version": "1.2.24",
3
+ "version": "1.2.26",
4
4
  "description": "A fully fledged audio module created for music apps",
5
5
  "contributors": [
6
6
  {