Twelve: Implement playback resumption
Paves the way to restore old queue/playback state
when the app is loaded after it has been killed
Change-Id: Ica0a2190ed8b9b226320a751be7d68b7a8b7c9f5
diff --git a/app/schemas/org.lineageos.twelve.database.TwelveDatabase/2.json b/app/schemas/org.lineageos.twelve.database.TwelveDatabase/2.json
new file mode 100644
index 0000000..e688f18
--- /dev/null
+++ b/app/schemas/org.lineageos.twelve.database.TwelveDatabase/2.json
@@ -0,0 +1,257 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 2,
+ "identityHash": "58b448c852bab0350bdd5a3eac0df202",
+ "entities": [
+ {
+ "tableName": "Playlist",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`playlist_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `last_modified` INTEGER NOT NULL, `track_count` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "playlist_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastModified",
+ "columnName": "last_modified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackCount",
+ "columnName": "track_count",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "playlist_id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`item_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `audio_uri` TEXT NOT NULL, `count` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "item_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "audioUri",
+ "columnName": "audio_uri",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "count",
+ "columnName": "count",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "item_id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_Item_audio_uri",
+ "unique": true,
+ "columnNames": [
+ "audio_uri"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Item_audio_uri` ON `${TABLE_NAME}` (`audio_uri`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "PlaylistItemCrossRef",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`playlist_id` INTEGER NOT NULL, `item_id` INTEGER NOT NULL, `last_modified` INTEGER NOT NULL, PRIMARY KEY(`playlist_id`, `item_id`), FOREIGN KEY(`playlist_id`) REFERENCES `Playlist`(`playlist_id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`item_id`) REFERENCES `Item`(`item_id`) ON UPDATE CASCADE ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "itemId",
+ "columnName": "item_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastModified",
+ "columnName": "last_modified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "playlist_id",
+ "item_id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_PlaylistItemCrossRef_item_id",
+ "unique": false,
+ "columnNames": [
+ "item_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_PlaylistItemCrossRef_item_id` ON `${TABLE_NAME}` (`item_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "Playlist",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "playlist_id"
+ ],
+ "referencedColumns": [
+ "playlist_id"
+ ]
+ },
+ {
+ "table": "Item",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "item_id"
+ ],
+ "referencedColumns": [
+ "item_id"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "ResumptionPlaylist",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`resumption_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `start_index` INTEGER NOT NULL, `start_position_ms` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "resumption_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "startIndex",
+ "columnName": "start_index",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "startPositionMs",
+ "columnName": "start_position_ms",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "resumption_id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ResumptionItem",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`playlist_index` INTEGER NOT NULL, `resumption_playlist_id` INTEGER NOT NULL, `media_id` TEXT NOT NULL, PRIMARY KEY(`playlist_index`), FOREIGN KEY(`resumption_playlist_id`) REFERENCES `ResumptionPlaylist`(`resumption_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "index",
+ "columnName": "playlist_index",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "resumptionPlaylistId",
+ "columnName": "resumption_playlist_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mediaId",
+ "columnName": "media_id",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "playlist_index"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_ResumptionItem_playlist_index",
+ "unique": true,
+ "columnNames": [
+ "playlist_index"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ResumptionItem_playlist_index` ON `${TABLE_NAME}` (`playlist_index`)"
+ },
+ {
+ "name": "index_ResumptionItem_resumption_playlist_id",
+ "unique": false,
+ "columnNames": [
+ "resumption_playlist_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_ResumptionItem_resumption_playlist_id` ON `${TABLE_NAME}` (`resumption_playlist_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "ResumptionPlaylist",
+ "onDelete": "CASCADE",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "resumption_playlist_id"
+ ],
+ "referencedColumns": [
+ "resumption_id"
+ ]
+ }
+ ]
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '58b448c852bab0350bdd5a3eac0df202')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/lineageos/twelve/TwelveApplication.kt b/app/src/main/java/org/lineageos/twelve/TwelveApplication.kt
index afb705a..c6f12d5 100644
--- a/app/src/main/java/org/lineageos/twelve/TwelveApplication.kt
+++ b/app/src/main/java/org/lineageos/twelve/TwelveApplication.kt
@@ -11,11 +11,13 @@
import com.google.android.material.color.DynamicColors
import org.lineageos.twelve.database.TwelveDatabase
import org.lineageos.twelve.repositories.MediaRepository
+import org.lineageos.twelve.repositories.ResumptionPlaylistRepository
@androidx.annotation.OptIn(UnstableApi::class)
class TwelveApplication : Application() {
private val database by lazy { TwelveDatabase.getInstance(applicationContext) }
val mediaRepository by lazy { MediaRepository(applicationContext, database) }
+ val resumptionPlaylistRepository by lazy { ResumptionPlaylistRepository(database) }
val audioSessionId by lazy { Util.generateAudioSessionIdV21(applicationContext) }
override fun onCreate() {
diff --git a/app/src/main/java/org/lineageos/twelve/database/TwelveDatabase.kt b/app/src/main/java/org/lineageos/twelve/database/TwelveDatabase.kt
index c54709f..e125ed5 100644
--- a/app/src/main/java/org/lineageos/twelve/database/TwelveDatabase.kt
+++ b/app/src/main/java/org/lineageos/twelve/database/TwelveDatabase.kt
@@ -6,6 +6,7 @@
package org.lineageos.twelve.database
import android.content.Context
+import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@@ -15,17 +16,28 @@
import org.lineageos.twelve.database.dao.PlaylistDao
import org.lineageos.twelve.database.dao.PlaylistItemCrossRefDao
import org.lineageos.twelve.database.dao.PlaylistWithItemsDao
+import org.lineageos.twelve.database.dao.ResumptionPlaylistDao
import org.lineageos.twelve.database.entities.Item
import org.lineageos.twelve.database.entities.Playlist
import org.lineageos.twelve.database.entities.PlaylistItemCrossRef
+import org.lineageos.twelve.database.entities.ResumptionItem
+import org.lineageos.twelve.database.entities.ResumptionPlaylist
@Database(
entities = [
+ /* Playlist */
Playlist::class,
Item::class,
PlaylistItemCrossRef::class,
+
+ /* Resumption */
+ ResumptionItem::class,
+ ResumptionPlaylist::class,
],
- version = 1,
+ version = 2,
+ autoMigrations = [
+ AutoMigration(from = 1, to = 2),
+ ],
)
@TypeConverters(UriConverter::class)
abstract class TwelveDatabase : RoomDatabase() {
@@ -33,6 +45,7 @@
abstract fun getPlaylistDao(): PlaylistDao
abstract fun getPlaylistItemCrossRefDao(): PlaylistItemCrossRefDao
abstract fun getPlaylistWithItemsDao(): PlaylistWithItemsDao
+ abstract fun getResumptionPlaylistDao(): ResumptionPlaylistDao
companion object {
@Volatile
diff --git a/app/src/main/java/org/lineageos/twelve/database/dao/ResumptionPlaylistDao.kt b/app/src/main/java/org/lineageos/twelve/database/dao/ResumptionPlaylistDao.kt
new file mode 100644
index 0000000..e56b933
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/database/dao/ResumptionPlaylistDao.kt
@@ -0,0 +1,78 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.twelve.database.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import androidx.room.Transaction
+import org.lineageos.twelve.database.entities.ResumptionPlaylistWithMediaItems
+
+@Dao
+@Suppress("FunctionName")
+interface ResumptionPlaylistDao {
+ /**
+ * Clear all resumption playlists.
+ */
+ @Query("DELETE FROM ResumptionPlaylist")
+ suspend fun _clearResumptionPlaylists()
+
+ /**
+ * Insert a resumption playlist.
+ */
+ @Query(
+ """
+ INSERT INTO ResumptionPlaylist (start_index, start_position_ms)
+ VALUES (:startIndex, :startPositionMs)
+ """
+ )
+ suspend fun _createResumptionPlaylist(startIndex: Int, startPositionMs: Long): Long
+
+ /**
+ * Add an item to a resumption playlist.
+ */
+ @Query(
+ """
+ INSERT INTO ResumptionItem (playlist_index, resumption_playlist_id, media_id)
+ VALUES (:index, :resumptionPlaylistId, :mediaItem)
+ """
+ )
+ suspend fun _addItemToResumptionPlaylist(
+ index: Long,
+ resumptionPlaylistId: Long,
+ mediaItem: String
+ )
+
+ /**
+ * Creates a new resumption playlist given a list of media items.
+ */
+ @Transaction
+ suspend fun createResumptionPlaylist(
+ startIndex: Int,
+ startPositionMs: Long,
+ mediaItems: List<String>
+ ) {
+ _clearResumptionPlaylists()
+
+ val id = _createResumptionPlaylist(startIndex, startPositionMs)
+
+ mediaItems.forEachIndexed { index, it ->
+ _addItemToResumptionPlaylist(index.toLong(), id, it)
+ }
+ }
+
+ /**
+ * Get resumption playlist with items
+ */
+ @Transaction
+ @Query("SELECT * FROM ResumptionPlaylist LIMIT 1")
+ suspend fun getResumptionPlaylistWithItems(): ResumptionPlaylistWithMediaItems?
+
+ /**
+ * Update resumption playlist.
+ */
+ @Query("UPDATE ResumptionPlaylist SET start_index = :currentMediaItemIndex, start_position_ms = :currentPosition")
+ suspend fun updateResumptionPlaylist(currentMediaItemIndex: Int, currentPosition: Long): Int
+}
diff --git a/app/src/main/java/org/lineageos/twelve/database/entities/ResumptionItem.kt b/app/src/main/java/org/lineageos/twelve/database/entities/ResumptionItem.kt
new file mode 100644
index 0000000..bcdb9ba
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/database/entities/ResumptionItem.kt
@@ -0,0 +1,40 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.twelve.database.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.ForeignKey
+import androidx.room.Index
+import androidx.room.PrimaryKey
+
+/**
+ * Resumption item.
+ *
+ * @param playlistIndex Index of this item in the playlist
+ * @param resumptionPlaylistId ID of the resumption playlist, this is only needed to easily build a
+ * [ResumptionPlaylistWithMediaItems]
+ * @param mediaId ID of the media item
+ */
+@Entity(
+ indices = [
+ Index(value = ["playlist_index"], unique = true),
+ Index(value = ["resumption_playlist_id"]),
+ ],
+ foreignKeys = [
+ ForeignKey(
+ entity = ResumptionPlaylist::class,
+ parentColumns = ["resumption_id"],
+ childColumns = ["resumption_playlist_id"],
+ onDelete = ForeignKey.CASCADE,
+ ),
+ ],
+)
+data class ResumptionItem(
+ @PrimaryKey @ColumnInfo(name = "playlist_index") val playlistIndex: Long,
+ @ColumnInfo(name = "resumption_playlist_id") val resumptionPlaylistId: Long,
+ @ColumnInfo(name = "media_id") val mediaId: String,
+)
diff --git a/app/src/main/java/org/lineageos/twelve/database/entities/ResumptionPlaylist.kt b/app/src/main/java/org/lineageos/twelve/database/entities/ResumptionPlaylist.kt
new file mode 100644
index 0000000..28bf3a0
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/database/entities/ResumptionPlaylist.kt
@@ -0,0 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.twelve.database.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity
+data class ResumptionPlaylist(
+ @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "resumption_id") val id: Long,
+ @ColumnInfo(name = "start_index") val startIndex: Int,
+ @ColumnInfo(name = "start_position_ms") val startPositionMs: Long,
+)
diff --git a/app/src/main/java/org/lineageos/twelve/database/entities/ResumptionPlaylistWithMediaItems.kt b/app/src/main/java/org/lineageos/twelve/database/entities/ResumptionPlaylistWithMediaItems.kt
new file mode 100644
index 0000000..c3cf682
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/database/entities/ResumptionPlaylistWithMediaItems.kt
@@ -0,0 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.twelve.database.entities
+
+import androidx.room.Embedded
+import androidx.room.Relation
+
+data class ResumptionPlaylistWithMediaItems(
+ @Embedded val resumptionPlaylist: ResumptionPlaylist,
+ @Relation(
+ parentColumn = "resumption_id",
+ entityColumn = "resumption_playlist_id",
+ ) val items: List<ResumptionItem>,
+)
diff --git a/app/src/main/java/org/lineageos/twelve/models/ResumptionPlaylist.kt b/app/src/main/java/org/lineageos/twelve/models/ResumptionPlaylist.kt
new file mode 100644
index 0000000..9d83ef4
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/models/ResumptionPlaylist.kt
@@ -0,0 +1,23 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.twelve.models
+
+/**
+ * A resumption playlist.
+ *
+ * @param mediaItemIds The list of audio media item IDs
+ * @param startIndex The start index, in the range [0..mediaItemIds.size)
+ * @param startPositionMs The playback position in milliseconds
+ */
+data class ResumptionPlaylist(
+ val mediaItemIds: List<String>,
+ val startIndex: Int = 0,
+ val startPositionMs: Long = 0L,
+) {
+ init {
+ require(startIndex in mediaItemIds.indices) { "Invalid start index" }
+ }
+}
diff --git a/app/src/main/java/org/lineageos/twelve/repositories/ResumptionPlaylistRepository.kt b/app/src/main/java/org/lineageos/twelve/repositories/ResumptionPlaylistRepository.kt
new file mode 100644
index 0000000..1f34a2b
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/repositories/ResumptionPlaylistRepository.kt
@@ -0,0 +1,50 @@
+package org.lineageos.twelve.repositories
+
+import org.lineageos.twelve.database.TwelveDatabase
+import org.lineageos.twelve.models.ResumptionPlaylist
+
+/**
+ * Manages the playlist used when the user wants to resume playback from the last queue.
+ */
+class ResumptionPlaylistRepository(val database: TwelveDatabase) {
+ /**
+ * Get the last resumption playlist or an empty one.
+ */
+ suspend fun getResumptionPlaylist() =
+ database.getResumptionPlaylistDao().getResumptionPlaylistWithItems()?.let {
+ ResumptionPlaylist(
+ it.items.sortedBy { item ->
+ item.playlistIndex
+ }.map { item ->
+ item.mediaId
+ },
+ it.resumptionPlaylist.startIndex,
+ it.resumptionPlaylist.startPositionMs,
+ )
+ } ?: ResumptionPlaylist(emptyList())
+
+ /**
+ * When the user changes the queue, create a new resumption playlist.
+ *
+ * @param mediaIds The list of audio media item IDs
+ * @param startIndex The start index
+ * @param startPositionMs The playback position in milliseconds
+ */
+ suspend fun onMediaItemsChanged(
+ mediaIds: List<String>,
+ startIndex: Int,
+ startPositionMs: Long,
+ ) = database.getResumptionPlaylistDao().createResumptionPlaylist(
+ startIndex,
+ startPositionMs,
+ mediaIds,
+ )
+
+ suspend fun onPlaybackPositionChanged(
+ startIndex: Int,
+ startPositionMs: Long,
+ ) = database.getResumptionPlaylistDao().updateResumptionPlaylist(
+ startIndex,
+ startPositionMs,
+ )
+}
diff --git a/app/src/main/java/org/lineageos/twelve/services/MediaRepositoryTree.kt b/app/src/main/java/org/lineageos/twelve/services/MediaRepositoryTree.kt
index 3f233f3..d1bc190 100644
--- a/app/src/main/java/org/lineageos/twelve/services/MediaRepositoryTree.kt
+++ b/app/src/main/java/org/lineageos/twelve/services/MediaRepositoryTree.kt
@@ -33,12 +33,6 @@
fun getRootMediaItem() = rootMediaItem
/**
- * Get the resumption playlist of the tree.
- * TODO
- */
- fun getResumptionPlaylist() = listOf<MediaItem>()
-
- /**
* Given a media ID, gets it's corresponding media item.
*/
suspend fun getItem(mediaId: String) = when (mediaId) {
diff --git a/app/src/main/java/org/lineageos/twelve/services/PlaybackService.kt b/app/src/main/java/org/lineageos/twelve/services/PlaybackService.kt
index d93dca5..d97bfec 100644
--- a/app/src/main/java/org/lineageos/twelve/services/PlaybackService.kt
+++ b/app/src/main/java/org/lineageos/twelve/services/PlaybackService.kt
@@ -27,6 +27,7 @@
import androidx.media3.session.MediaSession
import androidx.media3.session.SessionError
import kotlinx.coroutines.guava.future
+import kotlinx.coroutines.launch
import org.lineageos.twelve.MainActivity
import org.lineageos.twelve.R
import org.lineageos.twelve.TwelveApplication
@@ -46,14 +47,46 @@
)
}
+ private val resumptionPlaylistRepository by lazy {
+ (application as TwelveApplication).resumptionPlaylistRepository
+ }
+
private val mediaLibrarySessionCallback = object : MediaLibrarySession.Callback {
override fun onPlaybackResumption(
mediaSession: MediaSession,
controller: MediaSession.ControllerInfo
) = lifecycle.coroutineScope.future {
- MediaSession.MediaItemsWithStartPosition(
- mediaRepositoryTree.getResumptionPlaylist(), 0, 0
- )
+ val resumptionPlaylist = resumptionPlaylistRepository.getResumptionPlaylist()
+
+ var startIndex = resumptionPlaylist.startIndex
+ var startPositionMs = resumptionPlaylist.startPositionMs
+
+ val mediaItems = resumptionPlaylist.mediaItemIds.mapIndexed { index, itemId ->
+ when (val mediaItem = mediaRepositoryTree.getItem(itemId)) {
+ null -> {
+ if (index == resumptionPlaylist.startIndex) {
+ // The playback position is now invalid
+ startPositionMs = 0
+
+ // Let's try the next item, this is done automatically since
+ // the next item will take this item's index
+ } else if (index < resumptionPlaylist.startIndex) {
+ // The missing media is before the start index, we have to offset
+ // the start by 1 entry
+ startIndex -= 1
+ }
+
+ null
+ }
+
+ else -> mediaItem
+ }
+ }.filterNotNull()
+
+ // Shouldn't be needed, but just to be sure
+ startIndex = startIndex.coerceIn(0, mediaItems.size - 1)
+
+ MediaSession.MediaItemsWithStartPosition(mediaItems, startIndex, startPositionMs)
}
override fun onGetLibraryRoot(
@@ -102,8 +135,18 @@
startIndex: Int,
startPositionMs: Long,
) = lifecycle.coroutineScope.future {
+ val resolvedMediaItems = mediaRepositoryTree.resolveMediaItems(mediaItems)
+
+ launch {
+ resumptionPlaylistRepository.onMediaItemsChanged(
+ resolvedMediaItems.map { it.mediaId },
+ startIndex,
+ startPositionMs,
+ )
+ }
+
MediaSession.MediaItemsWithStartPosition(
- mediaRepositoryTree.resolveMediaItems(mediaItems),
+ resolvedMediaItems,
startIndex,
startPositionMs
)
@@ -221,6 +264,16 @@
closeAudioEffectSession()
}
}
+
+ // Update startIndex and startPositionMs in resumption playlist.
+ if (events.containsAny(Player.EVENT_MEDIA_ITEM_TRANSITION)) {
+ lifecycle.coroutineScope.launch {
+ resumptionPlaylistRepository.onPlaybackPositionChanged(
+ player.currentMediaItemIndex,
+ player.currentPosition
+ )
+ }
+ }
}
private fun openAudioEffectSession() {