react-native-queue-player 1.0.0

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 (1218) hide show
  1. package/LICENSE +201 -0
  2. package/QueuePlayer.podspec +114 -0
  3. package/README.md +194 -0
  4. package/android/CMakeLists.txt +41 -0
  5. package/android/build.gradle +257 -0
  6. package/android/src/androidTest/java/com/margelo/nitro/queueplayer/SignalsmithStretchNativeTest.kt +136 -0
  7. package/android/src/main/AndroidManifest.xml +173 -0
  8. package/android/src/main/cpp/CMakeLists.txt +221 -0
  9. package/android/src/main/cpp/THIRD_PARTY_LICENSES +128 -0
  10. package/android/src/main/cpp/THIRD_PARTY_VERSIONS.txt +32 -0
  11. package/android/src/main/cpp/airplay2_audio.c +537 -0
  12. package/android/src/main/cpp/airplay2_audio.h +66 -0
  13. package/android/src/main/cpp/airplay2_control.c +680 -0
  14. package/android/src/main/cpp/airplay2_control.h +101 -0
  15. package/android/src/main/cpp/airplay2_crypto.c +548 -0
  16. package/android/src/main/cpp/airplay2_crypto.h +213 -0
  17. package/android/src/main/cpp/airplay2_dmap.c +81 -0
  18. package/android/src/main/cpp/airplay2_dmap.h +53 -0
  19. package/android/src/main/cpp/airplay2_filelog.h +100 -0
  20. package/android/src/main/cpp/airplay2_jni.cpp +579 -0
  21. package/android/src/main/cpp/airplay2_ntp.c +203 -0
  22. package/android/src/main/cpp/airplay2_ntp.h +39 -0
  23. package/android/src/main/cpp/airplay2_pair.c +653 -0
  24. package/android/src/main/cpp/airplay2_pair.h +114 -0
  25. package/android/src/main/cpp/airplay2_plist.c +840 -0
  26. package/android/src/main/cpp/airplay2_plist.h +184 -0
  27. package/android/src/main/cpp/airplay2_ptp.c +509 -0
  28. package/android/src/main/cpp/airplay2_ptp.h +40 -0
  29. package/android/src/main/cpp/airplay2_rtsp.c +693 -0
  30. package/android/src/main/cpp/airplay2_rtsp.h +90 -0
  31. package/android/src/main/cpp/airplay2_session.c +196 -0
  32. package/android/src/main/cpp/airplay2_session.h +197 -0
  33. package/android/src/main/cpp/airplay2_tlv.c +135 -0
  34. package/android/src/main/cpp/airplay2_tlv.h +87 -0
  35. package/android/src/main/cpp/airplay_jni.cpp +589 -0
  36. package/android/src/main/cpp/build-openssl-android.sh +94 -0
  37. package/android/src/main/cpp/cpp-adapter.cpp +11 -0
  38. package/android/src/main/cpp/prebuilt/libsodium/arm64-v8a/lib/libsodium.a +0 -0
  39. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/core.h +28 -0
  40. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_aead_aegis128l.h +92 -0
  41. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_aead_aegis256.h +92 -0
  42. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_aead_aes256gcm.h +179 -0
  43. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_aead_chacha20poly1305.h +180 -0
  44. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_aead_xchacha20poly1305.h +100 -0
  45. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_auth.h +46 -0
  46. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_auth_hmacsha256.h +70 -0
  47. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_auth_hmacsha512.h +68 -0
  48. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_auth_hmacsha512256.h +65 -0
  49. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_box.h +177 -0
  50. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_box_curve25519xchacha20poly1305.h +164 -0
  51. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_box_curve25519xsalsa20poly1305.h +112 -0
  52. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_ed25519.h +104 -0
  53. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_hchacha20.h +36 -0
  54. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_hsalsa20.h +36 -0
  55. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_ristretto255.h +100 -0
  56. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_salsa20.h +36 -0
  57. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_salsa2012.h +36 -0
  58. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_core_salsa208.h +40 -0
  59. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_generichash.h +84 -0
  60. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_generichash_blake2b.h +122 -0
  61. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_hash.h +40 -0
  62. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_hash_sha256.h +60 -0
  63. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_hash_sha512.h +60 -0
  64. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_kdf.h +53 -0
  65. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_kdf_blake2b.h +44 -0
  66. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_kdf_hkdf_sha256.h +74 -0
  67. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_kdf_hkdf_sha512.h +75 -0
  68. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_kx.h +66 -0
  69. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_onetimeauth.h +65 -0
  70. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_onetimeauth_poly1305.h +72 -0
  71. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_pwhash.h +147 -0
  72. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_pwhash_argon2i.h +122 -0
  73. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_pwhash_argon2id.h +122 -0
  74. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_pwhash_scryptsalsa208sha256.h +120 -0
  75. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_scalarmult.h +46 -0
  76. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_scalarmult_curve25519.h +42 -0
  77. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_scalarmult_ed25519.h +51 -0
  78. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_scalarmult_ristretto255.h +43 -0
  79. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_secretbox.h +93 -0
  80. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_secretbox_xchacha20poly1305.h +70 -0
  81. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_secretbox_xsalsa20poly1305.h +69 -0
  82. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_secretstream_xchacha20poly1305.h +108 -0
  83. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_shorthash.h +41 -0
  84. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_shorthash_siphash24.h +50 -0
  85. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_sign.h +107 -0
  86. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_sign_ed25519.h +124 -0
  87. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_sign_edwards25519sha512batch.h +55 -0
  88. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream.h +59 -0
  89. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_chacha20.h +106 -0
  90. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_salsa20.h +61 -0
  91. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_salsa2012.h +53 -0
  92. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_salsa208.h +56 -0
  93. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_xchacha20.h +61 -0
  94. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_stream_xsalsa20.h +61 -0
  95. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_verify_16.h +23 -0
  96. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_verify_32.h +23 -0
  97. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/crypto_verify_64.h +23 -0
  98. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/export.h +57 -0
  99. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/randombytes.h +72 -0
  100. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/randombytes_internal_random.h +22 -0
  101. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/randombytes_sysrandom.h +19 -0
  102. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/runtime.h +55 -0
  103. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/utils.h +179 -0
  104. package/android/src/main/cpp/prebuilt/libsodium/include/sodium/version.h +33 -0
  105. package/android/src/main/cpp/prebuilt/libsodium/include/sodium.h +75 -0
  106. package/android/src/main/cpp/prebuilt/libsodium/x86_64/lib/libsodium.a +0 -0
  107. package/android/src/main/cpp/prebuilt/openssl/arm64-v8a/lib/libcrypto.a +0 -0
  108. package/android/src/main/cpp/prebuilt/openssl/arm64-v8a/lib/libssl.a +0 -0
  109. package/android/src/main/cpp/prebuilt/openssl/x86_64/lib/libcrypto.a +0 -0
  110. package/android/src/main/cpp/prebuilt/openssl/x86_64/lib/libssl.a +0 -0
  111. package/android/src/main/cpp/stretch_jni.cpp +171 -0
  112. package/android/src/main/cpp/third_party/alac/addons/alac_wrapper.cpp +198 -0
  113. package/android/src/main/cpp/third_party/alac/addons/alac_wrapper.h +46 -0
  114. package/android/src/main/cpp/third_party/alac/codec/ALACAudioTypes.h +197 -0
  115. package/android/src/main/cpp/third_party/alac/codec/ALACBitUtilities.c +260 -0
  116. package/android/src/main/cpp/third_party/alac/codec/ALACBitUtilities.h +104 -0
  117. package/android/src/main/cpp/third_party/alac/codec/ALACDecoder.cpp +730 -0
  118. package/android/src/main/cpp/third_party/alac/codec/ALACDecoder.h +65 -0
  119. package/android/src/main/cpp/third_party/alac/codec/ALACEncoder.cpp +1425 -0
  120. package/android/src/main/cpp/third_party/alac/codec/ALACEncoder.h +92 -0
  121. package/android/src/main/cpp/third_party/alac/codec/EndianPortable.c +181 -0
  122. package/android/src/main/cpp/third_party/alac/codec/EndianPortable.h +59 -0
  123. package/android/src/main/cpp/third_party/alac/codec/ag_dec.c +362 -0
  124. package/android/src/main/cpp/third_party/alac/codec/ag_enc.c +370 -0
  125. package/android/src/main/cpp/third_party/alac/codec/aglib.h +81 -0
  126. package/android/src/main/cpp/third_party/alac/codec/dp_dec.c +381 -0
  127. package/android/src/main/cpp/third_party/alac/codec/dp_enc.c +386 -0
  128. package/android/src/main/cpp/third_party/alac/codec/dplib.h +61 -0
  129. package/android/src/main/cpp/third_party/alac/codec/matrix_dec.c +390 -0
  130. package/android/src/main/cpp/third_party/alac/codec/matrix_enc.c +342 -0
  131. package/android/src/main/cpp/third_party/alac/codec/matrixlib.h +80 -0
  132. package/android/src/main/cpp/third_party/crosstools/cross_log.c +95 -0
  133. package/android/src/main/cpp/third_party/crosstools/cross_log.h +25 -0
  134. package/android/src/main/cpp/third_party/crosstools/cross_net.c +1139 -0
  135. package/android/src/main/cpp/third_party/crosstools/cross_net.h +51 -0
  136. package/android/src/main/cpp/third_party/crosstools/cross_ssl.c +371 -0
  137. package/android/src/main/cpp/third_party/crosstools/cross_ssl.h +15 -0
  138. package/android/src/main/cpp/third_party/crosstools/cross_thread.c +134 -0
  139. package/android/src/main/cpp/third_party/crosstools/cross_thread.h +20 -0
  140. package/android/src/main/cpp/third_party/crosstools/cross_util.c +753 -0
  141. package/android/src/main/cpp/third_party/crosstools/cross_util.h +125 -0
  142. package/android/src/main/cpp/third_party/crosstools/platform.c +227 -0
  143. package/android/src/main/cpp/third_party/crosstools/platform.h +127 -0
  144. package/android/src/main/cpp/third_party/curve25519/include/curve25519_dh.h +53 -0
  145. package/android/src/main/cpp/third_party/curve25519/include/ed25519_signature.h +98 -0
  146. package/android/src/main/cpp/third_party/curve25519/include/external_calls.h +36 -0
  147. package/android/src/main/cpp/third_party/curve25519/source/BaseTypes.h +125 -0
  148. package/android/src/main/cpp/third_party/curve25519/source/base_folding8.h +1288 -0
  149. package/android/src/main/cpp/third_party/curve25519/source/curve25519_dh.c +208 -0
  150. package/android/src/main/cpp/third_party/curve25519/source/curve25519_mehdi.c +410 -0
  151. package/android/src/main/cpp/third_party/curve25519/source/curve25519_mehdi.h +175 -0
  152. package/android/src/main/cpp/third_party/curve25519/source/curve25519_order.c +155 -0
  153. package/android/src/main/cpp/third_party/curve25519/source/curve25519_utils.c +153 -0
  154. package/android/src/main/cpp/third_party/curve25519/source/custom_blind.c +27 -0
  155. package/android/src/main/cpp/third_party/curve25519/source/custom_blind.h +11 -0
  156. package/android/src/main/cpp/third_party/curve25519/source/ed25519_sign.c +419 -0
  157. package/android/src/main/cpp/third_party/curve25519/source/ed25519_signature.h +98 -0
  158. package/android/src/main/cpp/third_party/curve25519/source/ed25519_verify.c +313 -0
  159. package/android/src/main/cpp/third_party/curve25519/source/sha512.c +294 -0
  160. package/android/src/main/cpp/third_party/curve25519/source/sha512.h +93 -0
  161. package/android/src/main/cpp/third_party/dmap-parser/dmap_parser.c +545 -0
  162. package/android/src/main/cpp/third_party/dmap-parser/dmap_parser.h +90 -0
  163. package/android/src/main/cpp/third_party/libraop/aes.c +863 -0
  164. package/android/src/main/cpp/third_party/libraop/aes.h +18 -0
  165. package/android/src/main/cpp/third_party/libraop/aes_ctr.c +89 -0
  166. package/android/src/main/cpp/third_party/libraop/aes_ctr.h +59 -0
  167. package/android/src/main/cpp/third_party/libraop/alac.c +1135 -0
  168. package/android/src/main/cpp/third_party/libraop/alac.h +55 -0
  169. package/android/src/main/cpp/third_party/libraop/raop_client.c +1500 -0
  170. package/android/src/main/cpp/third_party/libraop/raop_client.h +178 -0
  171. package/android/src/main/cpp/third_party/libraop/rtsp_client.c +743 -0
  172. package/android/src/main/cpp/third_party/libraop/rtsp_client.h +50 -0
  173. package/android/src/main/cpp/third_party/openssl/include/openssl/__DECC_INCLUDE_EPILOGUE.H +22 -0
  174. package/android/src/main/cpp/third_party/openssl/include/openssl/__DECC_INCLUDE_PROLOGUE.H +26 -0
  175. package/android/src/main/cpp/third_party/openssl/include/openssl/aes.h +111 -0
  176. package/android/src/main/cpp/third_party/openssl/include/openssl/asn1.h +1134 -0
  177. package/android/src/main/cpp/third_party/openssl/include/openssl/asn1.h.in +967 -0
  178. package/android/src/main/cpp/third_party/openssl/include/openssl/asn1err.h +142 -0
  179. package/android/src/main/cpp/third_party/openssl/include/openssl/asn1t.h +946 -0
  180. package/android/src/main/cpp/third_party/openssl/include/openssl/asn1t.h.in +923 -0
  181. package/android/src/main/cpp/third_party/openssl/include/openssl/async.h +104 -0
  182. package/android/src/main/cpp/third_party/openssl/include/openssl/asyncerr.h +29 -0
  183. package/android/src/main/cpp/third_party/openssl/include/openssl/bio.h +1022 -0
  184. package/android/src/main/cpp/third_party/openssl/include/openssl/bio.h.in +999 -0
  185. package/android/src/main/cpp/third_party/openssl/include/openssl/bioerr.h +72 -0
  186. package/android/src/main/cpp/third_party/openssl/include/openssl/blowfish.h +78 -0
  187. package/android/src/main/cpp/third_party/openssl/include/openssl/bn.h +590 -0
  188. package/android/src/main/cpp/third_party/openssl/include/openssl/bnerr.h +47 -0
  189. package/android/src/main/cpp/third_party/openssl/include/openssl/buffer.h +62 -0
  190. package/android/src/main/cpp/third_party/openssl/include/openssl/buffererr.h +25 -0
  191. package/android/src/main/cpp/third_party/openssl/include/openssl/byteorder.h +339 -0
  192. package/android/src/main/cpp/third_party/openssl/include/openssl/camellia.h +117 -0
  193. package/android/src/main/cpp/third_party/openssl/include/openssl/cast.h +71 -0
  194. package/android/src/main/cpp/third_party/openssl/include/openssl/cmac.h +52 -0
  195. package/android/src/main/cpp/third_party/openssl/include/openssl/cmp.h +727 -0
  196. package/android/src/main/cpp/third_party/openssl/include/openssl/cmp.h.in +584 -0
  197. package/android/src/main/cpp/third_party/openssl/include/openssl/cmp_util.h +56 -0
  198. package/android/src/main/cpp/third_party/openssl/include/openssl/cmperr.h +134 -0
  199. package/android/src/main/cpp/third_party/openssl/include/openssl/cms.h +511 -0
  200. package/android/src/main/cpp/third_party/openssl/include/openssl/cms.h.in +413 -0
  201. package/android/src/main/cpp/third_party/openssl/include/openssl/cmserr.h +126 -0
  202. package/android/src/main/cpp/third_party/openssl/include/openssl/comp.h +98 -0
  203. package/android/src/main/cpp/third_party/openssl/include/openssl/comp.h.in +76 -0
  204. package/android/src/main/cpp/third_party/openssl/include/openssl/comperr.h +38 -0
  205. package/android/src/main/cpp/third_party/openssl/include/openssl/conf.h +214 -0
  206. package/android/src/main/cpp/third_party/openssl/include/openssl/conf.h.in +177 -0
  207. package/android/src/main/cpp/third_party/openssl/include/openssl/conf_api.h +46 -0
  208. package/android/src/main/cpp/third_party/openssl/include/openssl/conferr.h +52 -0
  209. package/android/src/main/cpp/third_party/openssl/include/openssl/configuration.h +212 -0
  210. package/android/src/main/cpp/third_party/openssl/include/openssl/configuration.h.in +75 -0
  211. package/android/src/main/cpp/third_party/openssl/include/openssl/conftypes.h +44 -0
  212. package/android/src/main/cpp/third_party/openssl/include/openssl/core.h +236 -0
  213. package/android/src/main/cpp/third_party/openssl/include/openssl/core_dispatch.h +1141 -0
  214. package/android/src/main/cpp/third_party/openssl/include/openssl/core_names.h +575 -0
  215. package/android/src/main/cpp/third_party/openssl/include/openssl/core_names.h.in +126 -0
  216. package/android/src/main/cpp/third_party/openssl/include/openssl/core_object.h +41 -0
  217. package/android/src/main/cpp/third_party/openssl/include/openssl/crmf.h +278 -0
  218. package/android/src/main/cpp/third_party/openssl/include/openssl/crmf.h.in +207 -0
  219. package/android/src/main/cpp/third_party/openssl/include/openssl/crmferr.h +57 -0
  220. package/android/src/main/cpp/third_party/openssl/include/openssl/crypto.h +583 -0
  221. package/android/src/main/cpp/third_party/openssl/include/openssl/crypto.h.in +560 -0
  222. package/android/src/main/cpp/third_party/openssl/include/openssl/cryptoerr.h +56 -0
  223. package/android/src/main/cpp/third_party/openssl/include/openssl/cryptoerr_legacy.h +1466 -0
  224. package/android/src/main/cpp/third_party/openssl/include/openssl/ct.h +573 -0
  225. package/android/src/main/cpp/third_party/openssl/include/openssl/ct.h.in +525 -0
  226. package/android/src/main/cpp/third_party/openssl/include/openssl/cterr.h +45 -0
  227. package/android/src/main/cpp/third_party/openssl/include/openssl/decoder.h +133 -0
  228. package/android/src/main/cpp/third_party/openssl/include/openssl/decodererr.h +28 -0
  229. package/android/src/main/cpp/third_party/openssl/include/openssl/des.h +211 -0
  230. package/android/src/main/cpp/third_party/openssl/include/openssl/dh.h +339 -0
  231. package/android/src/main/cpp/third_party/openssl/include/openssl/dherr.h +59 -0
  232. package/android/src/main/cpp/third_party/openssl/include/openssl/dsa.h +280 -0
  233. package/android/src/main/cpp/third_party/openssl/include/openssl/dsaerr.h +44 -0
  234. package/android/src/main/cpp/third_party/openssl/include/openssl/dtls1.h +57 -0
  235. package/android/src/main/cpp/third_party/openssl/include/openssl/e_os2.h +310 -0
  236. package/android/src/main/cpp/third_party/openssl/include/openssl/e_ostime.h +38 -0
  237. package/android/src/main/cpp/third_party/openssl/include/openssl/ebcdic.h +39 -0
  238. package/android/src/main/cpp/third_party/openssl/include/openssl/ec.h +1588 -0
  239. package/android/src/main/cpp/third_party/openssl/include/openssl/ecdh.h +10 -0
  240. package/android/src/main/cpp/third_party/openssl/include/openssl/ecdsa.h +10 -0
  241. package/android/src/main/cpp/third_party/openssl/include/openssl/ecerr.h +104 -0
  242. package/android/src/main/cpp/third_party/openssl/include/openssl/encoder.h +124 -0
  243. package/android/src/main/cpp/third_party/openssl/include/openssl/encodererr.h +28 -0
  244. package/android/src/main/cpp/third_party/openssl/include/openssl/engine.h +833 -0
  245. package/android/src/main/cpp/third_party/openssl/include/openssl/engineerr.h +63 -0
  246. package/android/src/main/cpp/third_party/openssl/include/openssl/err.h +512 -0
  247. package/android/src/main/cpp/third_party/openssl/include/openssl/err.h.in +501 -0
  248. package/android/src/main/cpp/third_party/openssl/include/openssl/ess.h +128 -0
  249. package/android/src/main/cpp/third_party/openssl/include/openssl/ess.h.in +81 -0
  250. package/android/src/main/cpp/third_party/openssl/include/openssl/esserr.h +32 -0
  251. package/android/src/main/cpp/third_party/openssl/include/openssl/evp.h +2310 -0
  252. package/android/src/main/cpp/third_party/openssl/include/openssl/evperr.h +142 -0
  253. package/android/src/main/cpp/third_party/openssl/include/openssl/fips_names.h +50 -0
  254. package/android/src/main/cpp/third_party/openssl/include/openssl/fipskey.h +41 -0
  255. package/android/src/main/cpp/third_party/openssl/include/openssl/fipskey.h.in +40 -0
  256. package/android/src/main/cpp/third_party/openssl/include/openssl/hmac.h +62 -0
  257. package/android/src/main/cpp/third_party/openssl/include/openssl/hpke.h +169 -0
  258. package/android/src/main/cpp/third_party/openssl/include/openssl/http.h +119 -0
  259. package/android/src/main/cpp/third_party/openssl/include/openssl/httperr.h +56 -0
  260. package/android/src/main/cpp/third_party/openssl/include/openssl/idea.h +82 -0
  261. package/android/src/main/cpp/third_party/openssl/include/openssl/indicator.h +31 -0
  262. package/android/src/main/cpp/third_party/openssl/include/openssl/kdf.h +138 -0
  263. package/android/src/main/cpp/third_party/openssl/include/openssl/kdferr.h +16 -0
  264. package/android/src/main/cpp/third_party/openssl/include/openssl/lhash.h +398 -0
  265. package/android/src/main/cpp/third_party/openssl/include/openssl/lhash.h.in +373 -0
  266. package/android/src/main/cpp/third_party/openssl/include/openssl/macros.h +349 -0
  267. package/android/src/main/cpp/third_party/openssl/include/openssl/md2.h +56 -0
  268. package/android/src/main/cpp/third_party/openssl/include/openssl/md4.h +63 -0
  269. package/android/src/main/cpp/third_party/openssl/include/openssl/md5.h +62 -0
  270. package/android/src/main/cpp/third_party/openssl/include/openssl/mdc2.h +55 -0
  271. package/android/src/main/cpp/third_party/openssl/include/openssl/ml_kem.h +31 -0
  272. package/android/src/main/cpp/third_party/openssl/include/openssl/modes.h +219 -0
  273. package/android/src/main/cpp/third_party/openssl/include/openssl/obj_mac.h +6636 -0
  274. package/android/src/main/cpp/third_party/openssl/include/openssl/objects.h +184 -0
  275. package/android/src/main/cpp/third_party/openssl/include/openssl/objectserr.h +28 -0
  276. package/android/src/main/cpp/third_party/openssl/include/openssl/ocsp.h +483 -0
  277. package/android/src/main/cpp/third_party/openssl/include/openssl/ocsp.h.in +387 -0
  278. package/android/src/main/cpp/third_party/openssl/include/openssl/ocsperr.h +53 -0
  279. package/android/src/main/cpp/third_party/openssl/include/openssl/opensslconf.h +17 -0
  280. package/android/src/main/cpp/third_party/openssl/include/openssl/opensslv.h +114 -0
  281. package/android/src/main/cpp/third_party/openssl/include/openssl/opensslv.h.in +113 -0
  282. package/android/src/main/cpp/third_party/openssl/include/openssl/ossl_typ.h +16 -0
  283. package/android/src/main/cpp/third_party/openssl/include/openssl/param_build.h +63 -0
  284. package/android/src/main/cpp/third_party/openssl/include/openssl/params.h +163 -0
  285. package/android/src/main/cpp/third_party/openssl/include/openssl/pem.h +547 -0
  286. package/android/src/main/cpp/third_party/openssl/include/openssl/pem2.h +19 -0
  287. package/android/src/main/cpp/third_party/openssl/include/openssl/pemerr.h +59 -0
  288. package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs12.h +366 -0
  289. package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs12.h.in +343 -0
  290. package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs12err.h +46 -0
  291. package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs7.h +430 -0
  292. package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs7.h.in +359 -0
  293. package/android/src/main/cpp/third_party/openssl/include/openssl/pkcs7err.h +63 -0
  294. package/android/src/main/cpp/third_party/openssl/include/openssl/prov_ssl.h +38 -0
  295. package/android/src/main/cpp/third_party/openssl/include/openssl/proverr.h +169 -0
  296. package/android/src/main/cpp/third_party/openssl/include/openssl/provider.h +94 -0
  297. package/android/src/main/cpp/third_party/openssl/include/openssl/quic.h +75 -0
  298. package/android/src/main/cpp/third_party/openssl/include/openssl/rand.h +131 -0
  299. package/android/src/main/cpp/third_party/openssl/include/openssl/randerr.h +70 -0
  300. package/android/src/main/cpp/third_party/openssl/include/openssl/rc2.h +68 -0
  301. package/android/src/main/cpp/third_party/openssl/include/openssl/rc4.h +47 -0
  302. package/android/src/main/cpp/third_party/openssl/include/openssl/rc5.h +79 -0
  303. package/android/src/main/cpp/third_party/openssl/include/openssl/ripemd.h +59 -0
  304. package/android/src/main/cpp/third_party/openssl/include/openssl/rsa.h +615 -0
  305. package/android/src/main/cpp/third_party/openssl/include/openssl/rsaerr.h +107 -0
  306. package/android/src/main/cpp/third_party/openssl/include/openssl/safestack.h +297 -0
  307. package/android/src/main/cpp/third_party/openssl/include/openssl/safestack.h.in +227 -0
  308. package/android/src/main/cpp/third_party/openssl/include/openssl/seed.h +113 -0
  309. package/android/src/main/cpp/third_party/openssl/include/openssl/self_test.h +112 -0
  310. package/android/src/main/cpp/third_party/openssl/include/openssl/sha.h +139 -0
  311. package/android/src/main/cpp/third_party/openssl/include/openssl/srp.h +285 -0
  312. package/android/src/main/cpp/third_party/openssl/include/openssl/srp.h.in +214 -0
  313. package/android/src/main/cpp/third_party/openssl/include/openssl/srtp.h +68 -0
  314. package/android/src/main/cpp/third_party/openssl/include/openssl/ssl.h +2933 -0
  315. package/android/src/main/cpp/third_party/openssl/include/openssl/ssl.h.in +2886 -0
  316. package/android/src/main/cpp/third_party/openssl/include/openssl/ssl2.h +30 -0
  317. package/android/src/main/cpp/third_party/openssl/include/openssl/ssl3.h +358 -0
  318. package/android/src/main/cpp/third_party/openssl/include/openssl/sslerr.h +382 -0
  319. package/android/src/main/cpp/third_party/openssl/include/openssl/sslerr_legacy.h +467 -0
  320. package/android/src/main/cpp/third_party/openssl/include/openssl/stack.h +90 -0
  321. package/android/src/main/cpp/third_party/openssl/include/openssl/store.h +377 -0
  322. package/android/src/main/cpp/third_party/openssl/include/openssl/storeerr.h +49 -0
  323. package/android/src/main/cpp/third_party/openssl/include/openssl/symhacks.h +39 -0
  324. package/android/src/main/cpp/third_party/openssl/include/openssl/thread.h +31 -0
  325. package/android/src/main/cpp/third_party/openssl/include/openssl/tls1.h +1220 -0
  326. package/android/src/main/cpp/third_party/openssl/include/openssl/trace.h +321 -0
  327. package/android/src/main/cpp/third_party/openssl/include/openssl/ts.h +522 -0
  328. package/android/src/main/cpp/third_party/openssl/include/openssl/tserr.h +67 -0
  329. package/android/src/main/cpp/third_party/openssl/include/openssl/txt_db.h +63 -0
  330. package/android/src/main/cpp/third_party/openssl/include/openssl/types.h +248 -0
  331. package/android/src/main/cpp/third_party/openssl/include/openssl/ui.h +407 -0
  332. package/android/src/main/cpp/third_party/openssl/include/openssl/ui.h.in +384 -0
  333. package/android/src/main/cpp/third_party/openssl/include/openssl/uierr.h +38 -0
  334. package/android/src/main/cpp/third_party/openssl/include/openssl/whrlpool.h +62 -0
  335. package/android/src/main/cpp/third_party/openssl/include/openssl/x509.h +1303 -0
  336. package/android/src/main/cpp/third_party/openssl/include/openssl/x509.h.in +1109 -0
  337. package/android/src/main/cpp/third_party/openssl/include/openssl/x509_acert.h +294 -0
  338. package/android/src/main/cpp/third_party/openssl/include/openssl/x509_acert.h.in +199 -0
  339. package/android/src/main/cpp/third_party/openssl/include/openssl/x509_vfy.h +903 -0
  340. package/android/src/main/cpp/third_party/openssl/include/openssl/x509_vfy.h.in +806 -0
  341. package/android/src/main/cpp/third_party/openssl/include/openssl/x509err.h +70 -0
  342. package/android/src/main/cpp/third_party/openssl/include/openssl/x509v3.h +1968 -0
  343. package/android/src/main/cpp/third_party/openssl/include/openssl/x509v3.h.in +1367 -0
  344. package/android/src/main/cpp/third_party/openssl/include/openssl/x509v3err.h +97 -0
  345. package/android/src/main/cpp/third_party/pair_ap/pair-internal.h +390 -0
  346. package/android/src/main/cpp/third_party/pair_ap/pair-tlv.c +221 -0
  347. package/android/src/main/cpp/third_party/pair_ap/pair-tlv.h +77 -0
  348. package/android/src/main/cpp/third_party/pair_ap/pair.c +806 -0
  349. package/android/src/main/cpp/third_party/pair_ap/pair.h +266 -0
  350. package/android/src/main/cpp/third_party/pair_ap/pair_fruit_stub.c +36 -0
  351. package/android/src/main/cpp/third_party/pair_ap/pair_homekit.c +3190 -0
  352. package/android/src/main/cpp/third_party/signalsmith/LICENSE-signalsmith-linear.txt +21 -0
  353. package/android/src/main/cpp/third_party/signalsmith/LICENSE-signalsmith-stretch.txt +21 -0
  354. package/android/src/main/cpp/third_party/signalsmith/THIRD_PARTY_VERSIONS.txt +9 -0
  355. package/android/src/main/cpp/third_party/signalsmith/include/signalsmith-linear/fft.h +1380 -0
  356. package/android/src/main/cpp/third_party/signalsmith/include/signalsmith-linear/linear.h +1075 -0
  357. package/android/src/main/cpp/third_party/signalsmith/include/signalsmith-linear/stft.h +643 -0
  358. package/android/src/main/cpp/third_party/signalsmith/include/signalsmith-stretch.h +1060 -0
  359. package/android/src/main/java/com/margelo/nitro/queueplayer/AirPlayEngine.kt +314 -0
  360. package/android/src/main/java/com/margelo/nitro/queueplayer/ArtworkContentProvider.kt +124 -0
  361. package/android/src/main/java/com/margelo/nitro/queueplayer/AudioFocusErrorMapping.kt +89 -0
  362. package/android/src/main/java/com/margelo/nitro/queueplayer/AudioPipelineRenderersFactory.kt +60 -0
  363. package/android/src/main/java/com/margelo/nitro/queueplayer/BrowseCallbackRegistry.kt +136 -0
  364. package/android/src/main/java/com/margelo/nitro/queueplayer/BrowseDataProvider.kt +128 -0
  365. package/android/src/main/java/com/margelo/nitro/queueplayer/BufferStateTranslator.kt +35 -0
  366. package/android/src/main/java/com/margelo/nitro/queueplayer/CacheMimeTypes.kt +79 -0
  367. package/android/src/main/java/com/margelo/nitro/queueplayer/CastManager.kt +629 -0
  368. package/android/src/main/java/com/margelo/nitro/queueplayer/CrossfadeEngine.kt +1350 -0
  369. package/android/src/main/java/com/margelo/nitro/queueplayer/CrossfadeForwardingPlayer.kt +66 -0
  370. package/android/src/main/java/com/margelo/nitro/queueplayer/CustomPresetStore.kt +100 -0
  371. package/android/src/main/java/com/margelo/nitro/queueplayer/Equalizer.kt +348 -0
  372. package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerEngine.kt +222 -0
  373. package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngine.kt +213 -0
  374. package/android/src/main/java/com/margelo/nitro/queueplayer/EqualizerPresets.kt +45 -0
  375. package/android/src/main/java/com/margelo/nitro/queueplayer/FFTProcessorTee.kt +173 -0
  376. package/android/src/main/java/com/margelo/nitro/queueplayer/FifoCacheEvictor.kt +96 -0
  377. package/android/src/main/java/com/margelo/nitro/queueplayer/GaplessEngine.kt +587 -0
  378. package/android/src/main/java/com/margelo/nitro/queueplayer/HeadlessJsMediaService.kt +220 -0
  379. package/android/src/main/java/com/margelo/nitro/queueplayer/IEqualizerEngine.kt +97 -0
  380. package/android/src/main/java/com/margelo/nitro/queueplayer/ListenerRegistry.kt +59 -0
  381. package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCache.kt +343 -0
  382. package/android/src/main/java/com/margelo/nitro/queueplayer/LookaheadCacheWriter.kt +533 -0
  383. package/android/src/main/java/com/margelo/nitro/queueplayer/MediaButtonDispatch.kt +88 -0
  384. package/android/src/main/java/com/margelo/nitro/queueplayer/MediaItemBuilder.kt +281 -0
  385. package/android/src/main/java/com/margelo/nitro/queueplayer/MediaItemMapper.kt +76 -0
  386. package/android/src/main/java/com/margelo/nitro/queueplayer/MilestoneTracker.kt +66 -0
  387. package/android/src/main/java/com/margelo/nitro/queueplayer/MimeCapturingDataSource.kt +64 -0
  388. package/android/src/main/java/com/margelo/nitro/queueplayer/MultiplexingAudioBufferSink.kt +92 -0
  389. package/android/src/main/java/com/margelo/nitro/queueplayer/NotificationProvider.kt +37 -0
  390. package/android/src/main/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractor.kt +166 -0
  391. package/android/src/main/java/com/margelo/nitro/queueplayer/PendingBrowseRequests.kt +70 -0
  392. package/android/src/main/java/com/margelo/nitro/queueplayer/PendingIntentBuffer.kt +82 -0
  393. package/android/src/main/java/com/margelo/nitro/queueplayer/PitchAwareAudioProcessorChain.kt +69 -0
  394. package/android/src/main/java/com/margelo/nitro/queueplayer/PitchCorrection.kt +27 -0
  395. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaceholderArtwork.kt +65 -0
  396. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaceholderFallbackBitmapLoader.kt +75 -0
  397. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackEngine.kt +336 -0
  398. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackErrorMapping.kt +175 -0
  399. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackModeStateMachine.kt +127 -0
  400. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackService.kt +1716 -0
  401. package/android/src/main/java/com/margelo/nitro/queueplayer/PlaybackServiceCallback.kt +755 -0
  402. package/android/src/main/java/com/margelo/nitro/queueplayer/ProgressEmissionGate.kt +70 -0
  403. package/android/src/main/java/com/margelo/nitro/queueplayer/QueueMutationArithmetic.kt +131 -0
  404. package/android/src/main/java/com/margelo/nitro/queueplayer/QueuePlayerPackage.kt +22 -0
  405. package/android/src/main/java/com/margelo/nitro/queueplayer/QueueSkipArithmetic.kt +113 -0
  406. package/android/src/main/java/com/margelo/nitro/queueplayer/RadixTwoFFT.kt +176 -0
  407. package/android/src/main/java/com/margelo/nitro/queueplayer/RemoteCommandDeduper.kt +81 -0
  408. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessor.kt +84 -0
  409. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainConfig.kt +34 -0
  410. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainData.kt +44 -0
  411. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainExtractor.kt +209 -0
  412. package/android/src/main/java/com/margelo/nitro/queueplayer/ReplayGainGain.kt +34 -0
  413. package/android/src/main/java/com/margelo/nitro/queueplayer/SearchResultCache.kt +72 -0
  414. package/android/src/main/java/com/margelo/nitro/queueplayer/SessionCommandForwardingPlayer.kt +167 -0
  415. package/android/src/main/java/com/margelo/nitro/queueplayer/SignalsmithStretchAudioProcessor.kt +225 -0
  416. package/android/src/main/java/com/margelo/nitro/queueplayer/SignalsmithStretchNative.kt +93 -0
  417. package/android/src/main/java/com/margelo/nitro/queueplayer/SleepTimerCore.kt +125 -0
  418. package/android/src/main/java/com/margelo/nitro/queueplayer/TrackPlayer.kt +4242 -0
  419. package/android/src/main/java/com/margelo/nitro/queueplayer/TransportStateTranslator.kt +70 -0
  420. package/android/src/main/java/com/margelo/nitro/queueplayer/UrlSchemes.kt +9 -0
  421. package/android/src/main/java/com/margelo/nitro/queueplayer/VisualizationGate.kt +35 -0
  422. package/android/src/main/java/com/margelo/nitro/queueplayer/Visualizer.kt +240 -0
  423. package/android/src/main/java/com/margelo/nitro/queueplayer/VisualizerTeeBuilder.kt +63 -0
  424. package/android/src/main/java/com/margelo/nitro/queueplayer/VoiceSearchExtrasParser.kt +103 -0
  425. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/AudioRouteState.kt +202 -0
  426. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/AuthRequiredException.kt +14 -0
  427. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastBackend.kt +41 -0
  428. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastBackends.kt +56 -0
  429. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastEventBridge.kt +122 -0
  430. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastMediaItem.kt +20 -0
  431. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastSession.kt +193 -0
  432. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastSinkRouter.kt +95 -0
  433. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/CastTransportRouter.kt +151 -0
  434. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/DiscoveryState.kt +53 -0
  435. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/MetadataAwareSession.kt +17 -0
  436. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/PlaybackStateRouter.kt +65 -0
  437. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/Receiver.kt +35 -0
  438. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2JNI.kt +129 -0
  439. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2MetadataSync.kt +189 -0
  440. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2Session.kt +123 -0
  441. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlay2Sink.kt +205 -0
  442. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayBackend.kt +585 -0
  443. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayBackendInitializer.kt +43 -0
  444. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayDiscovery.kt +511 -0
  445. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayDiscoveryLifecycleObserver.kt +57 -0
  446. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayMetadataSync.kt +199 -0
  447. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayReceiverMapper.kt +42 -0
  448. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayRenderersFactory.kt +43 -0
  449. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlaySession.kt +101 -0
  450. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlaySink.kt +265 -0
  451. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayTxtParser.kt +203 -0
  452. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/AirplayJNI.kt +106 -0
  453. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/airplay/MulticastLockHolder.kt +75 -0
  454. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/CastMediaItemConverter.kt +67 -0
  455. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/CastVolumeForwardingPlayer.kt +156 -0
  456. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastBackend.kt +171 -0
  457. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastOptionsProvider.kt +51 -0
  458. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastReceiverMapper.kt +44 -0
  459. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/ChromecastSession.kt +544 -0
  460. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/MediaInfoBuilder.kt +92 -0
  461. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/chromecast/RemoteMediaCallbacks.kt +113 -0
  462. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServer.kt +160 -0
  463. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/LocalMediaServerLifecycle.kt +15 -0
  464. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaServerHandle.kt +44 -0
  465. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MediaTokenRegistry.kt +37 -0
  466. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/http/MimeTypes.kt +27 -0
  467. package/android/src/main/java/com/margelo/nitro/queueplayer/cast/net/LocalAddressMonitor.kt +86 -0
  468. package/android/src/main/res/raw/rnqp_placeholder.jpg +0 -0
  469. package/android/src/main/res/values/cast_strings.xml +15 -0
  470. package/android/src/main/res/values/strings.xml +24 -0
  471. package/android/src/main/res/xml/automotive_app_desc.xml +15 -0
  472. package/android/src/test/java/androidx/media3/session/MediaSessionControllerRequestTestSeam.kt +27 -0
  473. package/android/src/test/java/com/margelo/nitro/queueplayer/ArtworkContentProviderTest.kt +124 -0
  474. package/android/src/test/java/com/margelo/nitro/queueplayer/AudioFocusErrorMappingTest.kt +122 -0
  475. package/android/src/test/java/com/margelo/nitro/queueplayer/AudioPipelineRenderersFactoryTest.kt +57 -0
  476. package/android/src/test/java/com/margelo/nitro/queueplayer/AvrcpMetadataTest.kt +264 -0
  477. package/android/src/test/java/com/margelo/nitro/queueplayer/BrowseCallbackRegistryTest.kt +104 -0
  478. package/android/src/test/java/com/margelo/nitro/queueplayer/BrowseDataProviderTest.kt +276 -0
  479. package/android/src/test/java/com/margelo/nitro/queueplayer/BufferStateTranslatorTest.kt +58 -0
  480. package/android/src/test/java/com/margelo/nitro/queueplayer/CacheMimeTypesTest.kt +116 -0
  481. package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineLifecycleTest.kt +214 -0
  482. package/android/src/test/java/com/margelo/nitro/queueplayer/CrossfadeEngineReplayGainTest.kt +180 -0
  483. package/android/src/test/java/com/margelo/nitro/queueplayer/CustomPresetStoreTest.kt +128 -0
  484. package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerEngineTest.kt +223 -0
  485. package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerKtTest.kt +274 -0
  486. package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerLegacyEngineTest.kt +214 -0
  487. package/android/src/test/java/com/margelo/nitro/queueplayer/EqualizerPresetsTest.kt +89 -0
  488. package/android/src/test/java/com/margelo/nitro/queueplayer/FFTProcessorTeeTest.kt +185 -0
  489. package/android/src/test/java/com/margelo/nitro/queueplayer/FifoCacheEvictorTest.kt +112 -0
  490. package/android/src/test/java/com/margelo/nitro/queueplayer/ForwardingPlayerTestAccess.kt +21 -0
  491. package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineLifecycleTest.kt +65 -0
  492. package/android/src/test/java/com/margelo/nitro/queueplayer/GaplessEngineReplayGainTest.kt +154 -0
  493. package/android/src/test/java/com/margelo/nitro/queueplayer/HeadlessJsMediaServiceTest.kt +106 -0
  494. package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheTest.kt +183 -0
  495. package/android/src/test/java/com/margelo/nitro/queueplayer/LookaheadCacheWriterTest.kt +649 -0
  496. package/android/src/test/java/com/margelo/nitro/queueplayer/MediaButtonDispatchTest.kt +192 -0
  497. package/android/src/test/java/com/margelo/nitro/queueplayer/MediaItemBuilderTest.kt +389 -0
  498. package/android/src/test/java/com/margelo/nitro/queueplayer/MediaItemMapperTest.kt +145 -0
  499. package/android/src/test/java/com/margelo/nitro/queueplayer/MilestoneTrackerTest.kt +81 -0
  500. package/android/src/test/java/com/margelo/nitro/queueplayer/MultiplexingAudioBufferSinkTest.kt +112 -0
  501. package/android/src/test/java/com/margelo/nitro/queueplayer/NotificationProviderTest.kt +86 -0
  502. package/android/src/test/java/com/margelo/nitro/queueplayer/NowPlayingFormatExtractorTest.kt +106 -0
  503. package/android/src/test/java/com/margelo/nitro/queueplayer/PendingBrowseRequestsTest.kt +82 -0
  504. package/android/src/test/java/com/margelo/nitro/queueplayer/PendingIntentBufferTest.kt +192 -0
  505. package/android/src/test/java/com/margelo/nitro/queueplayer/PitchAwareAudioProcessorChainTest.kt +147 -0
  506. package/android/src/test/java/com/margelo/nitro/queueplayer/PitchCorrectionTest.kt +55 -0
  507. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaceholderFallbackBitmapLoaderTest.kt +187 -0
  508. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackEngineInterfaceTest.kt +134 -0
  509. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackModeStateMachineTest.kt +157 -0
  510. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceArtworkLoaderTest.kt +95 -0
  511. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackBrowseTest.kt +480 -0
  512. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackSearchTest.kt +171 -0
  513. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCallbackTest.kt +773 -0
  514. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceCastBindTest.kt +100 -0
  515. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceLifecycleTest.kt +315 -0
  516. package/android/src/test/java/com/margelo/nitro/queueplayer/PlaybackServiceSessionActivityTest.kt +121 -0
  517. package/android/src/test/java/com/margelo/nitro/queueplayer/PlayerCommandsForTest.kt +246 -0
  518. package/android/src/test/java/com/margelo/nitro/queueplayer/ProgressEmissionGateTest.kt +113 -0
  519. package/android/src/test/java/com/margelo/nitro/queueplayer/QueueMutationArithmeticTest.kt +252 -0
  520. package/android/src/test/java/com/margelo/nitro/queueplayer/QueueSkipArithmeticTest.kt +343 -0
  521. package/android/src/test/java/com/margelo/nitro/queueplayer/RadixTwoFFTTest.kt +109 -0
  522. package/android/src/test/java/com/margelo/nitro/queueplayer/RemoteCommandDeduperTest.kt +106 -0
  523. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainAudioProcessorTest.kt +141 -0
  524. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainConfigTest.kt +103 -0
  525. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainExtractorTest.kt +469 -0
  526. package/android/src/test/java/com/margelo/nitro/queueplayer/ReplayGainGainTest.kt +129 -0
  527. package/android/src/test/java/com/margelo/nitro/queueplayer/RobolectricServiceBindHelper.kt +83 -0
  528. package/android/src/test/java/com/margelo/nitro/queueplayer/SearchResultCacheTest.kt +128 -0
  529. package/android/src/test/java/com/margelo/nitro/queueplayer/ServiceReadyReasonTest.kt +42 -0
  530. package/android/src/test/java/com/margelo/nitro/queueplayer/SessionCommandForwardingPlayerTest.kt +521 -0
  531. package/android/src/test/java/com/margelo/nitro/queueplayer/SleepTimerCoreTest.kt +125 -0
  532. package/android/src/test/java/com/margelo/nitro/queueplayer/SmokeTest.kt +27 -0
  533. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAirPlayMetadataWiringTest.kt +305 -0
  534. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerAudioFocusTest.kt +179 -0
  535. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerEventsTest.kt +497 -0
  536. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLifecycleTest.kt +209 -0
  537. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerLookaheadConfigTest.kt +417 -0
  538. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerMutationTest.kt +473 -0
  539. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerPitchCorrectionTest.kt +61 -0
  540. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerPlaybackModeTest.kt +28 -0
  541. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerProgressThrottleTest.kt +89 -0
  542. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerQueueTest.kt +290 -0
  543. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerReadersTest.kt +151 -0
  544. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerReplayGainTest.kt +69 -0
  545. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipCapabilityTest.kt +324 -0
  546. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSkipTest.kt +287 -0
  547. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerSourceClassifierTest.kt +82 -0
  548. package/android/src/test/java/com/margelo/nitro/queueplayer/TrackPlayerTransportTest.kt +222 -0
  549. package/android/src/test/java/com/margelo/nitro/queueplayer/TransportStateTranslatorTest.kt +287 -0
  550. package/android/src/test/java/com/margelo/nitro/queueplayer/VisualizationGateTest.kt +96 -0
  551. package/android/src/test/java/com/margelo/nitro/queueplayer/VisualizerKtTest.kt +202 -0
  552. package/android/src/test/java/com/margelo/nitro/queueplayer/VoiceSearchExtrasParserTest.kt +211 -0
  553. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/CastSinkRouterTest.kt +120 -0
  554. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/FakeRemotePlayer.kt +62 -0
  555. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/PlaybackStateRouterTest.kt +43 -0
  556. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayDiscoveryFilterTest.kt +86 -0
  557. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/airplay/AirPlayTxtParserClassificationTest.kt +34 -0
  558. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/chromecast/CastMediaItemConverterTest.kt +78 -0
  559. package/android/src/test/java/com/margelo/nitro/queueplayer/cast/chromecast/CastVolumeForwardingPlayerTest.kt +176 -0
  560. package/app.plugin.js +466 -0
  561. package/ios/AVPlayerItemQueueItemId.swift +61 -0
  562. package/ios/AVQueueBuilder.swift +200 -0
  563. package/ios/Array+SafeSubscript.swift +6 -0
  564. package/ios/ArtworkLoader.swift +136 -0
  565. package/ios/ArtworkResolver.swift +87 -0
  566. package/ios/AudioCategoryMapping.swift +48 -0
  567. package/ios/AudioSession.swift +220 -0
  568. package/ios/AudioSessionError.swift +44 -0
  569. package/ios/AudioTapProvider.swift +647 -0
  570. package/ios/BiquadCoefficients.swift +80 -0
  571. package/ios/CarPlayBridge.swift +178 -0
  572. package/ios/CarPlayBrowseBuilder.swift +368 -0
  573. package/ios/CarPlayBrowseDataProvider.swift +162 -0
  574. package/ios/CarPlayCoordinator.swift +42 -0
  575. package/ios/CarPlaySceneDelegate.swift +199 -0
  576. package/ios/Cast/AirPlayRouteState.swift +281 -0
  577. package/ios/Cast/Chromecast/ChromecastDiscoveryAdapter.swift +27 -0
  578. package/ios/Cast/Chromecast/ChromecastReceiverMapper.swift +48 -0
  579. package/ios/Cast/Chromecast/ChromecastSession.swift +740 -0
  580. package/ios/Cast/Chromecast/ChromecastSessionAdapter.swift +114 -0
  581. package/ios/Cast/Chromecast/ChromecastSupport.swift +63 -0
  582. package/ios/Cast/Chromecast/MediaInfoBuilder.swift +104 -0
  583. package/ios/Cast/Chromecast/RemoteMediaObserver.swift +147 -0
  584. package/ios/Cast/Core/CastEventBridge.swift +210 -0
  585. package/ios/Cast/Core/CastMediaItem.swift +41 -0
  586. package/ios/Cast/Core/CastNowPlayingController.swift +310 -0
  587. package/ios/Cast/Core/CastReceiverState.swift +22 -0
  588. package/ios/Cast/Core/CastSession.swift +147 -0
  589. package/ios/Cast/Core/CastTransportRouter.swift +162 -0
  590. package/ios/Cast/Core/ChromecastBackend.swift +259 -0
  591. package/ios/Cast/Core/LocalAddressMonitor.swift +110 -0
  592. package/ios/Cast/Core/LocalMediaServer.swift +270 -0
  593. package/ios/Cast/Core/LocalNetworkPermissionProbe.swift +265 -0
  594. package/ios/Cast/Core/MediaHTTPConnection.swift +349 -0
  595. package/ios/Cast/Core/MediaServerHandle.swift +49 -0
  596. package/ios/Cast/Core/MediaTokenRegistry.swift +51 -0
  597. package/ios/Cast/Core/MimeTypes.swift +26 -0
  598. package/ios/Cast/Core/PlaybackStateRouter.swift +123 -0
  599. package/ios/CastManager.swift +493 -0
  600. package/ios/CrossfadeEngine.swift +1343 -0
  601. package/ios/CustomPresetStore.swift +78 -0
  602. package/ios/EQProcessor.swift +321 -0
  603. package/ios/EQTap.swift +241 -0
  604. package/ios/Equalizer.swift +321 -0
  605. package/ios/EqualizerPresets.swift +48 -0
  606. package/ios/FFTProcessor.swift +237 -0
  607. package/ios/GaplessEngine.swift +385 -0
  608. package/ios/InputGuards.swift +63 -0
  609. package/ios/InterruptionEventMapping.swift +51 -0
  610. package/ios/ListenerRegistry.swift +73 -0
  611. package/ios/LookaheadCache.swift +833 -0
  612. package/ios/LookaheadCacheEviction.swift +83 -0
  613. package/ios/LookaheadCachePrefetcher.swift +503 -0
  614. package/ios/MetadataReader.swift +555 -0
  615. package/ios/MilestoneTracker.swift +63 -0
  616. package/ios/NowPlayingFormatExtractor.swift +362 -0
  617. package/ios/NowPlayingInfo.swift +190 -0
  618. package/ios/OutputRouteMonitor.swift +125 -0
  619. package/ios/PendingBrowseRequests.swift +62 -0
  620. package/ios/PitchAlgorithm.swift +24 -0
  621. package/ios/PlaceholderArtwork.swift +46 -0
  622. package/ios/PlaybackEngine.swift +319 -0
  623. package/ios/PlaybackErrorMapping.swift +195 -0
  624. package/ios/PlaybackModeStateMachine.swift +159 -0
  625. package/ios/PlayerStateDerivation.swift +45 -0
  626. package/ios/ProgressEmissionGate.swift +54 -0
  627. package/ios/QueueMutationArithmetic.swift +129 -0
  628. package/ios/QueueSkipArithmetic.swift +109 -0
  629. package/ios/QueueState.swift +49 -0
  630. package/ios/RemoteCommands.swift +229 -0
  631. package/ios/ReplayGainData.swift +58 -0
  632. package/ios/ReplayGainExtractor.swift +82 -0
  633. package/ios/ReplayGainProcessor.swift +46 -0
  634. package/ios/Resources/RNQP-placeholder.jpg +0 -0
  635. package/ios/Resources/silence.m4a +0 -0
  636. package/ios/Siri/PendingIntentBuffer.swift +100 -0
  637. package/ios/Siri/PlayMediaIntentHandler.swift +283 -0
  638. package/ios/Siri/QueuePlayerMediaIntents.swift +45 -0
  639. package/ios/Siri/VoiceDonation.swift +50 -0
  640. package/ios/SleepTimerCore.swift +128 -0
  641. package/ios/StreamingBitrateProbe.swift +318 -0
  642. package/ios/Tests/AVQueueBuilderTests.swift +430 -0
  643. package/ios/Tests/ArtworkResolverTests.swift +540 -0
  644. package/ios/Tests/AudioSessionTests.swift +222 -0
  645. package/ios/Tests/AudioTapProviderReplayGainTests.swift +136 -0
  646. package/ios/Tests/AudioTapProviderVisualizationFlagTests.swift +104 -0
  647. package/ios/Tests/BiquadCoefficientsTests.swift +84 -0
  648. package/ios/Tests/CarPlayBridgeTests.swift +91 -0
  649. package/ios/Tests/CarPlayBrowseBuilderTests.swift +221 -0
  650. package/ios/Tests/CarPlayBrowseDataProviderTests.swift +218 -0
  651. package/ios/Tests/CastNowPlayingControllerTests.swift +204 -0
  652. package/ios/Tests/CrossfadeEngineStubTests.swift +43 -0
  653. package/ios/Tests/EQProcessorTests.swift +302 -0
  654. package/ios/Tests/EQTapTests.swift +49 -0
  655. package/ios/Tests/EqualizerAudioMixProviderTests.swift +110 -0
  656. package/ios/Tests/EqualizerHybridTests.swift +303 -0
  657. package/ios/Tests/EqualizerPresetsTests.swift +69 -0
  658. package/ios/Tests/FFTProcessorTests.swift +303 -0
  659. package/ios/Tests/Fixtures/silence-aac-128.m4a +0 -0
  660. package/ios/Tests/Fixtures/silence-cbr-128.mp3 +0 -0
  661. package/ios/Tests/Fixtures/silence-vbr.mp3 +0 -0
  662. package/ios/Tests/GaplessEngineLifecycleTests.swift +160 -0
  663. package/ios/Tests/InputGuardsTests.swift +75 -0
  664. package/ios/Tests/InterruptionEventMappingTests.swift +91 -0
  665. package/ios/Tests/LookaheadCacheEvictionTests.swift +217 -0
  666. package/ios/Tests/LookaheadCachePrefetcherTests.swift +630 -0
  667. package/ios/Tests/LookaheadCacheRuntimeConfigTests.swift +247 -0
  668. package/ios/Tests/LookaheadCacheTests.swift +717 -0
  669. package/ios/Tests/MediaHTTPConnectionTests.swift +163 -0
  670. package/ios/Tests/MetadataReaderTests.swift +605 -0
  671. package/ios/Tests/MilestoneTrackerTests.swift +76 -0
  672. package/ios/Tests/NowPlayingFormatExtractorTests.swift +158 -0
  673. package/ios/Tests/NowPlayingInfoTests.swift +292 -0
  674. package/ios/Tests/PendingBrowseRequestsTests.swift +69 -0
  675. package/ios/Tests/PendingIntentBufferTests.swift +164 -0
  676. package/ios/Tests/PlaybackEngineProtocolTests.swift +92 -0
  677. package/ios/Tests/PlaybackErrorMappingTests.swift +179 -0
  678. package/ios/Tests/PlaybackModeStateMachineTests.swift +40 -0
  679. package/ios/Tests/PlaybackStateRouterTests.swift +178 -0
  680. package/ios/Tests/PlayerStateDerivationTests.swift +69 -0
  681. package/ios/Tests/ProgressEmissionGateTests.swift +113 -0
  682. package/ios/Tests/QueueMutationArithmeticTests.swift +245 -0
  683. package/ios/Tests/RemoteCommandsTests.swift +147 -0
  684. package/ios/Tests/ReplayGainExtractorTests.swift +197 -0
  685. package/ios/Tests/ReplayGainProcessorTests.swift +90 -0
  686. package/ios/Tests/SkipCapabilityTests.swift +98 -0
  687. package/ios/Tests/SkipIndexTests.swift +259 -0
  688. package/ios/Tests/SleepTimerCoreTests.swift +98 -0
  689. package/ios/Tests/StreamingBitrateProbeTests.swift +29 -0
  690. package/ios/Tests/TrackSourceClassifierTests.swift +50 -0
  691. package/ios/Tests/VoiceDonationTests.swift +26 -0
  692. package/ios/TrackPlayer.swift +4766 -0
  693. package/ios/Visualizer.swift +274 -0
  694. package/ios/WindowApplicationSceneDelegate.swift +59 -0
  695. package/ios/tests-harness/Podfile +56 -0
  696. package/ios/tests-harness/TestHost/App.swift +12 -0
  697. package/ios/tests-harness/TestHost/Info.plist +37 -0
  698. package/ios/tests-harness/TestHost.xcodeproj/project.pbxproj +472 -0
  699. package/ios/tests-harness/TestHost.xcodeproj/xcshareddata/xcschemes/TestHost.xcscheme +68 -0
  700. package/ios/tests-harness/package.json +10 -0
  701. package/ios/tests-harness/scripts/seed-xcodeproj.rb +118 -0
  702. package/lib/module/CastManager.nitro.js +4 -0
  703. package/lib/module/CastManager.nitro.js.map +1 -0
  704. package/lib/module/Equalizer.nitro.js +4 -0
  705. package/lib/module/Equalizer.nitro.js.map +1 -0
  706. package/lib/module/TrackPlayer.nitro.js +4 -0
  707. package/lib/module/TrackPlayer.nitro.js.map +1 -0
  708. package/lib/module/Visualizer.nitro.js +4 -0
  709. package/lib/module/Visualizer.nitro.js.map +1 -0
  710. package/lib/module/cast/index.js +156 -0
  711. package/lib/module/cast/index.js.map +1 -0
  712. package/lib/module/clients.js +43 -0
  713. package/lib/module/clients.js.map +1 -0
  714. package/lib/module/equalizer.js +85 -0
  715. package/lib/module/equalizer.js.map +1 -0
  716. package/lib/module/hooks/index.js +16 -0
  717. package/lib/module/hooks/index.js.map +1 -0
  718. package/lib/module/hooks/useActiveTrack.js +50 -0
  719. package/lib/module/hooks/useActiveTrack.js.map +1 -0
  720. package/lib/module/hooks/useAudioRoute.js +60 -0
  721. package/lib/module/hooks/useAudioRoute.js.map +1 -0
  722. package/lib/module/hooks/useBufferState.js +34 -0
  723. package/lib/module/hooks/useBufferState.js.map +1 -0
  724. package/lib/module/hooks/useCanSkip.js +67 -0
  725. package/lib/module/hooks/useCanSkip.js.map +1 -0
  726. package/lib/module/hooks/useCast.js +107 -0
  727. package/lib/module/hooks/useCast.js.map +1 -0
  728. package/lib/module/hooks/useEqualizer.js +91 -0
  729. package/lib/module/hooks/useEqualizer.js.map +1 -0
  730. package/lib/module/hooks/useFullyBuffered.js +33 -0
  731. package/lib/module/hooks/useFullyBuffered.js.map +1 -0
  732. package/lib/module/hooks/useLookaheadCache.js +49 -0
  733. package/lib/module/hooks/useLookaheadCache.js.map +1 -0
  734. package/lib/module/hooks/useNowPlayingFormat.js +42 -0
  735. package/lib/module/hooks/useNowPlayingFormat.js.map +1 -0
  736. package/lib/module/hooks/usePlaybackState.js +36 -0
  737. package/lib/module/hooks/usePlaybackState.js.map +1 -0
  738. package/lib/module/hooks/useProgress.js +31 -0
  739. package/lib/module/hooks/useProgress.js.map +1 -0
  740. package/lib/module/hooks/useSleepTimer.js +65 -0
  741. package/lib/module/hooks/useSleepTimer.js.map +1 -0
  742. package/lib/module/hooks/useVisualizerState.js +99 -0
  743. package/lib/module/hooks/useVisualizerState.js.map +1 -0
  744. package/lib/module/index.js +37 -0
  745. package/lib/module/index.js.map +1 -0
  746. package/lib/module/package.json +1 -0
  747. package/lib/module/playbackService.js +174 -0
  748. package/lib/module/playbackService.js.map +1 -0
  749. package/lib/module/types.js +592 -0
  750. package/lib/module/types.js.map +1 -0
  751. package/lib/typescript/CastManager.nitro.d.ts +154 -0
  752. package/lib/typescript/CastManager.nitro.d.ts.map +1 -0
  753. package/lib/typescript/Equalizer.nitro.d.ts +30 -0
  754. package/lib/typescript/Equalizer.nitro.d.ts.map +1 -0
  755. package/lib/typescript/TrackPlayer.nitro.d.ts +584 -0
  756. package/lib/typescript/TrackPlayer.nitro.d.ts.map +1 -0
  757. package/lib/typescript/Visualizer.nitro.d.ts +36 -0
  758. package/lib/typescript/Visualizer.nitro.d.ts.map +1 -0
  759. package/lib/typescript/cast/index.d.ts +65 -0
  760. package/lib/typescript/cast/index.d.ts.map +1 -0
  761. package/lib/typescript/clients.d.ts +9 -0
  762. package/lib/typescript/clients.d.ts.map +1 -0
  763. package/lib/typescript/equalizer.d.ts +45 -0
  764. package/lib/typescript/equalizer.d.ts.map +1 -0
  765. package/lib/typescript/hooks/index.d.ts +22 -0
  766. package/lib/typescript/hooks/index.d.ts.map +1 -0
  767. package/lib/typescript/hooks/useActiveTrack.d.ts +21 -0
  768. package/lib/typescript/hooks/useActiveTrack.d.ts.map +1 -0
  769. package/lib/typescript/hooks/useAudioRoute.d.ts +13 -0
  770. package/lib/typescript/hooks/useAudioRoute.d.ts.map +1 -0
  771. package/lib/typescript/hooks/useBufferState.d.ts +18 -0
  772. package/lib/typescript/hooks/useBufferState.d.ts.map +1 -0
  773. package/lib/typescript/hooks/useCanSkip.d.ts +43 -0
  774. package/lib/typescript/hooks/useCanSkip.d.ts.map +1 -0
  775. package/lib/typescript/hooks/useCast.d.ts +32 -0
  776. package/lib/typescript/hooks/useCast.d.ts.map +1 -0
  777. package/lib/typescript/hooks/useEqualizer.d.ts +20 -0
  778. package/lib/typescript/hooks/useEqualizer.d.ts.map +1 -0
  779. package/lib/typescript/hooks/useFullyBuffered.d.ts +15 -0
  780. package/lib/typescript/hooks/useFullyBuffered.d.ts.map +1 -0
  781. package/lib/typescript/hooks/useLookaheadCache.d.ts +13 -0
  782. package/lib/typescript/hooks/useLookaheadCache.d.ts.map +1 -0
  783. package/lib/typescript/hooks/useNowPlayingFormat.d.ts +18 -0
  784. package/lib/typescript/hooks/useNowPlayingFormat.d.ts.map +1 -0
  785. package/lib/typescript/hooks/usePlaybackState.d.ts +9 -0
  786. package/lib/typescript/hooks/usePlaybackState.d.ts.map +1 -0
  787. package/lib/typescript/hooks/useProgress.d.ts +15 -0
  788. package/lib/typescript/hooks/useProgress.d.ts.map +1 -0
  789. package/lib/typescript/hooks/useSleepTimer.d.ts +26 -0
  790. package/lib/typescript/hooks/useSleepTimer.d.ts.map +1 -0
  791. package/lib/typescript/hooks/useVisualizerState.d.ts +40 -0
  792. package/lib/typescript/hooks/useVisualizerState.d.ts.map +1 -0
  793. package/lib/typescript/index.d.ts +14 -0
  794. package/lib/typescript/index.d.ts.map +1 -0
  795. package/lib/typescript/package.json +1 -0
  796. package/lib/typescript/playbackService.d.ts +140 -0
  797. package/lib/typescript/playbackService.d.ts.map +1 -0
  798. package/lib/typescript/types.d.ts +1215 -0
  799. package/lib/typescript/types.d.ts.map +1 -0
  800. package/nitro.json +53 -0
  801. package/nitrogen/generated/android/c++/JAudioCategoryOptions.hpp +65 -0
  802. package/nitrogen/generated/android/c++/JAudioCodec.hpp +73 -0
  803. package/nitrogen/generated/android/c++/JAudioContainer.hpp +76 -0
  804. package/nitrogen/generated/android/c++/JAudioContentType.hpp +58 -0
  805. package/nitrogen/generated/android/c++/JAudioRoute.hpp +75 -0
  806. package/nitrogen/generated/android/c++/JAudioRouteKind.hpp +91 -0
  807. package/nitrogen/generated/android/c++/JBrowseItem.hpp +78 -0
  808. package/nitrogen/generated/android/c++/JBrowseSection.hpp +97 -0
  809. package/nitrogen/generated/android/c++/JBrowseSnapshot.hpp +87 -0
  810. package/nitrogen/generated/android/c++/JBufferState.hpp +64 -0
  811. package/nitrogen/generated/android/c++/JCacheStatus.hpp +92 -0
  812. package/nitrogen/generated/android/c++/JCastConfigureOptions.hpp +62 -0
  813. package/nitrogen/generated/android/c++/JCastConnectOptions.hpp +57 -0
  814. package/nitrogen/generated/android/c++/JCastDeviceKind.hpp +70 -0
  815. package/nitrogen/generated/android/c++/JCastDiscoveryState.hpp +57 -0
  816. package/nitrogen/generated/android/c++/JCastLocalNetworkPermission.hpp +61 -0
  817. package/nitrogen/generated/android/c++/JCastLocalNetworkPermissionEvent.hpp +58 -0
  818. package/nitrogen/generated/android/c++/JCastProtocol.hpp +67 -0
  819. package/nitrogen/generated/android/c++/JCastReceiver.hpp +89 -0
  820. package/nitrogen/generated/android/c++/JCastReceiverCapabilities.hpp +66 -0
  821. package/nitrogen/generated/android/c++/JCastRoute.hpp +85 -0
  822. package/nitrogen/generated/android/c++/JCastSessionDiedEvent.hpp +63 -0
  823. package/nitrogen/generated/android/c++/JCastSessionEndReason.hpp +76 -0
  824. package/nitrogen/generated/android/c++/JCastStreamingModel.hpp +58 -0
  825. package/nitrogen/generated/android/c++/JEqualizerBand.hpp +61 -0
  826. package/nitrogen/generated/android/c++/JEqualizerPreset.hpp +76 -0
  827. package/nitrogen/generated/android/c++/JEvictionPolicy.hpp +58 -0
  828. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_BrowseItem______std__string.hpp +146 -0
  829. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______MediaSearchRequest.hpp +161 -0
  830. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______std__string.hpp +147 -0
  831. package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  832. package/nitrogen/generated/android/c++/JFunc_void_AudioRoute.hpp +80 -0
  833. package/nitrogen/generated/android/c++/JFunc_void_BufferState.hpp +77 -0
  834. package/nitrogen/generated/android/c++/JFunc_void_CacheStatus.hpp +79 -0
  835. package/nitrogen/generated/android/c++/JFunc_void_CastDiscoveryState.hpp +77 -0
  836. package/nitrogen/generated/android/c++/JFunc_void_CastLocalNetworkPermissionEvent.hpp +79 -0
  837. package/nitrogen/generated/android/c++/JFunc_void_CastRoute.hpp +86 -0
  838. package/nitrogen/generated/android/c++/JFunc_void_CastSessionDiedEvent.hpp +80 -0
  839. package/nitrogen/generated/android/c++/JFunc_void_PlaybackError.hpp +80 -0
  840. package/nitrogen/generated/android/c++/JFunc_void_PlayerProgress.hpp +77 -0
  841. package/nitrogen/generated/android/c++/JFunc_void_PlayerState_StateChangeReason.hpp +79 -0
  842. package/nitrogen/generated/android/c++/JFunc_void_ServiceReadyReason.hpp +77 -0
  843. package/nitrogen/generated/android/c++/JFunc_void_SkipCapability.hpp +77 -0
  844. package/nitrogen/generated/android/c++/JFunc_void_SleepTimerState.hpp +78 -0
  845. package/nitrogen/generated/android/c++/JFunc_void_VisualizerErrorReason.hpp +77 -0
  846. package/nitrogen/generated/android/c++/JFunc_void_VisualizerFrame.hpp +80 -0
  847. package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +75 -0
  848. package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +75 -0
  849. package/nitrogen/generated/android/c++/JFunc_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.hpp +82 -0
  850. package/nitrogen/generated/android/c++/JFunc_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.hpp +93 -0
  851. package/nitrogen/generated/android/c++/JFunc_void_std__vector_CastReceiver_.hpp +105 -0
  852. package/nitrogen/generated/android/c++/JFunc_void_std__vector_EqualizerBand_.hpp +96 -0
  853. package/nitrogen/generated/android/c++/JHybridCastManagerSpec.cpp +388 -0
  854. package/nitrogen/generated/android/c++/JHybridCastManagerSpec.hpp +84 -0
  855. package/nitrogen/generated/android/c++/JHybridEqualizerSpec.cpp +233 -0
  856. package/nitrogen/generated/android/c++/JHybridEqualizerSpec.hpp +74 -0
  857. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +1052 -0
  858. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +130 -0
  859. package/nitrogen/generated/android/c++/JHybridVisualizerSpec.cpp +84 -0
  860. package/nitrogen/generated/android/c++/JHybridVisualizerSpec.hpp +64 -0
  861. package/nitrogen/generated/android/c++/JLookaheadCacheConfig.hpp +61 -0
  862. package/nitrogen/generated/android/c++/JMediaSearchOrigin.hpp +58 -0
  863. package/nitrogen/generated/android/c++/JMediaSearchQueueLocation.hpp +61 -0
  864. package/nitrogen/generated/android/c++/JMediaSearchReference.hpp +58 -0
  865. package/nitrogen/generated/android/c++/JMediaSearchReleaseDateRange.hpp +61 -0
  866. package/nitrogen/generated/android/c++/JMediaSearchRepeatMode.hpp +61 -0
  867. package/nitrogen/generated/android/c++/JMediaSearchRequest.hpp +126 -0
  868. package/nitrogen/generated/android/c++/JMediaSearchType.hpp +67 -0
  869. package/nitrogen/generated/android/c++/JNowPlayingFormat.hpp +123 -0
  870. package/nitrogen/generated/android/c++/JNowPlayingFormatAndroidExtras.hpp +71 -0
  871. package/nitrogen/generated/android/c++/JNowPlayingFormatIosExtras.hpp +61 -0
  872. package/nitrogen/generated/android/c++/JPitchCorrectionMode.hpp +61 -0
  873. package/nitrogen/generated/android/c++/JPlaybackError.hpp +87 -0
  874. package/nitrogen/generated/android/c++/JPlaybackErrorCode.hpp +88 -0
  875. package/nitrogen/generated/android/c++/JPlaybackMode.hpp +63 -0
  876. package/nitrogen/generated/android/c++/JPlaybackModeKind.hpp +58 -0
  877. package/nitrogen/generated/android/c++/JPlayerConfig.hpp +126 -0
  878. package/nitrogen/generated/android/c++/JPlayerProgress.hpp +65 -0
  879. package/nitrogen/generated/android/c++/JPlayerState.hpp +73 -0
  880. package/nitrogen/generated/android/c++/JRemoteControlOptions.hpp +66 -0
  881. package/nitrogen/generated/android/c++/JRemoteSkipMode.hpp +58 -0
  882. package/nitrogen/generated/android/c++/JRepeatMode.hpp +61 -0
  883. package/nitrogen/generated/android/c++/JReplayGainMode.hpp +61 -0
  884. package/nitrogen/generated/android/c++/JSectionIcon.hpp +91 -0
  885. package/nitrogen/generated/android/c++/JServiceReadyReason.hpp +58 -0
  886. package/nitrogen/generated/android/c++/JShuffleResult.hpp +88 -0
  887. package/nitrogen/generated/android/c++/JSkipCapability.hpp +61 -0
  888. package/nitrogen/generated/android/c++/JSleepTimerState.hpp +69 -0
  889. package/nitrogen/generated/android/c++/JStateChangeReason.hpp +73 -0
  890. package/nitrogen/generated/android/c++/JTrackChangeReason.hpp +73 -0
  891. package/nitrogen/generated/android/c++/JTrackItem.hpp +100 -0
  892. package/nitrogen/generated/android/c++/JTrackSource.hpp +61 -0
  893. package/nitrogen/generated/android/c++/JVariant_NullType_Double.cpp +26 -0
  894. package/nitrogen/generated/android/c++/JVariant_NullType_Double.hpp +69 -0
  895. package/nitrogen/generated/android/c++/JVariant_NullType_NowPlayingFormat.cpp +26 -0
  896. package/nitrogen/generated/android/c++/JVariant_NullType_NowPlayingFormat.hpp +83 -0
  897. package/nitrogen/generated/android/c++/JVariant_NullType_String.cpp +26 -0
  898. package/nitrogen/generated/android/c++/JVariant_NullType_String.hpp +70 -0
  899. package/nitrogen/generated/android/c++/JVisualizerConfig.hpp +65 -0
  900. package/nitrogen/generated/android/c++/JVisualizerErrorReason.hpp +64 -0
  901. package/nitrogen/generated/android/c++/JVisualizerFrame.hpp +67 -0
  902. package/nitrogen/generated/android/c++/JVoiceVocabulary.hpp +98 -0
  903. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioCategoryOptions.kt +61 -0
  904. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioCodec.kt +28 -0
  905. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioContainer.kt +29 -0
  906. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioContentType.kt +23 -0
  907. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioRoute.kt +71 -0
  908. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/AudioRouteKind.kt +34 -0
  909. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/BrowseItem.kt +76 -0
  910. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/BrowseSection.kt +71 -0
  911. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/BrowseSnapshot.kt +56 -0
  912. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/BufferState.kt +25 -0
  913. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CacheStatus.kt +71 -0
  914. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastConfigureOptions.kt +56 -0
  915. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastConnectOptions.kt +51 -0
  916. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastDeviceKind.kt +27 -0
  917. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastDiscoveryState.kt +51 -0
  918. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastLocalNetworkPermission.kt +24 -0
  919. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastLocalNetworkPermissionEvent.kt +51 -0
  920. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastProtocol.kt +26 -0
  921. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastReceiver.kt +81 -0
  922. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastReceiverCapabilities.kt +61 -0
  923. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastRoute.kt +76 -0
  924. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastSessionDiedEvent.kt +56 -0
  925. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastSessionEndReason.kt +29 -0
  926. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/CastStreamingModel.kt +23 -0
  927. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/EqualizerBand.kt +56 -0
  928. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/EqualizerPreset.kt +61 -0
  929. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/EvictionPolicy.kt +23 -0
  930. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_BrowseItem______std__string.kt +80 -0
  931. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______MediaSearchRequest.kt +80 -0
  932. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______std__string.kt +80 -0
  933. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void.kt +80 -0
  934. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_AudioRoute.kt +80 -0
  935. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_BufferState.kt +80 -0
  936. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CacheStatus.kt +80 -0
  937. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastDiscoveryState.kt +80 -0
  938. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastLocalNetworkPermissionEvent.kt +80 -0
  939. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastRoute.kt +80 -0
  940. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_CastSessionDiedEvent.kt +80 -0
  941. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlaybackError.kt +80 -0
  942. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerProgress.kt +80 -0
  943. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_PlayerState_StateChangeReason.kt +80 -0
  944. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_ServiceReadyReason.kt +80 -0
  945. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SkipCapability.kt +80 -0
  946. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_SleepTimerState.kt +80 -0
  947. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerErrorReason.kt +80 -0
  948. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_VisualizerFrame.kt +80 -0
  949. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_bool.kt +80 -0
  950. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_double_double.kt +80 -0
  951. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.kt +80 -0
  952. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.kt +80 -0
  953. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_CastReceiver_.kt +80 -0
  954. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Func_void_std__vector_EqualizerBand_.kt +80 -0
  955. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridCastManagerSpec.kt +169 -0
  956. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridEqualizerSpec.kt +109 -0
  957. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridTrackPlayerSpec.kt +419 -0
  958. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/HybridVisualizerSpec.kt +63 -0
  959. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/LookaheadCacheConfig.kt +56 -0
  960. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchOrigin.kt +23 -0
  961. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchQueueLocation.kt +24 -0
  962. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchReference.kt +23 -0
  963. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchReleaseDateRange.kt +56 -0
  964. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchRepeatMode.kt +24 -0
  965. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchRequest.kt +121 -0
  966. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/MediaSearchType.kt +26 -0
  967. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/NowPlayingFormat.kt +116 -0
  968. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/NowPlayingFormatAndroidExtras.kt +61 -0
  969. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/NowPlayingFormatIosExtras.kt +51 -0
  970. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PitchCorrectionMode.kt +24 -0
  971. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlaybackError.kt +86 -0
  972. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlaybackErrorCode.kt +33 -0
  973. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlaybackMode.kt +56 -0
  974. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlaybackModeKind.kt +23 -0
  975. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerConfig.kt +111 -0
  976. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerProgress.kt +61 -0
  977. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/PlayerState.kt +28 -0
  978. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/RemoteControlOptions.kt +61 -0
  979. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/RemoteSkipMode.kt +23 -0
  980. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/RepeatMode.kt +24 -0
  981. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/ReplayGainMode.kt +24 -0
  982. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/SectionIcon.kt +34 -0
  983. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/ServiceReadyReason.kt +23 -0
  984. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/ShuffleResult.kt +61 -0
  985. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/SkipCapability.kt +56 -0
  986. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/SleepTimerState.kt +66 -0
  987. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/StateChangeReason.kt +28 -0
  988. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackChangeReason.kt +28 -0
  989. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackItem.kt +86 -0
  990. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/TrackSource.kt +24 -0
  991. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Variant_NullType_Double.kt +62 -0
  992. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Variant_NullType_NowPlayingFormat.kt +62 -0
  993. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/Variant_NullType_String.kt +62 -0
  994. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/VisualizerConfig.kt +61 -0
  995. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/VisualizerErrorReason.kt +25 -0
  996. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/VisualizerFrame.kt +61 -0
  997. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/VoiceVocabulary.kt +56 -0
  998. package/nitrogen/generated/android/kotlin/com/margelo/nitro/queueplayer/queueplayerOnLoad.kt +35 -0
  999. package/nitrogen/generated/android/queueplayer+autolinking.cmake +90 -0
  1000. package/nitrogen/generated/android/queueplayer+autolinking.gradle +27 -0
  1001. package/nitrogen/generated/android/queueplayerOnLoad.cpp +152 -0
  1002. package/nitrogen/generated/android/queueplayerOnLoad.hpp +34 -0
  1003. package/nitrogen/generated/ios/QueuePlayer+autolinking.rb +62 -0
  1004. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.cpp +343 -0
  1005. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Bridge.hpp +1831 -0
  1006. package/nitrogen/generated/ios/QueuePlayer-Swift-Cxx-Umbrella.hpp +256 -0
  1007. package/nitrogen/generated/ios/QueuePlayerAutolinking.mm +57 -0
  1008. package/nitrogen/generated/ios/QueuePlayerAutolinking.swift +62 -0
  1009. package/nitrogen/generated/ios/c++/HybridCastManagerSpecSwift.cpp +11 -0
  1010. package/nitrogen/generated/ios/c++/HybridCastManagerSpecSwift.hpp +298 -0
  1011. package/nitrogen/generated/ios/c++/HybridEqualizerSpecSwift.cpp +11 -0
  1012. package/nitrogen/generated/ios/c++/HybridEqualizerSpecSwift.hpp +178 -0
  1013. package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.cpp +11 -0
  1014. package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +749 -0
  1015. package/nitrogen/generated/ios/c++/HybridVisualizerSpecSwift.cpp +11 -0
  1016. package/nitrogen/generated/ios/c++/HybridVisualizerSpecSwift.hpp +103 -0
  1017. package/nitrogen/generated/ios/swift/AudioCategoryOptions.swift +78 -0
  1018. package/nitrogen/generated/ios/swift/AudioCodec.swift +60 -0
  1019. package/nitrogen/generated/ios/swift/AudioContainer.swift +64 -0
  1020. package/nitrogen/generated/ios/swift/AudioContentType.swift +40 -0
  1021. package/nitrogen/generated/ios/swift/AudioRoute.swift +49 -0
  1022. package/nitrogen/generated/ios/swift/AudioRouteKind.swift +84 -0
  1023. package/nitrogen/generated/ios/swift/BrowseItem.swift +93 -0
  1024. package/nitrogen/generated/ios/swift/BrowseSection.swift +74 -0
  1025. package/nitrogen/generated/ios/swift/BrowseSnapshot.swift +40 -0
  1026. package/nitrogen/generated/ios/swift/BufferState.swift +48 -0
  1027. package/nitrogen/generated/ios/swift/CacheStatus.swift +55 -0
  1028. package/nitrogen/generated/ios/swift/CastConfigureOptions.swift +47 -0
  1029. package/nitrogen/generated/ios/swift/CastConnectOptions.swift +29 -0
  1030. package/nitrogen/generated/ios/swift/CastDeviceKind.swift +56 -0
  1031. package/nitrogen/generated/ios/swift/CastDiscoveryState.swift +29 -0
  1032. package/nitrogen/generated/ios/swift/CastLocalNetworkPermission.swift +44 -0
  1033. package/nitrogen/generated/ios/swift/CastLocalNetworkPermissionEvent.swift +29 -0
  1034. package/nitrogen/generated/ios/swift/CastProtocol.swift +52 -0
  1035. package/nitrogen/generated/ios/swift/CastReceiver.swift +59 -0
  1036. package/nitrogen/generated/ios/swift/CastReceiverCapabilities.swift +39 -0
  1037. package/nitrogen/generated/ios/swift/CastRoute.swift +54 -0
  1038. package/nitrogen/generated/ios/swift/CastSessionDiedEvent.swift +34 -0
  1039. package/nitrogen/generated/ios/swift/CastSessionEndReason.swift +64 -0
  1040. package/nitrogen/generated/ios/swift/CastStreamingModel.swift +40 -0
  1041. package/nitrogen/generated/ios/swift/EqualizerBand.swift +34 -0
  1042. package/nitrogen/generated/ios/swift/EqualizerPreset.swift +45 -0
  1043. package/nitrogen/generated/ios/swift/EvictionPolicy.swift +40 -0
  1044. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_BrowseItem______std__string.swift +67 -0
  1045. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______MediaSearchRequest.swift +67 -0
  1046. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_TrackItem______std__string.swift +67 -0
  1047. package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
  1048. package/nitrogen/generated/ios/swift/Func_void_AudioRoute.swift +46 -0
  1049. package/nitrogen/generated/ios/swift/Func_void_BufferState.swift +46 -0
  1050. package/nitrogen/generated/ios/swift/Func_void_CacheStatus.swift +46 -0
  1051. package/nitrogen/generated/ios/swift/Func_void_CastDiscoveryState.swift +46 -0
  1052. package/nitrogen/generated/ios/swift/Func_void_CastLocalNetworkPermission.swift +46 -0
  1053. package/nitrogen/generated/ios/swift/Func_void_CastLocalNetworkPermissionEvent.swift +46 -0
  1054. package/nitrogen/generated/ios/swift/Func_void_CastRoute.swift +46 -0
  1055. package/nitrogen/generated/ios/swift/Func_void_CastSessionDiedEvent.swift +46 -0
  1056. package/nitrogen/generated/ios/swift/Func_void_PlaybackError.swift +46 -0
  1057. package/nitrogen/generated/ios/swift/Func_void_PlayerProgress.swift +46 -0
  1058. package/nitrogen/generated/ios/swift/Func_void_PlayerState_StateChangeReason.swift +46 -0
  1059. package/nitrogen/generated/ios/swift/Func_void_ServiceReadyReason.swift +46 -0
  1060. package/nitrogen/generated/ios/swift/Func_void_ShuffleResult.swift +46 -0
  1061. package/nitrogen/generated/ios/swift/Func_void_SkipCapability.swift +46 -0
  1062. package/nitrogen/generated/ios/swift/Func_void_SleepTimerState.swift +46 -0
  1063. package/nitrogen/generated/ios/swift/Func_void_VisualizerErrorReason.swift +46 -0
  1064. package/nitrogen/generated/ios/swift/Func_void_VisualizerFrame.swift +46 -0
  1065. package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
  1066. package/nitrogen/generated/ios/swift/Func_void_double_double.swift +46 -0
  1067. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  1068. package/nitrogen/generated/ios/swift/Func_void_std__optional_TrackItem__double_TrackChangeReason_std__optional_double_.swift +53 -0
  1069. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__variant_nitro__NullType__NowPlayingFormat__.swift +65 -0
  1070. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_std__vector_BrowseItem___.swift +66 -0
  1071. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_std__vector_TrackItem___.swift +66 -0
  1072. package/nitrogen/generated/ios/swift/Func_void_std__vector_BrowseItem_.swift +46 -0
  1073. package/nitrogen/generated/ios/swift/Func_void_std__vector_CastReceiver_.swift +46 -0
  1074. package/nitrogen/generated/ios/swift/Func_void_std__vector_EqualizerBand_.swift +46 -0
  1075. package/nitrogen/generated/ios/swift/Func_void_std__vector_TrackItem_.swift +46 -0
  1076. package/nitrogen/generated/ios/swift/HybridCastManagerSpec.swift +76 -0
  1077. package/nitrogen/generated/ios/swift/HybridCastManagerSpec_cxx.swift +519 -0
  1078. package/nitrogen/generated/ios/swift/HybridEqualizerSpec.swift +66 -0
  1079. package/nitrogen/generated/ios/swift/HybridEqualizerSpec_cxx.swift +353 -0
  1080. package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +122 -0
  1081. package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +1430 -0
  1082. package/nitrogen/generated/ios/swift/HybridVisualizerSpec.swift +56 -0
  1083. package/nitrogen/generated/ios/swift/HybridVisualizerSpec_cxx.swift +163 -0
  1084. package/nitrogen/generated/ios/swift/LookaheadCacheConfig.swift +34 -0
  1085. package/nitrogen/generated/ios/swift/MediaSearchOrigin.swift +40 -0
  1086. package/nitrogen/generated/ios/swift/MediaSearchQueueLocation.swift +44 -0
  1087. package/nitrogen/generated/ios/swift/MediaSearchReference.swift +40 -0
  1088. package/nitrogen/generated/ios/swift/MediaSearchReleaseDateRange.swift +60 -0
  1089. package/nitrogen/generated/ios/swift/MediaSearchRepeatMode.swift +44 -0
  1090. package/nitrogen/generated/ios/swift/MediaSearchRequest.swift +239 -0
  1091. package/nitrogen/generated/ios/swift/MediaSearchType.swift +52 -0
  1092. package/nitrogen/generated/ios/swift/NowPlayingFormat.swift +394 -0
  1093. package/nitrogen/generated/ios/swift/NowPlayingFormatAndroidExtras.swift +135 -0
  1094. package/nitrogen/generated/ios/swift/NowPlayingFormatIosExtras.swift +61 -0
  1095. package/nitrogen/generated/ios/swift/PitchCorrectionMode.swift +44 -0
  1096. package/nitrogen/generated/ios/swift/PlaybackError.swift +64 -0
  1097. package/nitrogen/generated/ios/swift/PlaybackErrorCode.swift +80 -0
  1098. package/nitrogen/generated/ios/swift/PlaybackMode.swift +47 -0
  1099. package/nitrogen/generated/ios/swift/PlaybackModeKind.swift +40 -0
  1100. package/nitrogen/generated/ios/swift/PlayerConfig.swift +251 -0
  1101. package/nitrogen/generated/ios/swift/PlayerProgress.swift +39 -0
  1102. package/nitrogen/generated/ios/swift/PlayerState.swift +60 -0
  1103. package/nitrogen/generated/ios/swift/RemoteControlOptions.swift +39 -0
  1104. package/nitrogen/generated/ios/swift/RemoteSkipMode.swift +40 -0
  1105. package/nitrogen/generated/ios/swift/RepeatMode.swift +44 -0
  1106. package/nitrogen/generated/ios/swift/ReplayGainMode.swift +44 -0
  1107. package/nitrogen/generated/ios/swift/SectionIcon.swift +84 -0
  1108. package/nitrogen/generated/ios/swift/ServiceReadyReason.swift +40 -0
  1109. package/nitrogen/generated/ios/swift/ShuffleResult.swift +51 -0
  1110. package/nitrogen/generated/ios/swift/SkipCapability.swift +34 -0
  1111. package/nitrogen/generated/ios/swift/SleepTimerState.swift +70 -0
  1112. package/nitrogen/generated/ios/swift/StateChangeReason.swift +60 -0
  1113. package/nitrogen/generated/ios/swift/TrackChangeReason.swift +60 -0
  1114. package/nitrogen/generated/ios/swift/TrackItem.swift +169 -0
  1115. package/nitrogen/generated/ios/swift/TrackSource.swift +44 -0
  1116. package/nitrogen/generated/ios/swift/Variant_NullType_Double.swift +30 -0
  1117. package/nitrogen/generated/ios/swift/Variant_NullType_NowPlayingFormat.swift +30 -0
  1118. package/nitrogen/generated/ios/swift/Variant_NullType_String.swift +30 -0
  1119. package/nitrogen/generated/ios/swift/VisualizerConfig.swift +52 -0
  1120. package/nitrogen/generated/ios/swift/VisualizerErrorReason.swift +48 -0
  1121. package/nitrogen/generated/ios/swift/VisualizerFrame.swift +52 -0
  1122. package/nitrogen/generated/ios/swift/VoiceVocabulary.swift +46 -0
  1123. package/nitrogen/generated/shared/c++/AudioCategoryOptions.hpp +91 -0
  1124. package/nitrogen/generated/shared/c++/AudioCodec.hpp +96 -0
  1125. package/nitrogen/generated/shared/c++/AudioContainer.hpp +100 -0
  1126. package/nitrogen/generated/shared/c++/AudioContentType.hpp +76 -0
  1127. package/nitrogen/generated/shared/c++/AudioRoute.hpp +101 -0
  1128. package/nitrogen/generated/shared/c++/AudioRouteKind.hpp +120 -0
  1129. package/nitrogen/generated/shared/c++/BrowseItem.hpp +104 -0
  1130. package/nitrogen/generated/shared/c++/BrowseSection.hpp +106 -0
  1131. package/nitrogen/generated/shared/c++/BrowseSnapshot.hpp +90 -0
  1132. package/nitrogen/generated/shared/c++/BufferState.hpp +84 -0
  1133. package/nitrogen/generated/shared/c++/CacheStatus.hpp +100 -0
  1134. package/nitrogen/generated/shared/c++/CastConfigureOptions.hpp +88 -0
  1135. package/nitrogen/generated/shared/c++/CastConnectOptions.hpp +83 -0
  1136. package/nitrogen/generated/shared/c++/CastDeviceKind.hpp +92 -0
  1137. package/nitrogen/generated/shared/c++/CastDiscoveryState.hpp +83 -0
  1138. package/nitrogen/generated/shared/c++/CastLocalNetworkPermission.hpp +80 -0
  1139. package/nitrogen/generated/shared/c++/CastLocalNetworkPermissionEvent.hpp +84 -0
  1140. package/nitrogen/generated/shared/c++/CastProtocol.hpp +88 -0
  1141. package/nitrogen/generated/shared/c++/CastReceiver.hpp +115 -0
  1142. package/nitrogen/generated/shared/c++/CastReceiverCapabilities.hpp +92 -0
  1143. package/nitrogen/generated/shared/c++/CastRoute.hpp +111 -0
  1144. package/nitrogen/generated/shared/c++/CastSessionDiedEvent.hpp +89 -0
  1145. package/nitrogen/generated/shared/c++/CastSessionEndReason.hpp +100 -0
  1146. package/nitrogen/generated/shared/c++/CastStreamingModel.hpp +76 -0
  1147. package/nitrogen/generated/shared/c++/EqualizerBand.hpp +87 -0
  1148. package/nitrogen/generated/shared/c++/EqualizerPreset.hpp +92 -0
  1149. package/nitrogen/generated/shared/c++/EvictionPolicy.hpp +76 -0
  1150. package/nitrogen/generated/shared/c++/HybridCastManagerSpec.cpp +42 -0
  1151. package/nitrogen/generated/shared/c++/HybridCastManagerSpec.hpp +115 -0
  1152. package/nitrogen/generated/shared/c++/HybridEqualizerSpec.cpp +32 -0
  1153. package/nitrogen/generated/shared/c++/HybridEqualizerSpec.hpp +81 -0
  1154. package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.cpp +88 -0
  1155. package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +209 -0
  1156. package/nitrogen/generated/shared/c++/HybridVisualizerSpec.cpp +22 -0
  1157. package/nitrogen/generated/shared/c++/HybridVisualizerSpec.hpp +71 -0
  1158. package/nitrogen/generated/shared/c++/LookaheadCacheConfig.hpp +87 -0
  1159. package/nitrogen/generated/shared/c++/MediaSearchOrigin.hpp +76 -0
  1160. package/nitrogen/generated/shared/c++/MediaSearchQueueLocation.hpp +80 -0
  1161. package/nitrogen/generated/shared/c++/MediaSearchReference.hpp +76 -0
  1162. package/nitrogen/generated/shared/c++/MediaSearchReleaseDateRange.hpp +87 -0
  1163. package/nitrogen/generated/shared/c++/MediaSearchRepeatMode.hpp +80 -0
  1164. package/nitrogen/generated/shared/c++/MediaSearchRequest.hpp +157 -0
  1165. package/nitrogen/generated/shared/c++/MediaSearchType.hpp +88 -0
  1166. package/nitrogen/generated/shared/c++/NowPlayingFormat.hpp +149 -0
  1167. package/nitrogen/generated/shared/c++/NowPlayingFormatAndroidExtras.hpp +94 -0
  1168. package/nitrogen/generated/shared/c++/NowPlayingFormatIosExtras.hpp +85 -0
  1169. package/nitrogen/generated/shared/c++/PitchCorrectionMode.hpp +80 -0
  1170. package/nitrogen/generated/shared/c++/PlaybackError.hpp +113 -0
  1171. package/nitrogen/generated/shared/c++/PlaybackErrorCode.hpp +116 -0
  1172. package/nitrogen/generated/shared/c++/PlaybackMode.hpp +89 -0
  1173. package/nitrogen/generated/shared/c++/PlaybackModeKind.hpp +76 -0
  1174. package/nitrogen/generated/shared/c++/PlayerConfig.hpp +141 -0
  1175. package/nitrogen/generated/shared/c++/PlayerProgress.hpp +91 -0
  1176. package/nitrogen/generated/shared/c++/PlayerState.hpp +96 -0
  1177. package/nitrogen/generated/shared/c++/RemoteControlOptions.hpp +92 -0
  1178. package/nitrogen/generated/shared/c++/RemoteSkipMode.hpp +76 -0
  1179. package/nitrogen/generated/shared/c++/RepeatMode.hpp +80 -0
  1180. package/nitrogen/generated/shared/c++/ReplayGainMode.hpp +80 -0
  1181. package/nitrogen/generated/shared/c++/SectionIcon.hpp +120 -0
  1182. package/nitrogen/generated/shared/c++/ServiceReadyReason.hpp +76 -0
  1183. package/nitrogen/generated/shared/c++/ShuffleResult.hpp +94 -0
  1184. package/nitrogen/generated/shared/c++/SkipCapability.hpp +87 -0
  1185. package/nitrogen/generated/shared/c++/SleepTimerState.hpp +95 -0
  1186. package/nitrogen/generated/shared/c++/StateChangeReason.hpp +96 -0
  1187. package/nitrogen/generated/shared/c++/TrackChangeReason.hpp +96 -0
  1188. package/nitrogen/generated/shared/c++/TrackItem.hpp +113 -0
  1189. package/nitrogen/generated/shared/c++/TrackSource.hpp +80 -0
  1190. package/nitrogen/generated/shared/c++/VisualizerConfig.hpp +91 -0
  1191. package/nitrogen/generated/shared/c++/VisualizerErrorReason.hpp +84 -0
  1192. package/nitrogen/generated/shared/c++/VisualizerFrame.hpp +92 -0
  1193. package/nitrogen/generated/shared/c++/VoiceVocabulary.hpp +88 -0
  1194. package/package.json +167 -0
  1195. package/src/CastManager.nitro.ts +206 -0
  1196. package/src/Equalizer.nitro.ts +37 -0
  1197. package/src/TrackPlayer.nitro.ts +667 -0
  1198. package/src/Visualizer.nitro.ts +48 -0
  1199. package/src/cast/index.ts +224 -0
  1200. package/src/clients.ts +50 -0
  1201. package/src/equalizer.ts +86 -0
  1202. package/src/hooks/index.ts +33 -0
  1203. package/src/hooks/useActiveTrack.ts +55 -0
  1204. package/src/hooks/useAudioRoute.ts +69 -0
  1205. package/src/hooks/useBufferState.ts +35 -0
  1206. package/src/hooks/useCanSkip.ts +92 -0
  1207. package/src/hooks/useCast.ts +121 -0
  1208. package/src/hooks/useEqualizer.ts +111 -0
  1209. package/src/hooks/useFullyBuffered.ts +32 -0
  1210. package/src/hooks/useLookaheadCache.ts +54 -0
  1211. package/src/hooks/useNowPlayingFormat.ts +43 -0
  1212. package/src/hooks/usePlaybackState.ts +34 -0
  1213. package/src/hooks/useProgress.ts +36 -0
  1214. package/src/hooks/useSleepTimer.ts +63 -0
  1215. package/src/hooks/useVisualizerState.ts +121 -0
  1216. package/src/index.ts +125 -0
  1217. package/src/playbackService.ts +304 -0
  1218. package/src/types.ts +1387 -0
@@ -0,0 +1,1716 @@
1
+ package com.margelo.nitro.queueplayer
2
+
3
+ import android.app.Notification
4
+ import android.app.NotificationChannel
5
+ import android.app.NotificationManager
6
+ import android.app.PendingIntent
7
+ import android.content.BroadcastReceiver
8
+ import android.content.Context
9
+ import android.app.Service
10
+ import android.content.Intent
11
+ import android.content.IntentFilter
12
+ import android.media.AudioManager
13
+ import android.os.Binder
14
+ import android.os.Build
15
+ import android.os.Handler
16
+ import android.os.IBinder
17
+ import android.os.Looper
18
+ import androidx.core.content.ContextCompat
19
+ import androidx.annotation.VisibleForTesting
20
+ import androidx.media3.common.C
21
+ import androidx.media3.common.MediaItem
22
+ import androidx.media3.common.Player
23
+ import androidx.media3.common.util.UnstableApi
24
+ import androidx.media3.datasource.DataSourceBitmapLoader
25
+ import androidx.media3.exoplayer.ExoPlayer
26
+ import androidx.media3.exoplayer.source.MediaSource
27
+ import androidx.media3.session.CacheBitmapLoader
28
+ import androidx.media3.session.CommandButton
29
+ import androidx.media3.session.LibraryResult
30
+ import androidx.media3.session.MediaLibraryService
31
+ import androidx.media3.session.MediaLibraryService.LibraryParams
32
+ import androidx.media3.session.MediaLibraryService.MediaLibrarySession
33
+ import androidx.media3.session.MediaSession
34
+ import androidx.media3.session.SessionResult
35
+ import com.google.common.collect.ImmutableList
36
+ import com.google.common.util.concurrent.Futures
37
+ import com.google.common.util.concurrent.ListenableFuture
38
+ import com.google.common.util.concurrent.MoreExecutors
39
+ import com.google.common.util.concurrent.SettableFuture
40
+ import com.margelo.nitro.core.Promise
41
+ import com.margelo.nitro.queueplayer.cast.PlaybackStateRouter
42
+ import java.util.UUID
43
+ import kotlinx.coroutines.cancel
44
+ import kotlinx.coroutines.currentCoroutineContext
45
+ import kotlinx.coroutines.delay
46
+ import kotlinx.coroutines.flow.collect
47
+ import kotlinx.coroutines.isActive
48
+ import kotlinx.coroutines.launch
49
+ import kotlinx.coroutines.withContext
50
+
51
+ /**
52
+ * Foreground service that owns the ExoPlayer + MediaLibrarySession.
53
+ * In-process clients (the `TrackPlayer` Hybrid) obtain a
54
+ * [LocalBinder] handle via [ACTION_LOCAL_BIND] to access the
55
+ * player + session directly; framework media controllers (Android
56
+ * Auto, Wear, Assistant) bind through the standard
57
+ * [MediaLibraryService] path declared in the manifest.
58
+ *
59
+ * Extends [HeadlessJsMediaService] (which extends
60
+ * [MediaLibraryService] which is a strict superset of
61
+ * `MediaSessionService`) to provide:
62
+ * - Browse + search affordances to Android Auto / Google Assistant
63
+ * via [PlaybackServiceCallback].
64
+ * - JS-task spawning on cold-wake (Auto / Assistant binds the
65
+ * service before any Activity has booted React) via
66
+ * [getTaskConfig] returning a [HeadlessJsTaskConfig] for known
67
+ * wake actions.
68
+ *
69
+ * Threading: ExoPlayer is constructed + accessed on the service's
70
+ * main looper, which equals the application's main looper in this
71
+ * single-process library. Heavy work (decode, buffer, network
72
+ * I/O) happens on ExoPlayer's internal threads regardless. Binder
73
+ * consumers MUST hop to [LocalBinder.handler] before touching the
74
+ * player so the host-handler indirection is canonical.
75
+ */
76
+ @UnstableApi
77
+ class PlaybackService : HeadlessJsMediaService() {
78
+
79
+ companion object {
80
+ /**
81
+ * Intent action consumers use with `bindService` to obtain the
82
+ * [LocalBinder]. Falls through to [MediaLibraryService.onBind]'s
83
+ * default behaviour for any other action so the framework's own
84
+ * media controller connection path keeps working.
85
+ */
86
+ const val ACTION_LOCAL_BIND = "com.margelo.nitro.queueplayer.LOCAL_BIND"
87
+
88
+ /**
89
+ * The currently-active playback engine. Mirrors the engine the
90
+ * service is wrapping. Set on engine boot + every swap; cleared
91
+ * on service destroy.
92
+ *
93
+ * The Equalizer + Visualizer Hybrids reach this on construction
94
+ * to wire themselves to an already-running engine — covers the
95
+ * case where a Hybrid is constructed AFTER the service has
96
+ * dispatched its initial wireToPlaybackEngine fires (which
97
+ * would have hit a `null` static reference). Without this, a
98
+ * Hybrid that lazy-constructs after service onCreate would
99
+ * silently never receive any wire.
100
+ */
101
+ @JvmStatic
102
+ @Volatile
103
+ internal var activeEngine: PlaybackEngine? = null
104
+ private set
105
+
106
+ @JvmSynthetic
107
+ internal fun setActiveEngine(engine: PlaybackEngine?) {
108
+ activeEngine = engine
109
+ }
110
+
111
+ /**
112
+ * NotificationChannel ID for the media-style foreground
113
+ * notification. Fully-qualified to avoid collision with
114
+ * channels created elsewhere in the consumer app. `internal`
115
+ * so [NotificationProvider] can pin it without a cross-class
116
+ * literal.
117
+ */
118
+ internal const val NOTIFICATION_CHANNEL_ID = "com.margelo.nitro.queueplayer.playback"
119
+
120
+ /**
121
+ * Notification ID for Media3's notification provider. Stable
122
+ * across the service lifecycle so updates replace the post in
123
+ * place rather than spawning duplicates. `internal` for the
124
+ * same reason as [NOTIFICATION_CHANNEL_ID].
125
+ */
126
+ internal const val NOTIFICATION_ID = 1001
127
+
128
+ /**
129
+ * Request code paired with the launcher [PendingIntent] handed
130
+ * to [MediaSession.Builder.setSessionActivity]. Stable across
131
+ * the process so [PendingIntent.FLAG_UPDATE_CURRENT] mutates
132
+ * the existing entry instead of spawning a duplicate.
133
+ */
134
+ private const val REQUEST_CODE_OPEN_APP = 0
135
+
136
+ /**
137
+ * Maximum wall-clock wait for a JS-side search-request handler
138
+ * to resolve. After this expires the dispatch surfaces an empty
139
+ * result list to the controller — better a clean empty than a
140
+ * spinner that never goes away. 10 s mirrors the standard
141
+ * Android Auto / Assistant search-tile timeout.
142
+ */
143
+ private const val SEARCH_DISPATCH_TIMEOUT_MS = 10_000L
144
+
145
+ /**
146
+ * Maximum wall-clock wait for a cold-start headless dispatch to
147
+ * resolve — covers (a) the JS bridge spinning up + the consumer's
148
+ * `registerPlaybackService` running, AND (b) the consumer's
149
+ * resolution Promise running. 20 s mirrors the iOS
150
+ * [PendingIntentBuffer] TTL: ~3 s for bridge boot + ~17 s of
151
+ * headroom for the consumer's network calls before the controller
152
+ * surfaces "no results" and the user retries. The two platforms'
153
+ * cold-start windows are deliberately the same so the manual
154
+ * playbook reads identically on iPhone + Android.
155
+ */
156
+ private const val HEADLESS_DISPATCH_TIMEOUT_MS = 20_000L
157
+
158
+ /**
159
+ * Intent action that flags an `onStartCommand` invocation as a
160
+ * headless-controller cold-wake. [getTaskConfig] returns a
161
+ * non-null config only for this action so an in-process
162
+ * `LOCAL_BIND` start does not also spawn the JS task.
163
+ */
164
+ internal const val ACTION_HEADLESS_WAKE =
165
+ "com.margelo.nitro.queueplayer.HEADLESS_WAKE"
166
+
167
+ /** Bundle key carrying the connecting controller's package name
168
+ * through the wake intent → JS task data map. */
169
+ internal const val EXTRA_WAKE_PACKAGE = "wakePackage"
170
+
171
+ /** Bundle key carrying the wake intent's `action` through the JS
172
+ * task data map so the consumer's playbackService handler can
173
+ * branch on the wake source if it wants to. */
174
+ internal const val EXTRA_WAKE_ACTION = "wakeAction"
175
+
176
+ /** Intent actions that signal a cold-wake — either an in-process
177
+ * self-wake from `onConnect` (`ACTION_HEADLESS_WAKE`) or an
178
+ * OS-routed hardware media-button intent landing on a service
179
+ * whose JS bridge isn't yet up (`ACTION_MEDIA_BUTTON`). */
180
+ private val HEADLESS_WAKE_ACTIONS: Set<String> = setOf(
181
+ ACTION_HEADLESS_WAKE,
182
+ Intent.ACTION_MEDIA_BUTTON
183
+ )
184
+
185
+ /**
186
+ * Headless task name handed to React Native's
187
+ * `HeadlessJsTaskService`. Must match the
188
+ * `AppRegistry.registerHeadlessTask` registration the lib's
189
+ * `registerPlaybackService` does on the JS side.
190
+ */
191
+ internal const val HEADLESS_TASK_NAME = "RNQueuePlayerHeadlessTask"
192
+
193
+ /** Logcat tag for service-level lifecycle + dispatch warnings.
194
+ * Member of the `QueuePlayer.<area>` hierarchy used across the
195
+ * lib's Android sources. */
196
+ private const val LOG_TAG = "QueuePlayer.Service"
197
+
198
+ /** Minimum spacing between consecutive `selfWakeForHeadlessClient`
199
+ * fires from the same service instance. Defence against
200
+ * gearhead's rapid bind/unbind retry loop when its initial
201
+ * `onConnect` resolves to a not-supported state. */
202
+ private const val SELF_WAKE_MIN_INTERVAL_MS = 30_000L
203
+
204
+ /** Grace window between an idle `onHeadlessJsTaskFinish` and the
205
+ * actual `stopSelf()`. The JS bundle-load side effects
206
+ * (`bootstrap.ts` → `configure()` + `installCarService()`) run
207
+ * AFTER the headless task body resolves; tearing the service
208
+ * down immediately races that chain. 5s comfortably covers the
209
+ * cold-start bootstrap timing without keeping a genuinely-idle
210
+ * service alive for long. */
211
+ private const val IDLE_STOP_GRACE_MS = 5_000L
212
+ }
213
+
214
+ /** Timestamp of the last actual self-wake fire — used by the
215
+ * rate-limiter in [selfWakeForHeadlessClient]. Zero means "never
216
+ * fired in this service instance". */
217
+ @Volatile
218
+ private var lastSelfWakeAtMs: Long = 0L
219
+
220
+ /** True when the consumer app's React host has an active context
221
+ * AND a foregrounded Activity. Used by [selfWakeForHeadlessClient]
222
+ * to skip the wake-intent self-start that would otherwise race
223
+ * the live JS instance. The `currentActivity` check is more
224
+ * reliable in Expo dev-client mode than reading
225
+ * `currentReactContext` alone — the context can be null during a
226
+ * JS reload while the activity is still up. */
227
+ private fun hasLiveReactContext(): Boolean {
228
+ val reactApp = application as? com.facebook.react.ReactApplication ?: return false
229
+ val ctx = reactApp.reactHost?.currentReactContext
230
+ return ctx != null && ctx.currentActivity != null
231
+ }
232
+
233
+ // Built in onCreate; released in onDestroy. `lateinit var` (vs
234
+ // `?:`) reflects the contract that any consumer reading these
235
+ // through the binder is guaranteed to be post-onCreate by Android's
236
+ // service lifecycle.
237
+ //
238
+ // The service owns a [GaplessEngine] wrapper around the ExoPlayer.
239
+ // Service-level wiring (audio focus listener, MediaSession.setPlayer)
240
+ // continues to access the underlying player via [engine].`exoPlayer`;
241
+ // the abstract [PlaybackEngine] surface is exposed via
242
+ // [LocalBinder.engine] for [TrackPlayer] and other engine-aware
243
+ // consumers.
244
+ private lateinit var engine: PlaybackEngine
245
+ private val exoPlayer: ExoPlayer get() = engine.mediaSessionPlayer
246
+ private var mediaSession: MediaLibrarySession? = null
247
+ private val mainHandler = Handler(Looper.getMainLooper())
248
+
249
+ /**
250
+ * Native cache for the consumer's `BrowseSnapshot`. Read by
251
+ * [PlaybackServiceCallback]'s library callbacks
252
+ * (`onGetLibraryRoot`, `onGetChildren`, `onGetItem`, etc.) so a
253
+ * cold-wake from Android Auto / Assistant doesn't have to wait
254
+ * for a JS bridge round-trip. Written via the binder by
255
+ * `TrackPlayer.setBrowseSnapshot`. Empty by default — the
256
+ * library surface returns `RESULT_ERROR_NOT_SUPPORTED` until a
257
+ * snapshot is pushed.
258
+ */
259
+ internal val browseDataProvider = BrowseDataProvider()
260
+
261
+ /**
262
+ * Per-query cache of JS-supplied search results. Populated by
263
+ * `TrackPlayer.setSearchResults` once the JS handler
264
+ * responds to a search request; read by
265
+ * [PlaybackServiceCallback.onGetSearchResult]. Empty by default —
266
+ * a controller's `getSearchResult` call before the JS responder
267
+ * has answered returns an empty page.
268
+ */
269
+ internal val searchResultCache = SearchResultCache()
270
+
271
+ /**
272
+ * Native queue of `parentId`s that a controller asked the
273
+ * MediaLibrarySession to enumerate before the consumer's JS bridge
274
+ * had pushed a [BrowseSnapshot]. Drained by
275
+ * [notifyChildrenChangedForParents] on the next snapshot push so
276
+ * the controller re-fetches with real data. See
277
+ * [PendingBrowseRequests] for the cold-start race rationale.
278
+ */
279
+ internal val pendingBrowseRequests = PendingBrowseRequests()
280
+
281
+ /**
282
+ * Holds the JS-side automotive callbacks (`onBrowseRequest`,
283
+ * `onSearchRequest`, `onPlayFromSearchRequest`,
284
+ * `onPlayFromIdRequest`, `onCarConnect`, `onCarDisconnect`).
285
+ * Registered by `TrackPlayer.on*` Hybrid methods; consulted by
286
+ * the [callbackEnvironment] hooks when the `MediaLibrarySession`
287
+ * callback path needs to dispatch into JS.
288
+ */
289
+ internal val browseCallbacks = BrowseCallbackRegistry()
290
+
291
+ /**
292
+ * Dependency surface [PlaybackServiceCallback] reads through.
293
+ * Lambda-based providers for fields that mutate over the service
294
+ * lifetime (`commandHandler`, `mediaButtonDispatch`) so the
295
+ * callback always sees the live value without a reference rebuild.
296
+ */
297
+ @VisibleForTesting
298
+ internal val callbackEnvironment = object : PlaybackServiceCallback.Environment {
299
+ override val browseDataProvider: BrowseDataProvider
300
+ get() = this@PlaybackService.browseDataProvider
301
+
302
+ override val searchResultCache: SearchResultCache
303
+ get() = this@PlaybackService.searchResultCache
304
+
305
+ override fun commandHandler() = this@PlaybackService.commandHandler
306
+
307
+ override fun mediaButtonDispatch() = this@PlaybackService.mediaButtonDispatch
308
+
309
+ override fun playerCommandsFor(
310
+ capability: SkipCapability,
311
+ hasMediaItems: Boolean
312
+ ): Player.Commands =
313
+ this@PlaybackService.playerCommandsFor(capability, hasMediaItems)
314
+
315
+ override fun onHeadlessControllerConnect(packageName: String) {
316
+ // Cold-wake the JS bridge so the consumer's
317
+ // `registerPlaybackService` can fire its callbacks before
318
+ // Media3's framework dispatches `onAddMediaItems`. Idempotent
319
+ // — `HeadlessJsMediaService.initialized` latches the JS task
320
+ // spawn so re-entrant connects do not double-spawn.
321
+ this@PlaybackService.selfWakeForHeadlessClient(packageName)
322
+ }
323
+
324
+ override fun onFirstHeadlessControllerAttached() {
325
+ // Cold-start race: the FIRST headless controller's onConnect
326
+ // fires BEFORE the JS task spawned by [selfWakeForHeadlessClient]
327
+ // has run `registerPlaybackService`, so reading the callback
328
+ // synchronously would silently drop the dispatch. Launch on
329
+ // [callbackDispatchScope] and poll up to
330
+ // [HEADLESS_DISPATCH_TIMEOUT_MS], matching dispatchAssistantPlay's
331
+ // shape. Subsequent connects in the same session land on a
332
+ // registered callback and fire on the first poll.
333
+ this@PlaybackService.dispatchCarConnect()
334
+ }
335
+
336
+ override fun onHeadlessControllerDisconnect() {
337
+ this@PlaybackService.browseCallbacks.carDisconnectCallback()?.invoke()
338
+ }
339
+
340
+ override fun onSearchQueryReceived(query: String) {
341
+ this@PlaybackService.dispatchSearchToJs(query)
342
+ }
343
+
344
+ override fun onPlayFromSearchRequest(
345
+ request: MediaSearchRequest
346
+ ): ListenableFuture<List<MediaItem>> =
347
+ this@PlaybackService.dispatchPlayFromSearchToJs(request)
348
+
349
+ override fun onPlayFromIdRequest(
350
+ mediaId: String
351
+ ): ListenableFuture<List<MediaItem>> =
352
+ this@PlaybackService.dispatchPlayFromIdToJs(mediaId)
353
+
354
+ override fun onPlayFromIdRequestNoSync(mediaId: String) {
355
+ this@PlaybackService.dispatchPlayFromIdToJsFireOnly(mediaId)
356
+ }
357
+
358
+ override fun enqueuePendingBrowseRequest(parentId: String) {
359
+ this@PlaybackService.pendingBrowseRequests.enqueue(parentId)
360
+ }
361
+
362
+ override fun onBrowseRequestForChildren(
363
+ parentId: String
364
+ ): ListenableFuture<List<BrowseItem>> =
365
+ this@PlaybackService.dispatchBrowseRequestToJs(parentId)
366
+
367
+ override fun libraryRootItem(): MediaItem = libraryRootItemFromPackage()
368
+ }
369
+
370
+ /** Cached display label for the consumer app. Resolved lazily on
371
+ * first `onGetLibraryRoot` since `packageManager` reads cost a
372
+ * PackageManager round-trip. */
373
+ @Volatile
374
+ private var cachedLibraryRootTitle: String? = null
375
+
376
+ /** Build the lib-owned root MediaItem with the consumer app's
377
+ * display label as title (e.g. "RNQP-Demo"). Cross-process
378
+ * controllers (Android Auto's gearhead) render this title in the
379
+ * browse drawer; an empty title makes the drawer entry look
380
+ * broken on some AA builds. */
381
+ private fun libraryRootItemFromPackage(): MediaItem {
382
+ val title = cachedLibraryRootTitle ?: run {
383
+ val resolved = try {
384
+ val pm = packageManager
385
+ val info = pm.getApplicationInfo(packageName, 0)
386
+ pm.getApplicationLabel(info).toString().ifBlank { "Library" }
387
+ } catch (_: Throwable) {
388
+ "Library"
389
+ }
390
+ cachedLibraryRootTitle = resolved
391
+ resolved
392
+ }
393
+ return MediaItemMapper.toMediaItem(
394
+ BrowseItem(
395
+ id = PlaybackServiceCallback.LIB_ROOT_ID,
396
+ title = title,
397
+ subtitle = null,
398
+ artworkUrl = null,
399
+ playable = false,
400
+ hasChildren = true
401
+ )
402
+ )
403
+ }
404
+
405
+ /**
406
+ * Binder exposed to in-process clients. Holds direct references
407
+ * to the ExoPlayer + MediaSession + the service's main handler.
408
+ * Single-process library: no IPC marshalling — the binder is
409
+ * effectively a typed pointer. Reads are guaranteed valid
410
+ * between the service's `onCreate` returning and `onDestroy`
411
+ * starting; consumers MUST `unbindService` before the service
412
+ * is destroyed.
413
+ */
414
+ inner class LocalBinder : Binder() {
415
+ val service: PlaybackService get() = this@PlaybackService
416
+
417
+ /**
418
+ * Stable identity for this service instance. JS-side
419
+ * `TrackPlayer.serviceConnection.onServiceConnected` compares
420
+ * the new binder's id against the previously-seen value to
421
+ * distinguish a first bind (`first-bind`) from a rebind after
422
+ * an OS-driven service kill (`service-reborn`). The OS rebuilds
423
+ * the service from scratch in the kill-and-restart path, so
424
+ * the new instance gets a fresh `instanceId` and the lib's
425
+ * `onServiceReady` dispatch fires with the right reason.
426
+ */
427
+ val instanceId: java.util.UUID = java.util.UUID.randomUUID()
428
+
429
+ /**
430
+ * Direct access to the underlying ExoPlayer. Retained as a
431
+ * back-compat shim for [TrackPlayer] until its transport /
432
+ * queue calls migrate onto the [engine] surface; engine-aware
433
+ * code paths use [engine] instead.
434
+ */
435
+ val player: ExoPlayer get() = exoPlayer
436
+
437
+ /**
438
+ * Strategy-pattern engine wrapping the active player(s).
439
+ * Routes through the abstract [PlaybackEngine] surface so
440
+ * consumers don't depend on the concrete engine class.
441
+ */
442
+ val engine: PlaybackEngine get() = this@PlaybackService.engine
443
+ val session: MediaLibrarySession get() = mediaSession!!
444
+ val handler: Handler get() = mainHandler
445
+
446
+ /**
447
+ * Register the [MediaSessionCommandHandler] that the
448
+ * `MediaSession.Callback` consults when a controller
449
+ * (lock-screen, Bluetooth, Android Auto) issues a player
450
+ * command, and that the [MediaButtonDispatch] consults when a
451
+ * raw `Intent.ACTION_MEDIA_BUTTON` lands via callback / service
452
+ * path. `null` clears the registration. Calls run on the service
453
+ * main looper.
454
+ */
455
+ internal fun setCommandHandler(handler: MediaSessionCommandHandler?) {
456
+ if (commandHandler === handler) return
457
+ commandHandler = handler
458
+ mediaButtonDispatch = handler?.let { MediaButtonDispatch(it) }
459
+ }
460
+
461
+ /**
462
+ * Push the latest skip-capability-derived [Player.Commands] to
463
+ * every connected controller. Called by [TrackPlayer] whenever
464
+ * `cachedSkipCapability` changes so the lock-screen, Bluetooth
465
+ * AVRCP, Android Auto, and Wear surfaces grey out skip buttons
466
+ * that the queue boundary makes invalid. No-op when no handler
467
+ * is registered (no authoritative capability source) or when no
468
+ * controllers have connected yet (next [PlaybackCallback.onConnect]
469
+ * picks up the fresh capability).
470
+ */
471
+ internal fun refreshAvailableCommands() {
472
+ val handler = commandHandler ?: return
473
+ val session = mediaSession ?: return
474
+ // The cap is global — same Player.Commands for every controller.
475
+ // `hasMediaItems` reads from the live player so a freshly-empty
476
+ // queue strips PLAY/PAUSE/PREPARE from the action mask and AA's
477
+ // dashboard tap routes to Library instead of an empty Now Playing.
478
+ val hasMediaItems = session.player.mediaItemCount > 0
479
+ val commands = playerCommandsFor(
480
+ handler.getCachedSkipCapability(),
481
+ hasMediaItems
482
+ )
483
+ for (controller in session.connectedControllers) {
484
+ session.setAvailableCommands(
485
+ controller,
486
+ MediaSession.ConnectionResult.DEFAULT_SESSION_COMMANDS,
487
+ commands,
488
+ )
489
+ }
490
+ }
491
+
492
+ /**
493
+ * Apply the lock-screen / notification remote-control config (skip mode +
494
+ * jump intervals). Called on the main thread from
495
+ * [TrackPlayer.setRemoteControls]; re-applies live.
496
+ */
497
+ internal fun setRemoteControls(mode: RemoteSkipMode, forwardMs: Long, backwardMs: Long) {
498
+ this@PlaybackService.applyRemoteControls(mode, forwardMs, backwardMs)
499
+ }
500
+
501
+ /**
502
+ * Mirror `PlayerConfig.clampSeekToBuffered` onto the forwarding player's
503
+ * interval skip-forward. Called on the main thread from
504
+ * [TrackPlayer.configureInternal] + on rebind.
505
+ */
506
+ internal fun setClampSeekToBuffered(enabled: Boolean) {
507
+ this@PlaybackService.applyClampSeekToBuffered(enabled)
508
+ }
509
+ }
510
+
511
+ private val localBinder by lazy { LocalBinder() }
512
+
513
+ /**
514
+ * Currently-registered handler consulted by [PlaybackCallback]
515
+ * before the default routing runs. `null` until
516
+ * [TrackPlayer.configureInternal] wires itself in; under that
517
+ * state every callback falls through to the Media3 default and
518
+ * raw media-button intents fall through to the framework.
519
+ */
520
+ private var commandHandler: MediaSessionCommandHandler? = null
521
+
522
+ /**
523
+ * Per-handler [MediaButtonDispatch] that absorbs duplicate fires
524
+ * when the same physical media-button press reaches the service
525
+ * via more than one path (callback `onMediaButtonEvent`, service
526
+ * `onStartCommand`, or a manifest-registered `MediaButtonReceiver`
527
+ * that forwards into `onStartCommand`). Created/torn down with the
528
+ * handler so its dedup state lives exactly as long as the handler
529
+ * does.
530
+ */
531
+ private var mediaButtonDispatch: MediaButtonDispatch? = null
532
+
533
+ /**
534
+ * Tracks the previous [Player.PlaybackSuppressionReason] so the
535
+ * audio-focus listener can detect entry into and exit from
536
+ * `TRANSIENT_AUDIO_FOCUS_LOSS`. Initialized to `REASON_NONE` to
537
+ * match the player's startup state; rewritten on every
538
+ * `onPlaybackSuppressionReasonChanged` fire after the mapping
539
+ * has been computed.
540
+ */
541
+ private var previousSuppressionReason: Int = Player.PLAYBACK_SUPPRESSION_REASON_NONE
542
+
543
+ /**
544
+ * Player instance the [audioFocusListener] is currently attached
545
+ * to. For GaplessEngine this is the underlying ExoPlayer; for
546
+ * CrossfadeEngine this is the [CrossfadeForwardingPlayer]
547
+ * wrapper (so listener events follow role swaps via the
548
+ * wrapper's `ForwardingSimpleBasePlayer` listener routing).
549
+ * Tracked here so the next [swapEngineInternal] can remove the
550
+ * listener from the same instance it was attached to.
551
+ */
552
+ private var audioFocusListenerHost: Player? = null
553
+
554
+ /**
555
+ * [Player.Listener] that translates Media3's audio-focus-driven
556
+ * state changes into typed [PlaybackError] events on the JS
557
+ * surface. `onPlayWhenReadyChanged` with reason
558
+ * `PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS` carries
559
+ * permanent loss; `onPlaybackSuppressionReasonChanged` carries
560
+ * transient loss + regain. Re-attached to the new player on every
561
+ * [configurePlayer] swap.
562
+ */
563
+ private val audioFocusListener = object : Player.Listener {
564
+ override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
565
+ val mapping = AudioFocusErrorMapping.mapPlayWhenReadyReason(reason) ?: return
566
+ commandHandler?.onAudioFocusEvent(mapping)
567
+ }
568
+
569
+ override fun onPlaybackSuppressionReasonChanged(reason: Int) {
570
+ val previous = previousSuppressionReason
571
+ previousSuppressionReason = reason
572
+ val mapping = AudioFocusErrorMapping.mapSuppressionReason(reason, previous) ?: return
573
+ commandHandler?.onAudioFocusEvent(mapping)
574
+ }
575
+ }
576
+
577
+ /**
578
+ * Receives [AudioManager.ACTION_AUDIO_BECOMING_NOISY] (headphone
579
+ * unplug, A2DP disconnect, USB DAC removal). Media3 already pauses
580
+ * playback in response to this broadcast via
581
+ * `setHandleAudioBecomingNoisy(true)` on the bound player; the
582
+ * receiver is purely a JS-event side-channel so consumers can render
583
+ * UX (e.g. "Paused — headphones disconnected" toast) without
584
+ * inferring the cause from a state-change emit. The receiver is NOT
585
+ * coupled to a specific player instance — [configurePlayer] swaps
586
+ * the ExoPlayer without disturbing the registration; the next
587
+ * broadcast routes through whatever handler is currently registered.
588
+ */
589
+ private val becomingNoisyReceiver = object : BroadcastReceiver() {
590
+ override fun onReceive(context: Context, intent: Intent) {
591
+ if (intent.action != AudioManager.ACTION_AUDIO_BECOMING_NOISY) return
592
+ commandHandler?.onBecomingNoisy()
593
+ }
594
+ }
595
+
596
+ /**
597
+ * Tracks whether [becomingNoisyReceiver] is currently registered so
598
+ * [onDestroy] is idempotent — `unregisterReceiver` throws
599
+ * `IllegalArgumentException` for receivers that aren't registered,
600
+ * which would surface if a service ever transitions through
601
+ * `onDestroy` more than once (e.g. a partial-init failure path or a
602
+ * test harness that walks the lifecycle twice).
603
+ */
604
+ private var becomingNoisyReceiverRegistered = false
605
+
606
+ /**
607
+ * The receiver's CastPlayer while a Chromecast session is active —
608
+ * the MediaSession is bound to it so the system notification +
609
+ * lockscreen follow the receiver. Non-null only while cast-bound;
610
+ * main-thread only. The local engine keeps running idle underneath.
611
+ */
612
+ private var castSessionPlayer: Player? = null
613
+
614
+ /**
615
+ * The local engine's MediaSession player — what is bound when not
616
+ * casting. Kept current by [swapEngineInternal] so a cast disconnect
617
+ * restores the right instance even if the engine changed (gapless↔
618
+ * crossfade) mid-cast.
619
+ */
620
+ private var localSessionPlayer: Player? = null
621
+
622
+ /**
623
+ * Stable wrapper the [mediaSession] is bound to for its lifetime.
624
+ * External-controller commands route through its `handle*` overrides
625
+ * (queue-aware skip + system-reason stamping); engine and cast swaps
626
+ * retarget its delegate via [SessionCommandForwardingPlayer.retarget]
627
+ * rather than re-binding the session player.
628
+ */
629
+ private var sessionForwardingPlayer: SessionCommandForwardingPlayer? = null
630
+
631
+ // Remote-control config (lock-screen / notification skip mode + jump
632
+ // intervals). Set from TrackPlayer via [LocalBinder.setRemoteControls];
633
+ // drives the notification button layout + the forwarding player's intervals.
634
+ private var remoteSkipMode: RemoteSkipMode = RemoteSkipMode.TRACK
635
+ private var remoteForwardMs: Long = 15_000
636
+ private var remoteBackwardMs: Long = 15_000
637
+ // Mirrors PlayerConfig.clampSeekToBuffered so the forwarding player's
638
+ // interval skip-forward obeys the same clamp as a JS seekTo. Survives a
639
+ // session rebuild (re-applied below); re-pushed by TrackPlayer on rebind.
640
+ private var clampSeekToBuffered: Boolean = false
641
+
642
+ /**
643
+ * Main-thread scope observing [PlaybackStateRouter.activeFlow] to bind
644
+ * the receiver's CastPlayer onto the MediaSession on cast connect and
645
+ * restore the local player on disconnect. Cancelled in [onDestroy].
646
+ * AirPlay never reaches here — `activeFlow` is RemotePlayer-only and a
647
+ * PcmStream (AirPlay) session is not a RemotePlayer.
648
+ */
649
+ private val castBindScope = kotlinx.coroutines.CoroutineScope(
650
+ kotlinx.coroutines.SupervisorJob() + kotlinx.coroutines.Dispatchers.Main.immediate
651
+ )
652
+
653
+ override fun onCreate() {
654
+ super.onCreate()
655
+ // Channel must exist before Media3 posts; `DefaultMediaNotificationProvider`
656
+ // does not create it. Android dedups by ID, so re-creation on each
657
+ // onCreate is idempotent.
658
+ createNotificationChannel()
659
+ engine = GaplessEngine(this)
660
+ exoPlayer.addListener(audioFocusListener)
661
+ audioFocusListenerHost = exoPlayer
662
+ localSessionPlayer = exoPlayer
663
+ // Unique per-onCreate session ID. Media3 keeps a process-wide
664
+ // static `MediaSession` registry keyed by ID — `MediaSession`'s
665
+ // constructor throws `Session ID must be unique` if a stale
666
+ // session for the same ID is still in the map. Service lifecycle
667
+ // is async (onDestroy of an old instance can race with onCreate
668
+ // of a fresh one), so a per-instance UUID is the only fully
669
+ // race-free strategy.
670
+ val callback = PlaybackServiceCallback(callbackEnvironment)
671
+ // Bind the session to a stable forwarding wrapper so external-
672
+ // controller commands route through its handle* overrides. The
673
+ // session + handler are read lazily: `mediaSession` is assigned just
674
+ // below, and `commandHandler` is registered later by TrackPlayer.
675
+ val forwardingPlayer = SessionCommandForwardingPlayer(
676
+ exoPlayer,
677
+ session = { mediaSession },
678
+ commandHandler = { commandHandler },
679
+ )
680
+ sessionForwardingPlayer = forwardingPlayer
681
+ // Re-apply any remote-control config set before this session existed (or
682
+ // carried across a reconfigure); the notification layout reads
683
+ // `remoteSkipMode` in `buildMediaButtonPreferences()` below.
684
+ forwardingPlayer.forwardJumpMs = remoteForwardMs
685
+ forwardingPlayer.backwardJumpMs = remoteBackwardMs
686
+ forwardingPlayer.clampSeekToBuffered = clampSeekToBuffered
687
+ val session = MediaLibrarySession.Builder(this, forwardingPlayer, callback)
688
+ .setId(UUID.randomUUID().toString())
689
+ .setBitmapLoader(buildBitmapLoader())
690
+ .setMediaButtonPreferences(buildMediaButtonPreferences())
691
+ // Skip when consumer is launcher-less (rare service-only consumer); the
692
+ // session has no sessionActivity in that case, so the notification body
693
+ // is built without a content intent.
694
+ .also { builder -> buildSessionActivityPendingIntent()?.let(builder::setSessionActivity) }
695
+ .build()
696
+ mediaSession = session
697
+ addSession(session)
698
+ observeCastBinding()
699
+ // Routes Media3's foreground-promotion notification through this
700
+ // channel + notification ID and uses the lib's
701
+ // [NotificationProvider] subclass; the provider reads media-button
702
+ // preferences off the session above to populate the compact view.
703
+ setMediaNotificationProvider(NotificationProvider(this))
704
+ // ContextCompat handles the API < 34 fallback. Explicit
705
+ // RECEIVER_NOT_EXPORTED satisfies the Android 14 requirement for
706
+ // dynamically-registered receivers and matches the lint guidance
707
+ // for system broadcasts that the app does not need to expose to
708
+ // other UIDs.
709
+ ContextCompat.registerReceiver(
710
+ this,
711
+ becomingNoisyReceiver,
712
+ IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY),
713
+ ContextCompat.RECEIVER_NOT_EXPORTED,
714
+ )
715
+ becomingNoisyReceiverRegistered = true
716
+
717
+ // Wire the equalizer's IEqualizerEngine to ExoPlayer's audio
718
+ // session as soon as the engine publishes a session ID. The
719
+ // Equalizer Hybrid is a JS-side singleton; the static reference
720
+ // captures the most recently constructed instance which always
721
+ // matches the JS getEqualizer() singleton in practice. The
722
+ // Visualizer Hybrid follows the same pattern — its FFT
723
+ // processors attach via the engine's bufferSinks accumulator.
724
+ setActiveEngine(engine)
725
+ Equalizer.current?.wireToPlaybackEngine(engine)
726
+ Visualizer.current?.wireToPlaybackEngine(engine)
727
+ }
728
+
729
+ /**
730
+ * Resolve the consumer app's launcher activity at runtime and wrap
731
+ * it in a [PendingIntent] that Media3 hands to
732
+ * `DefaultMediaNotificationProvider` as the notification's content
733
+ * intent. Returns `null` when the consumer process exposes no
734
+ * launcher activity (rare service-only hosts) so the
735
+ * `setSessionActivity` call site can skip the wiring without
736
+ * crashing the service.
737
+ *
738
+ * `FLAG_ACTIVITY_SINGLE_TOP | FLAG_ACTIVITY_CLEAR_TOP` matches the
739
+ * standard "tap to return to running app" semantics — reuse the
740
+ * existing task instead of spawning a duplicate, and clear any
741
+ * activities stacked on top of the launcher so the user lands
742
+ * back on the app's root.
743
+ *
744
+ * `FLAG_IMMUTABLE` satisfies API 31+'s mutability requirement
745
+ * (Media3 does not mutate the intent) and `FLAG_UPDATE_CURRENT`
746
+ * keeps a single PendingIntent slot per [REQUEST_CODE_OPEN_APP]
747
+ * across service-recreate cycles.
748
+ */
749
+ private fun buildSessionActivityPendingIntent(): PendingIntent? {
750
+ val launchIntent = packageManager.getLaunchIntentForPackage(packageName) ?: return null
751
+ launchIntent.flags = launchIntent.flags or
752
+ Intent.FLAG_ACTIVITY_SINGLE_TOP or
753
+ Intent.FLAG_ACTIVITY_CLEAR_TOP
754
+ return PendingIntent.getActivity(
755
+ this,
756
+ REQUEST_CODE_OPEN_APP,
757
+ launchIntent,
758
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
759
+ )
760
+ }
761
+
762
+ /**
763
+ * Build the BitmapLoader chain Media3 uses to resolve artwork for
764
+ * the lock-screen + notification surface:
765
+ * PlaceholderFallbackBitmapLoader → CacheBitmapLoader →
766
+ * DataSourceBitmapLoader.
767
+ *
768
+ * `DataSourceBitmapLoader` reuses Media3's own DataSource stack
769
+ * (the same one ExoPlayer drives for HTTP playback), so consumer
770
+ * `userAgent` + custom HTTP headers configured on the player's
771
+ * `MediaSource.Factory` propagate into artwork fetches without a
772
+ * second HTTP client. `CacheBitmapLoader` deduplicates repeat
773
+ * requests for the same URI inside a single foreground session.
774
+ * The outer [PlaceholderFallbackBitmapLoader] guarantees a bitmap
775
+ * always reaches the notification — the OEM-skin failure mode
776
+ * where a null bitmap drops the transport controls is closed.
777
+ *
778
+ * Resolving the placeholder bitmap performs a synchronous read of
779
+ * `res/raw/rnqp_placeholder.jpg` once per [onCreate] (~10KB JPEG,
780
+ * ~5–15ms). Acceptable on the service-creation path; the result
781
+ * is cached for the service's lifetime by [PlaceholderArtwork].
782
+ */
783
+ @VisibleForTesting
784
+ internal fun buildBitmapLoader(): PlaceholderFallbackBitmapLoader =
785
+ PlaceholderFallbackBitmapLoader(
786
+ delegate = CacheBitmapLoader(DataSourceBitmapLoader.Builder(this).build()),
787
+ placeholder = PlaceholderArtwork.bitmap(this),
788
+ )
789
+
790
+ /**
791
+ * The compact notification view shows three buttons in priority
792
+ * order: prev / play-pause / next. Media3 1.9+ resolves these from
793
+ * standard player commands set via [CommandButton.Builder.setPlayerCommand]
794
+ * — no custom command handlers required. The expanded view shows
795
+ * the same three buttons plus the scrub progress bar that
796
+ * `DefaultMediaNotificationProvider` adds automatically when the
797
+ * session reports a known duration.
798
+ */
799
+ private fun buildMediaButtonPreferences(): List<CommandButton> {
800
+ val playPause = CommandButton.Builder(CommandButton.ICON_PLAY)
801
+ .setPlayerCommand(Player.COMMAND_PLAY_PAUSE)
802
+ .setSlots(CommandButton.SLOT_CENTRAL)
803
+ .setDisplayName(getString(R.string.rnqp_action_play_pause))
804
+ .build()
805
+ // In `interval` mode the back/forward slots jump within the current track
806
+ // (COMMAND_SEEK_BACK / COMMAND_SEEK_FORWARD, honored by
807
+ // [SessionCommandForwardingPlayer] with the runtime interval); in `track`
808
+ // mode they are previous/next-track.
809
+ return when (remoteSkipMode) {
810
+ RemoteSkipMode.INTERVAL -> listOf(
811
+ CommandButton.Builder(CommandButton.ICON_SKIP_BACK)
812
+ .setPlayerCommand(Player.COMMAND_SEEK_BACK)
813
+ .setSlots(CommandButton.SLOT_BACK)
814
+ .setDisplayName(getString(R.string.rnqp_action_jump_backward))
815
+ .build(),
816
+ playPause,
817
+ CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD)
818
+ .setPlayerCommand(Player.COMMAND_SEEK_FORWARD)
819
+ .setSlots(CommandButton.SLOT_FORWARD)
820
+ .setDisplayName(getString(R.string.rnqp_action_jump_forward))
821
+ .build(),
822
+ )
823
+ RemoteSkipMode.TRACK -> listOf(
824
+ CommandButton.Builder(CommandButton.ICON_PREVIOUS)
825
+ .setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
826
+ .setSlots(CommandButton.SLOT_BACK)
827
+ .setDisplayName(getString(R.string.rnqp_action_previous))
828
+ .build(),
829
+ playPause,
830
+ CommandButton.Builder(CommandButton.ICON_NEXT)
831
+ .setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
832
+ .setSlots(CommandButton.SLOT_FORWARD)
833
+ .setDisplayName(getString(R.string.rnqp_action_next))
834
+ .build(),
835
+ )
836
+ }
837
+ }
838
+
839
+ /**
840
+ * Apply a remote-control config: point the forwarding player at the new
841
+ * jump intervals and swap the notification button layout live. Called from
842
+ * [LocalBinder.setRemoteControls] on the main thread.
843
+ */
844
+ internal fun applyRemoteControls(mode: RemoteSkipMode, forwardMs: Long, backwardMs: Long) {
845
+ remoteSkipMode = mode
846
+ remoteForwardMs = forwardMs
847
+ remoteBackwardMs = backwardMs
848
+ sessionForwardingPlayer?.forwardJumpMs = forwardMs
849
+ sessionForwardingPlayer?.backwardJumpMs = backwardMs
850
+ mediaSession?.setMediaButtonPreferences(buildMediaButtonPreferences())
851
+ }
852
+
853
+ /**
854
+ * Mirror `PlayerConfig.clampSeekToBuffered` onto the forwarding player so the
855
+ * controller interval skip-forward obeys the same clamp as a JS `seekTo`.
856
+ * Called from [LocalBinder.setClampSeekToBuffered] on the main thread.
857
+ */
858
+ internal fun applyClampSeekToBuffered(enabled: Boolean) {
859
+ clampSeekToBuffered = enabled
860
+ sessionForwardingPlayer?.clampSeekToBuffered = enabled
861
+ }
862
+
863
+ /**
864
+ * Create the playback NotificationChannel. No-op below API 26
865
+ * (channels do not exist). Settings tune the channel as a silent,
866
+ * lockscreen-visible transport surface — not a message indicator.
867
+ */
868
+ private fun createNotificationChannel() {
869
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
870
+ val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
871
+ val channel = NotificationChannel(
872
+ NOTIFICATION_CHANNEL_ID,
873
+ getString(R.string.rnqp_playback_channel_name),
874
+ NotificationManager.IMPORTANCE_LOW
875
+ ).apply {
876
+ description = getString(R.string.rnqp_playback_channel_description)
877
+ setShowBadge(false)
878
+ lockscreenVisibility = Notification.VISIBILITY_PUBLIC
879
+ }
880
+ manager.createNotificationChannel(channel)
881
+ }
882
+
883
+ /**
884
+ * Replace the ExoPlayer attached to the current MediaSession with
885
+ * one built around the supplied [factory]. The session itself is
886
+ * NOT recreated — Media3 supports `MediaSession.setPlayer(...)`
887
+ * for atomic player swaps, which keeps the session ID stable +
888
+ * avoids the "Session ID must be unique" static-registry collision.
889
+ *
890
+ * Called from `TrackPlayer.configureInternal` when the consumer's
891
+ * `PlayerConfig` headers / userAgent / audioContentType change —
892
+ * Media3 doesn't support swapping a `MediaSource.Factory` on a
893
+ * live player, so the factory is bound at player build time. The
894
+ * `audioContentType` likewise feeds into the new player's
895
+ * `AudioAttributes` and is re-applied on every swap.
896
+ */
897
+ fun configurePlayer(
898
+ factory: MediaSource.Factory,
899
+ audioContentType: AudioContentType? = null
900
+ ): ExoPlayer {
901
+ val newEngine = GaplessEngine(
902
+ context = this,
903
+ mediaSourceFactory = factory,
904
+ audioContentType = audioContentType
905
+ )
906
+ return swapEngineInternal(newEngine).mediaSessionPlayer
907
+ }
908
+
909
+ /**
910
+ * Mode-switch entry point — replace the active engine with one
911
+ * implementing the consumer's requested `PlaybackMode`. Called
912
+ * by `TrackPlayer.setPlaybackMode` when the
913
+ * `PlaybackModeStateMachine` returns `ImmediateSwap`.
914
+ *
915
+ * Sibling to [configurePlayer]: `configurePlayer` rebuilds the
916
+ * engine when the consumer's `PlayerConfig` changes and always
917
+ * stays on `GaplessEngine`; `swapEngine` flips between
918
+ * `GaplessEngine` and `CrossfadeEngine` when the mode changes.
919
+ *
920
+ * Same service-level wiring: re-attach `audioFocusListener` to
921
+ * the new leading player, swap `MediaSession` to the new
922
+ * player, carry the delegate over, re-wire the Equalizer, and
923
+ * release the old engine. Queue migration is the caller's
924
+ * responsibility — `TrackPlayer` re-installs the canonical
925
+ * queue model onto the new engine before the swap if needed.
926
+ */
927
+ fun swapEngine(newEngine: PlaybackEngine): PlaybackEngine {
928
+ return swapEngineInternal(newEngine)
929
+ }
930
+
931
+ private fun swapEngineInternal(newEngine: PlaybackEngine): PlaybackEngine {
932
+ val oldEngine = engine
933
+ val oldPlayer = oldEngine.mediaSessionPlayer
934
+ val newPlayer = newEngine.mediaSessionPlayer
935
+
936
+ // Bind MediaSession to a stable wrapper for CrossfadeEngine so
937
+ // the lock-screen scrubber survives mid-fade role swaps. The
938
+ // wrapper retargets via setPlayer + invalidateState whenever the
939
+ // engine flips currentRoleIsA. GaplessEngine binds directly —
940
+ // single ExoPlayer, no role to swap.
941
+ val sessionPlayer: Player = if (newEngine is CrossfadeEngine) {
942
+ val wrapper = CrossfadeForwardingPlayer(newEngine)
943
+ newEngine.onRoleSwap = { wrapper.onRoleSwapped() }
944
+ newEngine.onCrossfadeBegin = { wrapper.onCrossfadeBegin() }
945
+ // Mid-fade cancel re-binds the wrapper back to the leading
946
+ // leg — `onRoleSwapped` already does setPlayer(leadingPlayer)
947
+ // + invalidateState which is exactly what we need on cancel
948
+ // (no role flip happened, leading is still leading).
949
+ newEngine.onCrossfadeCancel = { wrapper.onRoleSwapped() }
950
+ wrapper
951
+ } else {
952
+ newPlayer
953
+ }
954
+
955
+ audioFocusListenerHost?.removeListener(audioFocusListener)
956
+ previousSuppressionReason = Player.PLAYBACK_SUPPRESSION_REASON_NONE
957
+ // Audio-focus listener attaches to whatever is bound to the
958
+ // MediaSession — for CrossfadeEngine that's the wrapper which
959
+ // forwards Player.Listener events from whichever leg is
960
+ // currently leading via ForwardingSimpleBasePlayer's listener
961
+ // routing. For GaplessEngine the wrapper IS the underlying
962
+ // player. Either way, the listener follows role swaps without
963
+ // leaking against a specific leg. Tracked in
964
+ // audioFocusListenerHost so the next swap can remove from the
965
+ // same instance.
966
+ localSessionPlayer = sessionPlayer
967
+ // While cast-bound the MediaSession stays on the CastPlayer; record
968
+ // the new engine's player so the cast disconnect binds it, but don't
969
+ // steal the session surface from the receiver here.
970
+ if (castSessionPlayer == null) {
971
+ sessionPlayer.addListener(audioFocusListener)
972
+ audioFocusListenerHost = sessionPlayer
973
+ sessionForwardingPlayer?.retarget(sessionPlayer)
974
+ }
975
+
976
+ newEngine.delegate = oldEngine.delegate
977
+ engine = newEngine
978
+
979
+ setActiveEngine(engine)
980
+ Equalizer.current?.wireToPlaybackEngine(engine)
981
+ Visualizer.current?.wireToPlaybackEngine(engine)
982
+ oldEngine.release()
983
+ return newEngine
984
+ }
985
+
986
+ /**
987
+ * Observe the active Chromecast session and bind/unbind its CastPlayer
988
+ * onto the MediaSession. The collector runs on the main thread because
989
+ * both `MediaSession.setPlayer` and the lazy `CastPlayer` construction
990
+ * (`session.mediaSessionPlayer`) are main-thread-bound.
991
+ */
992
+ private fun observeCastBinding() {
993
+ castBindScope.launch {
994
+ PlaybackStateRouter.activeFlow.collect { session ->
995
+ val castPlayer = session?.mediaSessionPlayer
996
+ if (castPlayer != null) bindCastSessionPlayer(castPlayer)
997
+ else unbindCastSessionPlayer()
998
+ }
999
+ }
1000
+ }
1001
+
1002
+ /**
1003
+ * Bind the receiver's CastPlayer to the MediaSession for the cast
1004
+ * session's duration so the system notification + lockscreen follow
1005
+ * the receiver. The receiver owns audio, so the local audio-focus
1006
+ * listener is detached (it tracks LOCAL focus, irrelevant while the
1007
+ * device isn't decoding). Idempotent; main thread only.
1008
+ */
1009
+ private fun bindCastSessionPlayer(castPlayer: Player) {
1010
+ if (castSessionPlayer === castPlayer) return
1011
+ audioFocusListenerHost?.removeListener(audioFocusListener)
1012
+ audioFocusListenerHost = null
1013
+ castSessionPlayer = castPlayer
1014
+ sessionForwardingPlayer?.retarget(castPlayer)
1015
+ }
1016
+
1017
+ /**
1018
+ * Restore the local engine's player on cast disconnect, reattach the
1019
+ * audio-focus listener, then release the cast player — setPlayer
1020
+ * before release, the same ordering [swapEngineInternal] uses, so the
1021
+ * MediaSession never points at a released player. The CastPlayer's
1022
+ * binding lifetime is owned here (ChromecastSession builds it but does
1023
+ * not release it). No-op when not cast-bound; main thread only.
1024
+ */
1025
+ private fun unbindCastSessionPlayer() {
1026
+ val castPlayer = castSessionPlayer ?: return
1027
+ castSessionPlayer = null
1028
+ val local = localSessionPlayer ?: engine.mediaSessionPlayer
1029
+ audioFocusListenerHost?.removeListener(audioFocusListener)
1030
+ previousSuppressionReason = Player.PLAYBACK_SUPPRESSION_REASON_NONE
1031
+ local.addListener(audioFocusListener)
1032
+ audioFocusListenerHost = local
1033
+ sessionForwardingPlayer?.retarget(local)
1034
+ castPlayer.release()
1035
+ }
1036
+
1037
+ override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? =
1038
+ mediaSession
1039
+
1040
+ override fun onBind(intent: Intent?): IBinder? {
1041
+ if (intent?.action == ACTION_LOCAL_BIND) {
1042
+ return localBinder
1043
+ }
1044
+ return super.onBind(intent)
1045
+ }
1046
+
1047
+ /**
1048
+ * Path B of the media-button routing. A raw media-button intent
1049
+ * reaches the service either because a manifest-registered
1050
+ * `MediaButtonReceiver` forwarded it, or because the OS started
1051
+ * the service directly with `ACTION_MEDIA_BUTTON`. The dispatcher
1052
+ * dedups against any prior fire of the same physical press routed
1053
+ * through path A (`onMediaButtonEvent`). Unrecognised actions are
1054
+ * ignored. The `super` call keeps `MediaLibraryService` lifecycle
1055
+ * semantics intact.
1056
+ */
1057
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
1058
+ if (intent?.action == Intent.ACTION_MEDIA_BUTTON) {
1059
+ mediaButtonDispatch?.handle(intent, RemoteCommandDeduper.Channel.BROADCAST)
1060
+ }
1061
+ return super.onStartCommand(intent, flags, startId)
1062
+ }
1063
+
1064
+ /**
1065
+ * Swiping the app away from recents is an explicit user kill: stop playback,
1066
+ * clear the notification, and stop the service so the process is reaped.
1067
+ * Unconditional by design — a paused-but-queued session is NOT kept alive
1068
+ * here (resurrecting audio the user just dismissed would be wrong). This is
1069
+ * distinct from home-backgrounding, where the foreground service keeps
1070
+ * playing. `stopSelf()` on this explicit action also prevents the
1071
+ * `START_STICKY` recreation the OS applies only to system-killed services.
1072
+ *
1073
+ * Deliberately does not call `super.onTaskRemoved` (whose only job is a
1074
+ * conditional `pauseAllPlayersAndStopSelf`): this service owns exactly one
1075
+ * session, so stopping its player + the explicit `stopSelf` below is the
1076
+ * complete kill. A future multi-session change would need to stop each.
1077
+ */
1078
+ override fun onTaskRemoved(rootIntent: Intent?) {
1079
+ mediaSession?.player?.stop()
1080
+ stopForeground(Service.STOP_FOREGROUND_REMOVE)
1081
+ stopSelf()
1082
+ }
1083
+
1084
+ override fun onDestroy() {
1085
+ callbackDispatchScope.cancel()
1086
+ browseCallbacks.clear()
1087
+ pendingBrowseRequests.clear()
1088
+ // Drop any pending idle stopSelf check — the service is going
1089
+ // away anyway, no need to fire the re-check on a dead instance.
1090
+ stopSelfHandler.removeCallbacks(stopSelfRunnable)
1091
+ if (becomingNoisyReceiverRegistered) {
1092
+ unregisterReceiver(becomingNoisyReceiver)
1093
+ becomingNoisyReceiverRegistered = false
1094
+ }
1095
+ // Detach the equalizer + visualizer before tearing the engine
1096
+ // down so the session-ID listener + audio-buffer sinks don't
1097
+ // fire against a released engine.
1098
+ setActiveEngine(null)
1099
+ Equalizer.current?.unwireFromPlaybackEngine()
1100
+ Visualizer.current?.unwireFromPlaybackEngine()
1101
+ audioFocusListenerHost?.removeListener(audioFocusListener)
1102
+ audioFocusListenerHost = null
1103
+ castBindScope.cancel()
1104
+ castSessionPlayer?.release()
1105
+ castSessionPlayer = null
1106
+ commandHandler = null
1107
+ mediaButtonDispatch = null
1108
+ mediaSession?.release()
1109
+ mediaSession = null
1110
+ sessionForwardingPlayer = null
1111
+ engine.release()
1112
+ super.onDestroy()
1113
+ }
1114
+
1115
+ /**
1116
+ * Coroutine scope tied to the service lifetime. Headless callback
1117
+ * dispatches (e.g. an Auto search awaiting JS) launch on this
1118
+ * scope so they cancel cleanly when [onDestroy] fires.
1119
+ *
1120
+ * `Dispatchers.IO` (not `Main`): the dispatch body is suspending
1121
+ * await on a Nitro Promise round-trip — never touches Player or
1122
+ * UI state — so blocking Main here would needlessly stall Media3
1123
+ * + UI. Future consumers run on `directExecutor()` and don't
1124
+ * inherit this dispatcher.
1125
+ */
1126
+ private val callbackDispatchScope =
1127
+ kotlinx.coroutines.CoroutineScope(
1128
+ kotlinx.coroutines.SupervisorJob() + kotlinx.coroutines.Dispatchers.IO
1129
+ )
1130
+
1131
+ /**
1132
+ * Dispatch [query] to the JS-registered `onSearchRequest`
1133
+ * callback, await the result, write the items into
1134
+ * [searchResultCache], and emit `notifySearchResultChanged` for
1135
+ * every connected controller so they re-pull via
1136
+ * `getSearchResult`. No-op when no callback is registered.
1137
+ *
1138
+ * Threading: launched on [callbackDispatchScope] so the await
1139
+ * doesn't block the Media3 callback thread + cancels when the
1140
+ * service is destroyed. JS callbacks return a Nitro
1141
+ * `Promise<Promise<...>>` (outer = Hybrid wrapper, inner = JS
1142
+ * promise) — both layers are awaited.
1143
+ */
1144
+ internal fun dispatchSearchToJs(query: String) {
1145
+ val callback = browseCallbacks.searchRequestCallback() ?: return
1146
+ callbackDispatchScope.launch {
1147
+ val items: List<BrowseItem> = try {
1148
+ kotlinx.coroutines.withTimeoutOrNull(SEARCH_DISPATCH_TIMEOUT_MS) {
1149
+ callback(query).await().await().toList()
1150
+ } ?: emptyList()
1151
+ } catch (t: Throwable) {
1152
+ // JS handler threw — surface as an empty answered result so
1153
+ // the controller stops waiting. The thrown error is the
1154
+ // consumer's bug; logging helps diagnose without taking the
1155
+ // process down.
1156
+ android.util.Log.w(
1157
+ LOG_TAG,
1158
+ "onSearchRequest('$query') threw — surfacing empty results",
1159
+ t
1160
+ )
1161
+ emptyList()
1162
+ }
1163
+ searchResultCache.setResults(query, items)
1164
+ notifySearchResultChanged(query, items.size)
1165
+ }
1166
+ }
1167
+
1168
+ /**
1169
+ * Dispatch a structured voice-search [request] (Google Assistant,
1170
+ * Auto voice tile, future Siri intent extension) to the
1171
+ * JS-registered `onPlayFromSearchRequest` handler. Awaits the
1172
+ * resolved [TrackItem] list, applies it to the lib's authoritative
1173
+ * queue state, and returns the [MediaItem] list Media3's framework
1174
+ * should set on the player.
1175
+ *
1176
+ * On a cold-start Assistant boot, the JS bridge may not yet have
1177
+ * registered the callback when `onAddMediaItems` lands. The
1178
+ * coroutine waits up to [HEADLESS_DISPATCH_TIMEOUT_MS] for the
1179
+ * registration to land before timing out + returning an empty
1180
+ * list (which surfaces as "no results" to Assistant — the user
1181
+ * retries).
1182
+ */
1183
+ internal fun dispatchPlayFromSearchToJs(
1184
+ request: MediaSearchRequest
1185
+ ): ListenableFuture<List<MediaItem>> = dispatchAssistantPlay(
1186
+ methodLabel = "onPlayFromSearchRequest",
1187
+ awaitSlot = { browseCallbacks.awaitPlayFromSearchRequestCallback() }
1188
+ ) { browseCallbacks.playFromSearchRequestCallback()?.invoke(request) }
1189
+
1190
+ /**
1191
+ * Dispatch a browse-leaf-tap [mediaId] (Auto / Wear OS browse-row
1192
+ * tap) to the JS-registered `onPlayFromIdRequest` handler. Same
1193
+ * cold-start race-handling + queue-sync flow as
1194
+ * [dispatchPlayFromSearchToJs].
1195
+ */
1196
+ internal fun dispatchPlayFromIdToJs(
1197
+ mediaId: String
1198
+ ): ListenableFuture<List<MediaItem>> = dispatchAssistantPlay(
1199
+ methodLabel = "onPlayFromIdRequest",
1200
+ awaitSlot = { browseCallbacks.awaitPlayFromIdRequestCallback() }
1201
+ ) { browseCallbacks.playFromIdRequestCallback()?.invoke(mediaId) }
1202
+
1203
+ /**
1204
+ * Fire-and-forget variant of [dispatchPlayFromIdToJs]: invoke the
1205
+ * JS callback for its `setQueue + play` side effect and discard the
1206
+ * resolved track list. Used by the browse-leaf tap
1207
+ * (`onSetMediaItems` mediaId path) where the JS handler is the
1208
+ * authoritative queue writer; calling the full sync variant would
1209
+ * trigger [TrackPlayer.applyResolvedAssistantQueue] which resets
1210
+ * `currentTrackIndex` to 0 and breaks skip-previous / skip-next
1211
+ * after a tap at any index > 0.
1212
+ *
1213
+ * Carries the same cold-start race handling as the sync variant —
1214
+ * the JS callback may not be registered when the OS hands off the
1215
+ * tap, so the dispatch waits up to [HEADLESS_DISPATCH_TIMEOUT_MS]
1216
+ * for `playFromIdRequestCallback()` to return non-null before
1217
+ * timing out. Errors are logged + swallowed; the JS side is the
1218
+ * surface for user-facing failure handling.
1219
+ */
1220
+ internal fun dispatchPlayFromIdToJsFireOnly(mediaId: String) {
1221
+ callbackDispatchScope.launch {
1222
+ try {
1223
+ kotlinx.coroutines.withTimeoutOrNull(HEADLESS_DISPATCH_TIMEOUT_MS) {
1224
+ val promise: Promise<Promise<Array<TrackItem>>>? =
1225
+ browseCallbacks.playFromIdRequestCallback()?.invoke(mediaId)
1226
+ ?: run {
1227
+ browseCallbacks.awaitPlayFromIdRequestCallback()
1228
+ browseCallbacks.playFromIdRequestCallback()?.invoke(mediaId)
1229
+ }
1230
+ promise?.await()?.await()
1231
+ }
1232
+ } catch (t: Throwable) {
1233
+ android.util.Log.w(
1234
+ LOG_TAG,
1235
+ "onPlayFromIdRequest (fire-only) threw — discarding",
1236
+ t
1237
+ )
1238
+ }
1239
+ }
1240
+ }
1241
+
1242
+ /** Dispatch an unknown-parent browse drilldown to the JS-registered
1243
+ * `onBrowseRequest` handler. Called by [PlaybackServiceCallback]
1244
+ * when `onGetChildren` fires for a parentId that the cached
1245
+ * snapshot does not contain. */
1246
+ internal fun dispatchBrowseRequestToJs(
1247
+ parentId: String
1248
+ ): ListenableFuture<List<BrowseItem>> {
1249
+ val future =
1250
+ SettableFuture.create<List<BrowseItem>>()
1251
+ val job = callbackDispatchScope.launch {
1252
+ val items: List<BrowseItem> = try {
1253
+ kotlinx.coroutines.withTimeoutOrNull(HEADLESS_DISPATCH_TIMEOUT_MS) {
1254
+ val promise: Promise<Promise<Array<BrowseItem>>>? =
1255
+ browseCallbacks.browseRequestCallback()?.invoke(parentId)
1256
+ ?: run {
1257
+ browseCallbacks.awaitBrowseRequestCallback()
1258
+ browseCallbacks.browseRequestCallback()?.invoke(parentId)
1259
+ }
1260
+ promise?.await()?.await()?.toList() ?: emptyList()
1261
+ } ?: emptyList()
1262
+ } catch (t: Throwable) {
1263
+ android.util.Log.w(
1264
+ LOG_TAG,
1265
+ "onBrowseRequest('$parentId') threw — surfacing empty children",
1266
+ t
1267
+ )
1268
+ emptyList()
1269
+ }
1270
+ future.set(items)
1271
+ }
1272
+ // Cancel the launched dispatch when the controller drops the
1273
+ // future (Media3 cancels in-flight resolution on disconnect).
1274
+ // Without this the coroutine would still hold the JS Promise
1275
+ // chain alive for a controller that has already given up.
1276
+ future.addListener(
1277
+ Runnable { if (future.isCancelled) job.cancel() },
1278
+ MoreExecutors.directExecutor()
1279
+ )
1280
+ return future
1281
+ }
1282
+
1283
+ private inline fun dispatchAssistantPlay(
1284
+ methodLabel: String,
1285
+ crossinline awaitSlot: suspend () -> Unit,
1286
+ crossinline invoke: () -> Promise<Promise<Array<TrackItem>>>?
1287
+ ): ListenableFuture<List<MediaItem>> {
1288
+ val future =
1289
+ SettableFuture.create<List<MediaItem>>()
1290
+ val job = callbackDispatchScope.launch {
1291
+ val tracks: List<TrackItem> = try {
1292
+ kotlinx.coroutines.withTimeoutOrNull(HEADLESS_DISPATCH_TIMEOUT_MS) {
1293
+ // [invoke] reads the registry slot; on null we suspend on
1294
+ // [awaitSlot] (the slot's StateFlow) until a registration
1295
+ // lands. The first non-null read after that wins.
1296
+ val promise = invoke() ?: run {
1297
+ awaitSlot()
1298
+ invoke()
1299
+ }
1300
+ promise?.await()?.await()?.toList() ?: emptyList()
1301
+ } ?: emptyList()
1302
+ } catch (t: Throwable) {
1303
+ android.util.Log.w(
1304
+ LOG_TAG,
1305
+ "$methodLabel threw — surfacing empty result",
1306
+ t
1307
+ )
1308
+ emptyList()
1309
+ }
1310
+ // Sync the lib's authoritative tracks state to the resolved
1311
+ // list. Media3's framework calls `player.setMediaItems(items, ...)`
1312
+ // with whatever this future resolves to, so we MUST return the
1313
+ // real items — returning emptyList here would wipe the queue
1314
+ // immediately after Media3 installs it.
1315
+ val items = applyAssistantQueueOnMain(tracks)
1316
+ future.set(items)
1317
+ }
1318
+ // Cancel the launched dispatch when the controller drops the
1319
+ // future (Media3 cancels in-flight resolution when the controller
1320
+ // disconnects). Without this the coroutine would still run +
1321
+ // mutate the lib's queue state for a controller that gave up.
1322
+ future.addListener(
1323
+ Runnable {
1324
+ if (future.isCancelled) job.cancel()
1325
+ },
1326
+ MoreExecutors.directExecutor()
1327
+ )
1328
+ return future
1329
+ }
1330
+
1331
+ /**
1332
+ * Fire the consumer's `onCarConnect` callback once it's registered.
1333
+ * The FIRST automotive controller's [onConnect] races the JS-bridge
1334
+ * boot — calling [browseCallbacks.carConnectCallback] synchronously
1335
+ * returns `null` until [registerPlaybackService] has run. Suspends
1336
+ * on the slot's StateFlow up to [HEADLESS_DISPATCH_TIMEOUT_MS];
1337
+ * warns on timeout (consumer didn't wire `onCarConnect`).
1338
+ */
1339
+ internal fun dispatchCarConnect() {
1340
+ callbackDispatchScope.launch {
1341
+ val callback = kotlinx.coroutines.withTimeoutOrNull(
1342
+ HEADLESS_DISPATCH_TIMEOUT_MS
1343
+ ) {
1344
+ browseCallbacks.awaitCarConnectCallback()
1345
+ }
1346
+ if (callback == null) {
1347
+ android.util.Log.w(
1348
+ LOG_TAG,
1349
+ "onCarConnect — no JS callback registered after " +
1350
+ "${HEADLESS_DISPATCH_TIMEOUT_MS}ms (consumer did not wire it?)"
1351
+ )
1352
+ return@launch
1353
+ }
1354
+ kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.Main) {
1355
+ callback()
1356
+ }
1357
+ }
1358
+ }
1359
+
1360
+ private suspend fun applyAssistantQueueOnMain(
1361
+ tracks: List<TrackItem>
1362
+ ): List<MediaItem> {
1363
+ if (tracks.isEmpty()) return emptyList()
1364
+ val player = TrackPlayer.current ?: return emptyList()
1365
+ return kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.Main) {
1366
+ player.applyResolvedAssistantQueue(tracks)
1367
+ }
1368
+ }
1369
+
1370
+ /**
1371
+ * Boot the JS task IF a headless-eligible controller has connected
1372
+ * AND no JS task is already running. Idempotent — the
1373
+ * `HeadlessJsMediaService.initialized` latch + the wake-intent
1374
+ * action gate prevent re-entrancy.
1375
+ */
1376
+ internal fun selfWakeForHeadlessClient(packageName: String) {
1377
+ // Skip the wake-intent self-start when the JS bridge is already
1378
+ // alive. The headless-wake path is for the cold-start case where
1379
+ // the OS has bound our service from a backgrounded / killed app
1380
+ // and we need a JS context to dispatch into. Firing startService
1381
+ // while a foreground JS context is running races with the live
1382
+ // React instance and on observed devices (Pixel 6a, Android 14)
1383
+ // tears down the bridge — playback stops, the UI hangs, the
1384
+ // process eventually errors out.
1385
+ //
1386
+ // Detection: read the ReactApplication's current React context
1387
+ // directly. This signals "JS bridge is alive" much earlier than
1388
+ // checking `browseCallbacks.carConnectCallback()` — the bridge
1389
+ // exists hundreds of milliseconds before the consumer's
1390
+ // `registerPlaybackService` call lands. gearhead's first
1391
+ // `onConnect` arrives in that window, so the callback-based
1392
+ // guard misses it (logged as the bind/unbind retry storm).
1393
+ if (hasLiveReactContext()) {
1394
+ android.util.Log.i(
1395
+ LOG_TAG,
1396
+ "selfWakeForHeadlessClient($packageName) skipped — React context already alive"
1397
+ )
1398
+ return
1399
+ }
1400
+ // Defence-in-depth: even if the context guard misses, rate-limit
1401
+ // self-wake to one per 30s. gearhead's retry-on-error loop fires
1402
+ // `onConnect` several times per second when our `onConnect` /
1403
+ // `onGetLibraryRoot` returns the wrong shape; without this gate,
1404
+ // each retry queues another `startService` → JS-task spawn,
1405
+ // amplifying the failure cascade.
1406
+ val now = System.currentTimeMillis()
1407
+ val last = lastSelfWakeAtMs
1408
+ if (last > 0 && now - last < SELF_WAKE_MIN_INTERVAL_MS) {
1409
+ android.util.Log.i(
1410
+ LOG_TAG,
1411
+ "selfWakeForHeadlessClient($packageName) rate-limited (${now - last}ms since last)"
1412
+ )
1413
+ return
1414
+ }
1415
+ lastSelfWakeAtMs = now
1416
+ android.util.Log.i(LOG_TAG, "selfWakeForHeadlessClient($packageName)")
1417
+ val intent = Intent(this, this::class.java).apply {
1418
+ action = ACTION_HEADLESS_WAKE
1419
+ putExtra(EXTRA_WAKE_PACKAGE, packageName)
1420
+ }
1421
+ try {
1422
+ // Plain `startService` rather than the foreground variant —
1423
+ // the wake intent's job is to fire `onStartCommand` so
1424
+ // [getTaskConfig] returns a non-null config + the JS context
1425
+ // boots. Foreground promotion is handled later by Media3's
1426
+ // own notification flow when playback actually begins. Using
1427
+ // `startForegroundService` here would oblige us to call
1428
+ // `startForeground` within ~5 s on Android 14+, but we have
1429
+ // nothing to post yet.
1430
+ startService(intent)
1431
+ } catch (e: IllegalStateException) {
1432
+ // Android 12+ throws when a service start happens off the
1433
+ // privileged window ("Not allowed to start service Intent…:
1434
+ // app is in background"). The service is already alive
1435
+ // (the controller bound to it before this ran), so JS never
1436
+ // wakes + the controller times out. Log so the failure is
1437
+ // visible in the bug report.
1438
+ android.util.Log.w(
1439
+ LOG_TAG,
1440
+ "selfWakeForHeadlessClient($packageName) blocked by background-start rules",
1441
+ e
1442
+ )
1443
+ } catch (e: SecurityException) {
1444
+ // OEM-skinned devices occasionally tighten the permission
1445
+ // surface for self-start; same fallback as the IllegalState
1446
+ // case above.
1447
+ android.util.Log.w(
1448
+ LOG_TAG,
1449
+ "selfWakeForHeadlessClient($packageName) denied by SecurityException",
1450
+ e
1451
+ )
1452
+ }
1453
+ }
1454
+
1455
+ override fun getTaskConfig(intent: Intent?): com.facebook.react.jstasks.HeadlessJsTaskConfig? {
1456
+ val action = intent?.action ?: return null
1457
+ if (action !in HEADLESS_WAKE_ACTIONS) return null
1458
+ // Robolectric / non-ReactApplication hosts cannot spawn a JS
1459
+ // task — short-circuit here so `Arguments.createMap()` never
1460
+ // has to load the React native libs that aren't on the test
1461
+ // classpath.
1462
+ if ((application as? com.facebook.react.ReactApplication) == null) return null
1463
+ // Belt-and-braces: if a wake intent somehow lands while a React
1464
+ // context is already alive (foreground app, prior wake already
1465
+ // ran the task to completion + the context survived), reject the
1466
+ // spawn. Returning a non-null config queues a second JS task that
1467
+ // would race the live context.
1468
+ if (hasLiveReactContext()) {
1469
+ android.util.Log.i(
1470
+ LOG_TAG,
1471
+ "getTaskConfig skipping $action — React context already alive"
1472
+ )
1473
+ return null
1474
+ }
1475
+ val data = com.facebook.react.bridge.Arguments.createMap().apply {
1476
+ putString(EXTRA_WAKE_ACTION, action)
1477
+ intent.getStringExtra(EXTRA_WAKE_PACKAGE)?.let {
1478
+ putString(EXTRA_WAKE_PACKAGE, it)
1479
+ }
1480
+ }
1481
+ return com.facebook.react.jstasks.HeadlessJsTaskConfig(
1482
+ HEADLESS_TASK_NAME,
1483
+ data,
1484
+ 0,
1485
+ true
1486
+ )
1487
+ }
1488
+
1489
+ override fun onHeadlessJsTaskFinish(taskId: Int) {
1490
+ activeTasks.remove(taskId)
1491
+ // A media service stays alive while the player has items —
1492
+ // browse/voice callbacks remain dispatchable as long as the
1493
+ // session is up. But a cold-wake that finished without any
1494
+ // playback (e.g. JS booted, dispatch resolved empty) leaves
1495
+ // the service in foreground-eligible-but-idle state which
1496
+ // Android 14+ treats as an orphan; tear it down.
1497
+ //
1498
+ // Deferred 5s grace window: the JS-side cold-start path runs
1499
+ // `bootstrap.ts` as a bundle-load side effect AFTER the
1500
+ // headless task body resolves. That side effect calls
1501
+ // `configure()` + `installCarService()` which push the browse
1502
+ // snapshot + bind controllers. Tearing the service down
1503
+ // immediately on task-finish races that side-effect chain —
1504
+ // the service stops just as the consumer's bootstrap is wiring
1505
+ // up. The 5s window lets that chain complete; the re-check at
1506
+ // fire time bails if any of the relevant signals (media,
1507
+ // active tasks, connected controllers) have arrived.
1508
+ if (activeTasks.isEmpty() && !hasMediaQueued()) {
1509
+ scheduleIdleStopSelfCheck()
1510
+ }
1511
+ }
1512
+
1513
+ private val stopSelfHandler =
1514
+ android.os.Handler(android.os.Looper.getMainLooper())
1515
+ private val stopSelfRunnable = Runnable {
1516
+ if (
1517
+ activeTasks.isEmpty() &&
1518
+ !hasMediaQueued() &&
1519
+ !hasConnectedControllers()
1520
+ ) {
1521
+ android.util.Log.i(
1522
+ LOG_TAG,
1523
+ "Idle grace window elapsed; calling stopSelf()"
1524
+ )
1525
+ stopSelf()
1526
+ } else {
1527
+ android.util.Log.i(
1528
+ LOG_TAG,
1529
+ "Idle grace window re-check: service has active surface, " +
1530
+ "leaving alive (activeTasks=${activeTasks.size}, " +
1531
+ "hasMedia=${hasMediaQueued()}, " +
1532
+ "controllers=${mediaSession?.connectedControllers?.size ?: 0})"
1533
+ )
1534
+ }
1535
+ }
1536
+
1537
+ private fun scheduleIdleStopSelfCheck() {
1538
+ stopSelfHandler.removeCallbacks(stopSelfRunnable)
1539
+ stopSelfHandler.postDelayed(stopSelfRunnable, IDLE_STOP_GRACE_MS)
1540
+ android.util.Log.i(
1541
+ LOG_TAG,
1542
+ "Scheduling idle stopSelf check in ${IDLE_STOP_GRACE_MS}ms"
1543
+ )
1544
+ }
1545
+
1546
+ /**
1547
+ * True when the active session's player has at least one media
1548
+ * item queued. Read by [onHeadlessJsTaskFinish] to decide whether
1549
+ * to keep the service alive after the JS task body completes.
1550
+ */
1551
+ private fun hasMediaQueued(): Boolean =
1552
+ (mediaSession?.player?.mediaItemCount ?: 0) > 0
1553
+
1554
+ /**
1555
+ * True when any automotive / Wear / Assistant controller is bound
1556
+ * to the active session. Read by the deferred stopSelf check so a
1557
+ * controller that connected during the grace window keeps the
1558
+ * service alive.
1559
+ */
1560
+ private fun hasConnectedControllers(): Boolean =
1561
+ (mediaSession?.connectedControllers?.isNotEmpty() ?: false)
1562
+
1563
+ /**
1564
+ * Fire `MediaLibrarySession.notifyChildrenChanged` for every
1565
+ * `parentId` in [parentIds] PLUS every parent the
1566
+ * [pendingBrowseRequests] queue accumulated while the snapshot
1567
+ * was empty. Invoked by `TrackPlayer.setBrowseSnapshot` after a
1568
+ * fresh snapshot lands. Caller is on main; the session's own
1569
+ * thread-safety contract handles the controller iteration.
1570
+ *
1571
+ * Draining the pending queue here closes the cold-start race —
1572
+ * a controller that fetched children before any snapshot existed
1573
+ * gets re-notified once data is available, so its UI replaces
1574
+ * the empty placeholder with the real items.
1575
+ */
1576
+ internal fun notifyChildrenChangedForParents(parentIds: Set<String>) {
1577
+ val drained = pendingBrowseRequests.drainAll()
1578
+ val all = if (drained.isEmpty()) parentIds else parentIds + drained
1579
+ val session = mediaSession ?: return
1580
+ for (controller in session.connectedControllers) {
1581
+ for (parentId in all) {
1582
+ // Pass count=Int.MAX_VALUE so the controller refetches the
1583
+ // whole subtree under the parent — the snapshot's section
1584
+ // sizes drive the actual returned count.
1585
+ session.notifyChildrenChanged(controller, parentId, Int.MAX_VALUE, null)
1586
+ }
1587
+ }
1588
+ }
1589
+
1590
+ /**
1591
+ * Fire `notifySearchResultChanged` for [query] across every
1592
+ * connected controller after [searchResultCache] is populated.
1593
+ */
1594
+ private fun notifySearchResultChanged(query: String, count: Int) {
1595
+ val session = mediaSession ?: return
1596
+ for (controller in session.connectedControllers) {
1597
+ session.notifySearchResultChanged(controller, query, count, null)
1598
+ }
1599
+ }
1600
+
1601
+ /**
1602
+ * Translate a [SkipCapability] into the [Player.Commands] set that
1603
+ * controllers should see. The cap reflects repeat-mode + queue
1604
+ * position rather than Media3's timeline-derived default, so
1605
+ * `repeat=queue` at the last index correctly leaves skip-next
1606
+ * enabled; only the four skip commands flip with the cap. Every
1607
+ * other command (play/pause, seek, set speed, etc.) is preserved
1608
+ * from `DEFAULT_PLAYER_COMMANDS`.
1609
+ */
1610
+ internal fun playerCommandsFor(
1611
+ capability: SkipCapability,
1612
+ hasMediaItems: Boolean
1613
+ ): Player.Commands {
1614
+ val builder = MediaSession.ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon()
1615
+ if (!hasMediaItems) {
1616
+ // Empty queue → strip every command that contributes to the
1617
+ // legacy `PlaybackStateCompat` action mask's PLAY-related bits.
1618
+ // Android Auto's dashboard-tap launch heuristic reads that mask:
1619
+ // STATE_NONE alone is not enough — the action mask must also
1620
+ // carry no ACTION_PLAY / ACTION_PLAY_PAUSE / ACTION_PREPARE.
1621
+ // Once both are zero, AA lands on the Library/Browse surface
1622
+ // instead of "Now Playing — To play something, tap the icon at
1623
+ // the top left."
1624
+ builder.remove(Player.COMMAND_PLAY_PAUSE)
1625
+ builder.remove(Player.COMMAND_PREPARE)
1626
+ builder.remove(Player.COMMAND_STOP)
1627
+ builder.remove(Player.COMMAND_SEEK_BACK)
1628
+ builder.remove(Player.COMMAND_SEEK_FORWARD)
1629
+ builder.remove(Player.COMMAND_SEEK_TO_DEFAULT_POSITION)
1630
+ builder.remove(Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)
1631
+ builder.remove(Player.COMMAND_SEEK_TO_MEDIA_ITEM)
1632
+ builder.remove(Player.COMMAND_SET_SHUFFLE_MODE)
1633
+ builder.remove(Player.COMMAND_SET_REPEAT_MODE)
1634
+ builder.remove(Player.COMMAND_SET_SPEED_AND_PITCH)
1635
+ }
1636
+ if (!capability.canSkipNext) {
1637
+ builder.remove(Player.COMMAND_SEEK_TO_NEXT)
1638
+ builder.remove(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
1639
+ }
1640
+ if (!capability.canSkipPrevious) {
1641
+ builder.remove(Player.COMMAND_SEEK_TO_PREVIOUS)
1642
+ builder.remove(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
1643
+ }
1644
+ return builder.build()
1645
+ }
1646
+
1647
+ /**
1648
+ * Hook the [PlaybackCallback] consults before letting Media3 route
1649
+ * a controller-initiated command to the bound player. Calls run on
1650
+ * the service main looper. Skip-next / skip-previous return true
1651
+ * when the implementation has handled the skip itself, so the
1652
+ * default routing is suppressed; the transport hook fires for play /
1653
+ * pause / seek / setPlaybackSpeed and is purely a side-channel
1654
+ * notification (no return value).
1655
+ */
1656
+ internal interface MediaSessionCommandHandler {
1657
+ /** Stamps `system` on the state-change channel for the upcoming
1658
+ * play / pause / seek / setPlaybackSpeed default routing. */
1659
+ fun onTransportCommand()
1660
+ /** Returns true if the lib intercepted the skip and the default
1661
+ * routing should be suppressed. */
1662
+ fun onSkipToNextCommand(): Boolean
1663
+ /** Returns true if the lib intercepted the skip and the default
1664
+ * routing should be suppressed. */
1665
+ fun onSkipToPreviousCommand(): Boolean
1666
+
1667
+ /**
1668
+ * Returns the lib's current authoritative skip capability —
1669
+ * driven by repeat-mode + queue position + queue size on the
1670
+ * `TrackPlayer` side. Read by [PlaybackCallback.onConnect] and
1671
+ * [LocalBinder.refreshAvailableCommands] so the available
1672
+ * `Player.Commands` reflect what skip arithmetic actually allows.
1673
+ */
1674
+ fun getCachedSkipCapability(): SkipCapability
1675
+
1676
+ // The following are invoked when a raw `KEYCODE_MEDIA_*` lands via
1677
+ // either the `onMediaButtonEvent` callback or `onStartCommand` —
1678
+ // there is no Media3 default routing to fall through to in that
1679
+ // case, so the handler executes the action itself.
1680
+
1681
+ /** Start playback with `system` reason stamped. */
1682
+ fun onMediaPlay()
1683
+ /** Pause playback with `system` reason stamped. */
1684
+ fun onMediaPause()
1685
+ /** Toggle play/pause based on the current player state, with
1686
+ * `system` reason stamped. */
1687
+ fun onMediaTogglePlayPause()
1688
+ /** Queue-aware skip-next with `system` reason stamped. */
1689
+ fun onMediaSkipNext()
1690
+ /** Queue-aware skip-previous with `system` reason stamped. */
1691
+ fun onMediaSkipPrevious()
1692
+
1693
+ /**
1694
+ * Surface a granular Media3 audio-focus event on the JS `onError`
1695
+ * channel. The lib does NOT change player response from here —
1696
+ * Media3's `setAudioAttributes(handleAudioFocus = true)` keeps
1697
+ * driving pause / duck / auto-resume. The handler just emits the
1698
+ * typed [PlaybackError] so JS consumers can render UX
1699
+ * (interruption banner, resume-prompt, etc.) without re-deriving
1700
+ * the focus kind from the playback-state stream.
1701
+ */
1702
+ fun onAudioFocusEvent(mapping: AudioFocusErrorMapping.Mapping)
1703
+
1704
+ /**
1705
+ * Surface an `AUDIO_BECOMING_NOISY` event on the JS `onError`
1706
+ * channel. Fires on headphone unplug / A2DP disconnect / USB DAC
1707
+ * removal. The lib does NOT pause the player here —
1708
+ * `setHandleAudioBecomingNoisy(true)` on the bound player already
1709
+ * routes the broadcast through Media3's own pause logic. The
1710
+ * handler just emits the typed [PlaybackError] so JS consumers
1711
+ * can render UX (e.g. "Paused — headphones disconnected").
1712
+ */
1713
+ fun onBecomingNoisy()
1714
+ }
1715
+
1716
+ }