mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Fix focus issues & add clear image cache option
This commit is contained in:
parent
370cd2c146
commit
4113f990b2
8 changed files with 50 additions and 22 deletions
|
|
@ -439,6 +439,13 @@ sealed interface AppPreference<T> {
|
||||||
indexToValue = { SkipSegmentBehavior.forNumber(it) },
|
indexToValue = { SkipSegmentBehavior.forNumber(it) },
|
||||||
valueToIndex = { it.number },
|
valueToIndex = { it.number },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val ClearImageCache =
|
||||||
|
AppClickablePreference(
|
||||||
|
title = R.string.clear_image_cache,
|
||||||
|
getter = { },
|
||||||
|
setter = { prefs, _ -> prefs },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -513,6 +520,7 @@ val advancedPreferences =
|
||||||
title = R.string.more,
|
title = R.string.more,
|
||||||
preferences =
|
preferences =
|
||||||
listOf(
|
listOf(
|
||||||
|
AppPreference.ClearImageCache,
|
||||||
AppPreference.OssLicenseInfo,
|
AppPreference.OssLicenseInfo,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import androidx.compose.runtime.remember
|
||||||
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.FocusState
|
import androidx.compose.ui.focus.FocusState
|
||||||
import androidx.compose.ui.focus.focusProperties
|
|
||||||
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.focus.onFocusChanged
|
||||||
|
|
@ -29,7 +28,6 @@ import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.dolphin.R
|
import com.github.damontecres.dolphin.R
|
||||||
import com.github.damontecres.dolphin.ui.PreviewTvSpec
|
import com.github.damontecres.dolphin.ui.PreviewTvSpec
|
||||||
import com.github.damontecres.dolphin.ui.theme.DolphinTheme
|
import com.github.damontecres.dolphin.ui.theme.DolphinTheme
|
||||||
import com.github.damontecres.dolphin.ui.tryRequestFocus
|
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
|
@ -139,14 +137,10 @@ fun ExpandablePlayButtons(
|
||||||
modifier =
|
modifier =
|
||||||
modifier
|
modifier
|
||||||
.focusGroup()
|
.focusGroup()
|
||||||
.focusProperties {
|
.focusRestorer(firstFocus),
|
||||||
onEnter = {
|
|
||||||
firstFocus.tryRequestFocus()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
if (resumePosition > Duration.ZERO) {
|
if (resumePosition > Duration.ZERO) {
|
||||||
item {
|
item("play") {
|
||||||
ExpandablePlayButton(
|
ExpandablePlayButton(
|
||||||
R.string.resume,
|
R.string.resume,
|
||||||
resumePosition,
|
resumePosition,
|
||||||
|
|
@ -157,7 +151,7 @@ fun ExpandablePlayButtons(
|
||||||
.focusRequester(firstFocus),
|
.focusRequester(firstFocus),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item("restart") {
|
||||||
ExpandablePlayButton(
|
ExpandablePlayButton(
|
||||||
R.string.restart,
|
R.string.restart,
|
||||||
Duration.ZERO,
|
Duration.ZERO,
|
||||||
|
|
@ -168,7 +162,7 @@ fun ExpandablePlayButtons(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
item {
|
item("play") {
|
||||||
ExpandablePlayButton(
|
ExpandablePlayButton(
|
||||||
R.string.play,
|
R.string.play,
|
||||||
Duration.ZERO,
|
Duration.ZERO,
|
||||||
|
|
@ -182,7 +176,7 @@ fun ExpandablePlayButtons(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watched button
|
// Watched button
|
||||||
item {
|
item("watched") {
|
||||||
ExpandableFaButton(
|
ExpandableFaButton(
|
||||||
title = if (watched) R.string.mark_unwatched else R.string.mark_watched,
|
title = if (watched) R.string.mark_unwatched else R.string.mark_watched,
|
||||||
iconStringRes = if (watched) R.string.fa_eye else R.string.fa_eye_slash,
|
iconStringRes = if (watched) R.string.fa_eye else R.string.fa_eye_slash,
|
||||||
|
|
@ -192,7 +186,7 @@ fun ExpandablePlayButtons(
|
||||||
}
|
}
|
||||||
|
|
||||||
// More button
|
// More button
|
||||||
item {
|
item("more") {
|
||||||
ExpandablePlayButton(
|
ExpandablePlayButton(
|
||||||
R.string.more,
|
R.string.more,
|
||||||
Duration.ZERO,
|
Duration.ZERO,
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ fun FocusedEpisodeHeader(
|
||||||
Text(
|
Text(
|
||||||
text = dto.episodeTitle ?: dto.name ?: "",
|
text = dto.episodeTitle ?: dto.name ?: "",
|
||||||
style = MaterialTheme.typography.headlineSmall,
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
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.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.key
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
|
@ -34,6 +33,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.text.style.TextOverflow
|
||||||
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.Tab
|
import androidx.tv.material3.Tab
|
||||||
|
|
@ -77,8 +77,6 @@ fun SeriesOverviewContent(
|
||||||
val tabRowFocusRequester = remember { FocusRequester() }
|
val tabRowFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
val focusedEpisode = episodes.getOrNull(position.episodeRowIndex)
|
val focusedEpisode = episodes.getOrNull(position.episodeRowIndex)
|
||||||
LaunchedEffect(position) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier =
|
modifier =
|
||||||
|
|
@ -184,6 +182,8 @@ fun SeriesOverviewContent(
|
||||||
Text(
|
Text(
|
||||||
text = it,
|
text = it,
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -249,12 +249,16 @@ fun MainPageHeader(
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.SemiBold),
|
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.SemiBold),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
subtitle?.let {
|
subtitle?.let {
|
||||||
Text(
|
Text(
|
||||||
text = subtitle,
|
text = subtitle,
|
||||||
style = MaterialTheme.typography.headlineSmall,
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (details.isNotEmpty()) {
|
if (details.isNotEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -280,10 +280,11 @@ class PlaybackViewModel
|
||||||
Timber.i("No change in playback for changeStreams")
|
Timber.i("No change in playback for changeStreams")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// TODO if the new audio or subtitle index is already in the streams (eg direct play), should toggle in the player instead
|
||||||
val maxBitrate =
|
val maxBitrate =
|
||||||
preferences.appPreferences.playbackPreferences.maxBitrate
|
preferences.appPreferences.playbackPreferences.maxBitrate
|
||||||
.takeIf { it > 0 } ?: AppPreference.DEFAULT_BITRATE
|
.takeIf { it > 0 } ?: AppPreference.DEFAULT_BITRATE
|
||||||
val response =
|
val response by
|
||||||
api.mediaInfoApi
|
api.mediaInfoApi
|
||||||
.getPostedPlaybackInfo(
|
.getPostedPlaybackInfo(
|
||||||
itemId,
|
itemId,
|
||||||
|
|
@ -302,7 +303,7 @@ class PlaybackViewModel
|
||||||
alwaysBurnInSubtitleWhenTranscoding = null,
|
alwaysBurnInSubtitleWhenTranscoding = null,
|
||||||
maxStreamingBitrate = maxBitrate.toInt(),
|
maxStreamingBitrate = maxBitrate.toInt(),
|
||||||
),
|
),
|
||||||
).content
|
)
|
||||||
val source = response.mediaSources.firstOrNull()
|
val source = response.mediaSources.firstOrNull()
|
||||||
source?.let { source ->
|
source?.let { source ->
|
||||||
val mediaUrl =
|
val mediaUrl =
|
||||||
|
|
@ -335,8 +336,8 @@ class PlaybackViewModel
|
||||||
?.let {
|
?.let {
|
||||||
it.deliveryUrl?.let { deliveryUrl ->
|
it.deliveryUrl?.let { deliveryUrl ->
|
||||||
var flags = 0
|
var flags = 0
|
||||||
if (it.isForced) flags = flags.and(C.SELECTION_FLAG_FORCED)
|
if (it.isForced) flags = flags.or(C.SELECTION_FLAG_FORCED)
|
||||||
if (it.isDefault) flags = flags.and(C.SELECTION_FLAG_DEFAULT)
|
if (it.isDefault) flags = flags.or(C.SELECTION_FLAG_DEFAULT)
|
||||||
MediaItem.SubtitleConfiguration
|
MediaItem.SubtitleConfiguration
|
||||||
.Builder(
|
.Builder(
|
||||||
api.createUrl(deliveryUrl).toUri(),
|
api.createUrl(deliveryUrl).toUri(),
|
||||||
|
|
@ -368,6 +369,7 @@ class PlaybackViewModel
|
||||||
)
|
)
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
// TODO, don't need to release & recreate when switching streams
|
||||||
this@PlaybackViewModel.activityListener?.let {
|
this@PlaybackViewModel.activityListener?.let {
|
||||||
it.release()
|
it.release()
|
||||||
player.removeListener(it)
|
player.removeListener(it)
|
||||||
|
|
@ -389,7 +391,7 @@ class PlaybackViewModel
|
||||||
positionMs,
|
positionMs,
|
||||||
)
|
)
|
||||||
if (audioIndex != null || subtitleIndex != null) {
|
if (audioIndex != null || subtitleIndex != null) {
|
||||||
val trackActivationListener =
|
val onTracksChangedListener =
|
||||||
object : Player.Listener {
|
object : Player.Listener {
|
||||||
override fun onTracksChanged(tracks: Tracks) {
|
override fun onTracksChanged(tracks: Tracks) {
|
||||||
Timber.v("onTracksChanged: $tracks")
|
Timber.v("onTracksChanged: $tracks")
|
||||||
|
|
@ -408,7 +410,7 @@ class PlaybackViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
player.addListener(trackActivationListener)
|
player.addListener(onTracksChangedListener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val trickPlayInfo =
|
val trickPlayInfo =
|
||||||
|
|
@ -416,7 +418,7 @@ class PlaybackViewModel
|
||||||
?.get(source.id)
|
?.get(source.id)
|
||||||
?.values
|
?.values
|
||||||
?.firstOrNull()
|
?.firstOrNull()
|
||||||
Timber.v("Trickplay info: $trickPlayInfo")
|
// Timber.v("Trickplay info: $trickPlayInfo")
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
trickplay.value = trickPlayInfo
|
trickplay.value = trickPlayInfo
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import androidx.tv.material3.surfaceColorAtElevation
|
import androidx.tv.material3.surfaceColorAtElevation
|
||||||
|
import coil3.SingletonImageLoader
|
||||||
import com.github.damontecres.dolphin.R
|
import com.github.damontecres.dolphin.R
|
||||||
import com.github.damontecres.dolphin.preferences.AppPreference
|
import com.github.damontecres.dolphin.preferences.AppPreference
|
||||||
import com.github.damontecres.dolphin.preferences.AppPreferences
|
import com.github.damontecres.dolphin.preferences.AppPreferences
|
||||||
|
|
@ -245,6 +246,22 @@ fun PreferencesContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppPreference.ClearImageCache -> {
|
||||||
|
ClickPreference(
|
||||||
|
title = stringResource(pref.title),
|
||||||
|
onClick = {
|
||||||
|
SingletonImageLoader.get(context).let {
|
||||||
|
it.memoryCache?.clear()
|
||||||
|
it.diskCache?.clear()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier,
|
||||||
|
summary = null,
|
||||||
|
onLongClick = {},
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val value = pref.getter.invoke(preferences)
|
val value = pref.getter.invoke(preferences)
|
||||||
ComposablePreference(
|
ComposablePreference(
|
||||||
|
|
|
||||||
|
|
@ -74,5 +74,6 @@
|
||||||
<string name="update_url_summary">URL used to check for app updates</string>
|
<string name="update_url_summary">URL used to check for app updates</string>
|
||||||
<string name="updates">Updates</string>
|
<string name="updates">Updates</string>
|
||||||
<string name="no_update_available">No update available</string>
|
<string name="no_update_available">No update available</string>
|
||||||
|
<string name="clear_image_cache">Clear image cache</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue