@javascriptcommon/react-native-track-player 1.2.9 → 1.2.23
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.
- package/android/build.gradle +61 -4
- package/android/src/main/AndroidManifest.xml +2 -4
- package/android/src/main/ic_home-playstore.png +0 -0
- package/android/src/main/ic_repeat-playstore.png +0 -0
- package/android/src/main/ic_repeat_50-playstore.png +0 -0
- package/android/src/main/ic_shuffle-playstore.png +0 -0
- package/android/src/main/ic_shuffle_50-playstore.png +0 -0
- package/android/src/main/ic_shuffle_sm-playstore.png +0 -0
- package/android/src/main/ic_stop-playstore.png +0 -0
- package/android/src/main/ic_test-playstore.png +0 -0
- package/android/src/main/java/com/guichaguri/trackplayer/{service/HeadlessJsMediaService.java → HeadlessJsMediaService.java} +84 -33
- package/android/src/main/java/com/guichaguri/trackplayer/TrackPlayer.kt +25 -0
- package/android/src/main/java/com/guichaguri/trackplayer/extensions/AudioPlayerStateExt.kt +19 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/event/EventHolder.kt +30 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/event/NotificationEventHolder.kt +20 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/event/PlayerEventHolder.kt +111 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/AAMediaSessionCallback.kt +10 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/AudioContentType.kt +10 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/AudioItem.kt +66 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/AudioItemTransitionReason.kt +33 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/AudioPlayerState.kt +30 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/BufferConfig.kt +8 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/CacheConfig.kt +17 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/Capability.kt +19 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/FocusChangeData.kt +3 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/MediaSessionCallback.kt +17 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/NotificationConfig.kt +43 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/NotificationMetadata.kt +8 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/NotificationState.kt +8 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/PlayWhenReadyChangeData.kt +5 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/PlaybackEndedReason.kt +5 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/PlaybackError.kt +6 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/PlaybackMetadata.kt +200 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/PlayerConfig.kt +33 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/PlayerOptions.kt +9 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/PositionChangedReason.kt +39 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/models/QueuedPlayerOptions.kt +49 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/notification/NotificationManager.kt +678 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/players/AudioPlayer.kt +10 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/players/BaseAudioPlayer.kt +864 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/players/QueuedAudioPlayer.kt +269 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/players/components/MediaSourceExt.kt +35 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/players/components/PlayerCache.kt +26 -0
- package/android/src/main/java/com/guichaguri/trackplayer/kotlinaudio/utils/Utils.kt +12 -0
- package/android/src/main/java/com/guichaguri/trackplayer/model/MetadataAdapter.kt +224 -0
- package/android/src/main/java/com/guichaguri/trackplayer/model/State.kt +13 -0
- package/android/src/main/java/com/guichaguri/trackplayer/model/Track.kt +120 -0
- package/android/src/main/java/com/guichaguri/trackplayer/model/TrackAudioItem.kt +19 -0
- package/android/src/main/java/com/guichaguri/trackplayer/model/TrackType.kt +11 -0
- package/android/src/main/java/com/guichaguri/trackplayer/module/AutoConnectionDetector.kt +151 -0
- package/android/src/main/java/com/guichaguri/trackplayer/module/MusicEvents.kt +66 -0
- package/android/src/main/java/com/guichaguri/trackplayer/module/MusicModule.kt +1177 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/BundleUtils.kt +117 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/MusicBinder.kt +31 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/MusicManager.kt +347 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/MusicService.kt +1254 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/Utils.kt +228 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/metadata/ButtonEvents.kt +141 -0
- package/android/src/main/java/com/guichaguri/trackplayer/service/metadata/MetadataManager.kt +396 -0
- package/android/src/main/res/drawable-hdpi/ic_home.png +0 -0
- package/android/src/main/res/drawable-mdpi/ic_home.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ic_home.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/ic_home.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/ic_home.png +0 -0
- package/android/src/main/res/mipmap-hdpi/ic_arrow_down_circle_foreground.png +0 -0
- package/android/src/main/res/mipmap-hdpi/ic_clock_now_foreground.png +0 -0
- package/android/src/main/res/mipmap-hdpi/ic_close_foreground.png +0 -0
- package/android/src/main/res/mipmap-hdpi/ic_heart_foreground.png +0 -0
- package/android/src/main/res/mipmap-hdpi/ic_heart_outlined_foreground.png +0 -0
- package/android/src/main/res/mipmap-hdpi/ic_repeat_off_foreground.png +0 -0
- package/android/src/main/res/mipmap-hdpi/ic_repeat_on_foreground.png +0 -0
- package/android/src/main/res/mipmap-hdpi/ic_shuffle_off_foreground.png +0 -0
- package/android/src/main/res/mipmap-hdpi/ic_shuffle_on_foreground.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_arrow_down_circle_foreground.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_clock_now_foreground.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_close_foreground.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_heart_foreground.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_heart_outlined_foreground.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_repeat_off_foreground.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_repeat_on_foreground.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_shuffle_off_foreground.png +0 -0
- package/android/src/main/res/mipmap-mdpi/ic_shuffle_on_foreground.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_arrow_down_circle_foreground.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_clock_now_foreground.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_close_foreground.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_heart_foreground.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_heart_outlined_foreground.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_repeat_off_foreground.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_repeat_on_foreground.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_shuffle_off_foreground.png +0 -0
- package/android/src/main/res/mipmap-xhdpi/ic_shuffle_on_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_arrow_down_circle_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_clock_now_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_close_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_heart_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_heart_outlined_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_repeat_off_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_repeat_on_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_shuffle_off_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxhdpi/ic_shuffle_on_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxxhdpi/ic_arrow_down_circle_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxxhdpi/ic_clock_now_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxxhdpi/ic_close_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxxhdpi/ic_heart_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxxhdpi/ic_heart_outlined_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxxhdpi/ic_repeat_off_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxxhdpi/ic_repeat_on_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxxhdpi/ic_shuffle_off_foreground.png +0 -0
- package/android/src/main/res/mipmap-xxxhdpi/ic_shuffle_on_foreground.png +0 -0
- package/android/src/main/res/raw/silent_5_seconds.mp3 +0 -0
- package/android/src/main/res/strings.xml +6 -0
- package/android/src/main/res/values/strings.xml +6 -0
- package/index.d.ts +62 -1
- package/lib/index.js +10 -9
- package/package.json +1 -1
- package/android/src/main/java/com/guichaguri/trackplayer/TrackPlayer.java +0 -28
- package/android/src/main/java/com/guichaguri/trackplayer/module/MusicEvents.java +0 -55
- package/android/src/main/java/com/guichaguri/trackplayer/module/MusicModule.java +0 -298
- package/android/src/main/java/com/guichaguri/trackplayer/service/MusicBinder.java +0 -47
- package/android/src/main/java/com/guichaguri/trackplayer/service/MusicManager.java +0 -383
- package/android/src/main/java/com/guichaguri/trackplayer/service/MusicService.java +0 -271
- package/android/src/main/java/com/guichaguri/trackplayer/service/Utils.java +0 -243
- package/android/src/main/java/com/guichaguri/trackplayer/service/metadata/ButtonEvents.java +0 -148
- package/android/src/main/java/com/guichaguri/trackplayer/service/metadata/MetadataManager.java +0 -379
- package/android/src/main/java/com/guichaguri/trackplayer/service/models/Track.java +0 -141
- package/android/src/main/java/com/guichaguri/trackplayer/service/models/TrackType.java +0 -35
- package/android/src/main/res/drawable-hdpi/ic_logo.png +0 -0
- package/android/src/main/res/drawable-mdpi/ic_logo.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ic_logo.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/ic_logo.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/ic_logo.png +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
package com.guichaguri.trackplayer.service
|
|
2
|
+
|
|
3
|
+
import android.content.ContentResolver
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import android.os.Bundle
|
|
7
|
+
import android.support.v4.media.RatingCompat
|
|
8
|
+
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @author Milen Pivchev @mpivchev
|
|
12
|
+
*/
|
|
13
|
+
object BundleUtils {
|
|
14
|
+
fun getUri(context: Context, data: Bundle?, key: String?): Uri? {
|
|
15
|
+
if (!data!!.containsKey(key)) return null
|
|
16
|
+
val obj = data[key]
|
|
17
|
+
if (obj is String) {
|
|
18
|
+
// Remote or Local Uri
|
|
19
|
+
if (obj.trim { it <= ' ' }.isEmpty()) throw RuntimeException("The URL cannot be empty")
|
|
20
|
+
return Uri.parse(obj as String?)
|
|
21
|
+
} else if (obj is Bundle) {
|
|
22
|
+
// require/import
|
|
23
|
+
val uri = obj.getString("uri")
|
|
24
|
+
val helper = ResourceDrawableIdHelper.getInstance()
|
|
25
|
+
val id = helper.getResourceDrawableId(context, uri)
|
|
26
|
+
return if (id > 0) {
|
|
27
|
+
// In production, we can obtain the resource uri
|
|
28
|
+
val res = context.resources
|
|
29
|
+
Uri.Builder()
|
|
30
|
+
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
|
31
|
+
.authority(res.getResourcePackageName(id))
|
|
32
|
+
.appendPath(res.getResourceTypeName(id))
|
|
33
|
+
.appendPath(res.getResourceEntryName(id))
|
|
34
|
+
.build()
|
|
35
|
+
} else {
|
|
36
|
+
// During development, the resources might come directly from the metro server
|
|
37
|
+
Uri.parse(uri)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fun getRawResourceId(context: Context, data: Bundle, key: String?): Int {
|
|
44
|
+
if (!data.containsKey(key)) return 0
|
|
45
|
+
val obj = data[key] as? Bundle ?: return 0
|
|
46
|
+
var name = obj.getString("uri")
|
|
47
|
+
if (name == null || name.isEmpty()) return 0
|
|
48
|
+
name = name.lowercase().replace("-", "_")
|
|
49
|
+
return try {
|
|
50
|
+
name.toInt()
|
|
51
|
+
} catch (ex: NumberFormatException) {
|
|
52
|
+
context.resources.getIdentifier(name, "raw", context.packageName)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fun getIcon(context: Context, options: Bundle, propertyName: String, defaultIcon: Int): Int {
|
|
57
|
+
if (!options.containsKey(propertyName)) return defaultIcon
|
|
58
|
+
|
|
59
|
+
val bundle = options.getBundle(propertyName) ?: return defaultIcon
|
|
60
|
+
|
|
61
|
+
val helper = ResourceDrawableIdHelper.getInstance()
|
|
62
|
+
val icon = helper.getResourceDrawableId(context, bundle.getString("uri"))
|
|
63
|
+
return if (icon == 0) defaultIcon else icon
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fun getIconOrNull(context: Context, options: Bundle, propertyName: String): Int? {
|
|
67
|
+
if (!options.containsKey(propertyName)) return null
|
|
68
|
+
|
|
69
|
+
val bundle = options.getBundle(propertyName) ?: return null
|
|
70
|
+
|
|
71
|
+
val helper = ResourceDrawableIdHelper.getInstance()
|
|
72
|
+
val icon = helper.getResourceDrawableId(context, bundle.getString("uri"))
|
|
73
|
+
return if (icon == 0) null else icon
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fun getRating(data: Bundle?, key: String?, ratingType: Int): RatingCompat? {
|
|
77
|
+
return if (!data!!.containsKey(key) || ratingType == RatingCompat.RATING_NONE) {
|
|
78
|
+
RatingCompat.newUnratedRating(ratingType)
|
|
79
|
+
} else if (ratingType == RatingCompat.RATING_HEART) {
|
|
80
|
+
RatingCompat.newHeartRating(data.getBoolean(key, true))
|
|
81
|
+
} else if (ratingType == RatingCompat.RATING_THUMB_UP_DOWN) {
|
|
82
|
+
RatingCompat.newThumbRating(data.getBoolean(key, true))
|
|
83
|
+
} else if (ratingType == RatingCompat.RATING_PERCENTAGE) {
|
|
84
|
+
RatingCompat.newPercentageRating(data.getFloat(key, 0f))
|
|
85
|
+
} else {
|
|
86
|
+
RatingCompat.newStarRating(ratingType, data.getFloat(key, 0f))
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fun setRating(data: Bundle, key: String?, rating: RatingCompat) {
|
|
91
|
+
if (!rating.isRated) return
|
|
92
|
+
val ratingType = rating.ratingStyle
|
|
93
|
+
if (ratingType == RatingCompat.RATING_HEART) {
|
|
94
|
+
data.putBoolean(key, rating.hasHeart())
|
|
95
|
+
} else if (ratingType == RatingCompat.RATING_THUMB_UP_DOWN) {
|
|
96
|
+
data.putBoolean(key, rating.isThumbUp)
|
|
97
|
+
} else if (ratingType == RatingCompat.RATING_PERCENTAGE) {
|
|
98
|
+
data.putDouble(key, rating.percentRating.toDouble())
|
|
99
|
+
} else {
|
|
100
|
+
data.putDouble(key, rating.starRating.toDouble())
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fun getInt(data: Bundle?, key: String?, defaultValue: Int): Int {
|
|
105
|
+
val value = data!![key]
|
|
106
|
+
return if (value is Number) {
|
|
107
|
+
value.toInt()
|
|
108
|
+
} else defaultValue
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fun getIntOrNull(data: Bundle?, key: String?): Int? {
|
|
112
|
+
val value = data!![key]
|
|
113
|
+
return if (value is Number) {
|
|
114
|
+
value.toInt()
|
|
115
|
+
} else null
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.guichaguri.trackplayer.service
|
|
2
|
+
|
|
3
|
+
import android.os.Binder
|
|
4
|
+
import android.os.Bundle
|
|
5
|
+
import android.os.Handler
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @author Guichaguri
|
|
9
|
+
*/
|
|
10
|
+
class MusicBinder(val service: MusicService?, val manager: MusicManager) : Binder() {
|
|
11
|
+
|
|
12
|
+
fun post(r: Runnable?) {
|
|
13
|
+
if (service == null) return
|
|
14
|
+
if (service.handler == null) service.handler = Handler()
|
|
15
|
+
service.handler!!.post(r!!)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fun updateOptions(bundle: Bundle) {
|
|
19
|
+
manager.setStopWithApp(bundle.getBoolean("stopWithApp", true))
|
|
20
|
+
manager.setAlwaysPauseOnInterruption(bundle.getBoolean("alwaysPauseOnInterruption", false))
|
|
21
|
+
manager.metadata.updateOptions(bundle)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
val ratingType: Int
|
|
25
|
+
get() = manager.metadata.ratingType
|
|
26
|
+
|
|
27
|
+
fun destroy() {
|
|
28
|
+
service!!.destroy(true)
|
|
29
|
+
service.stopSelf()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
package com.guichaguri.trackplayer.service
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.BroadcastReceiver
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.content.Intent
|
|
7
|
+
import android.content.IntentFilter
|
|
8
|
+
import android.media.AudioAttributes
|
|
9
|
+
import android.media.AudioFocusRequest
|
|
10
|
+
import android.media.AudioManager
|
|
11
|
+
import android.net.wifi.WifiManager
|
|
12
|
+
import android.os.Build
|
|
13
|
+
import android.os.Bundle
|
|
14
|
+
import android.os.Handler
|
|
15
|
+
import android.os.PowerManager
|
|
16
|
+
import android.support.v4.media.session.PlaybackStateCompat
|
|
17
|
+
import android.util.Log
|
|
18
|
+
import androidx.annotation.RequiresApi
|
|
19
|
+
import com.guichaguri.trackplayer.module.MusicEvents
|
|
20
|
+
import com.guichaguri.trackplayer.service.metadata.MetadataManager
|
|
21
|
+
import com.guichaguri.trackplayer.model.Track
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @author Guichaguri
|
|
25
|
+
*/
|
|
26
|
+
class MusicManager @SuppressLint("InvalidWakeLockTag") constructor(private val service: MusicService) :
|
|
27
|
+
AudioManager.OnAudioFocusChangeListener {
|
|
28
|
+
private val wakeLock: PowerManager.WakeLock
|
|
29
|
+
private val wifiLock: WifiManager.WifiLock
|
|
30
|
+
val metadata: MetadataManager
|
|
31
|
+
var currentTrack: Track? = null
|
|
32
|
+
private var state = 0
|
|
33
|
+
private var previousState = 0
|
|
34
|
+
private var position: Long = 0
|
|
35
|
+
private var bufferedPosition: Long = 0
|
|
36
|
+
|
|
37
|
+
@RequiresApi(26)
|
|
38
|
+
private var focus: AudioFocusRequest? = null
|
|
39
|
+
private var hasAudioFocus = false
|
|
40
|
+
private var wasDucking = false
|
|
41
|
+
private val noisyReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
|
42
|
+
override fun onReceive(context: Context, intent: Intent) {
|
|
43
|
+
service.emit(MusicEvents.BUTTON_PAUSE, null)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
private var receivingNoisyEvents = false
|
|
47
|
+
private var stopWithApp = true
|
|
48
|
+
private var alwaysPauseOnInterruption = false
|
|
49
|
+
|
|
50
|
+
init {
|
|
51
|
+
metadata = MetadataManager(service, this)
|
|
52
|
+
val powerManager = service.getSystemService(Context.POWER_SERVICE) as PowerManager
|
|
53
|
+
wakeLock =
|
|
54
|
+
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "track-player-wake-lock")
|
|
55
|
+
wakeLock.setReferenceCounted(false)
|
|
56
|
+
|
|
57
|
+
// Android 7: Use the application context here to prevent any memory leaks
|
|
58
|
+
val wifiManager =
|
|
59
|
+
service.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
|
60
|
+
wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "track-player-wifi-lock")
|
|
61
|
+
wifiLock.setReferenceCounted(false)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fun shouldStopWithApp(): Boolean {
|
|
65
|
+
return stopWithApp
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fun setStopWithApp(stopWithApp: Boolean) {
|
|
69
|
+
this.stopWithApp = stopWithApp
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fun setAlwaysPauseOnInterruption(alwaysPauseOnInterruption: Boolean) {
|
|
73
|
+
this.alwaysPauseOnInterruption = alwaysPauseOnInterruption
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
val handler: Handler?
|
|
77
|
+
get() = service.handler
|
|
78
|
+
|
|
79
|
+
fun getState(): Long {
|
|
80
|
+
return state.toLong()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fun setState(state: Int, position: Long) {
|
|
84
|
+
this.state = state
|
|
85
|
+
if (position != -1L) {
|
|
86
|
+
this.position = position
|
|
87
|
+
bufferedPosition = position
|
|
88
|
+
}
|
|
89
|
+
onPlayerStateChanged()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@SuppressLint("WakelockTimeout")
|
|
93
|
+
fun onPlay() {
|
|
94
|
+
/*if(playback == null) return;
|
|
95
|
+
|
|
96
|
+
Track track = playback.getCurrentTrack();
|
|
97
|
+
if(track == null) return;
|
|
98
|
+
|
|
99
|
+
if(!playback.isRemote()) {
|
|
100
|
+
requestFocus();
|
|
101
|
+
|
|
102
|
+
if(!receivingNoisyEvents) {
|
|
103
|
+
receivingNoisyEvents = true;
|
|
104
|
+
service.registerReceiver(noisyReceiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if(!wakeLock.isHeld()) wakeLock.acquire();
|
|
108
|
+
|
|
109
|
+
if(!Utils.isLocal(track.uri)) {
|
|
110
|
+
if(!wifiLock.isHeld()) wifiLock.acquire();
|
|
111
|
+
}
|
|
112
|
+
}*/requestFocus()
|
|
113
|
+
if (!receivingNoisyEvents) {
|
|
114
|
+
receivingNoisyEvents = true
|
|
115
|
+
service.registerReceiver(
|
|
116
|
+
noisyReceiver,
|
|
117
|
+
IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
if (!wakeLock.isHeld) wakeLock.acquire()
|
|
121
|
+
|
|
122
|
+
/*if(!Utils.isLocal(currentTrack.uri)) {
|
|
123
|
+
if(!wifiLock.isHeld()) wifiLock.acquire();
|
|
124
|
+
}*/metadata.setActive(true)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fun onPause() {
|
|
128
|
+
// Unregisters the noisy receiver
|
|
129
|
+
if (receivingNoisyEvents) {
|
|
130
|
+
try {
|
|
131
|
+
service.unregisterReceiver(noisyReceiver)
|
|
132
|
+
receivingNoisyEvents = false
|
|
133
|
+
} catch (e: Exception) {
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Release the wake and the wifi locks
|
|
138
|
+
if (wakeLock.isHeld) wakeLock.release()
|
|
139
|
+
if (wifiLock.isHeld) wifiLock.release()
|
|
140
|
+
metadata.setActive(true)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
fun onStop() {
|
|
144
|
+
Log.d(Utils.LOG, "onStop")
|
|
145
|
+
|
|
146
|
+
// Unregisters the noisy receiver
|
|
147
|
+
if (receivingNoisyEvents) {
|
|
148
|
+
try {
|
|
149
|
+
service.unregisterReceiver(noisyReceiver)
|
|
150
|
+
receivingNoisyEvents = false
|
|
151
|
+
} catch (e: Exception) {
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Release the wake and the wifi locks
|
|
156
|
+
if (wakeLock.isHeld) wakeLock.release()
|
|
157
|
+
if (wifiLock.isHeld) wifiLock.release()
|
|
158
|
+
abandonFocus()
|
|
159
|
+
metadata.setActive(false)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fun onPlayerStateChanged() {
|
|
163
|
+
if (Utils.isPlaying(state) && !Utils.isPlaying(previousState)) {
|
|
164
|
+
onPlay()
|
|
165
|
+
} else if (Utils.isPaused(state) && !Utils.isPaused(previousState)) {
|
|
166
|
+
onPause()
|
|
167
|
+
} else if (Utils.isStopped(state) && !Utils.isStopped(previousState)) {
|
|
168
|
+
onStop()
|
|
169
|
+
}
|
|
170
|
+
onStateChange(state, position, bufferedPosition)
|
|
171
|
+
previousState = state
|
|
172
|
+
if (state == PlaybackStateCompat.STATE_STOPPED) {
|
|
173
|
+
onEnd(currentTrack, 0)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
fun onStateChange(state: Int, position: Long, bufferedPosition: Long) {
|
|
178
|
+
Log.d(Utils.LOG, "onStateChange")
|
|
179
|
+
val bundle = Bundle()
|
|
180
|
+
bundle.putInt("state", state)
|
|
181
|
+
service.emit(MusicEvents.PLAYBACK_STATE, bundle)
|
|
182
|
+
metadata.updatePlayback(state, position, bufferedPosition, 1)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
fun onTrackUpdate(previous: Track?, prevPos: Long, next: Track?) {
|
|
186
|
+
Log.d(Utils.LOG, "onTrackUpdate")
|
|
187
|
+
if (next != null) metadata.updateMetadata(next)
|
|
188
|
+
val bundle = Bundle()
|
|
189
|
+
bundle.putString("track", previous?.id)
|
|
190
|
+
bundle.putDouble("position", Utils.toSeconds(prevPos))
|
|
191
|
+
bundle.putString("nextTrack", next?.id)
|
|
192
|
+
service.emit(MusicEvents.PLAYBACK_TRACK_CHANGED, bundle)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
fun onReset() {
|
|
196
|
+
metadata.removeNotifications()
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
fun onEnd(previous: Track?, prevPos: Long) {
|
|
200
|
+
Log.d(Utils.LOG, "onEnd")
|
|
201
|
+
val bundle = Bundle()
|
|
202
|
+
bundle.putString("track", previous?.id)
|
|
203
|
+
bundle.putDouble("position", Utils.toSeconds(prevPos))
|
|
204
|
+
service.emit(MusicEvents.PLAYBACK_QUEUE_ENDED, bundle)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
fun onMetadataReceived(
|
|
208
|
+
source: String,
|
|
209
|
+
title: String?,
|
|
210
|
+
url: String?,
|
|
211
|
+
artist: String?,
|
|
212
|
+
album: String?,
|
|
213
|
+
date: String?,
|
|
214
|
+
genre: String?
|
|
215
|
+
) {
|
|
216
|
+
Log.d(
|
|
217
|
+
Utils.LOG,
|
|
218
|
+
"onMetadataReceived: $source"
|
|
219
|
+
)
|
|
220
|
+
val bundle = Bundle()
|
|
221
|
+
bundle.putString("source", source)
|
|
222
|
+
bundle.putString("title", title)
|
|
223
|
+
bundle.putString("url", url)
|
|
224
|
+
bundle.putString("artist", artist)
|
|
225
|
+
bundle.putString("album", album)
|
|
226
|
+
bundle.putString("date", date)
|
|
227
|
+
bundle.putString("genre", genre)
|
|
228
|
+
service.emit(MusicEvents.PLAYBACK_METADATA, bundle)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
fun onError(code: String, error: String) {
|
|
232
|
+
Log.d(Utils.LOG, "onError")
|
|
233
|
+
Log.e(
|
|
234
|
+
Utils.LOG,
|
|
235
|
+
"Playback error: $code - $error"
|
|
236
|
+
)
|
|
237
|
+
val bundle = Bundle()
|
|
238
|
+
bundle.putString("code", code)
|
|
239
|
+
bundle.putString("message", error)
|
|
240
|
+
service.emit(MusicEvents.PLAYBACK_ERROR, bundle)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
override fun onAudioFocusChange(focus: Int) {
|
|
244
|
+
Log.d(Utils.LOG, "onDuck")
|
|
245
|
+
var permanent = false
|
|
246
|
+
var paused = false
|
|
247
|
+
var ducking = false
|
|
248
|
+
when (focus) {
|
|
249
|
+
AudioManager.AUDIOFOCUS_GAIN -> paused = false
|
|
250
|
+
AudioManager.AUDIOFOCUS_LOSS -> {
|
|
251
|
+
permanent = true
|
|
252
|
+
paused = true
|
|
253
|
+
abandonFocus()
|
|
254
|
+
}
|
|
255
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> paused = true
|
|
256
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> if (alwaysPauseOnInterruption) paused =
|
|
257
|
+
true else ducking = true
|
|
258
|
+
else -> {}
|
|
259
|
+
}
|
|
260
|
+
if (ducking) {
|
|
261
|
+
//playback.setVolumeMultiplier(0.5F);
|
|
262
|
+
wasDucking = true
|
|
263
|
+
} else if (wasDucking) {
|
|
264
|
+
//playback.setVolumeMultiplier(1.0F);
|
|
265
|
+
wasDucking = false
|
|
266
|
+
}
|
|
267
|
+
val bundle = Bundle()
|
|
268
|
+
bundle.putBoolean("permanent", permanent)
|
|
269
|
+
bundle.putBoolean("paused", paused)
|
|
270
|
+
bundle.putBoolean("ducking", ducking)
|
|
271
|
+
service.emit(MusicEvents.BUTTON_DUCK, bundle)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private fun requestFocus() {
|
|
275
|
+
if (hasAudioFocus) return
|
|
276
|
+
Log.d(Utils.LOG, "Requesting audio focus...")
|
|
277
|
+
val manager = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
278
|
+
val r: Int
|
|
279
|
+
if (manager == null) {
|
|
280
|
+
r = AudioManager.AUDIOFOCUS_REQUEST_FAILED
|
|
281
|
+
} else if (Build.VERSION.SDK_INT >= 26) {
|
|
282
|
+
focus = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
|
283
|
+
.setOnAudioFocusChangeListener(this)
|
|
284
|
+
.setAudioAttributes(
|
|
285
|
+
AudioAttributes.Builder()
|
|
286
|
+
.setUsage(AudioAttributes.USAGE_MEDIA)
|
|
287
|
+
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
|
288
|
+
.build()
|
|
289
|
+
)
|
|
290
|
+
.setWillPauseWhenDucked(alwaysPauseOnInterruption)
|
|
291
|
+
.build()
|
|
292
|
+
r = if (focus != null) {
|
|
293
|
+
manager.requestAudioFocus(focus!!)
|
|
294
|
+
} else {
|
|
295
|
+
AudioManager.AUDIOFOCUS_REQUEST_FAILED
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
r = manager.requestAudioFocus(
|
|
299
|
+
this,
|
|
300
|
+
AudioManager.STREAM_MUSIC,
|
|
301
|
+
AudioManager.AUDIOFOCUS_GAIN
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
hasAudioFocus = r == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private fun abandonFocus() {
|
|
308
|
+
if (!hasAudioFocus) return
|
|
309
|
+
Log.d(Utils.LOG, "Abandoning audio focus...")
|
|
310
|
+
val manager = service.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
311
|
+
val r: Int
|
|
312
|
+
r = if (manager == null) {
|
|
313
|
+
AudioManager.AUDIOFOCUS_REQUEST_FAILED
|
|
314
|
+
} else if (Build.VERSION.SDK_INT >= 26) {
|
|
315
|
+
manager.abandonAudioFocusRequest(focus!!)
|
|
316
|
+
} else {
|
|
317
|
+
manager.abandonAudioFocus(this)
|
|
318
|
+
}
|
|
319
|
+
hasAudioFocus = r != AudioManager.AUDIOFOCUS_REQUEST_GRANTED
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
fun destroy(intentToStop: Boolean) {
|
|
323
|
+
Log.d(Utils.LOG, "Releasing service resources...")
|
|
324
|
+
|
|
325
|
+
// Disable audio focus
|
|
326
|
+
abandonFocus()
|
|
327
|
+
|
|
328
|
+
// Stop receiving audio becoming noisy events
|
|
329
|
+
if (receivingNoisyEvents) {
|
|
330
|
+
try {
|
|
331
|
+
service.unregisterReceiver(noisyReceiver)
|
|
332
|
+
receivingNoisyEvents = false
|
|
333
|
+
} catch (e: Exception) {
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Release the playback resources
|
|
338
|
+
//if(playback != null) playback.destroy();
|
|
339
|
+
|
|
340
|
+
// Release the metadata resources
|
|
341
|
+
metadata.destroy(intentToStop)
|
|
342
|
+
|
|
343
|
+
// Release the locks
|
|
344
|
+
if (wifiLock.isHeld) wifiLock.release()
|
|
345
|
+
if (wakeLock.isHeld) wakeLock.release()
|
|
346
|
+
}
|
|
347
|
+
}
|