mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Some minor optimizations to improve UI speed (#593)
## Description Many tiny changes that improve UI rendering speed and reduces recompositions Still plenty of room for improvement though.
This commit is contained in:
parent
622a99631b
commit
243e08b635
10 changed files with 62 additions and 110 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
package com.github.damontecres.wholphin.data.model
|
package com.github.damontecres.wholphin.data.model
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
import com.github.damontecres.wholphin.ui.detail.CardGridItem
|
import com.github.damontecres.wholphin.ui.detail.CardGridItem
|
||||||
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds
|
import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds
|
||||||
import com.github.damontecres.wholphin.ui.formatDateTime
|
import com.github.damontecres.wholphin.ui.formatDateTime
|
||||||
|
|
@ -17,6 +18,7 @@ import org.jellyfin.sdk.model.extensions.ticks
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@Stable
|
||||||
data class BaseItem(
|
data class BaseItem(
|
||||||
val data: BaseItemDto,
|
val data: BaseItemDto,
|
||||||
val useSeriesForPrimary: Boolean,
|
val useSeriesForPrimary: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -8,22 +8,15 @@ import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.focusRestorer
|
import androidx.compose.ui.focus.focusRestorer
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
|
||||||
import com.github.damontecres.wholphin.ui.AspectRatios
|
|
||||||
import com.github.damontecres.wholphin.util.FocusPair
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> ItemRow(
|
fun <T> ItemRow(
|
||||||
|
|
@ -39,13 +32,10 @@ fun <T> ItemRow(
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
) -> Unit,
|
) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
focusPair: FocusPair? = null,
|
|
||||||
cardOnFocus: ((isFocused: Boolean, index: Int) -> Unit)? = null,
|
|
||||||
horizontalPadding: Dp = 16.dp,
|
horizontalPadding: Dp = 16.dp,
|
||||||
) {
|
) {
|
||||||
val state = rememberLazyListState()
|
val state = rememberLazyListState()
|
||||||
val firstFocus = remember { FocusRequester() }
|
val firstFocus = remember { FocusRequester() }
|
||||||
var focusedIndex by remember { mutableIntStateOf(focusPair?.column ?: 0) }
|
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -62,23 +52,14 @@ fun <T> ItemRow(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.focusRestorer(focusPair?.focusRequester ?: firstFocus),
|
.focusRestorer(firstFocus),
|
||||||
) {
|
) {
|
||||||
itemsIndexed(items) { index, item ->
|
itemsIndexed(items) { index, item ->
|
||||||
val cardModifier =
|
val cardModifier =
|
||||||
if (index == 0 && focusPair == null) {
|
if (index == 0) {
|
||||||
Modifier.focusRequester(firstFocus)
|
Modifier.focusRequester(firstFocus)
|
||||||
} else {
|
} else {
|
||||||
if (focusPair != null && focusPair.column == index) {
|
Modifier
|
||||||
Modifier.focusRequester(focusPair.focusRequester)
|
|
||||||
} else {
|
|
||||||
Modifier
|
|
||||||
}
|
|
||||||
}.onFocusChanged {
|
|
||||||
if (it.isFocused) {
|
|
||||||
focusedIndex = index
|
|
||||||
}
|
|
||||||
cardOnFocus?.invoke(it.isFocused, index)
|
|
||||||
}
|
}
|
||||||
cardContent.invoke(
|
cardContent.invoke(
|
||||||
index,
|
index,
|
||||||
|
|
@ -91,38 +72,3 @@ fun <T> ItemRow(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun BannerItemRow(
|
|
||||||
title: String,
|
|
||||||
items: List<BaseItem?>,
|
|
||||||
onClickItem: (Int, BaseItem) -> Unit,
|
|
||||||
onLongClickItem: (Int, BaseItem) -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
focusPair: FocusPair? = null,
|
|
||||||
cardOnFocus: ((isFocused: Boolean, index: Int) -> Unit)? = null,
|
|
||||||
aspectRatioOverride: Float? = null,
|
|
||||||
) = ItemRow(
|
|
||||||
title = title,
|
|
||||||
items = items,
|
|
||||||
onClickItem = onClickItem,
|
|
||||||
onLongClickItem = onLongClickItem,
|
|
||||||
modifier = modifier,
|
|
||||||
cardContent = { index, item, modifier, onClick, onLongClick ->
|
|
||||||
BannerCard(
|
|
||||||
name = title,
|
|
||||||
item = item,
|
|
||||||
aspectRatio =
|
|
||||||
aspectRatioOverride ?: item?.aspectRatio ?: AspectRatios.WIDE,
|
|
||||||
cornerText = item?.data?.indexNumber?.let { "E$it" },
|
|
||||||
played = item?.data?.userData?.played ?: false,
|
|
||||||
playPercent = item?.data?.userData?.playedPercentage ?: 0.0,
|
|
||||||
onClick = onClick,
|
|
||||||
onLongClick = onLongClick,
|
|
||||||
modifier = modifier,
|
|
||||||
interactionSource = null,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
focusPair = focusPair,
|
|
||||||
cardOnFocus = cardOnFocus,
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.github.damontecres.wholphin.ui.components
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
@ -21,7 +22,7 @@ fun MovieQuickDetails(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val now = LocalClock.current.now
|
val now by LocalClock.current.now
|
||||||
val details =
|
val details =
|
||||||
remember(dto, now) {
|
remember(dto, now) {
|
||||||
buildList {
|
buildList {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.github.damontecres.wholphin.ui.components
|
package com.github.damontecres.wholphin.ui.components
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
@ -59,7 +60,7 @@ fun EpisodeQuickDetails(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val now = LocalClock.current.now
|
val now by LocalClock.current.now
|
||||||
val details =
|
val details =
|
||||||
remember(dto, now) {
|
remember(dto, now) {
|
||||||
buildList {
|
buildList {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.github.damontecres.wholphin.ui.components
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
@ -13,8 +14,9 @@ import com.github.damontecres.wholphin.ui.util.LocalClock
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BoxScope.TimeDisplay(modifier: Modifier = Modifier) {
|
fun BoxScope.TimeDisplay(modifier: Modifier = Modifier) {
|
||||||
|
val timeString by LocalClock.current.timeString
|
||||||
Text(
|
Text(
|
||||||
text = LocalClock.current.timeString,
|
text = timeString,
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
|
|
||||||
|
|
@ -410,7 +410,7 @@ fun PlaylistItem(
|
||||||
},
|
},
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
item?.data?.runTimeTicks?.ticks?.roundMinutes?.let { duration ->
|
item?.data?.runTimeTicks?.ticks?.roundMinutes?.let { duration ->
|
||||||
val now = LocalClock.current.now
|
val now by LocalClock.current.now
|
||||||
val endTimeStr =
|
val endTimeStr =
|
||||||
remember(item, now) {
|
remember(item, now) {
|
||||||
val endTime = now.toLocalTime().plusSeconds(duration.inWholeSeconds)
|
val endTime = now.toLocalTime().plusSeconds(duration.inWholeSeconds)
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,6 @@ import com.github.damontecres.wholphin.ui.detail.MoreDialogActions
|
||||||
import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
|
import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
|
||||||
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
||||||
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
|
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
|
||||||
import com.github.damontecres.wholphin.ui.ifElse
|
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.playback.isPlayKeyUp
|
import com.github.damontecres.wholphin.ui.playback.isPlayKeyUp
|
||||||
|
|
@ -226,14 +225,15 @@ fun HomePageContent(
|
||||||
var position by rememberSaveable(stateSaver = RowColumnSaver) {
|
var position by rememberSaveable(stateSaver = RowColumnSaver) {
|
||||||
mutableStateOf(RowColumn(firstRow, 0))
|
mutableStateOf(RowColumn(firstRow, 0))
|
||||||
}
|
}
|
||||||
var focusedItem =
|
val focusedItem =
|
||||||
position.let {
|
remember(position) {
|
||||||
(homeRows.getOrNull(it.row) as? HomeRowLoadingState.Success)?.items?.getOrNull(it.column)
|
position.let {
|
||||||
|
(homeRows.getOrNull(it.row) as? HomeRowLoadingState.Success)?.items?.getOrNull(it.column)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val focusRequester = remember { FocusRequester() }
|
val rowFocusRequesters = remember(homeRows.size) { List(homeRows.size) { FocusRequester() } }
|
||||||
val positionFocusRequester = remember { FocusRequester() }
|
|
||||||
var focused by remember { mutableStateOf(false) }
|
var focused by remember { mutableStateOf(false) }
|
||||||
LaunchedEffect(homeRows) {
|
LaunchedEffect(homeRows) {
|
||||||
if (!focused) {
|
if (!focused) {
|
||||||
|
|
@ -241,7 +241,7 @@ fun HomePageContent(
|
||||||
.indexOfFirst { it is HomeRowLoadingState.Success && it.items.isNotEmpty() }
|
.indexOfFirst { it is HomeRowLoadingState.Success && it.items.isNotEmpty() }
|
||||||
.takeIf { it >= 0 }
|
.takeIf { it >= 0 }
|
||||||
?.let {
|
?.let {
|
||||||
positionFocusRequester.tryRequestFocus()
|
rowFocusRequesters[it].tryRequestFocus()
|
||||||
delay(50)
|
delay(50)
|
||||||
listState.animateScrollToItem(position.row)
|
listState.animateScrollToItem(position.row)
|
||||||
focused = true
|
focused = true
|
||||||
|
|
@ -251,9 +251,6 @@ fun HomePageContent(
|
||||||
LaunchedEffect(position) {
|
LaunchedEffect(position) {
|
||||||
listState.animateScrollToItem(position.row)
|
listState.animateScrollToItem(position.row)
|
||||||
}
|
}
|
||||||
LaunchedEffect(focusedItem) {
|
|
||||||
focusedItem?.let(onUpdateBackdrop)
|
|
||||||
}
|
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
HomePageHeader(
|
HomePageHeader(
|
||||||
|
|
@ -275,16 +272,7 @@ fun HomePageContent(
|
||||||
),
|
),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.focusRestorer()
|
.focusRestorer(),
|
||||||
.onKeyEvent {
|
|
||||||
val item = focusedItem
|
|
||||||
if (isPlayKeyUp(it) && item?.type?.playable == true) {
|
|
||||||
Timber.v("Clicked play on ${item.id}")
|
|
||||||
onClickPlay.invoke(position, item)
|
|
||||||
return@onKeyEvent true
|
|
||||||
}
|
|
||||||
return@onKeyEvent false
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
itemsIndexed(homeRows) { rowIndex, row ->
|
itemsIndexed(homeRows) { rowIndex, row ->
|
||||||
when (val r = row) {
|
when (val r = row) {
|
||||||
|
|
@ -347,18 +335,13 @@ fun HomePageContent(
|
||||||
onClickItem = { index, item ->
|
onClickItem = { index, item ->
|
||||||
onClickItem.invoke(RowColumn(rowIndex, index), item)
|
onClickItem.invoke(RowColumn(rowIndex, index), item)
|
||||||
},
|
},
|
||||||
cardOnFocus = { isFocused, index ->
|
|
||||||
if (isFocused) {
|
|
||||||
focusedItem = row.items.getOrNull(index)
|
|
||||||
position = RowColumn(rowIndex, index)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onLongClickItem = { index, item ->
|
onLongClickItem = { index, item ->
|
||||||
onLongClickItem.invoke(RowColumn(rowIndex, index), item)
|
onLongClickItem.invoke(RowColumn(rowIndex, index), item)
|
||||||
},
|
},
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.focusRequester(rowFocusRequesters[rowIndex])
|
||||||
.animateItem(),
|
.animateItem(),
|
||||||
cardContent = { index, item, cardModifier, onClick, onLongClick ->
|
cardContent = { index, item, cardModifier, onClick, onLongClick ->
|
||||||
val cornerText =
|
val cornerText =
|
||||||
|
|
@ -385,29 +368,32 @@ fun HomePageContent(
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
modifier =
|
modifier =
|
||||||
cardModifier
|
cardModifier
|
||||||
.ifElse(
|
.onFocusChanged {
|
||||||
focusedItem == item,
|
|
||||||
Modifier.focusRequester(focusRequester),
|
|
||||||
).ifElse(
|
|
||||||
RowColumn(rowIndex, index) == position,
|
|
||||||
Modifier.focusRequester(
|
|
||||||
positionFocusRequester,
|
|
||||||
),
|
|
||||||
).onFocusChanged {
|
|
||||||
if (it.isFocused) {
|
if (it.isFocused) {
|
||||||
|
position = RowColumn(rowIndex, index)
|
||||||
|
item?.let(onUpdateBackdrop)
|
||||||
|
}
|
||||||
|
if (it.isFocused && onFocusPosition != null) {
|
||||||
val nonEmptyRowBefore =
|
val nonEmptyRowBefore =
|
||||||
homeRows
|
homeRows
|
||||||
.subList(0, rowIndex)
|
.subList(0, rowIndex)
|
||||||
.count {
|
.count {
|
||||||
it is HomeRowLoadingState.Success && it.items.isEmpty()
|
it is HomeRowLoadingState.Success && it.items.isEmpty()
|
||||||
}
|
}
|
||||||
onFocusPosition?.invoke(
|
onFocusPosition.invoke(
|
||||||
RowColumn(
|
RowColumn(
|
||||||
rowIndex - nonEmptyRowBefore,
|
rowIndex - nonEmptyRowBefore,
|
||||||
index,
|
index,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}.onKeyEvent {
|
||||||
|
if (isPlayKeyUp(it) && item?.type?.playable == true) {
|
||||||
|
Timber.v("Clicked play on ${item.id}")
|
||||||
|
onClickPlay.invoke(position, item)
|
||||||
|
return@onKeyEvent true
|
||||||
|
}
|
||||||
|
return@onKeyEvent false
|
||||||
},
|
},
|
||||||
interactionSource = null,
|
interactionSource = null,
|
||||||
cardHeight = Cards.height2x3,
|
cardHeight = Cards.height2x3,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import android.content.Context
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.animation.animateColorAsState
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.focusGroup
|
import androidx.compose.foundation.focusGroup
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||||
|
|
@ -37,11 +36,11 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.drawBehind
|
||||||
import androidx.compose.ui.focus.FocusDirection
|
import androidx.compose.ui.focus.FocusDirection
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusProperties
|
import androidx.compose.ui.focus.focusProperties
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
|
|
@ -346,10 +345,8 @@ fun NavDrawer(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.width(drawerWidth)
|
.width(drawerWidth)
|
||||||
.background(drawerBackground)
|
.drawBehind {
|
||||||
.onFocusChanged {
|
drawRect(drawerBackground)
|
||||||
if (!it.hasFocus) {
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
// Even though some must be clicked, focusing on it should clear other focused items
|
// Even though some must be clicked, focusing on it should clear other focused items
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,10 @@ package com.github.damontecres.wholphin.ui.util
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import com.github.damontecres.wholphin.ui.TimeFormatter
|
import com.github.damontecres.wholphin.ui.TimeFormatter
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
|
|
@ -22,20 +21,28 @@ data class Clock(
|
||||||
/**
|
/**
|
||||||
* The current [LocalDateTime]
|
* The current [LocalDateTime]
|
||||||
*/
|
*/
|
||||||
val now: LocalDateTime,
|
val now: MutableState<LocalDateTime>,
|
||||||
/**
|
/**
|
||||||
* The current time formatted as a string with [TimeFormatter]
|
* The current time formatted as a string with [TimeFormatter]
|
||||||
*/
|
*/
|
||||||
val timeString: String,
|
val timeString: MutableState<String>,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ProvideLocalClock(content: @Composable () -> Unit) {
|
fun ProvideLocalClock(content: @Composable () -> Unit) {
|
||||||
var clock by remember { mutableStateOf(LocalDateTime.now().let { Clock(it, TimeFormatter.format(it)) }) }
|
val clock =
|
||||||
|
remember {
|
||||||
|
LocalDateTime
|
||||||
|
.now()
|
||||||
|
.let { Clock(mutableStateOf(it), mutableStateOf(TimeFormatter.format(it))) }
|
||||||
|
}
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
clock = LocalDateTime.now().let { Clock(it, TimeFormatter.format(it)) }
|
val now = LocalDateTime.now()
|
||||||
delay(1_000)
|
val time = TimeFormatter.format(now)
|
||||||
|
clock.now.value = now
|
||||||
|
clock.timeString.value = time
|
||||||
|
delay(2_000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CompositionLocalProvider(LocalClock provides clock, content)
|
CompositionLocalProvider(LocalClock provides clock, content)
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,16 @@ class TestStreamChoiceServiceBasic(
|
||||||
),
|
),
|
||||||
itemPlayback = itemPlayback(subtitleIndex = TrackIndex.UNSPECIFIED),
|
itemPlayback = itemPlayback(subtitleIndex = TrackIndex.UNSPECIFIED),
|
||||||
),
|
),
|
||||||
|
TestInput(
|
||||||
|
1,
|
||||||
|
SubtitlePlaybackMode.ALWAYS,
|
||||||
|
subtitles =
|
||||||
|
listOf(
|
||||||
|
subtitle(0, "eng", forced = true, default = true),
|
||||||
|
subtitle(1, "eng", false),
|
||||||
|
subtitle(2, "eng", false),
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue