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.
Files changed (53) hide show
  1. package/README.md +133 -127
  2. package/android/build.gradle +31 -18
  3. package/android/gradle/wrapper/gradle-wrapper.properties +1 -1
  4. package/android/src/main/java/org/dwbn/plugins/playlist/AudioMediaItemFactory.java +96 -0
  5. package/android/src/main/java/org/dwbn/plugins/playlist/PlaylistPlugin.kt +3 -3
  6. package/android/src/main/java/org/dwbn/plugins/playlist/RmxAudioPlayer.java +145 -188
  7. package/android/src/main/java/org/dwbn/plugins/playlist/RmxPlaybackErrorMapper.kt +26 -0
  8. package/android/src/main/java/org/dwbn/plugins/playlist/data/AudioTrack.kt +28 -28
  9. package/android/src/main/java/org/dwbn/plugins/playlist/handoff/VideoPlayerBridge.kt +102 -0
  10. package/android/src/main/java/org/dwbn/plugins/playlist/manager/PlaybackProgress.kt +8 -0
  11. package/android/src/main/java/org/dwbn/plugins/playlist/manager/PlaylistManager.kt +418 -183
  12. package/android/src/main/java/org/dwbn/plugins/playlist/manager/PlaylistPlaybackPolicy.kt +98 -0
  13. package/android/src/main/java/org/dwbn/plugins/playlist/manager/RmxPlaybackState.kt +11 -0
  14. package/android/src/main/java/org/dwbn/plugins/playlist/playlist/AudioPlaylistHandler.java +107 -211
  15. package/android/src/main/java/org/dwbn/plugins/playlist/service/GlideBitmapLoader.kt +121 -0
  16. package/android/src/main/java/org/dwbn/plugins/playlist/service/HandoffForwardingPlayer.kt +185 -0
  17. package/android/src/main/java/org/dwbn/plugins/playlist/service/MediaNotificationPolicy.kt +253 -0
  18. package/android/src/main/java/org/dwbn/plugins/playlist/service/MediaService.kt +260 -68
  19. package/android/src/main/java/org/dwbn/plugins/playlist/service/PlaylistMediaSessionCallback.kt +39 -0
  20. package/android/src/test/java/org/dwbn/plugins/playlist/AudioMediaItemFactoryMimeTest.kt +98 -0
  21. package/android/src/test/java/org/dwbn/plugins/playlist/AudioMediaItemFactoryTest.java +71 -0
  22. package/android/src/test/java/org/dwbn/plugins/playlist/data/AudioTrackTest.kt +53 -0
  23. package/android/src/test/java/org/dwbn/plugins/playlist/handoff/VideoPlayerBridgeTest.kt +84 -0
  24. package/android/src/test/java/org/dwbn/plugins/playlist/manager/PlaylistPlaybackPolicyTest.kt +128 -0
  25. package/android/src/test/java/org/dwbn/plugins/playlist/playlist/AudioPlaylistHandlerTest.kt +145 -0
  26. package/android/src/test/java/org/dwbn/plugins/playlist/service/GlideBitmapLoaderTest.kt +76 -0
  27. package/android/src/test/java/org/dwbn/plugins/playlist/service/HandoffForwardingPlayerTest.kt +282 -0
  28. package/android/src/test/java/org/dwbn/plugins/playlist/service/MediaNotificationPolicyTest.kt +285 -0
  29. package/android/src/test/java/org/dwbn/plugins/playlist/service/VideoHandoffNotificationCommandsTest.kt +54 -0
  30. package/android/src/test/java/org/dwbn/plugins/playlist/service/VideoHandoffNotificationPolicyTest.kt +47 -0
  31. package/dist/docs.json +12 -846
  32. package/dist/esm/RmxAudioPlayer.d.ts +2 -2
  33. package/dist/esm/RmxAudioPlayer.js.map +1 -1
  34. package/dist/esm/definitions.d.ts +7 -7
  35. package/dist/esm/interfaces.d.ts +7 -1
  36. package/dist/esm/utils.d.ts +1 -1
  37. package/dist/esm/utils.js +2 -2
  38. package/dist/esm/utils.js.map +1 -1
  39. package/dist/esm/web.d.ts +3 -3
  40. package/dist/esm/web.js +5 -5
  41. package/dist/esm/web.js.map +1 -1
  42. package/dist/plugin.cjs.js +7 -7
  43. package/dist/plugin.cjs.js.map +1 -1
  44. package/dist/plugin.js +7 -7
  45. package/dist/plugin.js.map +1 -1
  46. package/ios/Sources/PlaylistPlugin/AVBidirectionalQueuePlayer.swift +45 -6
  47. package/ios/Sources/PlaylistPlugin/AudioTrack.swift +15 -2
  48. package/ios/Tests/PlaylistPluginTests/PositionResumeTests.swift +82 -0
  49. package/package.json +15 -19
  50. package/android/src/main/java/org/dwbn/plugins/playlist/notification/PlaylistNotificationProvider.kt +0 -26
  51. package/android/src/main/java/org/dwbn/plugins/playlist/playlist/AudioApi.kt +0 -114
  52. package/android/src/main/java/org/dwbn/plugins/playlist/playlist/BaseMediaApi.kt +0 -36
  53. 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.11.2",
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 . --ext ts",
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": ">=18.0.0"
37
+ "node": ">=22.0.0"
38
38
  },
39
39
  "devDependencies": {
40
- "@capacitor/android": "^8.0.0",
41
- "@capacitor/core": "^8.0.0",
40
+ "@capacitor/android": "^8.5.2",
41
+ "@capacitor/core": "^8.5.2",
42
42
  "@capacitor/docgen": "^0.3.1",
43
- "@capacitor/ios": "^8.0.0",
44
- "@ionic/eslint-config": "^0.4.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
- "@types/hls.js": "^0.13.2",
48
- "eslint": "^8.57.1",
49
- "jsdom": "^29.1.1",
50
- "prettier": "^3.6.2",
51
- "prettier-plugin-java": "^2.7.7",
52
- "rimraf": "^6.1.0",
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": "^5.9.3",
56
- "vitest": "^4.1.10"
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",
@@ -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
- }