Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions projectBlueWater/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ dependencies {
implementation 'androidx.activity:activity-compose:1.10.1'
implementation 'dev.olshevski.navigation:reimagined:1.5.0'
implementation 'com.google.code.gson:gson:2.13.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx3:1.10.2'
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
testCompileOnly 'junit:junit:4.13.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,9 @@ private fun LazyListScope.settingsList(
}
}

items(libraries) { (libraryId, name) ->
items(libraries, key = { (l, _) -> l }) { (libraryId, name) ->
Row(
modifier = standardRowModifier
.clickable { applicationNavigation.viewServerSettings(libraryId) },
modifier = standardRowModifier.clickable { applicationNavigation.viewServerSettings(libraryId) },
verticalAlignment = Alignment.CenterVertically,
) {
Text(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ class ApplicationSettingsViewModel(
libraryChosenSubscription.close()
}

override fun act() {
mutableIsLoading.value = false
}

fun loadSettings(): Promise<*> {
mutableIsLoading.value = true

Expand Down Expand Up @@ -79,7 +83,7 @@ class ApplicationSettingsViewModel(
}
)
}
.then { it -> mutableLibraries.value = it.sortedBy { it.first.id }.toList() }
.then { it -> mutableLibraries.value = it.sortedBy { it.first.id } }

return Promise
.whenAll(promisedSimpleValuesUpdate, promisedEngineTypeUpdate, promisedLibrariesUpdate)
Expand Down Expand Up @@ -118,8 +122,4 @@ class ApplicationSettingsViewModel(
chosenLibraryId = chosenLibraryId.value.id
)
)

override fun act() {
mutableIsLoading.value = false
}
}
Original file line number Diff line number Diff line change
@@ -1,43 +1,29 @@
package com.lasthopesoftware.bluewater.shared.observables

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.rx3.asFlow
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext

@Composable
fun <T, S : InteractionState<T>> S.subscribeAsState(): State<T> {
val state = remember { mutableStateOf(value) }
DisposableEffect(this) {
val disposable = subscribe {
state.value = it.value
}
onDispose { disposable.dispose() }
}
return state
}
fun <T, S : InteractionState<T>> S.subscribeAsState(
context: CoroutineContext = EmptyCoroutineContext
): State<T> = updatingState(context)

@Composable
fun <T, S : MutableInteractionState<T>> S.subscribeAsMutableState(
context: CoroutineContext = EmptyCoroutineContext
): MutableState<T> {
val state = remember { mutableStateOf(value) }
DisposableEffect(key1 = this) {
val disposable = subscribe { state.value = it.value }
val state = updatingState(context)

onDispose {
disposable.dispose()
}
}

LaunchedEffect(key1 = this) {
LaunchedEffect(key1 = this, context) {
value = state.value
if (context == EmptyCoroutineContext) {
snapshotFlow { state.value }.collect {
Expand All @@ -51,3 +37,20 @@ fun <T, S : MutableInteractionState<T>> S.subscribeAsMutableState(
}
return state
}

@Composable
private fun <T, S : InteractionState<T>> S.updatingState(context: CoroutineContext): MutableState<T> {
val state = remember { mutableStateOf(value) }
LaunchedEffect(this, context) {
if (context == EmptyCoroutineContext) {
asFlow().collect {
state.value = it.value
}
} else withContext(context) {
asFlow().collect {
state.value = it.value
}
}
}
return state
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ import com.lasthopesoftware.bluewater.client.playback.engine.bootstrap.ManagedPl
import com.lasthopesoftware.bluewater.client.playback.engine.preparation.PreparedPlaybackQueueResourceManagement
import com.lasthopesoftware.bluewater.client.playback.file.PositionedFile
import com.lasthopesoftware.bluewater.client.playback.file.PositionedPlayingFile
import com.lasthopesoftware.bluewater.client.playback.file.preparation.FakeMappedPlayableFilePreparationSourceProvider
import com.lasthopesoftware.bluewater.client.playback.file.fakes.FakePreparedPlayableFile
import com.lasthopesoftware.bluewater.client.playback.file.fakes.ResolvablePlaybackHandler
import com.lasthopesoftware.bluewater.client.playback.file.preparation.queues.CompletingFileQueueProvider
import com.lasthopesoftware.bluewater.client.playback.nowplaying.storage.NowPlaying
import com.lasthopesoftware.bluewater.client.playback.nowplaying.storage.NowPlayingRepository
import com.lasthopesoftware.bluewater.client.playback.volume.PlaylistVolumeManager
import com.lasthopesoftware.bluewater.shared.promises.extensions.DeferredPromise
import com.lasthopesoftware.bluewater.shared.promises.extensions.toExpiringFuture
import com.lasthopesoftware.promises.extensions.toPromise
import com.namehillsoftware.handoff.promises.Promise
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.joda.time.Duration
import org.junit.jupiter.api.BeforeAll
Expand All @@ -31,15 +35,14 @@ class `When Playback Is Resumed` {
}

private val mut by lazy {
val fakePlaybackPreparerProvider = FakeMappedPlayableFilePreparationSourceProvider(
listOf(
ServiceFile("1"),
ServiceFile("2"),
ServiceFile("3"),
ServiceFile("4"),
ServiceFile("5")
)
)
val playlist = listOf(
ServiceFile("1"),
ServiceFile("2"),
ServiceFile("3"),
ServiceFile("4"),
ServiceFile("5")
)

val library = Library(id = libraryId)
val libraryProvider = FakeLibraryRepository(library)
val nowPlayingRepository =
Expand All @@ -49,7 +52,22 @@ class `When Playback Is Resumed` {
)
val preparedPlaybackQueueResourceManagement =
PreparedPlaybackQueueResourceManagement(
fakePlaybackPreparerProvider,
mockk {
every { providePlayableFilePreparationSource() } returns mockk {
every { promisePreparedPlaybackFile(LibraryId(libraryId), any(), any()) } answers {
val prepareAt = thirdArg<Duration>()
preparedAt = prepareAt
val playbackHandler = ResolvablePlaybackHandler()
playbackHandler.setCurrentPosition(prepareAt.millis.toInt())

FakePreparedPlayableFile(playbackHandler).toPromise()
}

val firstPlaybackHandler = ResolvablePlaybackHandler()
firstPlaybackHandler.setCurrentPosition(450)
every { promisePreparedPlaybackFile(LibraryId(libraryId), ServiceFile("1"), Duration.ZERO) } returns FakePreparedPlayableFile(firstPlaybackHandler).toPromise()
}
},
FakePlaybackQueueConfiguration(maxQueueSize = 0)
)
val playbackBootstrapper = ManagedPlaylistPlayer(
Expand All @@ -66,7 +84,7 @@ class `When Playback Is Resumed` {
playbackBootstrapper,
playbackBootstrapper,
)
Triple(fakePlaybackPreparerProvider, nowPlayingRepository, playbackEngine)
Triple(playlist, nowPlayingRepository, playbackEngine)
}

private var preparedAt: Duration? = null
Expand All @@ -75,34 +93,18 @@ class `When Playback Is Resumed` {

@BeforeAll
fun before() {
val (fakePlaybackPreparerProvider, nowPlayingRepository, playbackEngine) = mut

val deferredResume = DeferredPromise(Unit)

fakePlaybackPreparerProvider.preparationSourceBeingProvided { serviceFile, deferredPreparedPlayableFile ->
val playbackHandler = deferredPreparedPlayableFile.resolve()
if (serviceFile == ServiceFile("1"))
playbackHandler.setCurrentPosition(450)

if (serviceFile == ServiceFile("2")) {
preparedAt = deferredPreparedPlayableFile.preparedAt
deferredResume.resolve()
}
}
val (playlist, nowPlayingRepository, playbackEngine) = mut

val promisedCollectedFiles = Promise {
val collectedFiles = mutableListOf<PositionedPlayingFile?>()
playbackEngine.setOnPlayingFileChanged { _, f ->
collectedFiles.add(f)
val collectedFiles = mutableListOf<PositionedPlayingFile?>()

deferredResume.then { _ -> it.sendResolution(collectedFiles) }
}
playbackEngine.setOnPlayingFileChanged { _, f ->
collectedFiles.add(f)
}

playbackEngine
.startPlaylist(
LibraryId(libraryId),
fakePlaybackPreparerProvider.deferredResolutions.keys.toList(),
playlist,
0
)
.toExpiringFuture()
Expand All @@ -112,7 +114,17 @@ class `When Playback Is Resumed` {

playbackEngine.skipToNext().toExpiringFuture().get()

val deferredResume = DeferredPromise(Unit)
val promisedCollectedFiles = Promise {
playbackEngine.setOnPlayingFileChanged { _, f ->
collectedFiles.add(f)

deferredResume.then { _ -> it.sendResolution(collectedFiles) }
}
}

playbackEngine.resume().toExpiringFuture().get()
deferredResume.resolve()

positionedFiles = promisedCollectedFiles.toExpiringFuture().get()

Expand Down