-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
31 changed files
with
622 additions
and
605 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
app/src/main/java/ru/stersh/youamp/player/ApiSonicPlayQueueSyncer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package ru.stersh.youamp.player | ||
|
||
import androidx.media3.common.Player | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.delay | ||
import kotlinx.coroutines.flow.collect | ||
import kotlinx.coroutines.flow.filterNotNull | ||
import kotlinx.coroutines.flow.flatMapLatest | ||
import kotlinx.coroutines.flow.flow | ||
import kotlinx.coroutines.withContext | ||
import ru.stersh.youamp.core.api.provider.ApiProvider | ||
import ru.stersh.youamp.shared.player.provider.PlayerProvider | ||
import ru.stersh.youamp.shared.player.utils.PlayerDispatcher | ||
import ru.stersh.youamp.shared.player.utils.mediaItems | ||
import ru.stersh.youamp.shared.player.utils.toMediaItem | ||
import timber.log.Timber | ||
|
||
internal class ApiSonicPlayQueueSyncer( | ||
private val playerProvider: PlayerProvider, | ||
private val apiProvider: ApiProvider | ||
) { | ||
|
||
suspend fun syncQueue() { | ||
apiProvider | ||
.flowApiId() | ||
.filterNotNull() | ||
.flatMapLatest { | ||
flow<Nothing> { | ||
val player = playerProvider.get() | ||
loadPlayQueue(player) | ||
var playQueue = getPlayQueue(player) | ||
while (true) { | ||
val currentPlayQueue = getPlayQueue(player) | ||
if (currentPlayQueue != playQueue) { | ||
syncPlayQueue(player) | ||
playQueue = currentPlayQueue | ||
} | ||
delay(SYNC_QUEUE_PERIOD) | ||
} | ||
} | ||
} | ||
.collect() | ||
} | ||
|
||
private suspend fun loadPlayQueue(player: Player) { | ||
val playQueue = withContext(Dispatchers.IO) { | ||
runCatching { | ||
apiProvider | ||
.getApi() | ||
.getPlayQueue() | ||
.playQueue | ||
} | ||
.onFailure { Timber.w(it) } | ||
.getOrNull() | ||
} ?: return | ||
|
||
val items = playQueue.entry.map { it.toMediaItem(apiProvider) } | ||
val currentIndex = items.indexOfFirst { it.mediaId == playQueue.current } | ||
val position = playQueue.position | ||
|
||
withContext(PlayerDispatcher) { | ||
if (currentIndex != -1) { | ||
player.setMediaItems(items, currentIndex, position ?: 0) | ||
} else { | ||
player.setMediaItems(items) | ||
} | ||
player.prepare() | ||
} | ||
} | ||
|
||
private suspend fun getPlayQueue(player: Player) = withContext(PlayerDispatcher) { | ||
val items = player.mediaItems.map { it.mediaId } | ||
val current = player.currentMediaItem?.mediaId | ||
val position = player.currentPosition | ||
return@withContext PlayQueue( | ||
items = items, | ||
current = current, | ||
position = position | ||
) | ||
} | ||
|
||
private suspend fun syncPlayQueue(player: Player) = withContext(PlayerDispatcher) { | ||
if (!player.isPlaying) { | ||
return@withContext | ||
} | ||
|
||
val items = player.mediaItems.map { it.mediaId } | ||
val current = player.currentMediaItem?.mediaId | ||
val position = player.currentPosition | ||
|
||
withContext(Dispatchers.IO) { | ||
runCatching { | ||
apiProvider | ||
.getApi() | ||
.savePlayQueue(items, current, position) | ||
}.onFailure { Timber.w(it) } | ||
} | ||
} | ||
|
||
private data class PlayQueue( | ||
val items: List<String>, | ||
val current: String?, | ||
val position: Long | ||
) | ||
|
||
companion object { | ||
private const val SYNC_QUEUE_PERIOD = 5000L | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
app/src/main/java/ru/stersh/youamp/player/PlayQueueSyncActivityCallback.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package ru.stersh.youamp.player | ||
|
||
import android.app.Activity | ||
import android.os.Bundle | ||
import androidx.lifecycle.lifecycleScope | ||
import kotlinx.coroutines.launch | ||
import ru.stersh.youamp.core.utils.EmptyActivityLifecycleCallback | ||
import ru.stersh.youamp.main.ui.MainActivity | ||
|
||
internal class PlayQueueSyncActivityCallback( | ||
private val apiSonicPlayQueueSyncer: ApiSonicPlayQueueSyncer | ||
) : EmptyActivityLifecycleCallback() { | ||
|
||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { | ||
if (activity !is MainActivity) { | ||
return | ||
} | ||
activity.lifecycleScope.launch { | ||
apiSonicPlayQueueSyncer.syncQueue() | ||
} | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
app/src/main/java/ru/stersh/youamp/player/PlayerProviderImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package ru.stersh.youamp.player | ||
|
||
import android.content.ComponentName | ||
import android.content.Context | ||
import androidx.media3.common.Player | ||
import androidx.media3.session.MediaController | ||
import androidx.media3.session.SessionToken | ||
import kotlinx.coroutines.guava.await | ||
import kotlinx.coroutines.withContext | ||
import ru.stersh.youamp.shared.player.android.MusicService | ||
import ru.stersh.youamp.shared.player.provider.PlayerProvider | ||
import ru.stersh.youamp.shared.player.utils.PlayerDispatcher | ||
|
||
internal class PlayerProviderImpl(private val context: Context) : PlayerProvider { | ||
|
||
override suspend fun get(): Player = withContext(PlayerDispatcher) { | ||
val sessionToken = SessionToken(context, ComponentName(context, MusicService::class.java)) | ||
return@withContext MediaController | ||
.Builder(context, sessionToken) | ||
.buildAsync() | ||
.await() | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
app/src/main/java/ru/stersh/youamp/player/ProgressSyncActivityCallback.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package ru.stersh.youamp.player | ||
|
||
import android.app.Activity | ||
import android.os.Bundle | ||
import androidx.lifecycle.lifecycleScope | ||
import kotlinx.coroutines.flow.filterNotNull | ||
import kotlinx.coroutines.flow.launchIn | ||
import kotlinx.coroutines.flow.onEach | ||
import kotlinx.coroutines.launch | ||
import kotlinx.coroutines.withContext | ||
import ru.stersh.youamp.core.api.provider.ApiProvider | ||
import ru.stersh.youamp.core.utils.EmptyActivityLifecycleCallback | ||
import ru.stersh.youamp.main.ui.MainActivity | ||
import ru.stersh.youamp.shared.player.progress.PlayerProgressStore | ||
import ru.stersh.youamp.shared.player.provider.PlayerProvider | ||
import ru.stersh.youamp.shared.player.utils.PlayerDispatcher | ||
|
||
internal class ProgressSyncActivityCallback( | ||
private val playerProgressStore: PlayerProgressStore, | ||
private val playerProvider: PlayerProvider, | ||
private val apiProvider: ApiProvider | ||
) : EmptyActivityLifecycleCallback() { | ||
private var scrobbleSender: ScrobbleSender? = null | ||
|
||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { | ||
if (activity !is MainActivity) { | ||
return | ||
} | ||
playerProgressStore | ||
.playerProgress() | ||
.filterNotNull() | ||
.onEach { progress -> | ||
val player = playerProvider.get() | ||
val currentItemId = withContext(PlayerDispatcher) { player.currentMediaItem?.mediaId } ?: return@onEach | ||
if (currentItemId != scrobbleSender?.id) { | ||
scrobbleSender = ScrobbleSender(currentItemId, apiProvider) | ||
} | ||
val sender = scrobbleSender ?: return@onEach | ||
if (progress.currentTimeMs > SEND_SCROBBLE_EVENT_TIME && !sender.scrobbleSent) { | ||
activity.lifecycleScope.launch { | ||
sender.trySendScrobble() | ||
} | ||
} | ||
if ((progress.currentTimeMs + SEND_SCROBBLE_EVENT_TIME) >= progress.totalTimeMs && !sender.submissionSent) { | ||
activity.lifecycleScope.launch { | ||
sender.trySendSubmission() | ||
} | ||
} | ||
} | ||
.launchIn(activity.lifecycleScope) | ||
} | ||
|
||
companion object { | ||
private const val SEND_SCROBBLE_EVENT_TIME = 5000L | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
core/utils/src/main/java/ru/stersh/youamp/core/utils/EmptyActivityLifecycleCallback.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package ru.stersh.youamp.core.utils | ||
|
||
import android.app.Activity | ||
import android.app.Application.ActivityLifecycleCallbacks | ||
import android.os.Bundle | ||
|
||
abstract class EmptyActivityLifecycleCallback : ActivityLifecycleCallbacks { | ||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} | ||
|
||
override fun onActivityStarted(activity: Activity) {} | ||
|
||
override fun onActivityResumed(activity: Activity) {} | ||
|
||
override fun onActivityPaused(activity: Activity) {} | ||
|
||
override fun onActivityStopped(activity: Activity) {} | ||
|
||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} | ||
|
||
override fun onActivityDestroyed(activity: Activity) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.