capacitor-plugin-playlist 0.11.2 → 0.13.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.
- package/README.md +133 -127
- package/android/build.gradle +31 -18
- package/android/gradle/wrapper/gradle-wrapper.properties +1 -1
- package/android/src/main/java/org/dwbn/plugins/playlist/AudioMediaItemFactory.java +96 -0
- package/android/src/main/java/org/dwbn/plugins/playlist/PlaylistPlugin.kt +3 -3
- package/android/src/main/java/org/dwbn/plugins/playlist/RmxAudioPlayer.java +145 -188
- package/android/src/main/java/org/dwbn/plugins/playlist/RmxPlaybackErrorMapper.kt +26 -0
- package/android/src/main/java/org/dwbn/plugins/playlist/data/AudioTrack.kt +28 -28
- package/android/src/main/java/org/dwbn/plugins/playlist/handoff/VideoPlayerBridge.kt +102 -0
- package/android/src/main/java/org/dwbn/plugins/playlist/manager/PlaybackProgress.kt +8 -0
- package/android/src/main/java/org/dwbn/plugins/playlist/manager/PlaylistManager.kt +418 -183
- package/android/src/main/java/org/dwbn/plugins/playlist/manager/PlaylistPlaybackPolicy.kt +98 -0
- package/android/src/main/java/org/dwbn/plugins/playlist/manager/RmxPlaybackState.kt +11 -0
- package/android/src/main/java/org/dwbn/plugins/playlist/playlist/AudioPlaylistHandler.java +107 -211
- package/android/src/main/java/org/dwbn/plugins/playlist/service/GlideBitmapLoader.kt +121 -0
- package/android/src/main/java/org/dwbn/plugins/playlist/service/HandoffForwardingPlayer.kt +185 -0
- package/android/src/main/java/org/dwbn/plugins/playlist/service/MediaNotificationPolicy.kt +253 -0
- package/android/src/main/java/org/dwbn/plugins/playlist/service/MediaService.kt +260 -68
- package/android/src/main/java/org/dwbn/plugins/playlist/service/PlaylistMediaSessionCallback.kt +39 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/AudioMediaItemFactoryMimeTest.kt +98 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/AudioMediaItemFactoryTest.java +71 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/data/AudioTrackTest.kt +53 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/handoff/VideoPlayerBridgeTest.kt +84 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/manager/PlaylistPlaybackPolicyTest.kt +128 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/playlist/AudioPlaylistHandlerTest.kt +145 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/service/GlideBitmapLoaderTest.kt +76 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/service/HandoffForwardingPlayerTest.kt +282 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/service/MediaNotificationPolicyTest.kt +285 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/service/VideoHandoffNotificationCommandsTest.kt +54 -0
- package/android/src/test/java/org/dwbn/plugins/playlist/service/VideoHandoffNotificationPolicyTest.kt +47 -0
- package/dist/docs.json +12 -846
- package/dist/esm/RmxAudioPlayer.d.ts +2 -2
- package/dist/esm/RmxAudioPlayer.js.map +1 -1
- package/dist/esm/definitions.d.ts +7 -7
- package/dist/esm/interfaces.d.ts +7 -1
- package/dist/esm/utils.d.ts +1 -1
- package/dist/esm/utils.js +2 -2
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/web.d.ts +3 -3
- package/dist/esm/web.js +5 -5
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +7 -7
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +7 -7
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/PlaylistPlugin/AVBidirectionalQueuePlayer.swift +45 -6
- package/ios/Sources/PlaylistPlugin/AudioTrack.swift +15 -2
- package/ios/Tests/PlaylistPluginTests/PositionResumeTests.swift +82 -0
- package/package.json +15 -19
- package/android/src/main/java/org/dwbn/plugins/playlist/notification/PlaylistNotificationProvider.kt +0 -26
- package/android/src/main/java/org/dwbn/plugins/playlist/playlist/AudioApi.kt +0 -114
- package/android/src/main/java/org/dwbn/plugins/playlist/playlist/BaseMediaApi.kt +0 -36
- package/android/src/main/java/org/dwbn/plugins/playlist/service/MediaImageProvider.kt +0 -83
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "capacitor-plugin-playlist",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Playlist ",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"verify:web": "npm run build",
|
|
21
21
|
"lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
|
|
22
22
|
"fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- autocorrect --format",
|
|
23
|
-
"eslint": "eslint .
|
|
23
|
+
"eslint": "eslint .",
|
|
24
24
|
"prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
|
|
25
25
|
"swiftlint": "node-swiftlint",
|
|
26
26
|
"docgen": "docgen --api PlaylistPlugin --output-readme README.md --output-json dist/docs.json",
|
|
@@ -34,35 +34,31 @@
|
|
|
34
34
|
"author": "phiamo",
|
|
35
35
|
"license": "MIT",
|
|
36
36
|
"engines": {
|
|
37
|
-
"node": ">=
|
|
37
|
+
"node": ">=22.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@capacitor/android": "^8.
|
|
41
|
-
"@capacitor/core": "^8.
|
|
40
|
+
"@capacitor/android": "^8.5.2",
|
|
41
|
+
"@capacitor/core": "^8.5.2",
|
|
42
42
|
"@capacitor/docgen": "^0.3.1",
|
|
43
|
-
"@capacitor/ios": "^8.
|
|
44
|
-
"@ionic/eslint-config": "^0.
|
|
43
|
+
"@capacitor/ios": "^8.5.2",
|
|
44
|
+
"@ionic/eslint-config": "^0.5.0",
|
|
45
45
|
"@ionic/prettier-config": "^4.0.0",
|
|
46
46
|
"@ionic/swiftlint-config": "^2.0.0",
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"prettier": "^
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"rollup": "^4.53.2",
|
|
47
|
+
"eslint": "^10.0.0",
|
|
48
|
+
"jsdom": "^30.1.0",
|
|
49
|
+
"prettier": "^3.9.8",
|
|
50
|
+
"prettier-plugin-java": "^2.10.4",
|
|
51
|
+
"rimraf": "^6.1.3",
|
|
52
|
+
"rollup": "^4.63.4",
|
|
54
53
|
"swiftlint": "^2.0.0",
|
|
55
|
-
"typescript": "^
|
|
56
|
-
"vitest": "^
|
|
54
|
+
"typescript": "^6.0.3",
|
|
55
|
+
"vitest": "^5.0.1"
|
|
57
56
|
},
|
|
58
57
|
"peerDependencies": {
|
|
59
58
|
"@capacitor/core": ">=8.0.0"
|
|
60
59
|
},
|
|
61
60
|
"prettier": "@ionic/prettier-config",
|
|
62
61
|
"swiftlint": "@ionic/swiftlint-config",
|
|
63
|
-
"eslintConfig": {
|
|
64
|
-
"extends": "@ionic/eslint-config/recommended"
|
|
65
|
-
},
|
|
66
62
|
"keywords": [
|
|
67
63
|
"capacitor",
|
|
68
64
|
"plugin",
|
package/android/src/main/java/org/dwbn/plugins/playlist/notification/PlaylistNotificationProvider.kt
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
package org.dwbn.plugins.playlist.notification
|
|
2
|
-
|
|
3
|
-
import android.annotation.SuppressLint
|
|
4
|
-
import android.app.PendingIntent
|
|
5
|
-
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
|
6
|
-
import android.app.PendingIntent.FLAG_IMMUTABLE
|
|
7
|
-
import android.content.Context
|
|
8
|
-
import android.content.Intent
|
|
9
|
-
import com.devbrackets.android.playlistcore.components.notification.DefaultPlaylistNotificationProvider
|
|
10
|
-
|
|
11
|
-
class PlaylistNotificationProvider(context: Context?) : DefaultPlaylistNotificationProvider(context!!) {
|
|
12
|
-
override val clickPendingIntent: PendingIntent?
|
|
13
|
-
@SuppressLint("UnspecifiedImmutableFlag")
|
|
14
|
-
get() {
|
|
15
|
-
val context = context
|
|
16
|
-
val pkgName = context.packageName
|
|
17
|
-
val intent = context
|
|
18
|
-
.packageManager
|
|
19
|
-
.getLaunchIntentForPackage(pkgName)
|
|
20
|
-
intent!!.addFlags(
|
|
21
|
-
Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
22
|
-
return PendingIntent.getActivity(this.context,
|
|
23
|
-
0, intent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
package org.dwbn.plugins.playlist.playlist
|
|
3
|
-
|
|
4
|
-
import android.content.Context
|
|
5
|
-
import android.media.AudioManager
|
|
6
|
-
import android.net.Uri
|
|
7
|
-
import android.os.PowerManager
|
|
8
|
-
import androidx.annotation.FloatRange
|
|
9
|
-
import androidx.annotation.IntRange
|
|
10
|
-
import androidx.annotation.OptIn
|
|
11
|
-
import androidx.media3.common.AudioAttributes
|
|
12
|
-
import androidx.media3.common.C
|
|
13
|
-
import androidx.media3.common.util.UnstableApi
|
|
14
|
-
import androidx.media3.exoplayer.util.EventLogger
|
|
15
|
-
import com.devbrackets.android.exomedia.AudioPlayer
|
|
16
|
-
import com.devbrackets.android.exomedia.listener.OnErrorListener
|
|
17
|
-
import com.devbrackets.android.playlistcore.manager.BasePlaylistManager
|
|
18
|
-
import org.dwbn.plugins.playlist.data.AudioTrack
|
|
19
|
-
import java.lang.ref.WeakReference
|
|
20
|
-
import java.util.concurrent.locks.ReentrantLock
|
|
21
|
-
|
|
22
|
-
@OptIn(UnstableApi::class)
|
|
23
|
-
class AudioApi(context: Context) : BaseMediaApi() {
|
|
24
|
-
private val audioPlayer: AudioPlayer = AudioPlayer(context.applicationContext)
|
|
25
|
-
|
|
26
|
-
private val errorListenersLock = ReentrantLock(true)
|
|
27
|
-
private val errorListeners = ArrayList<WeakReference<OnErrorListener>>()
|
|
28
|
-
|
|
29
|
-
override val isPlaying: Boolean
|
|
30
|
-
get() = audioPlayer.isPlaying
|
|
31
|
-
|
|
32
|
-
override val handlesOwnAudioFocus: Boolean
|
|
33
|
-
get() = false
|
|
34
|
-
|
|
35
|
-
override val currentPosition: Long
|
|
36
|
-
get() = if (prepared) audioPlayer.currentPosition else 0
|
|
37
|
-
|
|
38
|
-
override val duration: Long
|
|
39
|
-
get() = if (prepared) audioPlayer.duration else 0
|
|
40
|
-
|
|
41
|
-
override val bufferedPercent: Int
|
|
42
|
-
get() = bufferPercent
|
|
43
|
-
|
|
44
|
-
init {
|
|
45
|
-
audioPlayer.setOnErrorListener(this)
|
|
46
|
-
audioPlayer.setOnPreparedListener(this)
|
|
47
|
-
audioPlayer.setOnCompletionListener(this)
|
|
48
|
-
audioPlayer.setOnSeekCompletionListener(this)
|
|
49
|
-
audioPlayer.setOnBufferUpdateListener(this)
|
|
50
|
-
|
|
51
|
-
audioPlayer.setWakeLevel(PowerManager.PARTIAL_WAKE_LOCK)
|
|
52
|
-
audioPlayer.setAudioAttributes(getAudioAttributes(C.USAGE_MEDIA, C.AUDIO_CONTENT_TYPE_MUSIC))
|
|
53
|
-
audioPlayer.setAnalyticsListener(EventLogger())
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
override fun play() {
|
|
57
|
-
audioPlayer.start()
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
override fun pause() {
|
|
61
|
-
audioPlayer.pause()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
override fun stop() {
|
|
65
|
-
audioPlayer.stop()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
override fun reset() {
|
|
69
|
-
audioPlayer.reset()
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
override fun release() {
|
|
73
|
-
audioPlayer.release()
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
override fun setVolume(@FloatRange(from = 0.0, to = 1.0) left: Float, @FloatRange(from = 0.0, to = 1.0) right: Float) {
|
|
77
|
-
audioPlayer.volume = (left + right) / 2
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
override fun seekTo(@IntRange(from = 0L) milliseconds: Long) {
|
|
81
|
-
audioPlayer.seekTo(milliseconds.toInt().toLong())
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
override fun handlesItem(item: AudioTrack): Boolean {
|
|
85
|
-
return item.mediaType == BasePlaylistManager.AUDIO
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
override fun playItem(item: AudioTrack) {
|
|
89
|
-
try {
|
|
90
|
-
bufferPercent = 0
|
|
91
|
-
audioPlayer.setMedia(Uri.parse(if (item.downloaded) item.downloadedMediaUri else item.mediaUrl))
|
|
92
|
-
} catch (e: Exception) {
|
|
93
|
-
//Purposefully left blank
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
fun setPlaybackSpeed(@FloatRange(from = 0.0, to = 1.0) speed: Float) {
|
|
98
|
-
audioPlayer.setPlaybackSpeed(speed)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
fun addErrorListener(listener: OnErrorListener) {
|
|
102
|
-
errorListenersLock.lock()
|
|
103
|
-
errorListeners.add(WeakReference<OnErrorListener>(listener))
|
|
104
|
-
errorListenersLock.unlock()
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
@Suppress("SameParameterValue")
|
|
108
|
-
private fun getAudioAttributes(@C.AudioUsage usage: Int, @C.AudioContentType contentType: Int): AudioAttributes {
|
|
109
|
-
return AudioAttributes.Builder()
|
|
110
|
-
.setUsage(usage)
|
|
111
|
-
.setContentType(contentType)
|
|
112
|
-
.build()
|
|
113
|
-
}
|
|
114
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
package org.dwbn.plugins.playlist.playlist
|
|
2
|
-
|
|
3
|
-
import com.devbrackets.android.exomedia.listener.*
|
|
4
|
-
import com.devbrackets.android.playlistcore.api.MediaPlayerApi
|
|
5
|
-
import com.devbrackets.android.playlistcore.listener.MediaStatusListener
|
|
6
|
-
import org.dwbn.plugins.playlist.data.AudioTrack
|
|
7
|
-
|
|
8
|
-
abstract class BaseMediaApi : MediaPlayerApi<AudioTrack>, OnPreparedListener, OnCompletionListener,
|
|
9
|
-
OnErrorListener, OnSeekCompletionListener, OnBufferUpdateListener {
|
|
10
|
-
protected var prepared: Boolean = false
|
|
11
|
-
protected var bufferPercent: Int = 0
|
|
12
|
-
|
|
13
|
-
protected var statusListener: MediaStatusListener<AudioTrack>? = null
|
|
14
|
-
override fun setMediaStatusListener(listener: MediaStatusListener<AudioTrack>) {
|
|
15
|
-
statusListener = listener
|
|
16
|
-
}
|
|
17
|
-
override fun onCompletion() {
|
|
18
|
-
statusListener?.onCompletion(this)
|
|
19
|
-
}
|
|
20
|
-
override fun onPrepared() {
|
|
21
|
-
prepared = true
|
|
22
|
-
statusListener?.onPrepared(this)
|
|
23
|
-
}
|
|
24
|
-
override fun onSeekComplete() {
|
|
25
|
-
statusListener?.onSeekComplete(this)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
override fun onError(e: Exception?): Boolean {
|
|
29
|
-
return statusListener?.onError(this) == true
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
override fun onBufferingUpdate(percent: Int) {
|
|
33
|
-
bufferPercent = percent
|
|
34
|
-
statusListener?.onBufferingUpdate(this, percent)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
package org.dwbn.plugins.playlist.service
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.graphics.Bitmap
|
|
5
|
-
import android.graphics.BitmapFactory
|
|
6
|
-
import com.bumptech.glide.Glide
|
|
7
|
-
import com.bumptech.glide.RequestManager
|
|
8
|
-
import com.bumptech.glide.request.target.CustomTarget
|
|
9
|
-
import com.bumptech.glide.request.transition.Transition
|
|
10
|
-
import com.devbrackets.android.playlistcore.components.image.ImageProvider
|
|
11
|
-
import org.dwbn.plugins.playlist.FakeR
|
|
12
|
-
import org.dwbn.plugins.playlist.data.AudioTrack
|
|
13
|
-
import org.dwbn.plugins.playlist.manager.Options
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class MediaImageProvider(
|
|
17
|
-
context: Context,
|
|
18
|
-
val onImageUpdatedListener: OnImageUpdatedListener,
|
|
19
|
-
options: Options
|
|
20
|
-
) : ImageProvider<AudioTrack> {
|
|
21
|
-
|
|
22
|
-
interface OnImageUpdatedListener {
|
|
23
|
-
fun onImageUpdated()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
private var options: Options? = null
|
|
27
|
-
private val glide: RequestManager = Glide.with(context.applicationContext)
|
|
28
|
-
private val fakeR: FakeR = FakeR(context.applicationContext)
|
|
29
|
-
private val remoteViewImageTarget = RemoteViewImageTarget()
|
|
30
|
-
private var defaultArtworkImage: Bitmap? = null
|
|
31
|
-
private var artworkImage: Bitmap? = null
|
|
32
|
-
private var notificationIconId = 0
|
|
33
|
-
override val notificationIconRes: Int
|
|
34
|
-
get() = mipmapIcon
|
|
35
|
-
|
|
36
|
-
override val remoteViewIconRes: Int
|
|
37
|
-
get() = mipmapIcon
|
|
38
|
-
|
|
39
|
-
override val largeNotificationImage: Bitmap?
|
|
40
|
-
get() = remoteViewArtwork
|
|
41
|
-
|
|
42
|
-
override var remoteViewArtwork: Bitmap? = null
|
|
43
|
-
get() = if (artworkImage != null) artworkImage else defaultArtworkImage
|
|
44
|
-
private set
|
|
45
|
-
|
|
46
|
-
override fun updateImages(playlistItem: AudioTrack) {
|
|
47
|
-
glide.asBitmap().load(playlistItem.artworkUrl).into(remoteViewImageTarget)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// return R.mipmap.icon; // this comes from cordova itself.
|
|
51
|
-
private val mipmapIcon: Int
|
|
52
|
-
get() {
|
|
53
|
-
// return R.mipmap.icon; // this comes from cordova itself.
|
|
54
|
-
if (notificationIconId <= 0) {
|
|
55
|
-
notificationIconId = fakeR.getId("drawable", options?.icon)
|
|
56
|
-
}
|
|
57
|
-
return notificationIconId
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* A class used to listen to the loading of the large lock screen images and perform
|
|
62
|
-
* the correct functionality to update the artwork once it is loaded.
|
|
63
|
-
*
|
|
64
|
-
* **NOTE:** This is a Glide Image loader class
|
|
65
|
-
*/
|
|
66
|
-
private inner class RemoteViewImageTarget : CustomTarget<Bitmap>() {
|
|
67
|
-
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
|
|
68
|
-
artworkImage = resource
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
override fun onLoadCleared(placeholder: android.graphics.drawable.Drawable?) {
|
|
72
|
-
// No cleanup needed
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
init {
|
|
77
|
-
this.options = options
|
|
78
|
-
defaultArtworkImage = BitmapFactory.decodeResource(
|
|
79
|
-
context.resources,
|
|
80
|
-
fakeR.getId("drawable", options.icon)
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
}
|