Cycle through subtitle messages under Android Auto

This commit is contained in:
Robert Stone 2024-08-07 21:56:07 -07:00 committed by Duncan McNamara
parent cb1fc7d8e2
commit 264fe69be7
3 changed files with 36 additions and 8 deletions

View File

@ -85,6 +85,11 @@
<string name="albums">Albums</string> <string name="albums">Albums</string>
<string name="tracks">Tracks</string> <string name="tracks">Tracks</string>
<plurals name="saved_bookmarks_quantity">
<item quantity="one">1 Saved Bookmark</item>
<item quantity="other">%d Saved Bookmarks</item>
</plurals>
<plurals name="albums_quantity"> <plurals name="albums_quantity">
<item quantity="one">1 album</item> <item quantity="one">1 album</item>
<item quantity="other">%d albums</item> <item quantity="other">%d albums</item>

View File

@ -181,7 +181,8 @@ internal class MediaSessionCallback(private val playbackService: PlaybackService
val bookmark = it.addBookmark(playbackService.getTime()) val bookmark = it.addBookmark(playbackService.getTime())
val bookmarkName = context.getString(R.string.bookmark_default_name, Tools.millisToString(playbackService.getTime())) val bookmarkName = context.getString(R.string.bookmark_default_name, Tools.millisToString(playbackService.getTime()))
bookmark?.setName(bookmarkName) bookmark?.setName(bookmarkName)
playbackService.displayPlaybackMessage(R.string.saved, bookmarkName) playbackService.displaySubtitleMessage(context.getString(R.string.saved, bookmarkName),
context.resources.getQuantityString(R.plurals.saved_bookmarks_quantity, it.bookmarks.size, it.bookmarks.size))
} }
} }
} }
@ -306,8 +307,10 @@ internal class MediaSessionCallback(private val playbackService: PlaybackService
} }
private fun checkForSeekFailure(forward: Boolean) { private fun checkForSeekFailure(forward: Boolean) {
if (playbackService.playlistManager.player.lastPosition == 0.0f && (forward || playbackService.getTime() > 0)) if (playbackService.playlistManager.player.lastPosition == 0.0f && (forward || playbackService.getTime() > 0)) {
playbackService.displayPlaybackMessage(R.string.unseekable_stream) val context = playbackService.applicationContext
playbackService.displaySubtitleMessage(context.getString(R.string.unseekable_stream))
}
} }
override fun onPlayFromUri(uri: Uri?, extras: Bundle?) = playbackService.loadUri(uri) override fun onPlayFromUri(uri: Uri?, extras: Bundle?) = playbackService.loadUri(uri)

View File

@ -211,7 +211,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc
private lateinit var artworkMap: MutableMap<String, Uri> private lateinit var artworkMap: MutableMap<String, Uri>
private val callbacks = mutableListOf<Callback>() private val callbacks = mutableListOf<Callback>()
private val subtitleMessage = ArrayDeque<String>(1) private val subtitleMessage = ArrayDeque<Pair<String,Long>>(1)
private lateinit var cbActor: SendChannel<CbAction> private lateinit var cbActor: SendChannel<CbAction>
var detectHeadset = true var detectHeadset = true
var headsetInserted = false var headsetInserted = false
@ -1136,7 +1136,8 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc
val length = length val length = length
lastLength = length lastLength = length
val chapterTitle = if (lastChaptersCount > 0) getCurrentChapter() else null val chapterTitle = if (lastChaptersCount > 0) getCurrentChapter() else null
val displayMsg = subtitleMessage.poll() val displayMsg = getSubtitleMessage()
displayMsg?.let { scheduler.scheduleAction(UPDATE_META, 5_000L) }
val bob = withContext(Dispatchers.Default) { val bob = withContext(Dispatchers.Default) {
val carMode = isCarMode() val carMode = isCarMode()
val title = media.nowPlaying ?: media.title val title = media.nowPlaying ?: media.title
@ -1322,6 +1323,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc
} }
fun notifyTrackChanged() { fun notifyTrackChanged() {
subtitleMessage.clear()
updateMetadata() updateMetadata()
updateWidget() updateWidget()
broadcastMetadata() broadcastMetadata()
@ -1557,12 +1559,28 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc
mediaSession.setPlaybackState(playbackState) mediaSession.setPlaybackState(playbackState)
} }
fun displayPlaybackMessage(@StringRes resId: Int, vararg formatArgs: String) { fun displaySubtitleMessage(vararg messages: String) {
val ctx = this@PlaybackService var endTime = System.currentTimeMillis()
subtitleMessage.push(ctx.getString(resId, *formatArgs)) subtitleMessage.clear()
messages.forEach { msg ->
endTime += 5000L
msg?.let { subtitleMessage.addLast(Pair(it, endTime)) }
}
updateMetadata() updateMetadata()
} }
private fun getSubtitleMessage(): String? {
return subtitleMessage.peek()?.let {
when {
System.currentTimeMillis() > it.second -> {
subtitleMessage.poll()
subtitleMessage.peek()?.first
}
else -> it.first
}
}
}
@MainThread @MainThread
fun load(media: MediaWrapper, position: Int = 0) = load(listOf(media), position) fun load(media: MediaWrapper, position: Int = 0) = load(listOf(media), position)
@ -1824,6 +1842,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc
currentToast = Toast.makeText(applicationContext, text, duration) currentToast = Toast.makeText(applicationContext, text, duration)
currentToast?.show() currentToast?.show()
} }
UPDATE_META -> updateMetadata()
END_MEDIASESSION -> if (::mediaSession.isInitialized) mediaSession.isActive = false END_MEDIASESSION -> if (::mediaSession.isInitialized) mediaSession.isActive = false
} }
} }
@ -1946,6 +1965,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc
private const val SHOW_TOAST = "show_toast" private const val SHOW_TOAST = "show_toast"
private const val END_MEDIASESSION = "end_mediasession" private const val END_MEDIASESSION = "end_mediasession"
private const val UPDATE_META = "update_meta"
val playerSleepTime by lazy(LazyThreadSafetyMode.NONE) { MutableLiveData<Calendar?>().apply { value = null } } val playerSleepTime by lazy(LazyThreadSafetyMode.NONE) { MutableLiveData<Calendar?>().apply { value = null } }