mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Add prefer logos instead of text titles setting (#1151)
## Description Adds an advanced settings to prefer logos for titles instead of text. It is enabled by default. ### Related issues Closes #383 ### Testing Emulator & onn ## Screenshots <details><summary>Click to show</summary> <p> ### Home page  ### Series overview  ### Movie Page  ### Square-ish logo  </p> </details> ## AI or LLM usage None
This commit is contained in:
parent
253b203c02
commit
2485d2c644
27 changed files with 290 additions and 122 deletions
|
|
@ -501,6 +501,18 @@ sealed interface AppPreference<Pref, T> {
|
||||||
valueToIndex = { if (it != AppThemeColors.UNRECOGNIZED) it.number else 0 },
|
valueToIndex = { if (it != AppThemeColors.UNRECOGNIZED) it.number else 0 },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val ShowLogos =
|
||||||
|
AppSwitchPreference<AppPreferences>(
|
||||||
|
title = R.string.prefer_logos,
|
||||||
|
defaultValue = true,
|
||||||
|
getter = { it.interfacePreferences.showLogos },
|
||||||
|
setter = { prefs, value ->
|
||||||
|
prefs.updateInterfacePreferences { showLogos = value }
|
||||||
|
},
|
||||||
|
summaryOn = R.string.enabled,
|
||||||
|
summaryOff = R.string.disabled,
|
||||||
|
)
|
||||||
|
|
||||||
val InstalledVersion =
|
val InstalledVersion =
|
||||||
AppClickablePreference<AppPreferences>(
|
AppClickablePreference<AppPreferences>(
|
||||||
title = R.string.installed_version,
|
title = R.string.installed_version,
|
||||||
|
|
@ -1122,6 +1134,7 @@ val advancedPreferences =
|
||||||
preferences =
|
preferences =
|
||||||
listOf(
|
listOf(
|
||||||
AppPreference.ShowClock,
|
AppPreference.ShowClock,
|
||||||
|
AppPreference.ShowLogos,
|
||||||
AppPreference.ManageMedia,
|
AppPreference.ManageMedia,
|
||||||
AppPreference.CombineContinueNext,
|
AppPreference.CombineContinueNext,
|
||||||
// Temporarily disabled, see https://github.com/damontecres/Wholphin/pull/127#issuecomment-3478058418
|
// Temporarily disabled, see https://github.com/damontecres/Wholphin/pull/127#issuecomment-3478058418
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ class AppPreferencesSerializer
|
||||||
AppPreference.NavDrawerSwitchOnFocus.defaultValue
|
AppPreference.NavDrawerSwitchOnFocus.defaultValue
|
||||||
showClock = AppPreference.ShowClock.defaultValue
|
showClock = AppPreference.ShowClock.defaultValue
|
||||||
backdropStyle = AppPreference.BackdropStylePref.defaultValue
|
backdropStyle = AppPreference.BackdropStylePref.defaultValue
|
||||||
|
showLogos = AppPreference.ShowLogos.defaultValue
|
||||||
|
|
||||||
subtitlesPreferences =
|
subtitlesPreferences =
|
||||||
SubtitlePreferences
|
SubtitlePreferences
|
||||||
|
|
|
||||||
|
|
@ -282,7 +282,7 @@ class AppUpgradeHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previous.isEqualOrBefore(Version.fromString("0.6.0-0-g0"))) {
|
if (previous.isEqualOrBefore(Version.fromString("0.5.4-6-g0"))) {
|
||||||
appPreferences.updateData {
|
appPreferences.updateData {
|
||||||
it.updateMusicPreferences {
|
it.updateMusicPreferences {
|
||||||
showBackdrop = true
|
showBackdrop = true
|
||||||
|
|
@ -291,5 +291,13 @@ class AppUpgradeHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previous.isEqualOrBefore(Version.fromString("0.5.4-15-g0"))) {
|
||||||
|
appPreferences.updateData {
|
||||||
|
it.updateInterfacePreferences {
|
||||||
|
showLogos = AppPreference.ShowLogos.defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -886,7 +886,7 @@ class HomeSettingsService
|
||||||
limit = limit,
|
limit = limit,
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
enableImages = true,
|
enableImages = true,
|
||||||
enableImageTypes = listOf(ImageType.PRIMARY),
|
enableImageTypes = listOf(ImageType.PRIMARY, ImageType.LOGO),
|
||||||
imageTypeLimit = 1,
|
imageTypeLimit = 1,
|
||||||
)
|
)
|
||||||
api.liveTvApi
|
api.liveTvApi
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||||
import org.jellyfin.sdk.api.client.extensions.userViewsApi
|
import org.jellyfin.sdk.api.client.extensions.userViewsApi
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.CollectionType
|
import org.jellyfin.sdk.model.api.CollectionType
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
|
||||||
import org.jellyfin.sdk.model.api.UserDto
|
import org.jellyfin.sdk.model.api.UserDto
|
||||||
import org.jellyfin.sdk.model.api.request.GetLatestMediaRequest
|
import org.jellyfin.sdk.model.api.request.GetLatestMediaRequest
|
||||||
import org.jellyfin.sdk.model.api.request.GetNextUpRequest
|
import org.jellyfin.sdk.model.api.request.GetNextUpRequest
|
||||||
|
|
@ -61,12 +60,13 @@ class LatestNextUpService
|
||||||
remove(BaseItemKind.EPISODE)
|
remove(BaseItemKind.EPISODE)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enableImageTypes =
|
// enableImageTypes =
|
||||||
listOf(
|
// listOf(
|
||||||
ImageType.PRIMARY,
|
// ImageType.PRIMARY,
|
||||||
ImageType.THUMB,
|
// ImageType.THUMB,
|
||||||
ImageType.BACKDROP,
|
// ImageType.BACKDROP,
|
||||||
),
|
// ImageType.LOGO,
|
||||||
|
// ),
|
||||||
)
|
)
|
||||||
val items =
|
val items =
|
||||||
api.itemsApi
|
api.itemsApi
|
||||||
|
|
@ -98,6 +98,13 @@ class LatestNextUpService
|
||||||
enableUserData = true,
|
enableUserData = true,
|
||||||
enableRewatching = enableRewatching,
|
enableRewatching = enableRewatching,
|
||||||
nextUpDateCutoff = nextUpDateCutoff,
|
nextUpDateCutoff = nextUpDateCutoff,
|
||||||
|
// enableImageTypes =
|
||||||
|
// listOf(
|
||||||
|
// ImageType.PRIMARY,
|
||||||
|
// ImageType.THUMB,
|
||||||
|
// ImageType.BACKDROP,
|
||||||
|
// ImageType.LOGO,
|
||||||
|
// ),
|
||||||
)
|
)
|
||||||
val nextUp =
|
val nextUp =
|
||||||
api.tvShowsApi
|
api.tvShowsApi
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,12 @@ class PlaylistCreator
|
||||||
req =
|
req =
|
||||||
GetItemsRequest(
|
GetItemsRequest(
|
||||||
parentId = item.id,
|
parentId = item.id,
|
||||||
enableImageTypes = listOf(ImageType.PRIMARY, ImageType.THUMB),
|
enableImageTypes =
|
||||||
|
listOf(
|
||||||
|
ImageType.PRIMARY,
|
||||||
|
ImageType.THUMB,
|
||||||
|
ImageType.LOGO,
|
||||||
|
),
|
||||||
includeItemTypes = includeItemTypes,
|
includeItemTypes = includeItemTypes,
|
||||||
recursive = true,
|
recursive = true,
|
||||||
excludeItemIds = listOf(item.id),
|
excludeItemIds = listOf(item.id),
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ fun BannerCardWithTitle(
|
||||||
) {
|
) {
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
val focused by interactionSource.collectIsFocusedAsState()
|
||||||
val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp)
|
val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp)
|
||||||
val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp)
|
val spaceBelow by animateDpAsState(if (focused) 0.dp else 8.dp)
|
||||||
val focusedAfterDelay by rememberFocusedAfterDelay(interactionSource)
|
val focusedAfterDelay by rememberFocusedAfterDelay(interactionSource)
|
||||||
val aspectRationToUse = aspectRatio.coerceAtLeast(AspectRatios.MIN)
|
val aspectRationToUse = aspectRatio.coerceAtLeast(AspectRatios.MIN)
|
||||||
val width = cardHeight * aspectRationToUse
|
val width = cardHeight * aspectRationToUse
|
||||||
|
|
|
||||||
|
|
@ -398,6 +398,7 @@ class CollectionFolderViewModel
|
||||||
ImageType.PRIMARY,
|
ImageType.PRIMARY,
|
||||||
ImageType.THUMB,
|
ImageType.THUMB,
|
||||||
ImageType.BACKDROP,
|
ImageType.BACKDROP,
|
||||||
|
ImageType.LOGO,
|
||||||
),
|
),
|
||||||
includeItemTypes = includeItemTypes,
|
includeItemTypes = includeItemTypes,
|
||||||
recursive = recursive,
|
recursive = recursive,
|
||||||
|
|
@ -960,11 +961,12 @@ fun CollectionFolderGridContent(
|
||||||
AnimatedVisibility(viewOptions.showDetails) {
|
AnimatedVisibility(viewOptions.showDetails) {
|
||||||
HomePageHeader(
|
HomePageHeader(
|
||||||
item = focusedItem,
|
item = focusedItem,
|
||||||
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(200.dp)
|
.height(200.dp)
|
||||||
.padding(top = 48.dp, bottom = 32.dp, start = 8.dp),
|
.padding(HeaderUtils.padding),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
when (val state = loadingState) {
|
when (val state = loadingState) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
object HeaderUtils {
|
||||||
|
val topPadding = 48.dp
|
||||||
|
val bottomPadding = 32.dp
|
||||||
|
val startPadding = 8.dp
|
||||||
|
|
||||||
|
val padding = PaddingValues(top = topPadding, bottom = bottomPadding, start = startPadding)
|
||||||
|
|
||||||
|
val height = 180.dp
|
||||||
|
|
||||||
|
val logoHeight = 60.dp
|
||||||
|
|
||||||
|
val modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(padding)
|
||||||
|
.height(height)
|
||||||
|
}
|
||||||
|
|
@ -207,6 +207,7 @@ fun RecommendedContent(
|
||||||
},
|
},
|
||||||
showClock = preferences.appPreferences.interfacePreferences.showClock,
|
showClock = preferences.appPreferences.interfacePreferences.showClock,
|
||||||
onUpdateBackdrop = viewModel::updateBackdrop,
|
onUpdateBackdrop = viewModel::updateBackdrop,
|
||||||
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.widthIn
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.tv.material3.MaterialTheme
|
||||||
|
import androidx.tv.material3.Text
|
||||||
|
import coil3.compose.AsyncImage
|
||||||
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
import com.github.damontecres.wholphin.ui.LocalImageUrlService
|
||||||
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TitleOrLogo(
|
||||||
|
title: String?,
|
||||||
|
logoImageUrl: String?,
|
||||||
|
showLogo: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
var imageError by remember { mutableStateOf(false) }
|
||||||
|
Box(
|
||||||
|
modifier = modifier.heightIn(max = HeaderUtils.logoHeight),
|
||||||
|
) {
|
||||||
|
if (showLogo && logoImageUrl != null && !imageError) {
|
||||||
|
AsyncImage(
|
||||||
|
model = logoImageUrl,
|
||||||
|
contentDescription = title,
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.height(HeaderUtils.logoHeight)
|
||||||
|
.widthIn(max = 320.dp),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Title(title, Modifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Title(
|
||||||
|
title: String?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title ?: "",
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TitleOrLogo(
|
||||||
|
item: BaseItem?,
|
||||||
|
showLogo: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val logoImageUrl = rememberLogoUrl(item)
|
||||||
|
TitleOrLogo(
|
||||||
|
title = item?.title,
|
||||||
|
logoImageUrl = logoImageUrl,
|
||||||
|
showLogo = showLogo,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun rememberLogoUrl(item: BaseItem?): String? {
|
||||||
|
val imageUrlService = LocalImageUrlService.current
|
||||||
|
return remember(item?.id) {
|
||||||
|
if (item?.type == BaseItemKind.EPISODE && item.data.seriesId != null && item.data.parentLogoImageTag != null) {
|
||||||
|
imageUrlService.getItemImageUrl(item.data.seriesId!!, ImageType.LOGO)
|
||||||
|
} else if (ImageType.LOGO in item?.data?.imageTags.orEmpty()) {
|
||||||
|
imageUrlService.getItemImageUrl(item, ImageType.LOGO)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,6 @@ import androidx.compose.foundation.focusable
|
||||||
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
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
|
@ -50,6 +49,7 @@ import com.github.damontecres.wholphin.ui.components.DialogItem
|
||||||
import com.github.damontecres.wholphin.ui.components.DialogParams
|
import com.github.damontecres.wholphin.ui.components.DialogParams
|
||||||
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
|
import com.github.damontecres.wholphin.ui.components.HeaderUtils
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.components.Optional
|
import com.github.damontecres.wholphin.ui.components.Optional
|
||||||
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
||||||
|
|
@ -379,11 +379,13 @@ fun CollectionDetailsContent(
|
||||||
if (state.viewOptions.cardViewOptions.showDetails) {
|
if (state.viewOptions.cardViewOptions.showDetails) {
|
||||||
HomePageHeader(
|
HomePageHeader(
|
||||||
item = focusedItem,
|
item = focusedItem,
|
||||||
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(top = 48.dp, start = 8.dp)
|
.padding(
|
||||||
.fillMaxHeight(.33f)
|
top = HeaderUtils.topPadding,
|
||||||
.fillMaxWidth(),
|
bottom = 8.dp,
|
||||||
|
).height(HeaderUtils.height),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -412,15 +414,16 @@ fun CollectionDetailsContent(
|
||||||
) {
|
) {
|
||||||
CollectionDetailsHeader(
|
CollectionDetailsHeader(
|
||||||
collection = state.collection!!,
|
collection = state.collection!!,
|
||||||
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
logoImageUrl = state.logoImageUrl,
|
logoImageUrl = state.logoImageUrl,
|
||||||
overviewOnClick = overviewOnClick,
|
overviewOnClick = overviewOnClick,
|
||||||
bringIntoViewRequester = bringIntoViewRequester,
|
bringIntoViewRequester = bringIntoViewRequester,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(top = 48.dp, start = 8.dp)
|
.padding(
|
||||||
// TODO
|
top = HeaderUtils.topPadding,
|
||||||
.fillMaxHeight(.36f)
|
bottom = HeaderUtils.bottomPadding,
|
||||||
.fillMaxWidth(),
|
).height(HeaderUtils.height),
|
||||||
)
|
)
|
||||||
CollectionButtons(
|
CollectionButtons(
|
||||||
state = state,
|
state = state,
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,24 @@ package com.github.damontecres.wholphin.ui.detail.collection
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
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.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
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 coil3.compose.AsyncImage
|
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.ui.components.GenreText
|
import com.github.damontecres.wholphin.ui.components.GenreText
|
||||||
|
import com.github.damontecres.wholphin.ui.components.HeaderUtils
|
||||||
import com.github.damontecres.wholphin.ui.components.OverviewText
|
import com.github.damontecres.wholphin.ui.components.OverviewText
|
||||||
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TitleOrLogo
|
||||||
import com.github.damontecres.wholphin.ui.letNotEmpty
|
import com.github.damontecres.wholphin.ui.letNotEmpty
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -32,6 +28,7 @@ import kotlinx.coroutines.launch
|
||||||
@Composable
|
@Composable
|
||||||
fun CollectionDetailsHeader(
|
fun CollectionDetailsHeader(
|
||||||
collection: BaseItem,
|
collection: BaseItem,
|
||||||
|
showLogo: Boolean,
|
||||||
logoImageUrl: String?,
|
logoImageUrl: String?,
|
||||||
overviewOnClick: () -> Unit,
|
overviewOnClick: () -> Unit,
|
||||||
bringIntoViewRequester: BringIntoViewRequester,
|
bringIntoViewRequester: BringIntoViewRequester,
|
||||||
|
|
@ -44,28 +41,15 @@ fun CollectionDetailsHeader(
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
if (logoImageUrl != null) {
|
TitleOrLogo(
|
||||||
AsyncImage(
|
title = collection.name,
|
||||||
model = logoImageUrl,
|
showLogo = showLogo,
|
||||||
contentDescription = collection.name,
|
logoImageUrl = logoImageUrl,
|
||||||
modifier = Modifier.height(80.dp),
|
modifier =
|
||||||
alignment = Alignment.TopStart,
|
Modifier
|
||||||
contentScale = ContentScale.Fit,
|
.fillMaxWidth(.75f)
|
||||||
)
|
.padding(start = HeaderUtils.startPadding),
|
||||||
} else {
|
)
|
||||||
// Title
|
|
||||||
Text(
|
|
||||||
text = collection.name ?: "",
|
|
||||||
color = MaterialTheme.colorScheme.onBackground,
|
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.SemiBold),
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth(.75f)
|
|
||||||
.padding(start = 8.dp),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
|
@ -74,18 +58,18 @@ fun CollectionDetailsHeader(
|
||||||
QuickDetails(
|
QuickDetails(
|
||||||
collection.ui.quickDetails,
|
collection.ui.quickDetails,
|
||||||
collection.timeRemainingOrRuntime,
|
collection.timeRemainingOrRuntime,
|
||||||
Modifier.padding(start = 8.dp),
|
Modifier.padding(start = HeaderUtils.startPadding),
|
||||||
)
|
)
|
||||||
|
|
||||||
dto.genres?.letNotEmpty {
|
dto.genres?.letNotEmpty {
|
||||||
GenreText(it, Modifier.padding(start = 8.dp))
|
GenreText(it, Modifier.padding(start = HeaderUtils.startPadding))
|
||||||
}
|
}
|
||||||
dto.taglines?.firstOrNull()?.let { tagline ->
|
dto.taglines?.firstOrNull()?.let { tagline ->
|
||||||
Text(
|
Text(
|
||||||
text = tagline,
|
text = tagline,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
fontStyle = FontStyle.Italic,
|
fontStyle = FontStyle.Italic,
|
||||||
modifier = Modifier.padding(start = 8.dp),
|
modifier = Modifier.padding(start = HeaderUtils.startPadding),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ fun CollectionRows(
|
||||||
onUpdateBackdrop = {},
|
onUpdateBackdrop = {},
|
||||||
headerComposable = {},
|
headerComposable = {},
|
||||||
takeFocus = false,
|
takeFocus = false,
|
||||||
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||||
import org.jellyfin.sdk.model.api.SortOrder
|
import org.jellyfin.sdk.model.api.SortOrder
|
||||||
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
||||||
|
|
@ -139,13 +140,12 @@ class CollectionViewModel
|
||||||
.content
|
.content
|
||||||
.let { BaseItem(it, false) }
|
.let { BaseItem(it, false) }
|
||||||
backdropService.submit(collection)
|
backdropService.submit(collection)
|
||||||
val logoImageUrl = null
|
val logoImageUrl =
|
||||||
// TODO add logo back
|
if (ImageType.LOGO in collection.data.imageTags.orEmpty()) {
|
||||||
// if (ImageType.LOGO in collection.data.imageTags.orEmpty()) {
|
imageUrlService.getItemImageUrl(collection, ImageType.LOGO)
|
||||||
// imageUrlService.getItemImageUrl(collection, ImageType.LOGO)
|
} else {
|
||||||
// } else {
|
null
|
||||||
// null
|
}
|
||||||
// }
|
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
collection = collection,
|
collection = collection,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import com.github.damontecres.wholphin.ui.components.DialogParams
|
||||||
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.ExpandablePlayButtons
|
import com.github.damontecres.wholphin.ui.components.ExpandablePlayButtons
|
||||||
|
import com.github.damontecres.wholphin.ui.components.HeaderUtils
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.components.Optional
|
import com.github.damontecres.wholphin.ui.components.Optional
|
||||||
import com.github.damontecres.wholphin.ui.components.chooseStream
|
import com.github.damontecres.wholphin.ui.components.chooseStream
|
||||||
|
|
@ -332,7 +333,7 @@ fun EpisodeDetailsContent(
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
contentPadding = PaddingValues(vertical = 8.dp),
|
contentPadding = PaddingValues(bottom = 8.dp),
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
|
|
@ -352,7 +353,7 @@ fun EpisodeDetailsContent(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 32.dp, bottom = 16.dp),
|
.padding(top = HeaderUtils.topPadding, bottom = 16.dp),
|
||||||
)
|
)
|
||||||
ExpandablePlayButtons(
|
ExpandablePlayButtons(
|
||||||
resumePosition = resumePosition,
|
resumePosition = resumePosition,
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ import com.github.damontecres.wholphin.ui.components.DialogParams
|
||||||
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.ExpandablePlayButtons
|
import com.github.damontecres.wholphin.ui.components.ExpandablePlayButtons
|
||||||
|
import com.github.damontecres.wholphin.ui.components.HeaderUtils
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.components.Optional
|
import com.github.damontecres.wholphin.ui.components.Optional
|
||||||
import com.github.damontecres.wholphin.ui.components.chooseStream
|
import com.github.damontecres.wholphin.ui.components.chooseStream
|
||||||
|
|
@ -439,7 +440,7 @@ fun MovieDetailsContent(
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
contentPadding = PaddingValues(vertical = 8.dp),
|
contentPadding = PaddingValues(bottom = 8.dp),
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
|
|
@ -459,7 +460,7 @@ fun MovieDetailsContent(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 40.dp, bottom = 16.dp),
|
.padding(top = HeaderUtils.topPadding, bottom = 16.dp),
|
||||||
)
|
)
|
||||||
ExpandablePlayButtons(
|
ExpandablePlayButtons(
|
||||||
resumePosition = resumePosition,
|
resumePosition = resumePosition,
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@ import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
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
|
||||||
|
|
@ -24,8 +22,10 @@ import com.github.damontecres.wholphin.data.ChosenStreams
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.components.GenreText
|
import com.github.damontecres.wholphin.ui.components.GenreText
|
||||||
|
import com.github.damontecres.wholphin.ui.components.HeaderUtils
|
||||||
import com.github.damontecres.wholphin.ui.components.OverviewText
|
import com.github.damontecres.wholphin.ui.components.OverviewText
|
||||||
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TitleOrLogo
|
||||||
import com.github.damontecres.wholphin.ui.components.VideoStreamDetails
|
import com.github.damontecres.wholphin.ui.components.VideoStreamDetails
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
import com.github.damontecres.wholphin.ui.letNotEmpty
|
import com.github.damontecres.wholphin.ui.letNotEmpty
|
||||||
|
|
@ -50,16 +50,13 @@ fun MovieDetailsHeader(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
// Title
|
// Title
|
||||||
Text(
|
TitleOrLogo(
|
||||||
text = movie.name ?: "",
|
item = movie,
|
||||||
color = MaterialTheme.colorScheme.onBackground,
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.SemiBold),
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth(.75f)
|
.fillMaxWidth(.75f)
|
||||||
.padding(start = 8.dp),
|
.padding(start = HeaderUtils.startPadding),
|
||||||
)
|
)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
|
@ -69,24 +66,29 @@ fun MovieDetailsHeader(
|
||||||
QuickDetails(
|
QuickDetails(
|
||||||
movie.ui.quickDetails,
|
movie.ui.quickDetails,
|
||||||
movie.timeRemainingOrRuntime,
|
movie.timeRemainingOrRuntime,
|
||||||
Modifier.padding(start = 8.dp),
|
Modifier.padding(start = HeaderUtils.startPadding),
|
||||||
)
|
)
|
||||||
|
|
||||||
dto.genres?.letNotEmpty {
|
dto.genres?.letNotEmpty {
|
||||||
GenreText(it, Modifier.padding(start = 8.dp))
|
GenreText(it, Modifier.padding(start = HeaderUtils.startPadding))
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoStreamDetails(
|
VideoStreamDetails(
|
||||||
chosenStreams = chosenStreams,
|
chosenStreams = chosenStreams,
|
||||||
numberOfVersions = movie.data.mediaSourceCount ?: 0,
|
numberOfVersions = movie.data.mediaSourceCount ?: 0,
|
||||||
modifier = Modifier.padding(start = 8.dp, top = 4.dp, bottom = 16.dp),
|
modifier =
|
||||||
|
Modifier.padding(
|
||||||
|
start = HeaderUtils.startPadding,
|
||||||
|
top = 4.dp,
|
||||||
|
bottom = 16.dp,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
dto.taglines?.firstOrNull()?.let { tagline ->
|
dto.taglines?.firstOrNull()?.let { tagline ->
|
||||||
Text(
|
Text(
|
||||||
text = tagline,
|
text = tagline,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
fontStyle = FontStyle.Italic,
|
fontStyle = FontStyle.Italic,
|
||||||
modifier = Modifier.padding(start = 8.dp),
|
modifier = Modifier.padding(start = HeaderUtils.startPadding),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,7 +124,7 @@ fun MovieDetailsHeader(
|
||||||
text = stringResource(R.string.directed_by, it),
|
text = stringResource(R.string.directed_by, it),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
modifier = Modifier.padding(start = 8.dp),
|
modifier = Modifier.padding(start = HeaderUtils.startPadding),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import com.github.damontecres.wholphin.data.ChosenStreams
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.components.EpisodeName
|
import com.github.damontecres.wholphin.ui.components.EpisodeName
|
||||||
|
import com.github.damontecres.wholphin.ui.components.HeaderUtils
|
||||||
import com.github.damontecres.wholphin.ui.components.OverviewText
|
import com.github.damontecres.wholphin.ui.components.OverviewText
|
||||||
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
||||||
import com.github.damontecres.wholphin.ui.components.VideoStreamDetails
|
import com.github.damontecres.wholphin.ui.components.VideoStreamDetails
|
||||||
|
|
@ -32,17 +33,21 @@ fun FocusedEpisodeHeader(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
EpisodeName(dto, modifier = Modifier.padding(start = 8.dp))
|
EpisodeName(dto, modifier = Modifier.padding(start = HeaderUtils.startPadding))
|
||||||
|
|
||||||
ep?.ui?.quickDetails?.let {
|
ep?.ui?.quickDetails?.let {
|
||||||
QuickDetails(it, ep.timeRemainingOrRuntime, Modifier.padding(start = 8.dp))
|
QuickDetails(
|
||||||
|
it,
|
||||||
|
ep.timeRemainingOrRuntime,
|
||||||
|
Modifier.padding(start = HeaderUtils.startPadding),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dto != null) {
|
if (dto != null) {
|
||||||
VideoStreamDetails(
|
VideoStreamDetails(
|
||||||
chosenStreams = chosenStreams,
|
chosenStreams = chosenStreams,
|
||||||
numberOfVersions = dto.mediaSourceCount ?: 0,
|
numberOfVersions = dto.mediaSourceCount ?: 0,
|
||||||
modifier = Modifier.padding(start = 8.dp),
|
modifier = Modifier.padding(start = HeaderUtils.startPadding),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
OverviewText(
|
OverviewText(
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,10 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
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.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||||
import androidx.tv.material3.MaterialTheme
|
|
||||||
import androidx.tv.material3.Text
|
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.ExtrasItem
|
import com.github.damontecres.wholphin.data.ExtrasItem
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
|
@ -69,10 +65,12 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.ExpandableFaButton
|
import com.github.damontecres.wholphin.ui.components.ExpandableFaButton
|
||||||
import com.github.damontecres.wholphin.ui.components.ExpandablePlayButton
|
import com.github.damontecres.wholphin.ui.components.ExpandablePlayButton
|
||||||
import com.github.damontecres.wholphin.ui.components.GenreText
|
import com.github.damontecres.wholphin.ui.components.GenreText
|
||||||
|
import com.github.damontecres.wholphin.ui.components.HeaderUtils
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.components.Optional
|
import com.github.damontecres.wholphin.ui.components.Optional
|
||||||
import com.github.damontecres.wholphin.ui.components.OverviewText
|
import com.github.damontecres.wholphin.ui.components.OverviewText
|
||||||
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TitleOrLogo
|
||||||
import com.github.damontecres.wholphin.ui.components.TrailerButton
|
import com.github.damontecres.wholphin.ui.components.TrailerButton
|
||||||
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
||||||
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog
|
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog
|
||||||
|
|
@ -386,7 +384,6 @@ fun SeriesDetailsContent(
|
||||||
Column(
|
Column(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(vertical = 16.dp)
|
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
|
@ -397,18 +394,19 @@ fun SeriesDetailsContent(
|
||||||
item {
|
item {
|
||||||
SeriesDetailsHeader(
|
SeriesDetailsHeader(
|
||||||
series = series,
|
series = series,
|
||||||
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
overviewOnClick = overviewOnClick,
|
overviewOnClick = overviewOnClick,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.bringIntoViewRequester(bringIntoViewRequester)
|
.bringIntoViewRequester(bringIntoViewRequester)
|
||||||
.padding(top = 32.dp, bottom = 16.dp),
|
.padding(top = HeaderUtils.topPadding, bottom = 16.dp),
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(start = 8.dp)
|
.padding(start = HeaderUtils.startPadding)
|
||||||
.focusRequester(focusRequesters[HEADER_ROW])
|
.focusRequester(focusRequesters[HEADER_ROW])
|
||||||
.focusRestorer(playFocusRequester)
|
.focusRestorer(playFocusRequester)
|
||||||
.focusGroup()
|
.focusGroup()
|
||||||
|
|
@ -684,6 +682,7 @@ fun SeriesDetailsContent(
|
||||||
@Composable
|
@Composable
|
||||||
fun SeriesDetailsHeader(
|
fun SeriesDetailsHeader(
|
||||||
series: BaseItem,
|
series: BaseItem,
|
||||||
|
showLogo: Boolean,
|
||||||
overviewOnClick: () -> Unit,
|
overviewOnClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
@ -693,24 +692,25 @@ fun SeriesDetailsHeader(
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
Text(
|
TitleOrLogo(
|
||||||
text = series.name ?: stringResource(R.string.unknown),
|
item = series,
|
||||||
color = MaterialTheme.colorScheme.onBackground,
|
showLogo = showLogo,
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.SemiBold),
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth(.75f)
|
.fillMaxWidth(.75f)
|
||||||
.padding(start = 8.dp),
|
.padding(start = HeaderUtils.startPadding),
|
||||||
)
|
)
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
modifier = Modifier.fillMaxWidth(.60f),
|
modifier = Modifier.fillMaxWidth(.60f),
|
||||||
) {
|
) {
|
||||||
QuickDetails(series.ui.quickDetails, null, Modifier.padding(start = 8.dp))
|
QuickDetails(
|
||||||
|
series.ui.quickDetails,
|
||||||
|
null,
|
||||||
|
Modifier.padding(start = HeaderUtils.startPadding),
|
||||||
|
)
|
||||||
dto.genres?.letNotEmpty {
|
dto.genres?.letNotEmpty {
|
||||||
GenreText(it, Modifier.padding(start = 8.dp, bottom = 8.dp))
|
GenreText(it, Modifier.padding(start = HeaderUtils.startPadding, bottom = 8.dp))
|
||||||
}
|
}
|
||||||
dto.overview?.let { overview ->
|
dto.overview?.let { overview ->
|
||||||
OverviewText(
|
OverviewText(
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,10 @@ import com.github.damontecres.wholphin.ui.AspectRatios
|
||||||
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
||||||
import com.github.damontecres.wholphin.ui.cards.PersonRow
|
import com.github.damontecres.wholphin.ui.cards.PersonRow
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
|
import com.github.damontecres.wholphin.ui.components.HeaderUtils
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.components.SeriesName
|
|
||||||
import com.github.damontecres.wholphin.ui.components.TabRow
|
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TitleOrLogo
|
||||||
import com.github.damontecres.wholphin.ui.ifElse
|
import com.github.damontecres.wholphin.ui.ifElse
|
||||||
import com.github.damontecres.wholphin.ui.logTab
|
import com.github.damontecres.wholphin.ui.logTab
|
||||||
import com.github.damontecres.wholphin.ui.playback.isPlayKeyUp
|
import com.github.damontecres.wholphin.ui.playback.isPlayKeyUp
|
||||||
|
|
@ -143,10 +144,12 @@ fun SeriesOverviewContent(
|
||||||
.bringIntoViewRequester(bringIntoViewRequester),
|
.bringIntoViewRequester(bringIntoViewRequester),
|
||||||
) {
|
) {
|
||||||
val paddingValues =
|
val paddingValues =
|
||||||
if (preferences.appPreferences.interfacePreferences.showClock) {
|
remember(preferences.appPreferences.interfacePreferences.showClock) {
|
||||||
PaddingValues(start = 0.dp, end = 184.dp)
|
if (preferences.appPreferences.interfacePreferences.showClock) {
|
||||||
} else {
|
PaddingValues(start = 0.dp, end = 184.dp)
|
||||||
PaddingValues(start = 0.dp, end = 16.dp)
|
} else {
|
||||||
|
PaddingValues(start = 0.dp, end = 16.dp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TabRow(
|
TabRow(
|
||||||
selectedTabIndex = selectedTabIndex,
|
selectedTabIndex = selectedTabIndex,
|
||||||
|
|
@ -164,7 +167,11 @@ fun SeriesOverviewContent(
|
||||||
.padding(bottom = 4.dp)
|
.padding(bottom = 4.dp)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
SeriesName(series.name, Modifier.padding(start = 8.dp))
|
TitleOrLogo(
|
||||||
|
item = series,
|
||||||
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
|
modifier = Modifier.padding(start = HeaderUtils.startPadding),
|
||||||
|
)
|
||||||
FocusedEpisodeHeader(
|
FocusedEpisodeHeader(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
ep = focusedEpisode,
|
ep = focusedEpisode,
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,8 @@ fun SeerrDiscoverPage(
|
||||||
overviewTwoLines = true,
|
overviewTwoLines = true,
|
||||||
quickDetails = details,
|
quickDetails = details,
|
||||||
timeRemaining = null,
|
timeRemaining = null,
|
||||||
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
|
logoImageUrl = null, // TODO
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(top = 24.dp, bottom = 16.dp, start = 32.dp)
|
.padding(top = 24.dp, bottom = 16.dp, start = 32.dp)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
|
@ -39,7 +38,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
|
|
@ -61,9 +59,12 @@ import com.github.damontecres.wholphin.ui.components.DialogPopup
|
||||||
import com.github.damontecres.wholphin.ui.components.EpisodeName
|
import com.github.damontecres.wholphin.ui.components.EpisodeName
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.FocusableItemRow
|
import com.github.damontecres.wholphin.ui.components.FocusableItemRow
|
||||||
|
import com.github.damontecres.wholphin.ui.components.HeaderUtils
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
||||||
import com.github.damontecres.wholphin.ui.components.RowColumnItem
|
import com.github.damontecres.wholphin.ui.components.RowColumnItem
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TitleOrLogo
|
||||||
|
import com.github.damontecres.wholphin.ui.components.rememberLogoUrl
|
||||||
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
||||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||||
import com.github.damontecres.wholphin.ui.detail.MoreDialogActions
|
import com.github.damontecres.wholphin.ui.detail.MoreDialogActions
|
||||||
|
|
@ -172,6 +173,7 @@ fun HomePage(
|
||||||
loadingState = refreshing,
|
loadingState = refreshing,
|
||||||
showClock = preferences.appPreferences.interfacePreferences.showClock,
|
showClock = preferences.appPreferences.interfacePreferences.showClock,
|
||||||
onUpdateBackdrop = viewModel::updateBackdrop,
|
onUpdateBackdrop = viewModel::updateBackdrop,
|
||||||
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
dialog?.let { params ->
|
dialog?.let { params ->
|
||||||
|
|
@ -222,6 +224,7 @@ fun HomePageContent(
|
||||||
onClickPlay: (RowColumn, BaseItem) -> Unit,
|
onClickPlay: (RowColumn, BaseItem) -> Unit,
|
||||||
showClock: Boolean,
|
showClock: Boolean,
|
||||||
onUpdateBackdrop: (BaseItem) -> Unit,
|
onUpdateBackdrop: (BaseItem) -> Unit,
|
||||||
|
showLogo: Boolean,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
loadingState: LoadingState? = null,
|
loadingState: LoadingState? = null,
|
||||||
listState: LazyListState = rememberLazyListState(),
|
listState: LazyListState = rememberLazyListState(),
|
||||||
|
|
@ -230,10 +233,8 @@ fun HomePageContent(
|
||||||
headerComposable: @Composable (focusedItem: BaseItem?) -> Unit = { focusedItem ->
|
headerComposable: @Composable (focusedItem: BaseItem?) -> Unit = { focusedItem ->
|
||||||
HomePageHeader(
|
HomePageHeader(
|
||||||
item = focusedItem,
|
item = focusedItem,
|
||||||
modifier =
|
showLogo = showLogo,
|
||||||
Modifier
|
modifier = HeaderUtils.modifier,
|
||||||
.padding(top = 48.dp, bottom = 32.dp, start = 8.dp)
|
|
||||||
.fillMaxHeight(.33f),
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
|
@ -410,6 +411,7 @@ fun HomePageContent(
|
||||||
@Composable
|
@Composable
|
||||||
fun HomePageHeader(
|
fun HomePageHeader(
|
||||||
item: BaseItem?,
|
item: BaseItem?,
|
||||||
|
showLogo: Boolean,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val isEpisode = item?.type == BaseItemKind.EPISODE
|
val isEpisode = item?.type == BaseItemKind.EPISODE
|
||||||
|
|
@ -421,6 +423,8 @@ fun HomePageHeader(
|
||||||
overviewTwoLines = isEpisode,
|
overviewTwoLines = isEpisode,
|
||||||
quickDetails = item?.ui?.quickDetails ?: AnnotatedString(""),
|
quickDetails = item?.ui?.quickDetails ?: AnnotatedString(""),
|
||||||
timeRemaining = item?.timeRemainingOrRuntime,
|
timeRemaining = item?.timeRemainingOrRuntime,
|
||||||
|
showLogo = showLogo,
|
||||||
|
logoImageUrl = rememberLogoUrl(item),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -433,31 +437,28 @@ fun HomePageHeader(
|
||||||
overviewTwoLines: Boolean,
|
overviewTwoLines: Boolean,
|
||||||
quickDetails: AnnotatedString?,
|
quickDetails: AnnotatedString?,
|
||||||
timeRemaining: Duration?,
|
timeRemaining: Duration?,
|
||||||
|
showLogo: Boolean,
|
||||||
|
logoImageUrl: String?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
title?.let {
|
TitleOrLogo(
|
||||||
Text(
|
title = title,
|
||||||
text = it,
|
logoImageUrl = logoImageUrl,
|
||||||
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.SemiBold),
|
showLogo = showLogo,
|
||||||
color = MaterialTheme.colorScheme.onBackground,
|
modifier = Modifier.fillMaxWidth(.75f),
|
||||||
maxLines = 1,
|
)
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier = Modifier.fillMaxWidth(.75f),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth(.6f)
|
.fillMaxWidth(.6f),
|
||||||
.fillMaxHeight(),
|
|
||||||
) {
|
) {
|
||||||
subtitle?.let {
|
if (subtitle != null) {
|
||||||
EpisodeName(it)
|
EpisodeName(subtitle)
|
||||||
}
|
}
|
||||||
QuickDetails(quickDetails ?: AnnotatedString(""), timeRemaining)
|
QuickDetails(quickDetails ?: AnnotatedString(""), timeRemaining)
|
||||||
val overviewModifier =
|
val overviewModifier =
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.components.ConfirmDialog
|
import com.github.damontecres.wholphin.ui.components.ConfirmDialog
|
||||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||||
import com.github.damontecres.wholphin.ui.detail.search.SearchForDialog
|
import com.github.damontecres.wholphin.ui.detail.search.SearchForDialog
|
||||||
|
|
@ -51,6 +52,7 @@ val settingsWidth = 360.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeSettingsPage(
|
fun HomeSettingsPage(
|
||||||
|
preferences: UserPreferences,
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
viewModel: HomeSettingsViewModel = hiltViewModel(),
|
viewModel: HomeSettingsViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
|
@ -310,6 +312,7 @@ fun HomeSettingsPage(
|
||||||
listState = listState,
|
listState = listState,
|
||||||
takeFocus = false,
|
takeFocus = false,
|
||||||
showEmptyRows = true,
|
showEmptyRows = true,
|
||||||
|
showLogo = preferences.appPreferences.interfacePreferences.showLogos,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ fun DestinationContent(
|
||||||
}
|
}
|
||||||
|
|
||||||
is Destination.HomeSettings -> {
|
is Destination.HomeSettings -> {
|
||||||
HomeSettingsPage(modifier)
|
HomeSettingsPage(preferences, modifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
is Destination.PlaybackList,
|
is Destination.PlaybackList,
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ message InterfacePreferences {
|
||||||
SubtitlePreferences hdr_subtitles_preferences = 10;
|
SubtitlePreferences hdr_subtitles_preferences = 10;
|
||||||
ScreensaverPreferences screensaver_preference = 11;
|
ScreensaverPreferences screensaver_preference = 11;
|
||||||
bool enable_media_management = 12;
|
bool enable_media_management = 12;
|
||||||
|
bool show_logos = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AdvancedPreferences {
|
message AdvancedPreferences {
|
||||||
|
|
|
||||||
|
|
@ -747,5 +747,6 @@
|
||||||
<string name="continue_string">Continue</string>
|
<string name="continue_string">Continue</string>
|
||||||
<string name="select_all">Select all</string>
|
<string name="select_all">Select all</string>
|
||||||
<string name="separate_types">Separate types</string>
|
<string name="separate_types">Separate types</string>
|
||||||
|
<string name="prefer_logos">Prefer showing logos for titles</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue