Improve media position and duration handling
This commit is contained in:
parent
c6608eaa90
commit
14902ba085
@ -13,7 +13,9 @@ import android.media.session.MediaSession
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.os.SystemClock
|
||||||
import android.service.notification.StatusBarNotification
|
import android.service.notification.StatusBarNotification
|
||||||
|
import android.util.Log
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
@ -28,6 +30,7 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.awaitCancellation
|
import kotlinx.coroutines.awaitCancellation
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
@ -38,6 +41,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
|
|||||||
import kotlinx.coroutines.flow.firstOrNull
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.shareIn
|
import kotlinx.coroutines.flow.shareIn
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
@ -50,6 +54,7 @@ interface MusicService {
|
|||||||
val artist: Flow<String?>
|
val artist: Flow<String?>
|
||||||
val album: Flow<String?>
|
val album: Flow<String?>
|
||||||
val albumArt: Flow<Bitmap?>
|
val albumArt: Flow<Bitmap?>
|
||||||
|
val position: Flow<Long?>
|
||||||
val duration: Flow<Long?>
|
val duration: Flow<Long?>
|
||||||
|
|
||||||
val lastPlayerPackage: String?
|
val lastPlayerPackage: String?
|
||||||
@ -141,26 +146,17 @@ internal class MusicServiceImpl(
|
|||||||
}
|
}
|
||||||
}.shareIn(scope, SharingStarted.WhileSubscribed(), 1)
|
}.shareIn(scope, SharingStarted.WhileSubscribed(), 1)
|
||||||
|
|
||||||
override val playbackState: SharedFlow<PlaybackState> = channelFlow {
|
private val currentState: SharedFlow<android.media.session.PlaybackState?> = channelFlow {
|
||||||
currentMediaController.collectLatest { controller ->
|
currentMediaController.collectLatest { controller ->
|
||||||
if (controller == null) return@collectLatest send(PlaybackState.Stopped)
|
if (controller == null) {
|
||||||
send(
|
send(null)
|
||||||
when (controller.playbackState?.state) {
|
return@collectLatest
|
||||||
android.media.session.PlaybackState.STATE_PLAYING -> PlaybackState.Playing
|
}
|
||||||
android.media.session.PlaybackState.STATE_PAUSED -> PlaybackState.Paused
|
send(controller.playbackState)
|
||||||
else -> PlaybackState.Stopped
|
|
||||||
}
|
|
||||||
)
|
|
||||||
val callback = object : MediaController.Callback() {
|
val callback = object : MediaController.Callback() {
|
||||||
override fun onPlaybackStateChanged(state: android.media.session.PlaybackState?) {
|
override fun onPlaybackStateChanged(state: android.media.session.PlaybackState?) {
|
||||||
super.onPlaybackStateChanged(state)
|
super.onPlaybackStateChanged(state)
|
||||||
trySend(
|
trySend(state)
|
||||||
when (state?.state) {
|
|
||||||
android.media.session.PlaybackState.STATE_PLAYING -> PlaybackState.Playing
|
|
||||||
android.media.session.PlaybackState.STATE_PAUSED -> PlaybackState.Paused
|
|
||||||
else -> PlaybackState.Stopped
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -172,6 +168,51 @@ internal class MusicServiceImpl(
|
|||||||
}
|
}
|
||||||
}.shareIn(scope, SharingStarted.WhileSubscribed(), 1)
|
}.shareIn(scope, SharingStarted.WhileSubscribed(), 1)
|
||||||
|
|
||||||
|
override val playbackState: SharedFlow<PlaybackState> = channelFlow {
|
||||||
|
currentState.collectLatest { state ->
|
||||||
|
if (state == null) {
|
||||||
|
send(PlaybackState.Stopped)
|
||||||
|
return@collectLatest
|
||||||
|
}
|
||||||
|
when (state.state) {
|
||||||
|
android.media.session.PlaybackState.STATE_PLAYING -> send(PlaybackState.Playing)
|
||||||
|
android.media.session.PlaybackState.STATE_PAUSED -> send(PlaybackState.Paused)
|
||||||
|
android.media.session.PlaybackState.STATE_STOPPED -> send(PlaybackState.Stopped)
|
||||||
|
else -> send(PlaybackState.Stopped)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.shareIn(scope, SharingStarted.WhileSubscribed(), 1)
|
||||||
|
|
||||||
|
private var lastPosition: Long? = null
|
||||||
|
get() {
|
||||||
|
if (field == null) {
|
||||||
|
field = preferences.getLong(PREFS_KEY_POSITION, -1).takeIf { it >= 0 }
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
preferences.edit {
|
||||||
|
putLong(PREFS_KEY_POSITION, value ?: -1)
|
||||||
|
}
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override val position: SharedFlow<Long?> = channelFlow {
|
||||||
|
currentState.collectLatest { state ->
|
||||||
|
if (state == null || state.state != android.media.session.PlaybackState.STATE_PLAYING) {
|
||||||
|
send(lastPosition)
|
||||||
|
return@collectLatest
|
||||||
|
}
|
||||||
|
while(isActive) {
|
||||||
|
val offset = SystemClock.elapsedRealtime() - state.lastPositionUpdateTime
|
||||||
|
val position = state.position + offset
|
||||||
|
lastPosition = position
|
||||||
|
send(position)
|
||||||
|
delay(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.shareIn(scope, SharingStarted.WhileSubscribed(), 1)
|
||||||
|
|
||||||
|
|
||||||
private var lastTitle: String? = null
|
private var lastTitle: String? = null
|
||||||
get() {
|
get() {
|
||||||
@ -312,13 +353,28 @@ internal class MusicServiceImpl(
|
|||||||
}
|
}
|
||||||
}.shareIn(scope, SharingStarted.WhileSubscribed(), 1)
|
}.shareIn(scope, SharingStarted.WhileSubscribed(), 1)
|
||||||
|
|
||||||
|
private var lastDuration: Long? = null
|
||||||
|
get() {
|
||||||
|
if (field == null) {
|
||||||
|
field = preferences.getLong(PREFS_KEY_DURATION, -1).takeIf { it >= 0 }
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
preferences.edit {
|
||||||
|
putLong(PREFS_KEY_DURATION, value ?: -1)
|
||||||
|
}
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
override val duration: Flow<Long?> = channelFlow {
|
override val duration: Flow<Long?> = channelFlow {
|
||||||
currentMetadata.collectLatest { metadata ->
|
currentMetadata.collectLatest { metadata ->
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
send(null)
|
send(lastDuration)
|
||||||
return@collectLatest
|
return@collectLatest
|
||||||
}
|
}
|
||||||
val duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION)
|
val duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION)
|
||||||
|
lastDuration = duration
|
||||||
send(duration.takeIf { it > 0 })
|
send(duration.takeIf { it > 0 })
|
||||||
}
|
}
|
||||||
}.shareIn(scope, SharingStarted.WhileSubscribed(), 1)
|
}.shareIn(scope, SharingStarted.WhileSubscribed(), 1)
|
||||||
@ -511,6 +567,8 @@ internal class MusicServiceImpl(
|
|||||||
|
|
||||||
private const val PREFS = "music"
|
private const val PREFS = "music"
|
||||||
private const val PREFS_KEY_TITLE = "title"
|
private const val PREFS_KEY_TITLE = "title"
|
||||||
|
private const val PREFS_KEY_DURATION = "duration"
|
||||||
|
private const val PREFS_KEY_POSITION = "position"
|
||||||
private const val PREFS_KEY_ARTIST = "artist"
|
private const val PREFS_KEY_ARTIST = "artist"
|
||||||
private const val PREFS_KEY_ALBUM = "album"
|
private const val PREFS_KEY_ALBUM = "album"
|
||||||
private const val PREFS_KEY_ALBUM_ART = "album_art"
|
private const val PREFS_KEY_ALBUM_ART = "album_art"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user