mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
UI Updates & Fixes (#114)
Fixes #113 - Ensures all audio and subtitle options are shown during playback - Fix home rows not being at top when going back - Show error message when background home page refresh fails Also shows more details for login failures and adds a link to the debug page (with log info) after multiple login failure attempts. Hopefully this can help diagnose #112 and #18
This commit is contained in:
parent
b1275ae210
commit
3f7c2ac730
11 changed files with 128 additions and 53 deletions
|
|
@ -227,7 +227,7 @@ dependencies {
|
||||||
ksp(libs.hilt.android.compiler)
|
ksp(libs.hilt.android.compiler)
|
||||||
|
|
||||||
implementation(libs.timber)
|
implementation(libs.timber)
|
||||||
implementation(libs.slf4j.simple)
|
implementation(libs.slf4j2.timber)
|
||||||
implementation(libs.aboutlibraries.core)
|
implementation(libs.aboutlibraries.core)
|
||||||
implementation(libs.aboutlibraries.compose.m3)
|
implementation(libs.aboutlibraries.compose.m3)
|
||||||
implementation(libs.multiplatform.markdown.renderer)
|
implementation(libs.multiplatform.markdown.renderer)
|
||||||
|
|
|
||||||
|
|
@ -150,8 +150,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LaunchedEffect(server) {
|
LaunchedEffect(server, user) {
|
||||||
serverEventListener.init()
|
serverEventListener.init(server, user)
|
||||||
}
|
}
|
||||||
ApplicationContent(
|
ApplicationContent(
|
||||||
user = user,
|
user = user,
|
||||||
|
|
|
||||||
|
|
@ -66,13 +66,13 @@ class DebugViewModel
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
itemPlaybacks.value = results
|
itemPlaybacks.value = results
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val logcat = getLogCatLines()
|
val logcat = getLogCatLines()
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
this@DebugViewModel.logcat.value = logcat
|
this@DebugViewModel.logcat.value = logcat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getLogCatLines(): List<LogcatLine> {
|
fun getLogCatLines(): List<LogcatLine> {
|
||||||
|
|
@ -95,7 +95,7 @@ class DebugViewModel
|
||||||
while (count < lineCount) {
|
while (count < lineCount) {
|
||||||
val line = reader.readLine()
|
val line = reader.readLine()
|
||||||
if (line != null) {
|
if (line != null) {
|
||||||
val level = line.split(" ").getOrNull(4)
|
val level = line.split(Regex("\\s+")).getOrNull(4)
|
||||||
val logLevel =
|
val logLevel =
|
||||||
when (level?.uppercase()) {
|
when (level?.uppercase()) {
|
||||||
"V" -> Log.VERBOSE
|
"V" -> Log.VERBOSE
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,9 @@ class SeriesViewModel
|
||||||
}.coerceAtLeast(0)
|
}.coerceAtLeast(0)
|
||||||
} else {
|
} else {
|
||||||
// Force the first page to to be fetched
|
// Force the first page to to be fetched
|
||||||
|
if (pager.isNotEmpty()) {
|
||||||
pager.getBlocking(0)
|
pager.getBlocking(0)
|
||||||
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
Timber.v("Loaded ${pager.size} episodes for season $seasonId, initialIndex=$initialIndex")
|
Timber.v("Loaded ${pager.size} episodes for season $seasonId, initialIndex=$initialIndex")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.github.damontecres.wholphin.ui.main
|
package com.github.damontecres.wholphin.ui.main
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
@ -20,6 +21,7 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
|
@ -32,6 +34,7 @@ import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
|
@ -62,6 +65,7 @@ import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import com.github.damontecres.wholphin.util.formatDateTime
|
import com.github.damontecres.wholphin.util.formatDateTime
|
||||||
import com.github.damontecres.wholphin.util.seasonEpisode
|
import com.github.damontecres.wholphin.util.seasonEpisode
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
@ -78,6 +82,7 @@ fun HomePage(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: HomeViewModel = hiltViewModel(),
|
viewModel: HomeViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
var firstLoad by rememberSaveable { mutableStateOf(true) }
|
var firstLoad by rememberSaveable { mutableStateOf(true) }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.init(preferences).join()
|
viewModel.init(preferences).join()
|
||||||
|
|
@ -85,6 +90,20 @@ fun HomePage(
|
||||||
}
|
}
|
||||||
val loading by viewModel.loadingState.observeAsState(LoadingState.Loading)
|
val loading by viewModel.loadingState.observeAsState(LoadingState.Loading)
|
||||||
val homeRows by viewModel.homeRows.observeAsState(listOf())
|
val homeRows by viewModel.homeRows.observeAsState(listOf())
|
||||||
|
LaunchedEffect(loading) {
|
||||||
|
val state = loading
|
||||||
|
if (!firstLoad && state is LoadingState.Error) {
|
||||||
|
// After the first load, refreshes occur in the background and an ErrorMessage won't show
|
||||||
|
// So send a Toast on errors instead
|
||||||
|
Toast
|
||||||
|
.makeText(
|
||||||
|
context,
|
||||||
|
"Home refresh error: ${state.localizedMessage}",
|
||||||
|
Toast.LENGTH_LONG,
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
when (val state = if (firstLoad) loading else LoadingState.Success) {
|
when (val state = if (firstLoad) loading else LoadingState.Success) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> ErrorMessage(state)
|
||||||
|
|
||||||
|
|
@ -148,6 +167,7 @@ fun HomePageContent(
|
||||||
onFocusPosition: ((RowColumn) -> Unit)? = null,
|
onFocusPosition: ((RowColumn) -> Unit)? = null,
|
||||||
loadingState: LoadingState? = null,
|
loadingState: LoadingState? = null,
|
||||||
) {
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
var position by rememberSaveable(stateSaver = RowColumnSaver) {
|
var position by rememberSaveable(stateSaver = RowColumnSaver) {
|
||||||
mutableStateOf(RowColumn(0, 0))
|
mutableStateOf(RowColumn(0, 0))
|
||||||
}
|
}
|
||||||
|
|
@ -158,6 +178,9 @@ fun HomePageContent(
|
||||||
val positionFocusRequester = remember { FocusRequester() }
|
val positionFocusRequester = remember { FocusRequester() }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
positionFocusRequester.tryRequestFocus()
|
positionFocusRequester.tryRequestFocus()
|
||||||
|
// Hacky, but mostly works
|
||||||
|
delay(50)
|
||||||
|
listState.animateScrollToItem(position.row)
|
||||||
}
|
}
|
||||||
LaunchedEffect(position) {
|
LaunchedEffect(position) {
|
||||||
listState.animateScrollToItem(position.row)
|
listState.animateScrollToItem(position.row)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.wrapContentSize
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
import androidx.compose.foundation.layout.wrapContentWidth
|
import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
|
@ -638,7 +640,7 @@ private fun BottomDialog(
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.background(Color.DarkGray, shape = RoundedCornerShape(16.dp)),
|
.background(Color.DarkGray, shape = RoundedCornerShape(16.dp)),
|
||||||
) {
|
) {
|
||||||
Column(
|
LazyColumn(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
@ -647,7 +649,7 @@ private fun BottomDialog(
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
choices.forEachIndexed { index, choice ->
|
itemsIndexed(choices) { index, choice ->
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val focused = interactionSource.collectIsFocusedAsState().value
|
val focused = interactionSource.collectIsFocusedAsState().value
|
||||||
val color =
|
val color =
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.tv.material3.Button
|
import androidx.tv.material3.Button
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
|
|
@ -61,6 +62,7 @@ fun SwitchUserContent(
|
||||||
var showAddUser by remember { mutableStateOf(false) }
|
var showAddUser by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val userState by viewModel.switchUserState.observeAsState(LoadingState.Pending)
|
val userState by viewModel.switchUserState.observeAsState(LoadingState.Pending)
|
||||||
|
val loginAttempts by viewModel.loginAttempts.observeAsState(0)
|
||||||
LaunchedEffect(userState) {
|
LaunchedEffect(userState) {
|
||||||
if (!showAddUser) {
|
if (!showAddUser) {
|
||||||
when (val s = userState) {
|
when (val s = userState) {
|
||||||
|
|
@ -129,6 +131,7 @@ fun SwitchUserContent(
|
||||||
var useQuickConnect by remember { mutableStateOf(quickConnectEnabled) }
|
var useQuickConnect by remember { mutableStateOf(quickConnectEnabled) }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.clearSwitchUserState()
|
viewModel.clearSwitchUserState()
|
||||||
|
viewModel.resetAttempts()
|
||||||
if (useQuickConnect) {
|
if (useQuickConnect) {
|
||||||
viewModel.initiateQuickConnect(server)
|
viewModel.initiateQuickConnect(server)
|
||||||
}
|
}
|
||||||
|
|
@ -138,6 +141,10 @@ fun SwitchUserContent(
|
||||||
viewModel.cancelQuickConnect()
|
viewModel.cancelQuickConnect()
|
||||||
showAddUser = false
|
showAddUser = false
|
||||||
},
|
},
|
||||||
|
properties =
|
||||||
|
DialogProperties(
|
||||||
|
usePlatformDefaultWidth = false,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
|
@ -145,7 +152,7 @@ fun SwitchUserContent(
|
||||||
Modifier
|
Modifier
|
||||||
.focusGroup()
|
.focusGroup()
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(.66f),
|
||||||
) {
|
) {
|
||||||
if (useQuickConnect) {
|
if (useQuickConnect) {
|
||||||
if (quickConnect == null && userState !is LoadingState.Error) {
|
if (quickConnect == null && userState !is LoadingState.Error) {
|
||||||
|
|
@ -206,7 +213,7 @@ fun SwitchUserContent(
|
||||||
UserStateError(userState)
|
UserStateError(userState)
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.focusGroup(),
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Username",
|
text = "Username",
|
||||||
|
|
@ -231,7 +238,7 @@ fun SwitchUserContent(
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier,
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Password",
|
text = "Password",
|
||||||
|
|
@ -265,6 +272,20 @@ fun SwitchUserContent(
|
||||||
Text("Login")
|
Text("Login")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (loginAttempts > 2) {
|
||||||
|
Text(
|
||||||
|
text = "Trouble logging in?",
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
viewModel.navigationManager.navigateTo(Destination.Debug)
|
||||||
|
},
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
|
) {
|
||||||
|
Text("Show debug info")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -288,12 +309,20 @@ private fun UserStateError(
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
s.exception?.localizedMessage?.let {
|
if (s.exception != null) {
|
||||||
|
s.exception.localizedMessage?.let {
|
||||||
Text(
|
Text(
|
||||||
text = it,
|
text = it,
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
s.exception.cause?.localizedMessage?.let {
|
||||||
|
Text(
|
||||||
|
text = "Cause: $it",
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@ class SwitchUserViewModel
|
||||||
val addServerState = MutableLiveData<LoadingState>(LoadingState.Pending)
|
val addServerState = MutableLiveData<LoadingState>(LoadingState.Pending)
|
||||||
val switchUserState = MutableLiveData<LoadingState>(LoadingState.Pending)
|
val switchUserState = MutableLiveData<LoadingState>(LoadingState.Pending)
|
||||||
|
|
||||||
|
val loginAttempts = MutableLiveData(0)
|
||||||
|
|
||||||
fun clearAddServerState() {
|
fun clearAddServerState() {
|
||||||
addServerState.value = LoadingState.Pending
|
addServerState.value = LoadingState.Pending
|
||||||
}
|
}
|
||||||
|
|
@ -63,6 +65,10 @@ class SwitchUserViewModel
|
||||||
switchUserState.value = LoadingState.Pending
|
switchUserState.value = LoadingState.Pending
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun resetAttempts() {
|
||||||
|
loginAttempts.value = 0
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
init()
|
init()
|
||||||
}
|
}
|
||||||
|
|
@ -338,6 +344,7 @@ class SwitchUserViewModel
|
||||||
msg: String? = null,
|
msg: String? = null,
|
||||||
ex: Exception? = null,
|
ex: Exception? = null,
|
||||||
) = withContext(Dispatchers.Main) {
|
) = withContext(Dispatchers.Main) {
|
||||||
|
loginAttempts.value = (loginAttempts.value ?: 0) + 1
|
||||||
switchUserState.value = LoadingState.Error(msg, ex)
|
switchUserState.value = LoadingState.Error(msg, ex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,8 @@ sealed interface LoadingState {
|
||||||
val exception: Throwable? = null,
|
val exception: Throwable? = null,
|
||||||
) : LoadingState {
|
) : LoadingState {
|
||||||
constructor(exception: Throwable) : this(null, exception)
|
constructor(exception: Throwable) : this(null, exception)
|
||||||
|
|
||||||
|
val localizedMessage: String =
|
||||||
|
listOfNotNull(message, exception?.localizedMessage).joinToString(" - ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,14 @@ package com.github.damontecres.wholphin.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
||||||
|
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
||||||
import com.github.damontecres.wholphin.hilt.IoCoroutineScope
|
import com.github.damontecres.wholphin.hilt.IoCoroutineScope
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.showToast
|
import com.github.damontecres.wholphin.ui.showToast
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
|
|
@ -27,11 +30,13 @@ class ServerEventListener
|
||||||
private val api: ApiClient,
|
private val api: ApiClient,
|
||||||
@param:IoCoroutineScope private val scope: CoroutineScope,
|
@param:IoCoroutineScope private val scope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
init {
|
private var listenJob: Job? = null
|
||||||
setupListeners()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun init() {
|
fun init(
|
||||||
|
server: JellyfinServer?,
|
||||||
|
user: JellyfinUser?,
|
||||||
|
) {
|
||||||
|
if (server != null && user != null && api.baseUrl != null && api.accessToken != null) {
|
||||||
scope.launchIO {
|
scope.launchIO {
|
||||||
api.sessionApi.postCapabilities(
|
api.sessionApi.postCapabilities(
|
||||||
playableMediaTypes = listOf(MediaType.VIDEO),
|
playableMediaTypes = listOf(MediaType.VIDEO),
|
||||||
|
|
@ -42,11 +47,15 @@ class ServerEventListener
|
||||||
),
|
),
|
||||||
supportsMediaControl = true,
|
supportsMediaControl = true,
|
||||||
)
|
)
|
||||||
|
setupListeners()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setupListeners() {
|
fun setupListeners() {
|
||||||
Timber.v("Subscribing to WebSocket")
|
Timber.v("Subscribing to WebSocket")
|
||||||
|
listenJob?.cancel()
|
||||||
|
listenJob =
|
||||||
api.webSocket
|
api.webSocket
|
||||||
.subscribe<GeneralCommandMessage>()
|
.subscribe<GeneralCommandMessage>()
|
||||||
.onEach { message ->
|
.onEach { message ->
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ composeBom = "2025.10.01"
|
||||||
compose-runtime = "1.9.4"
|
compose-runtime = "1.9.4"
|
||||||
multiplatformMarkdownRenderer = "0.37.0"
|
multiplatformMarkdownRenderer = "0.37.0"
|
||||||
programguide = "1.6.0"
|
programguide = "1.6.0"
|
||||||
slf4j = "2.0.17"
|
slf4j2Timber = "1.2"
|
||||||
timber = "5.0.1"
|
timber = "5.0.1"
|
||||||
tvFoundation = "1.0.0-alpha12"
|
tvFoundation = "1.0.0-alpha12"
|
||||||
tvMaterial = "1.0.1"
|
tvMaterial = "1.0.1"
|
||||||
|
|
@ -98,7 +98,7 @@ androidx-room-common-jvm = { group = "androidx.room", name = "room-common-jvm",
|
||||||
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
||||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||||
|
|
||||||
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
slf4j2-timber = { module = "uk.kulikov:slf4j2-timber", version.ref = "slf4j2Timber" }
|
||||||
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
|
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
|
||||||
androidx-preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preferenceKtx" }
|
androidx-preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preferenceKtx" }
|
||||||
androidx-room-testing = { group = "androidx.room", name = "room-testing", version.ref = "room" }
|
androidx-room-testing = { group = "androidx.room", name = "room-testing", version.ref = "room" }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue