mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Merge remote-tracking branch 'origin/main' into develop/mpv
This commit is contained in:
commit
9a093afd38
17 changed files with 326 additions and 341 deletions
|
|
@ -655,6 +655,18 @@ sealed interface AppPreference<T> {
|
|||
summaryOff = R.string.disabled,
|
||||
)
|
||||
|
||||
val OneClickPause =
|
||||
AppSwitchPreference(
|
||||
title = R.string.one_click_pause,
|
||||
defaultValue = false,
|
||||
getter = { it.playbackPreferences.oneClickPause },
|
||||
setter = { prefs, value ->
|
||||
prefs.updatePlaybackPreferences { oneClickPause = value }
|
||||
},
|
||||
summaryOn = R.string.one_click_pause_summary_on,
|
||||
summaryOff = R.string.disabled,
|
||||
)
|
||||
|
||||
val PlayerBackendPref =
|
||||
AppChoicePreference<PlayerBackend>(
|
||||
title = R.string.player_backend,
|
||||
|
|
@ -756,6 +768,7 @@ val advancedPreferences =
|
|||
title = R.string.playback,
|
||||
preferences =
|
||||
listOf(
|
||||
AppPreference.OneClickPause,
|
||||
AppPreference.SkipIntros,
|
||||
AppPreference.SkipOutros,
|
||||
AppPreference.SkipCommercials,
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.focusGroup
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
|
|
@ -33,12 +36,17 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.ui.AppColors
|
||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||
import com.github.damontecres.wholphin.ui.playOnClickSound
|
||||
import com.github.damontecres.wholphin.ui.playSoundOnFocus
|
||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import java.util.Locale
|
||||
|
||||
enum class StarRatingPrecision {
|
||||
FULL,
|
||||
|
|
@ -63,6 +71,33 @@ val EmptyStarColor = Color(0x2AFFC700)
|
|||
|
||||
val ratingBarHeight: Dp = 32.dp
|
||||
|
||||
@Composable
|
||||
fun SimpleStarRating(
|
||||
communityRating: Float?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier,
|
||||
) {
|
||||
communityRating?.let {
|
||||
Text(
|
||||
text = String.format(Locale.getDefault(), "%.1f", it),
|
||||
fontSize = 14.sp,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier,
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Star,
|
||||
tint = FilledStarColor,
|
||||
contentDescription = null,
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a rating out of 5 stars
|
||||
*/
|
||||
|
|
@ -211,3 +246,15 @@ fun StarRating(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewTvSpec
|
||||
@Composable
|
||||
private fun SimpleStarRatingPreview() {
|
||||
WholphinTheme {
|
||||
Column {
|
||||
SimpleStarRating(7.5f, Modifier.height(32.dp))
|
||||
SimpleStarRating(7.5f, Modifier.height(20.dp))
|
||||
SimpleStarRating(null, Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,182 @@
|
|||
package com.github.damontecres.wholphin.ui.components
|
||||
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusProperties
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
|
||||
@Composable
|
||||
fun TabRow(
|
||||
selectedTabIndex: Int,
|
||||
tabs: List<String>,
|
||||
onClick: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val state = rememberLazyListState()
|
||||
LaunchedEffect(Unit) {
|
||||
state.animateScrollToItem(selectedTabIndex)
|
||||
}
|
||||
val focusRequesters = remember(tabs) { List(tabs.size) { FocusRequester() } }
|
||||
var rowHasFocus by remember { mutableStateOf(false) }
|
||||
LazyRow(
|
||||
state = state,
|
||||
modifier =
|
||||
modifier
|
||||
.onFocusChanged {
|
||||
rowHasFocus = it.hasFocus
|
||||
}.focusProperties {
|
||||
onEnter = {
|
||||
focusRequesters.getOrNull(selectedTabIndex)?.tryRequestFocus()
|
||||
}
|
||||
},
|
||||
) {
|
||||
itemsIndexed(tabs) { index, tabTitle ->
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
Tab(
|
||||
title = tabTitle,
|
||||
selected = index == selectedTabIndex,
|
||||
rowActive = rowHasFocus,
|
||||
interactionSource = interactionSource,
|
||||
onClick = {
|
||||
onClick.invoke(index)
|
||||
},
|
||||
modifier = Modifier.focusRequester(focusRequesters[index]),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Tab(
|
||||
title: String,
|
||||
selected: Boolean,
|
||||
rowActive: Boolean,
|
||||
onClick: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var tabWidth by remember { mutableStateOf(0.dp) }
|
||||
val density = LocalDensity.current
|
||||
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
val contentColor =
|
||||
if (rowActive || selected) {
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface.copy(alpha = .5f)
|
||||
}
|
||||
Box(
|
||||
modifier =
|
||||
modifier
|
||||
.clickable(
|
||||
enabled = true,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
indication = null,
|
||||
).onGloballyPositioned {
|
||||
tabWidth = with(density) { it.size.width.toDp() }
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier,
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
fontSize = 16.sp,
|
||||
color = contentColor,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
TabIndicator(
|
||||
selected = selected,
|
||||
rowActive = rowActive,
|
||||
focused = focused,
|
||||
tabWidth = tabWidth,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TabIndicator(
|
||||
selected: Boolean,
|
||||
rowActive: Boolean,
|
||||
focused: Boolean,
|
||||
tabWidth: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val width by animateDpAsState(if (rowActive && focused) tabWidth else tabWidth * .25f)
|
||||
val backgroundColor =
|
||||
if (rowActive && focused) {
|
||||
MaterialTheme.colorScheme.border
|
||||
} else if (selected) {
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
} else {
|
||||
Color.Transparent
|
||||
}
|
||||
Box(
|
||||
modifier =
|
||||
modifier
|
||||
.height(2.dp)
|
||||
.fillMaxWidth()
|
||||
.width(width)
|
||||
.background(backgroundColor),
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewTvSpec
|
||||
@Composable
|
||||
private fun TabRowPreview() {
|
||||
WholphinTheme {
|
||||
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
|
||||
TabRow(
|
||||
selectedTabIndex = 1,
|
||||
tabs = listOf("Tab 1", "Tab 2", "Tab 3"),
|
||||
onClick = {},
|
||||
)
|
||||
Tab(
|
||||
title = "This is a Tab",
|
||||
selected = true,
|
||||
rowActive = true,
|
||||
onClick = {},
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
modifier = Modifier.width(120.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,20 +20,12 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Tab
|
||||
import androidx.tv.material3.TabDefaults
|
||||
import androidx.tv.material3.TabRow
|
||||
import androidx.tv.material3.TabRowDefaults
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
|
|
@ -41,10 +33,10 @@ import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
|||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.ui.components.CollectionFolderGrid
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||
import com.github.damontecres.wholphin.ui.data.VideoSortOptions
|
||||
import com.github.damontecres.wholphin.ui.detail.livetv.DvrSchedule
|
||||
import com.github.damontecres.wholphin.ui.detail.livetv.TvGuideGrid
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.nav.NavigationManager
|
||||
|
|
@ -103,7 +95,6 @@ fun CollectionFolderLiveTv(
|
|||
TabId(stringResource(R.string.tv_dvr_schedule), UUID.randomUUID()),
|
||||
) + folders
|
||||
|
||||
var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||
var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
|
|
@ -129,55 +120,13 @@ fun CollectionFolderLiveTv(
|
|||
exit = slideOutVertically() + fadeOut(),
|
||||
) {
|
||||
TabRow(
|
||||
selectedTabIndex = focusTabIndex,
|
||||
selectedTabIndex = selectedTabIndex,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 32.dp, top = 16.dp, bottom = 16.dp)
|
||||
.focusRestorer(firstTabFocusRequester)
|
||||
.onFocusChanged {
|
||||
if (!it.isFocused) {
|
||||
focusTabIndex = selectedTabIndex
|
||||
}
|
||||
},
|
||||
indicator =
|
||||
@Composable { tabPositions, doesTabRowHaveFocus ->
|
||||
tabPositions.getOrNull(focusTabIndex)?.let { currentTabPosition ->
|
||||
// TabRowDefaults.PillIndicator(
|
||||
// currentTabPosition = currentTabPosition,
|
||||
// doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||
// )
|
||||
TabRowDefaults.UnderlinedIndicator(
|
||||
currentTabPosition = currentTabPosition,
|
||||
doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||
activeColor = MaterialTheme.colorScheme.border,
|
||||
)
|
||||
}
|
||||
},
|
||||
tabs = {
|
||||
tabs.forEachIndexed { index, tabId ->
|
||||
Tab(
|
||||
selected = focusTabIndex == index,
|
||||
onClick = { selectedTabIndex = index },
|
||||
onFocus = { focusTabIndex = index },
|
||||
colors =
|
||||
TabDefaults.pillIndicatorTabColors(
|
||||
focusedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = tabId.title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(8.dp)
|
||||
.ifElse(
|
||||
index == selectedTabIndex,
|
||||
Modifier.focusRequester(firstTabFocusRequester),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
.focusRequester(firstTabFocusRequester),
|
||||
tabs = tabs.map { it.title },
|
||||
onClick = { selectedTabIndex = it },
|
||||
)
|
||||
}
|
||||
when (selectedTabIndex) {
|
||||
|
|
|
|||
|
|
@ -19,17 +19,9 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Tab
|
||||
import androidx.tv.material3.TabDefaults
|
||||
import androidx.tv.material3.TabRow
|
||||
import androidx.tv.material3.TabRowDefaults
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||
|
|
@ -38,9 +30,9 @@ import com.github.damontecres.wholphin.ui.components.CollectionFolderGrid
|
|||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.GenreCardGrid
|
||||
import com.github.damontecres.wholphin.ui.components.RecommendedMovie
|
||||
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||
import com.github.damontecres.wholphin.ui.data.MovieSortOptions
|
||||
import com.github.damontecres.wholphin.ui.data.VideoSortOptions
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
|
|
@ -63,7 +55,6 @@ fun CollectionFolderMovie(
|
|||
stringResource(R.string.collections),
|
||||
stringResource(R.string.genres),
|
||||
)
|
||||
var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||
var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
|
|
@ -90,55 +81,13 @@ fun CollectionFolderMovie(
|
|||
exit = slideOutVertically() + fadeOut(),
|
||||
) {
|
||||
TabRow(
|
||||
selectedTabIndex = focusTabIndex,
|
||||
selectedTabIndex = selectedTabIndex,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 32.dp, top = 16.dp, bottom = 16.dp)
|
||||
.focusRestorer(firstTabFocusRequester)
|
||||
.onFocusChanged {
|
||||
if (!it.isFocused) {
|
||||
focusTabIndex = selectedTabIndex
|
||||
}
|
||||
},
|
||||
indicator =
|
||||
@Composable { tabPositions, doesTabRowHaveFocus ->
|
||||
tabPositions.getOrNull(focusTabIndex)?.let { currentTabPosition ->
|
||||
// TabRowDefaults.PillIndicator(
|
||||
// currentTabPosition = currentTabPosition,
|
||||
// doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||
// )
|
||||
TabRowDefaults.UnderlinedIndicator(
|
||||
currentTabPosition = currentTabPosition,
|
||||
doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||
activeColor = MaterialTheme.colorScheme.border,
|
||||
)
|
||||
}
|
||||
},
|
||||
tabs = {
|
||||
tabs.forEachIndexed { index, title ->
|
||||
Tab(
|
||||
selected = focusTabIndex == index,
|
||||
onClick = { selectedTabIndex = index },
|
||||
onFocus = { focusTabIndex = index },
|
||||
colors =
|
||||
TabDefaults.pillIndicatorTabColors(
|
||||
focusedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(8.dp)
|
||||
.ifElse(
|
||||
index == selectedTabIndex,
|
||||
Modifier.focusRequester(firstTabFocusRequester),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
.focusRequester(firstTabFocusRequester),
|
||||
tabs = tabs,
|
||||
onClick = { selectedTabIndex = it },
|
||||
)
|
||||
}
|
||||
when (selectedTabIndex) {
|
||||
|
|
|
|||
|
|
@ -19,17 +19,9 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Tab
|
||||
import androidx.tv.material3.TabDefaults
|
||||
import androidx.tv.material3.TabRow
|
||||
import androidx.tv.material3.TabRowDefaults
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||
|
|
@ -38,8 +30,8 @@ import com.github.damontecres.wholphin.ui.components.CollectionFolderGrid
|
|||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.GenreCardGrid
|
||||
import com.github.damontecres.wholphin.ui.components.RecommendedTvShow
|
||||
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||
import com.github.damontecres.wholphin.ui.data.SeriesSortOptions
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
|
|
@ -61,7 +53,6 @@ fun CollectionFolderTv(
|
|||
stringResource(R.string.library),
|
||||
stringResource(R.string.genres),
|
||||
)
|
||||
var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||
var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
|
|
@ -88,55 +79,13 @@ fun CollectionFolderTv(
|
|||
exit = slideOutVertically() + fadeOut(),
|
||||
) {
|
||||
TabRow(
|
||||
selectedTabIndex = focusTabIndex,
|
||||
selectedTabIndex = selectedTabIndex,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 32.dp, top = 16.dp, bottom = 16.dp)
|
||||
.focusRestorer(firstTabFocusRequester)
|
||||
.onFocusChanged {
|
||||
if (!it.isFocused) {
|
||||
focusTabIndex = selectedTabIndex
|
||||
}
|
||||
},
|
||||
indicator =
|
||||
@Composable { tabPositions, doesTabRowHaveFocus ->
|
||||
tabPositions.getOrNull(focusTabIndex)?.let { currentTabPosition ->
|
||||
// TabRowDefaults.PillIndicator(
|
||||
// currentTabPosition = currentTabPosition,
|
||||
// doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||
// )
|
||||
TabRowDefaults.UnderlinedIndicator(
|
||||
currentTabPosition = currentTabPosition,
|
||||
doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||
activeColor = MaterialTheme.colorScheme.border,
|
||||
)
|
||||
}
|
||||
},
|
||||
tabs = {
|
||||
tabs.forEachIndexed { index, title ->
|
||||
Tab(
|
||||
selected = focusTabIndex == index,
|
||||
onClick = { selectedTabIndex = index },
|
||||
onFocus = { focusTabIndex = index },
|
||||
colors =
|
||||
TabDefaults.pillIndicatorTabColors(
|
||||
focusedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(8.dp)
|
||||
.ifElse(
|
||||
index == selectedTabIndex,
|
||||
Modifier.focusRequester(firstTabFocusRequester),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
.focusRequester(firstTabFocusRequester),
|
||||
tabs = tabs,
|
||||
onClick = { selectedTabIndex = it },
|
||||
)
|
||||
}
|
||||
when (selectedTabIndex) {
|
||||
|
|
|
|||
|
|
@ -19,23 +19,16 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Tab
|
||||
import androidx.tv.material3.TabDefaults
|
||||
import androidx.tv.material3.TabRow
|
||||
import androidx.tv.material3.TabRowDefaults
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.ui.components.CollectionFolderGrid
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||
import com.github.damontecres.wholphin.ui.data.EpisodeSortOptions
|
||||
import com.github.damontecres.wholphin.ui.data.MovieSortOptions
|
||||
import com.github.damontecres.wholphin.ui.data.SeriesSortOptions
|
||||
|
|
@ -66,14 +59,9 @@ fun FavoritesPage(
|
|||
stringResource(R.string.tv_shows),
|
||||
stringResource(R.string.episodes),
|
||||
)
|
||||
val tabFocusRequesters = remember { List(tabs.size) { FocusRequester() } }
|
||||
var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||
var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
tabFocusRequesters.getOrNull(selectedTabIndex)?.tryRequestFocus()
|
||||
}
|
||||
LaunchedEffect(selectedTabIndex) {
|
||||
preferencesViewModel.saveRememberedTab(
|
||||
preferences,
|
||||
|
|
@ -97,52 +85,12 @@ fun FavoritesPage(
|
|||
exit = slideOutVertically() + fadeOut(),
|
||||
) {
|
||||
TabRow(
|
||||
selectedTabIndex = focusTabIndex,
|
||||
selectedTabIndex = selectedTabIndex,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(start = 32.dp, top = 16.dp, bottom = 16.dp)
|
||||
.focusRestorer(tabFocusRequesters[0])
|
||||
.onFocusChanged {
|
||||
if (!it.isFocused) {
|
||||
focusTabIndex = selectedTabIndex
|
||||
}
|
||||
},
|
||||
indicator =
|
||||
@Composable { tabPositions, doesTabRowHaveFocus ->
|
||||
tabPositions.getOrNull(focusTabIndex)?.let { currentTabPosition ->
|
||||
// TabRowDefaults.PillIndicator(
|
||||
// currentTabPosition = currentTabPosition,
|
||||
// doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||
// )
|
||||
TabRowDefaults.UnderlinedIndicator(
|
||||
currentTabPosition = currentTabPosition,
|
||||
doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||
activeColor = MaterialTheme.colorScheme.border,
|
||||
)
|
||||
}
|
||||
},
|
||||
tabs = {
|
||||
tabs.forEachIndexed { index, title ->
|
||||
Tab(
|
||||
selected = focusTabIndex == index,
|
||||
onClick = { selectedTabIndex = index },
|
||||
onFocus = { focusTabIndex = index },
|
||||
colors =
|
||||
TabDefaults.pillIndicatorTabColors(
|
||||
focusedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(8.dp)
|
||||
.focusRequester(tabFocusRequesters[index]),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
.padding(start = 32.dp, top = 16.dp, bottom = 16.dp),
|
||||
tabs = tabs,
|
||||
onClick = { selectedTabIndex = it },
|
||||
)
|
||||
}
|
||||
when (selectedTabIndex) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.github.damontecres.wholphin.ui.detail
|
|||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
|
@ -9,6 +11,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -17,6 +21,7 @@ import androidx.compose.runtime.livedata.observeAsState
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
|
|
@ -322,9 +327,11 @@ fun PersonHeader(
|
|||
overviewOnClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp),
|
||||
modifier = modifier,
|
||||
modifier = modifier.bringIntoViewRequester(bringIntoViewRequester),
|
||||
) {
|
||||
AsyncImage(
|
||||
model = imageUrl,
|
||||
|
|
@ -414,10 +421,18 @@ fun PersonHeader(
|
|||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
if (focused) {
|
||||
LaunchedEffect(Unit) {
|
||||
bringIntoViewRequester.bringIntoView()
|
||||
}
|
||||
}
|
||||
OverviewText(
|
||||
overview = overview ?: "",
|
||||
maxLines = 3,
|
||||
onClick = overviewOnClick,
|
||||
interactionSource = interactionSource,
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
|||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -34,8 +33,7 @@ import com.github.damontecres.wholphin.data.model.chooseStream
|
|||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.ui.components.DotSeparatedRow
|
||||
import com.github.damontecres.wholphin.ui.components.OverviewText
|
||||
import com.github.damontecres.wholphin.ui.components.StarRating
|
||||
import com.github.damontecres.wholphin.ui.components.StarRatingPrecision
|
||||
import com.github.damontecres.wholphin.ui.components.SimpleStarRating
|
||||
import com.github.damontecres.wholphin.ui.components.TitleValueText
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.roundMinutes
|
||||
|
|
@ -102,24 +100,13 @@ fun MovieDetailsHeader(
|
|||
DotSeparatedRow(
|
||||
texts = details,
|
||||
textStyle = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
dto.communityRating?.let {
|
||||
if (it > 0f) {
|
||||
StarRating(
|
||||
rating100 = (it * 10).toInt(),
|
||||
onRatingChange = {},
|
||||
enabled = false,
|
||||
precision = StarRatingPrecision.HALF,
|
||||
playSoundOnFocus = true,
|
||||
modifier = Modifier.height(32.dp),
|
||||
)
|
||||
} else {
|
||||
Spacer(Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
|
||||
SimpleStarRating(
|
||||
dto.communityRating,
|
||||
Modifier.height(20.dp),
|
||||
)
|
||||
dto.taglines?.firstOrNull()?.let { tagline ->
|
||||
Text(
|
||||
text = tagline,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.github.damontecres.wholphin.ui.detail.series
|
|||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -16,8 +15,7 @@ import androidx.tv.material3.Text
|
|||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.ui.components.DotSeparatedRow
|
||||
import com.github.damontecres.wholphin.ui.components.OverviewText
|
||||
import com.github.damontecres.wholphin.ui.components.StarRating
|
||||
import com.github.damontecres.wholphin.ui.components.StarRatingPrecision
|
||||
import com.github.damontecres.wholphin.ui.components.SimpleStarRating
|
||||
import com.github.damontecres.wholphin.ui.roundMinutes
|
||||
import com.github.damontecres.wholphin.ui.timeRemaining
|
||||
import com.github.damontecres.wholphin.util.formatDateTime
|
||||
|
|
@ -69,20 +67,10 @@ fun FocusedEpisodeHeader(
|
|||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
dto.communityRating?.let {
|
||||
if (it > 0f) {
|
||||
StarRating(
|
||||
rating100 = (it * 10).toInt(),
|
||||
onRatingChange = {},
|
||||
enabled = false,
|
||||
precision = StarRatingPrecision.HALF,
|
||||
playSoundOnFocus = true,
|
||||
modifier = Modifier.height(24.dp),
|
||||
)
|
||||
} else {
|
||||
Spacer(Modifier.height(24.dp))
|
||||
}
|
||||
} ?: Spacer(Modifier.height(24.dp))
|
||||
SimpleStarRating(
|
||||
dto.communityRating,
|
||||
Modifier.height(20.dp),
|
||||
)
|
||||
}
|
||||
OverviewText(
|
||||
overview = dto.overview ?: "",
|
||||
|
|
|
|||
|
|
@ -61,8 +61,7 @@ import com.github.damontecres.wholphin.ui.components.ExpandableFaButton
|
|||
import com.github.damontecres.wholphin.ui.components.ExpandablePlayButton
|
||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||
import com.github.damontecres.wholphin.ui.components.OverviewText
|
||||
import com.github.damontecres.wholphin.ui.components.StarRating
|
||||
import com.github.damontecres.wholphin.ui.components.StarRatingPrecision
|
||||
import com.github.damontecres.wholphin.ui.components.SimpleStarRating
|
||||
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog
|
||||
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
|
|
@ -467,18 +466,10 @@ fun SeriesDetailsHeader(
|
|||
)
|
||||
}
|
||||
|
||||
dto.communityRating?.let {
|
||||
if (it >= 0f) {
|
||||
StarRating(
|
||||
rating100 = (it * 10).toInt(),
|
||||
onRatingChange = {},
|
||||
enabled = false,
|
||||
precision = StarRatingPrecision.HALF,
|
||||
playSoundOnFocus = true,
|
||||
modifier = Modifier.height(24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
SimpleStarRating(
|
||||
dto.communityRating,
|
||||
Modifier.height(20.dp),
|
||||
)
|
||||
|
||||
dto.overview?.let { overview ->
|
||||
OverviewText(
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import androidx.compose.ui.draw.drawWithContent
|
|||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
|
|
@ -37,10 +36,6 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Tab
|
||||
import androidx.tv.material3.TabDefaults
|
||||
import androidx.tv.material3.TabRow
|
||||
import androidx.tv.material3.TabRowDefaults
|
||||
import androidx.tv.material3.Text
|
||||
import coil3.compose.AsyncImage
|
||||
import com.github.damontecres.wholphin.R
|
||||
|
|
@ -52,6 +47,7 @@ import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
|||
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
|
|
@ -80,10 +76,7 @@ fun SeriesOverviewContent(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||
var focusTabIndex by rememberSaveable(position) { mutableIntStateOf(position.seasonTabIndex) }
|
||||
var selectedTabIndex by rememberSaveable(position) { mutableIntStateOf(position.seasonTabIndex) }
|
||||
val focusRequesters = remember(seasons.size) { List(seasons.size) { FocusRequester() } }
|
||||
var resolvedTabIndex by remember { mutableIntStateOf(selectedTabIndex) }
|
||||
val tabRowFocusRequester = remember { FocusRequester() }
|
||||
|
||||
val focusedEpisode =
|
||||
|
|
@ -139,62 +132,22 @@ fun SeriesOverviewContent(
|
|||
PaddingValues(start = 16.dp, end = 16.dp)
|
||||
}
|
||||
TabRow(
|
||||
selectedTabIndex = focusTabIndex,
|
||||
selectedTabIndex = selectedTabIndex,
|
||||
tabs =
|
||||
seasons.mapNotNull {
|
||||
it?.name
|
||||
?: (stringResource(R.string.tv_season) + " ${it?.data?.indexNumber}")
|
||||
},
|
||||
onClick = {
|
||||
selectedTabIndex = it
|
||||
onFocus.invoke(SeriesOverviewPosition(it, 0))
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.ifElse(
|
||||
focusRequesters.size > selectedTabIndex,
|
||||
{ Modifier.focusRestorer(focusRequesters[selectedTabIndex]) },
|
||||
).focusRequester(tabRowFocusRequester)
|
||||
.focusRequester(tabRowFocusRequester)
|
||||
.padding(paddingValues)
|
||||
.fillMaxWidth()
|
||||
.onFocusChanged {
|
||||
if (!it.isFocused) {
|
||||
focusTabIndex = selectedTabIndex
|
||||
}
|
||||
},
|
||||
indicator =
|
||||
@Composable { tabPositions, doesTabRowHaveFocus ->
|
||||
tabPositions.getOrNull(focusTabIndex)?.let { currentTabPosition ->
|
||||
// TabRowDefaults.PillIndicator(
|
||||
// currentTabPosition = currentTabPosition,
|
||||
// doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||
// )
|
||||
TabRowDefaults.UnderlinedIndicator(
|
||||
currentTabPosition = currentTabPosition,
|
||||
doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||
activeColor = MaterialTheme.colorScheme.border,
|
||||
)
|
||||
}
|
||||
},
|
||||
) {
|
||||
seasons.forEachIndexed { index, season ->
|
||||
season?.let { season ->
|
||||
Tab(
|
||||
selected = focusTabIndex == index,
|
||||
onFocus = { focusTabIndex = index },
|
||||
onClick = {
|
||||
selectedTabIndex = index
|
||||
onFocus.invoke(SeriesOverviewPosition(index, 0))
|
||||
},
|
||||
colors =
|
||||
TabDefaults.pillIndicatorTabColors(
|
||||
focusedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.focusRequester(focusRequesters[index]),
|
||||
) {
|
||||
Text(
|
||||
text =
|
||||
season.name
|
||||
?: (stringResource(R.string.tv_season) + " ${season.data.indexNumber}"),
|
||||
modifier = Modifier.padding(8.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
series.name?.let {
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class HomeViewModel
|
|||
if (resume.isNotEmpty()) {
|
||||
add(
|
||||
HomeRow(
|
||||
titleRes = R.string.recently_added,
|
||||
titleRes = R.string.continue_watching,
|
||||
items = resume,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ class PlaybackKeyHandler(
|
|||
private val controllerViewState: ControllerViewState,
|
||||
private val updateSkipIndicator: (Long) -> Unit,
|
||||
private val skipBackOnResume: Duration?,
|
||||
private val oneClickPause: Boolean,
|
||||
private val onInteraction: () -> Unit,
|
||||
) {
|
||||
fun onKeyEvent(it: KeyEvent): Boolean {
|
||||
|
|
@ -33,7 +34,7 @@ class PlaybackKeyHandler(
|
|||
result = false
|
||||
} else if (it.type != KeyEventType.KeyUp) {
|
||||
result = false
|
||||
} else if (isDpad(it)) {
|
||||
} else if (isDirectionalDpad(it) || isEnterKey(it)) {
|
||||
if (!controllerViewState.controlsVisible) {
|
||||
if (skipWithLeftRight && it.key == Key.DirectionLeft) {
|
||||
updateSkipIndicator(-seekBack.inWholeMilliseconds)
|
||||
|
|
@ -41,6 +42,15 @@ class PlaybackKeyHandler(
|
|||
} else if (skipWithLeftRight && it.key == Key.DirectionRight) {
|
||||
player.seekForward(seekForward)
|
||||
updateSkipIndicator(seekForward.inWholeMilliseconds)
|
||||
} else if (oneClickPause && isEnterKey(it)) {
|
||||
Util.handlePlayPauseButtonAction(player)
|
||||
if (!player.isPlaying) {
|
||||
controllerViewState.showControls()
|
||||
} else {
|
||||
skipBackOnResume?.let {
|
||||
player.seekBack(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
controllerViewState.showControls()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ fun PlaybackPage(
|
|||
updateSkipIndicator = updateSkipIndicator,
|
||||
skipBackOnResume = preferences.appPreferences.playbackPreferences.skipBackOnResume,
|
||||
onInteraction = viewModel::reportInteraction,
|
||||
oneClickPause = preferences.appPreferences.playbackPreferences.oneClickPause,
|
||||
)
|
||||
|
||||
val showSegment =
|
||||
|
|
|
|||
|
|
@ -67,8 +67,9 @@ message PlaybackPreferences {
|
|||
int64 pass_out_protection_ms = 16;
|
||||
PrefContentScale global_content_scale = 17;
|
||||
ShowNextUpWhen show_next_up_when = 18;
|
||||
PlayerBackend player_backend = 19;
|
||||
MpvOptions mpv_options = 20;
|
||||
bool one_click_pause = 19;
|
||||
PlayerBackend player_backend = 20;
|
||||
MpvOptions mpv_options = 21;
|
||||
}
|
||||
|
||||
message HomePagePreferences{
|
||||
|
|
|
|||
|
|
@ -142,6 +142,8 @@
|
|||
<string name="aired_episode_order">Aired Episode Order</string>
|
||||
<string name="create_playlist">Create new playlist</string>
|
||||
<string name="add_to_playlist">Add to playlist</string>
|
||||
<string name="one_click_pause">Pause with one click</string>
|
||||
<string name="one_click_pause_summary_on">Press D-Pad center to pause/play</string>
|
||||
<string name="player_backend">Playback Backend</string>
|
||||
<string name="mpv_hardware_decoding">MPV: Use hardware decoding</string>
|
||||
<string name="disable_if_crash">Disable if you experience crashes</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue