react-native-audio-api 0.6.0-rc.0 → 0.6.0-rc.1

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 (152) hide show
  1. package/android/CMakeLists.txt +6 -3
  2. package/android/build.gradle +1 -0
  3. package/android/src/main/java/com/swmansion/audioapi/AudioManagerModule.kt +15 -12
  4. package/android/src/main/java/com/swmansion/audioapi/system/AudioFocusListener.kt +60 -0
  5. package/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt +294 -0
  6. package/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt +279 -0
  7. package/android/src/main/java/com/swmansion/audioapi/system/MediaReceiver.kt +46 -0
  8. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt +39 -0
  9. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionEventEmitter.kt +84 -0
  10. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionManager.kt +144 -0
  11. package/android/src/main/res/drawable/next.xml +9 -0
  12. package/android/src/main/res/drawable/pause.xml +9 -0
  13. package/android/src/main/res/drawable/play.xml +9 -0
  14. package/android/src/main/res/drawable/previous.xml +9 -0
  15. package/android/src/main/res/drawable/skip_backward_5.xml +9 -0
  16. package/android/src/main/res/drawable/skip_forward_5.xml +9 -0
  17. package/android/src/main/res/drawable/stop.xml +9 -0
  18. package/app.plugin.js +1 -0
  19. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +1 -6
  20. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +8 -4
  21. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +1 -0
  22. package/common/cpp/audioapi/core/utils/AudioNodeDestructor.cpp +3 -3
  23. package/ios/audioapi/ios/AudioManagerModule.mm +9 -7
  24. package/ios/audioapi/ios/system/LockScreenManager.mm +4 -8
  25. package/ios/audioapi/ios/system/NotificationManager.h +7 -1
  26. package/ios/audioapi/ios/system/NotificationManager.mm +66 -44
  27. package/lib/commonjs/api.js +197 -0
  28. package/lib/commonjs/api.js.map +1 -0
  29. package/lib/commonjs/api.web.js +219 -0
  30. package/lib/commonjs/api.web.js.map +1 -0
  31. package/lib/commonjs/core/AnalyserNode.js +71 -0
  32. package/lib/commonjs/core/AnalyserNode.js.map +1 -0
  33. package/lib/commonjs/core/AudioBuffer.js +44 -0
  34. package/lib/commonjs/core/AudioBuffer.js.map +1 -0
  35. package/lib/commonjs/core/AudioBufferSourceNode.js +68 -0
  36. package/lib/commonjs/core/AudioBufferSourceNode.js.map +1 -0
  37. package/lib/commonjs/core/AudioContext.js +29 -0
  38. package/lib/commonjs/core/AudioContext.js.map +1 -0
  39. package/lib/commonjs/core/AudioDestinationNode.js +11 -0
  40. package/lib/commonjs/core/AudioDestinationNode.js.map +1 -0
  41. package/lib/commonjs/core/AudioNode.js +30 -0
  42. package/lib/commonjs/core/AudioNode.js.map +1 -0
  43. package/lib/commonjs/core/AudioParam.js +82 -0
  44. package/lib/commonjs/core/AudioParam.js.map +1 -0
  45. package/lib/commonjs/core/AudioScheduledSourceNode.js +38 -0
  46. package/lib/commonjs/core/AudioScheduledSourceNode.js.map +1 -0
  47. package/lib/commonjs/core/BaseAudioContext.js +80 -0
  48. package/lib/commonjs/core/BaseAudioContext.js.map +1 -0
  49. package/lib/commonjs/core/BiquadFilterNode.js +33 -0
  50. package/lib/commonjs/core/BiquadFilterNode.js.map +1 -0
  51. package/lib/commonjs/core/GainNode.js +17 -0
  52. package/lib/commonjs/core/GainNode.js.map +1 -0
  53. package/lib/commonjs/core/OfflineAudioContext.js +63 -0
  54. package/lib/commonjs/core/OfflineAudioContext.js.map +1 -0
  55. package/lib/commonjs/core/OscillatorNode.js +32 -0
  56. package/lib/commonjs/core/OscillatorNode.js.map +1 -0
  57. package/lib/commonjs/core/PeriodicWave.js +15 -0
  58. package/lib/commonjs/core/PeriodicWave.js.map +1 -0
  59. package/lib/commonjs/core/StereoPannerNode.js +17 -0
  60. package/lib/commonjs/core/StereoPannerNode.js.map +1 -0
  61. package/lib/commonjs/errors/IndexSizeError.js +14 -0
  62. package/lib/commonjs/errors/IndexSizeError.js.map +1 -0
  63. package/lib/commonjs/errors/InvalidAccessError.js +14 -0
  64. package/lib/commonjs/errors/InvalidAccessError.js.map +1 -0
  65. package/lib/commonjs/errors/InvalidStateError.js +14 -0
  66. package/lib/commonjs/errors/InvalidStateError.js.map +1 -0
  67. package/lib/commonjs/errors/NotSupportedError.js +14 -0
  68. package/lib/commonjs/errors/NotSupportedError.js.map +1 -0
  69. package/lib/commonjs/errors/RangeError.js +14 -0
  70. package/lib/commonjs/errors/RangeError.js.map +1 -0
  71. package/lib/commonjs/errors/index.js +42 -0
  72. package/lib/commonjs/errors/index.js.map +1 -0
  73. package/lib/commonjs/index.js +17 -0
  74. package/lib/commonjs/index.js.map +1 -0
  75. package/lib/commonjs/interfaces.js +6 -0
  76. package/lib/commonjs/interfaces.js.map +1 -0
  77. package/lib/commonjs/package.json +1 -0
  78. package/lib/commonjs/plugin/withAudioAPI.js +62 -0
  79. package/lib/commonjs/plugin/withAudioAPI.js.map +1 -0
  80. package/lib/commonjs/specs/NativeAudioAPIModule.js +9 -0
  81. package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -0
  82. package/lib/commonjs/specs/NativeAudioManagerModule.js +33 -0
  83. package/lib/commonjs/specs/NativeAudioManagerModule.js.map +1 -0
  84. package/lib/commonjs/specs/index.js +27 -0
  85. package/lib/commonjs/specs/index.js.map +1 -0
  86. package/lib/commonjs/system/AudioManager.js +79 -0
  87. package/lib/commonjs/system/AudioManager.js.map +1 -0
  88. package/lib/commonjs/system/index.js +14 -0
  89. package/lib/commonjs/system/index.js.map +1 -0
  90. package/lib/commonjs/system/types.js +2 -0
  91. package/lib/commonjs/system/types.js.map +1 -0
  92. package/lib/commonjs/types.js +2 -0
  93. package/lib/commonjs/types.js.map +1 -0
  94. package/lib/commonjs/utils/index.js +10 -0
  95. package/lib/commonjs/utils/index.js.map +1 -0
  96. package/lib/commonjs/web-core/AnalyserNode.js +38 -0
  97. package/lib/commonjs/web-core/AnalyserNode.js.map +1 -0
  98. package/lib/commonjs/web-core/AudioBuffer.js +44 -0
  99. package/lib/commonjs/web-core/AudioBuffer.js.map +1 -0
  100. package/lib/commonjs/web-core/AudioBufferSourceNode.js +214 -0
  101. package/lib/commonjs/web-core/AudioBufferSourceNode.js.map +1 -0
  102. package/lib/commonjs/web-core/AudioContext.js +93 -0
  103. package/lib/commonjs/web-core/AudioContext.js.map +1 -0
  104. package/lib/commonjs/web-core/AudioDestinationNode.js +11 -0
  105. package/lib/commonjs/web-core/AudioDestinationNode.js.map +1 -0
  106. package/lib/commonjs/web-core/AudioNode.js +33 -0
  107. package/lib/commonjs/web-core/AudioNode.js.map +1 -0
  108. package/lib/commonjs/web-core/AudioParam.js +81 -0
  109. package/lib/commonjs/web-core/AudioParam.js.map +1 -0
  110. package/lib/commonjs/web-core/AudioScheduledSourceNode.js +41 -0
  111. package/lib/commonjs/web-core/AudioScheduledSourceNode.js.map +1 -0
  112. package/lib/commonjs/web-core/BaseAudioContext.js +2 -0
  113. package/lib/commonjs/web-core/BaseAudioContext.js.map +1 -0
  114. package/lib/commonjs/web-core/BiquadFilterNode.js +33 -0
  115. package/lib/commonjs/web-core/BiquadFilterNode.js.map +1 -0
  116. package/lib/commonjs/web-core/GainNode.js +17 -0
  117. package/lib/commonjs/web-core/GainNode.js.map +1 -0
  118. package/lib/commonjs/web-core/OfflineAudioContext.js +96 -0
  119. package/lib/commonjs/web-core/OfflineAudioContext.js.map +1 -0
  120. package/lib/commonjs/web-core/OscillatorNode.js +31 -0
  121. package/lib/commonjs/web-core/OscillatorNode.js.map +1 -0
  122. package/lib/commonjs/web-core/PeriodicWave.js +15 -0
  123. package/lib/commonjs/web-core/PeriodicWave.js.map +1 -0
  124. package/lib/commonjs/web-core/StereoPannerNode.js +17 -0
  125. package/lib/commonjs/web-core/StereoPannerNode.js.map +1 -0
  126. package/lib/commonjs/web-core/custom/LoadCustomWasm.js +37 -0
  127. package/lib/commonjs/web-core/custom/LoadCustomWasm.js.map +1 -0
  128. package/lib/commonjs/web-core/custom/index.js +14 -0
  129. package/lib/commonjs/web-core/custom/index.js.map +1 -0
  130. package/lib/commonjs/web-core/custom/signalsmithStretch/LICENSE.txt +21 -0
  131. package/lib/commonjs/web-core/custom/signalsmithStretch/README.md +46 -0
  132. package/lib/commonjs/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs +826 -0
  133. package/lib/commonjs/web-core/custom/signalsmithStretch/SignalsmithStretch.mjs.map +1 -0
  134. package/lib/module/plugin/withAudioAPI.js +58 -0
  135. package/lib/module/plugin/withAudioAPI.js.map +1 -0
  136. package/lib/module/specs/NativeAudioManagerModule.js +5 -6
  137. package/lib/module/specs/NativeAudioManagerModule.js.map +1 -1
  138. package/lib/module/system/AudioManager.js +9 -0
  139. package/lib/module/system/AudioManager.js.map +1 -1
  140. package/lib/typescript/plugin/withAudioAPI.d.ts +9 -0
  141. package/lib/typescript/plugin/withAudioAPI.d.ts.map +1 -0
  142. package/lib/typescript/specs/NativeAudioManagerModule.d.ts +2 -1
  143. package/lib/typescript/specs/NativeAudioManagerModule.d.ts.map +1 -1
  144. package/lib/typescript/system/AudioManager.d.ts +4 -2
  145. package/lib/typescript/system/AudioManager.d.ts.map +1 -1
  146. package/lib/typescript/system/types.d.ts +16 -4
  147. package/lib/typescript/system/types.d.ts.map +1 -1
  148. package/package.json +6 -3
  149. package/src/plugin/withAudioAPI.ts +90 -0
  150. package/src/specs/NativeAudioManagerModule.ts +8 -11
  151. package/src/system/AudioManager.ts +42 -15
  152. package/src/system/types.ts +20 -4
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.12.0)
2
2
  project(react-native-audio-api)
3
3
 
4
4
  set(CMAKE_VERBOSE_MAKEFILE ON)
5
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
5
6
  set(CMAKE_CXX_STANDARD 20)
6
7
 
7
8
  # Detect the processor and SIMD support
@@ -14,9 +15,11 @@ endif()
14
15
  include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
15
16
  add_compile_options(${folly_FLAGS})
16
17
 
17
- # string(APPEND CMAKE_CXX_FLAGS
18
- # " -fexceptions -fno-omit-frame-pointer -frtti -fstack-protector-all\
19
- # -std=c++${CMAKE_CXX_STANDARD} -Wall -Werror")
18
+ # frtti - enable Run-Time Type Information (dynamic_cast, typeid)
19
+ # -std=c++20 - use C++20 standard
20
+ # -Wall - enable all compiler's warning messages
21
+ string(APPEND CMAKE_CXX_FLAGS
22
+ " -frtti -std=c++${CMAKE_CXX_STANDARD} -Wall")
20
23
 
21
24
  if(${IS_NEW_ARCHITECTURE_ENABLED})
22
25
  string(APPEND CMAKE_CXX_FLAGS " -DRCT_NEW_ARCH_ENABLED")
@@ -218,6 +218,7 @@ dependencies {
218
218
  implementation 'androidx.core:core-ktx:1.13.1'
219
219
  implementation 'com.facebook.fbjni:fbjni:0.6.0'
220
220
  implementation 'com.google.oboe:oboe:1.9.0'
221
+ implementation 'androidx.media:media:1.7.0'
221
222
  }
222
223
 
223
224
  def assertMinimalReactNativeVersion = task assertMinimalReactNativeVersionTask {
@@ -1,12 +1,11 @@
1
1
  package com.swmansion.audioapi
2
2
 
3
- import android.content.Context
4
- import android.media.AudioManager
5
3
  import com.facebook.react.bridge.ReactApplicationContext
6
4
  import com.facebook.react.bridge.ReactContextBaseJavaModule
7
5
  import com.facebook.react.bridge.ReactMethod
8
6
  import com.facebook.react.bridge.ReadableArray
9
7
  import com.facebook.react.bridge.ReadableMap
8
+ import com.swmansion.audioapi.system.MediaSessionManager
10
9
 
11
10
  class AudioManagerModule(
12
11
  reactContext: ReactApplicationContext,
@@ -15,7 +14,7 @@ class AudioManagerModule(
15
14
  const val NAME = "AudioManagerModule"
16
15
  }
17
16
 
18
- private val audioManager: AudioManager = reactContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
17
+ private val mediaSessionManager: MediaSessionManager = MediaSessionManager(reactContext)
19
18
 
20
19
  init {
21
20
  try {
@@ -25,35 +24,39 @@ class AudioManagerModule(
25
24
  }
26
25
  }
27
26
 
28
- @ReactMethod
27
+ @ReactMethod(isBlockingSynchronousMethod = true)
29
28
  fun setLockScreenInfo(info: ReadableMap?) {
29
+ mediaSessionManager.setLockScreenInfo(info)
30
30
  }
31
31
 
32
- @ReactMethod
32
+ @ReactMethod(isBlockingSynchronousMethod = true)
33
33
  fun resetLockScreenInfo() {
34
+ mediaSessionManager.resetLockScreenInfo()
34
35
  }
35
36
 
36
- @ReactMethod
37
+ @ReactMethod(isBlockingSynchronousMethod = true)
37
38
  fun enableRemoteCommand(
38
- name: String?,
39
+ name: String,
39
40
  enabled: Boolean,
40
41
  ) {
42
+ mediaSessionManager.enableRemoteCommand(name, enabled)
41
43
  }
42
44
 
43
- @ReactMethod
45
+ @ReactMethod(isBlockingSynchronousMethod = true)
44
46
  fun setAudioSessionOptions(
45
47
  category: String?,
46
48
  mode: String?,
47
49
  options: ReadableArray?,
48
50
  active: Boolean,
49
51
  ) {
52
+ // Nothing to do here
50
53
  }
51
54
 
52
55
  @ReactMethod(isBlockingSynchronousMethod = true)
53
- fun getDevicePreferredSampleRate(): Double {
54
- val sampleRate = this.audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)
55
- return sampleRate.toDouble()
56
- }
56
+ fun getDevicePreferredSampleRate(): Double = mediaSessionManager.getDevicePreferredSampleRate()
57
+
58
+ @ReactMethod(isBlockingSynchronousMethod = true)
59
+ fun observeAudioInterruptions(enable: Boolean) = mediaSessionManager.observeAudioInterruptions(enable)
57
60
 
58
61
  override fun getName(): String = NAME
59
62
  }
@@ -0,0 +1,60 @@
1
+ package com.swmansion.audioapi.system
2
+
3
+ import android.media.AudioFocusRequest
4
+ import android.media.AudioManager
5
+ import android.os.Build
6
+ import android.util.Log
7
+
8
+ class AudioFocusListener(
9
+ private val audioManager: AudioManager,
10
+ val eventEmitter: MediaSessionEventEmitter,
11
+ private val lockScreenManager: LockScreenManager,
12
+ ) : AudioManager.OnAudioFocusChangeListener {
13
+ private var playOnAudioFocus = false
14
+ private var focusRequest: AudioFocusRequest? = null
15
+
16
+ override fun onAudioFocusChange(focusChange: Int) {
17
+ Log.d("AudioFocusListener", "onAudioFocusChange: $focusChange")
18
+ when (focusChange) {
19
+ AudioManager.AUDIOFOCUS_LOSS -> {
20
+ playOnAudioFocus = false
21
+ eventEmitter.onInterruption(mapOf("type" to "began", "shouldResume" to false))
22
+ }
23
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
24
+ playOnAudioFocus = lockScreenManager.isPlaying
25
+ eventEmitter.onInterruption(mapOf("type" to "began", "shouldResume" to playOnAudioFocus))
26
+ }
27
+ AudioManager.AUDIOFOCUS_GAIN -> {
28
+ if (playOnAudioFocus) {
29
+ eventEmitter.onInterruption(mapOf("type" to "ended", "shouldResume" to true))
30
+ } else {
31
+ eventEmitter.onInterruption(mapOf("type" to "ended", "shouldResume" to false))
32
+ }
33
+
34
+ playOnAudioFocus = false
35
+ }
36
+ }
37
+ }
38
+
39
+ fun requestAudioFocus() {
40
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
41
+ this.focusRequest =
42
+ AudioFocusRequest
43
+ .Builder(AudioManager.AUDIOFOCUS_GAIN)
44
+ .setOnAudioFocusChangeListener(this)
45
+ .build()
46
+
47
+ audioManager.requestAudioFocus(focusRequest!!)
48
+ } else {
49
+ audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
50
+ }
51
+ }
52
+
53
+ fun abandonAudioFocus() {
54
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this.focusRequest != null) {
55
+ audioManager.abandonAudioFocusRequest(focusRequest!!)
56
+ } else {
57
+ audioManager.abandonAudioFocus(this)
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,294 @@
1
+ package com.swmansion.audioapi.system
2
+
3
+ import android.graphics.Bitmap
4
+ import android.graphics.BitmapFactory
5
+ import android.graphics.drawable.BitmapDrawable
6
+ import android.support.v4.media.MediaMetadataCompat
7
+ import android.support.v4.media.session.MediaSessionCompat
8
+ import android.support.v4.media.session.PlaybackStateCompat
9
+ import android.util.Log
10
+ import androidx.core.app.NotificationCompat
11
+ import androidx.media.app.NotificationCompat.MediaStyle
12
+ import com.facebook.react.bridge.ReactApplicationContext
13
+ import com.facebook.react.bridge.ReadableMap
14
+ import com.facebook.react.bridge.ReadableType
15
+ import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper.Companion.instance
16
+ import java.io.IOException
17
+ import java.net.URL
18
+
19
+ class LockScreenManager(
20
+ private val reactContext: ReactApplicationContext,
21
+ private val mediaSession: MediaSessionCompat,
22
+ private val mediaNotificationManager: MediaNotificationManager,
23
+ val channelId: String,
24
+ ) {
25
+ private var pb: PlaybackStateCompat.Builder = PlaybackStateCompat.Builder()
26
+ private var state: PlaybackStateCompat = pb.build()
27
+ private var controls: Long = 0
28
+ var isPlaying: Boolean = false
29
+
30
+ private var nb: NotificationCompat.Builder = NotificationCompat.Builder(reactContext, channelId)
31
+
32
+ private var artworkThread: Thread? = null
33
+
34
+ private var title: String? = null
35
+ private var artist: String? = null
36
+ private var album: String? = null
37
+ private var description: String? = null
38
+ private var duration: Long = 0
39
+ private var speed: Float = 1.0F
40
+ private var elapsedTime: Long = 0L
41
+ private var artwork: String? = null
42
+ private var playbackState: Int = PlaybackStateCompat.STATE_PAUSED
43
+
44
+ init {
45
+ this.pb.setActions(controls)
46
+
47
+ this.nb.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
48
+ this.nb.setPriority(NotificationCompat.PRIORITY_HIGH)
49
+
50
+ updateNotificationMediaStyle()
51
+
52
+ mediaNotificationManager.updateActions(controls)
53
+ }
54
+
55
+ fun setLockScreenInfo(info: ReadableMap?) {
56
+ if (artworkThread != null && artworkThread!!.isAlive) {
57
+ artworkThread!!.interrupt()
58
+ }
59
+
60
+ artworkThread = null
61
+
62
+ if (info == null) {
63
+ return
64
+ }
65
+
66
+ val md = MediaMetadataCompat.Builder()
67
+
68
+ if (info.hasKey("title")) {
69
+ title = info.getString("title")
70
+ }
71
+
72
+ if (info.hasKey("artist")) {
73
+ artist = info.getString("artist")
74
+ }
75
+
76
+ if (info.hasKey("album")) {
77
+ album = info.getString("album")
78
+ }
79
+
80
+ if (info.hasKey("description")) {
81
+ description = info.getString("description")
82
+ }
83
+
84
+ if (info.hasKey("duration")) {
85
+ duration = (info.getDouble("duration") * 1000).toLong()
86
+ }
87
+
88
+ md.putText(MediaMetadataCompat.METADATA_KEY_TITLE, title)
89
+ md.putText(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
90
+ md.putText(MediaMetadataCompat.METADATA_KEY_ALBUM, album)
91
+ md.putText(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, description)
92
+ md.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration)
93
+
94
+ nb.setContentTitle(title)
95
+ nb.setContentText(artist)
96
+ nb.setContentInfo(album)
97
+
98
+ if (info.hasKey("artwork")) {
99
+ var localArtwork = false
100
+
101
+ if (info.getType("artwork") == ReadableType.Map) {
102
+ artwork = info.getMap("artwork")?.getString("uri")
103
+ localArtwork = true
104
+ } else {
105
+ artwork = info.getString("artwork")
106
+ }
107
+
108
+ val artworkLocal = localArtwork
109
+
110
+ artworkThread =
111
+ Thread {
112
+ try {
113
+ val bitmap: Bitmap? = artwork?.let { loadArtwork(it, artworkLocal) }
114
+
115
+ val currentMetadata: MediaMetadataCompat = mediaSession.controller.metadata
116
+ val newBuilder =
117
+ MediaMetadataCompat.Builder(
118
+ currentMetadata,
119
+ )
120
+ mediaSession.setMetadata(
121
+ newBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmap).build(),
122
+ )
123
+
124
+ nb.setLargeIcon(bitmap)
125
+ mediaNotificationManager.show(nb, isPlaying)
126
+
127
+ artworkThread = null
128
+ } catch (ex: Exception) {
129
+ ex.printStackTrace()
130
+ }
131
+ }
132
+ artworkThread!!.start()
133
+ } else {
134
+ md.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, null)
135
+ nb.setLargeIcon(null as Bitmap?)
136
+ }
137
+
138
+ speed =
139
+ if (info.hasKey("speed")) {
140
+ info.getDouble("speed").toFloat()
141
+ } else {
142
+ state.playbackSpeed
143
+ }
144
+
145
+ elapsedTime =
146
+ if (info.hasKey("elapsedTime")) {
147
+ info.getDouble("elapsedTime").toLong()
148
+ } else {
149
+ state.position
150
+ }
151
+
152
+ if (info.hasKey("state")) {
153
+ val state = info.getString("state")
154
+
155
+ when (state) {
156
+ "state_playing" -> {
157
+ this.playbackState = PlaybackStateCompat.STATE_PLAYING
158
+ }
159
+ "state_paused" -> {
160
+ this.playbackState = PlaybackStateCompat.STATE_PAUSED
161
+ }
162
+ }
163
+ }
164
+
165
+ updatePlaybackState(this.playbackState)
166
+
167
+ mediaSession.setMetadata(md.build())
168
+ mediaSession.setActive(true)
169
+ mediaNotificationManager.show(nb, isPlaying)
170
+ }
171
+
172
+ fun resetLockScreenInfo() {
173
+ if (artworkThread != null && artworkThread!!.isAlive) artworkThread!!.interrupt()
174
+ artworkThread = null
175
+
176
+ mediaNotificationManager.hide()
177
+ mediaSession.setActive(false)
178
+ }
179
+
180
+ fun enableRemoteCommand(
181
+ name: String,
182
+ enabled: Boolean,
183
+ ) {
184
+ var controlValue = 0L
185
+ when (name) {
186
+ "play" -> controlValue = PlaybackStateCompat.ACTION_PLAY
187
+ "pause" -> controlValue = PlaybackStateCompat.ACTION_PAUSE
188
+ "stop" -> controlValue = PlaybackStateCompat.ACTION_STOP
189
+ "togglePlayPause" -> controlValue = PlaybackStateCompat.ACTION_PLAY_PAUSE
190
+ "nextTrack" -> controlValue = PlaybackStateCompat.ACTION_SKIP_TO_NEXT
191
+ "previousTrack" -> controlValue = PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
192
+ "skipForward" -> controlValue = PlaybackStateCompat.ACTION_REWIND
193
+ "skipBackward" -> controlValue = PlaybackStateCompat.ACTION_REWIND
194
+ }
195
+
196
+ controls =
197
+ if (enabled) {
198
+ controls or controlValue
199
+ } else {
200
+ controls and controlValue.inv()
201
+ }
202
+
203
+ mediaNotificationManager.updateActions(controls)
204
+ pb.setActions(controls)
205
+
206
+ state = pb.build()
207
+ mediaSession.setPlaybackState(state)
208
+
209
+ updateNotificationMediaStyle()
210
+
211
+ if (mediaSession.isActive) {
212
+ mediaNotificationManager.show(nb, isPlaying)
213
+ }
214
+ }
215
+
216
+ private fun loadArtwork(
217
+ url: String,
218
+ local: Boolean,
219
+ ): Bitmap? {
220
+ var bitmap: Bitmap? = null
221
+
222
+ try {
223
+ // If we are running the app in debug mode, the "local" image will be served from htt://localhost:8080, so we need to check for this case and load those images from URL
224
+ if (local && !url.startsWith("http")) {
225
+ // Gets the drawable from the RN's helper for local resources
226
+ val helper = instance
227
+ val image = helper.getResourceDrawable(reactContext, url)
228
+
229
+ bitmap =
230
+ if (image is BitmapDrawable) {
231
+ image.bitmap
232
+ } else {
233
+ BitmapFactory.decodeFile(url)
234
+ }
235
+ } else {
236
+ // Open connection to the URL and decodes the image
237
+ val con = URL(url).openConnection()
238
+ con.connect()
239
+ val input = con.getInputStream()
240
+ bitmap = BitmapFactory.decodeStream(input)
241
+ input.close()
242
+ }
243
+ } catch (ex: IOException) {
244
+ Log.w("MediaSessionManager", "Could not load the artwork", ex)
245
+ } catch (ex: IndexOutOfBoundsException) {
246
+ Log.w("MediaSessionManager", "Could not load the artwork", ex)
247
+ }
248
+
249
+ return bitmap
250
+ }
251
+
252
+ fun updatePlaybackState(playbackState: Int) {
253
+ isPlaying = playbackState == PlaybackStateCompat.STATE_PLAYING
254
+
255
+ pb.setState(playbackState, elapsedTime, speed)
256
+ pb.setActions(controls)
257
+ state = pb.build()
258
+ mediaSession.setPlaybackState(state)
259
+ }
260
+
261
+ private fun hasControl(control: Long): Boolean = (controls and control) == control
262
+
263
+ private fun updateNotificationMediaStyle() {
264
+ val style = MediaStyle()
265
+ style.setMediaSession(mediaSession.sessionToken)
266
+ var controlCount = 0
267
+ if (hasControl(PlaybackStateCompat.ACTION_PLAY) ||
268
+ hasControl(PlaybackStateCompat.ACTION_PAUSE) ||
269
+ hasControl(
270
+ PlaybackStateCompat.ACTION_PLAY_PAUSE,
271
+ )
272
+ ) {
273
+ controlCount += 1
274
+ }
275
+ if (hasControl(PlaybackStateCompat.ACTION_SKIP_TO_NEXT)) {
276
+ controlCount += 1
277
+ }
278
+ if (hasControl(PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)) {
279
+ controlCount += 1
280
+ }
281
+ if (hasControl(PlaybackStateCompat.ACTION_FAST_FORWARD)) {
282
+ controlCount += 1
283
+ }
284
+ if (hasControl(PlaybackStateCompat.ACTION_REWIND)) {
285
+ controlCount += 1
286
+ }
287
+ val actions = IntArray(controlCount)
288
+ for (i in actions.indices) {
289
+ actions[i] = i
290
+ }
291
+ style.setShowActionsInCompactView(*actions)
292
+ nb.setStyle(style)
293
+ }
294
+ }