@javascriptcommon/react-native-track-player 1.2.24 → 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.
@@ -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
 
@@ -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.session.MediaButtonReceiver
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
- @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
155
+ @MainThread
156
+ override fun onBind(intent: Intent?): IBinder? {
157
+ if (intent == null) {
158
+ return super.onBind(intent)
164
159
  }
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
- }
160
+
161
+ val intentAction = intent.action
162
+ if (intentAction == MediaBrowserServiceCompat.SERVICE_INTERFACE) {
163
+ return super.onBind(intent)
186
164
  }
187
- }
188
165
 
189
- override fun onBind(intent: Intent): IBinder? {
190
- /* if(Utils.CONNECT_INTENT.equals(intent.getAction())) {
191
- return new MusicBinder(this, manager);
166
+ if (manager == null) {
167
+ manager = MusicManager(this)
192
168
  }
193
- return super.onBind(intent); */
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) {
@@ -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,14 +646,12 @@ 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: NotificationCompat.Builder = NotificationCompat.Builder(
677
- this, "Playback"
678
- )
649
+ val notificationBuilder = NotificationCompat.Builder(this, "Playback")
679
650
  .setPriority(NotificationCompat.PRIORITY_LOW)
680
- .setCategory(NotificationCompat.CATEGORY_SERVICE)
651
+ .setCategory(Notification.CATEGORY_SERVICE)
681
652
  .setSmallIcon(logo)
682
653
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
683
- notificationBuilder.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
654
+ notificationBuilder.foregroundServiceBehavior = NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE
684
655
  }
685
656
  val notification = notificationBuilder.build()
686
657
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
@@ -697,43 +668,9 @@ class MusicService : HeadlessJsMediaService() {
697
668
  }
698
669
 
699
670
  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
700
-
701
- if (intent == null) {
702
- return START_NOT_STICKY
703
- }
704
-
705
- 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
- @Suppress("DEPRECATION")
715
- val intentExtra: KeyEvent? = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)
716
- if (intentExtra!!.keyCode == KEYCODE_MEDIA_STOP) {
717
- intentToStop = true
718
- startServiceOreoAndAbove()
719
- stopSelf()
720
- } else {
721
- intentToStop = false
722
- }
723
-
724
- if (manager != null) {
725
- MediaButtonReceiver.handleIntent(manager!!.metadata.session, intent)
726
- return START_NOT_STICKY
727
- }
728
- }
729
-
730
- if (manager == null) manager = MusicManager(this)
731
-
732
- @Suppress("DEPRECATION")
733
- if (handler == null) handler = Handler()
734
-
735
- super.onStartCommand(intent, flags, startId)
736
- return START_NOT_STICKY
671
+ startTask(getTaskConfig(intent))
672
+ if (intent != null) startAndStopEmptyNotificationToAvoidANR()
673
+ return START_STICKY
737
674
  }
738
675
 
739
676
  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.25",
4
4
  "description": "A fully fledged audio module created for music apps",
5
5
  "contributors": [
6
6
  {