diff --git a/apollo-compiler/src/main/java/com/github/damontecres/apollo/compiler/StashApolloCompilerPlugin.kt b/apollo-compiler/src/main/java/com/github/damontecres/apollo/compiler/StashApolloCompilerPlugin.kt index 36ce58647..723b499a8 100644 --- a/apollo-compiler/src/main/java/com/github/damontecres/apollo/compiler/StashApolloCompilerPlugin.kt +++ b/apollo-compiler/src/main/java/com/github/damontecres/apollo/compiler/StashApolloCompilerPlugin.kt @@ -55,7 +55,13 @@ class StashApolloCompilerPlugin : ApolloCompilerPlugin { "SavedFindFilterType", ) ) || - file.name.endsWith("CriterionInput") + file.name.endsWith("CriterionInput") || + file.name in + setOf( + "FingerprintFilterInput", + "VideoFileFilterInput", + "ImageFileFilterInput", + ) ) { // Modify filter or filter input types handleFilterInput(file, stashFilterInterface) diff --git a/app/src/main/graphql/Configuration.graphql b/app/src/main/graphql/Configuration.graphql index 7dd6ff431..4fca37ba3 100644 --- a/app/src/main/graphql/Configuration.graphql +++ b/app/src/main/graphql/Configuration.graphql @@ -30,6 +30,7 @@ query Configuration { showStudioAsText customLocalesEnabled customLocales + sfwContentMode } ui } diff --git a/app/src/main/graphql/FindGroups.graphql b/app/src/main/graphql/FindGroups.graphql index 112e988d0..cdfc89533 100644 --- a/app/src/main/graphql/FindGroups.graphql +++ b/app/src/main/graphql/FindGroups.graphql @@ -85,6 +85,7 @@ fragment GroupData on Group { } scene_count performer_count + o_counter front_image_path back_image_path created_at @catch(to: NULL) diff --git a/app/src/main/graphql/FindStudios.graphql b/app/src/main/graphql/FindStudios.graphql index c165c781b..c73db2b6e 100644 --- a/app/src/main/graphql/FindStudios.graphql +++ b/app/src/main/graphql/FindStudios.graphql @@ -62,6 +62,7 @@ fragment StudioData on Studio { # performer_count_all: performer_count(depth: -1) group_count # group_count_all: group_count(depth: -1) + o_counter details rating100 aliases diff --git a/app/src/main/java/com/github/damontecres/stashapp/SceneDetailsFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/SceneDetailsFragment.kt index 615dfda94..65d18630e 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/SceneDetailsFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/SceneDetailsFragment.kt @@ -677,7 +677,7 @@ class SceneDetailsFragment : DetailsSupportFragment() { StashCoroutineExceptionHandler( Toast.makeText( requireContext(), - getString(R.string.failed_o_counter), + getString(R.string.failed_update), Toast.LENGTH_SHORT, ), ), diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/SortOption.kt b/app/src/main/java/com/github/damontecres/stashapp/data/SortOption.kt index 67109e1e0..517f1f0c3 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/SortOption.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/SortOption.kt @@ -119,6 +119,9 @@ sealed interface SortOption { @Serializable data object Path : SortOptionImpl("path", R.string.stashapp_path) + @Serializable + data object PerformerAge : SortOptionImpl("performer_age", R.string.stashapp_performer_age) + @Serializable data object PenisLength : SortOptionImpl("penis_length", R.string.stashapp_penis_length) @@ -153,6 +156,10 @@ sealed interface SortOption { @Serializable data object SceneCode : SortOptionImpl("code", R.string.stashapp_scene_code, Version.V0_28_0) + @Serializable + data object ScenesDuration : + SortOptionImpl("scenes_duration", R.string.stashapp_scenes_duration) + @Serializable data object SceneId : SortOptionImpl("scene_id", R.string.stashapp_scene_id) @@ -170,6 +177,9 @@ sealed interface SortOption { @Serializable data object Seconds : SortOptionImpl("seconds", R.string.stashapp_seconds) + @Serializable + data object Studio : SortOptionImpl("studio", R.string.stashapp_studio) + @Serializable data object TagCount : SortOptionImpl("tag_count", R.string.stashapp_tag_count) @@ -253,12 +263,14 @@ sealed interface SortOption { Organized, Path, PerceptualSimilarity, + PerformerAge, PerformerCount, PlayCount, PlayDuration, Rating, ResumeTime, SceneCode, + Studio, TagCount, Title, ) @@ -298,6 +310,7 @@ sealed interface SortOption { Date, Duration, Name, + OCounter, Rating, ScenesCount, ) @@ -317,6 +330,7 @@ sealed interface SortOption { PlayCount, Rating, ScenesCount, + ScenesDuration, TagCount, CareerLength, Measurements, @@ -342,6 +356,7 @@ sealed interface SortOption { Name, Rating, ScenesCount, + ScenesDuration, TagCount, ) @@ -354,6 +369,7 @@ sealed interface SortOption { PerformersCount, SceneMarkersCount, ScenesCount, + ScenesDuration, ) fun sortByName(dataType: DataType): SortOption = diff --git a/app/src/main/java/com/github/damontecres/stashapp/filter/FilterOption.kt b/app/src/main/java/com/github/damontecres/stashapp/filter/FilterOption.kt index 3a12f1e0b..df6fd7fe2 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/filter/FilterOption.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/filter/FilterOption.kt @@ -174,7 +174,7 @@ private val SceneFilterOptions = ), FilterOption( "o_counter", - R.string.stashapp_o_counter, + R.string.stashapp_o_count, null, IntCriterionInput::class, { it.o_counter }, @@ -493,7 +493,7 @@ private val PerformerFilterOptions = ), FilterOption( "o_counter", - R.string.stashapp_o_counter, + R.string.stashapp_o_count, null, IntCriterionInput::class, { it.o_counter }, @@ -663,7 +663,7 @@ private val ImageFilterOptions = ), FilterOption( "o_counter", - R.string.stashapp_o_counter, + R.string.stashapp_o_count, null, IntCriterionInput::class, { it.o_counter }, @@ -1217,6 +1217,15 @@ private val GroupFilterOptions = { filter, value -> filter.copy(duration = value) }, TwoValueCriterionModifiers, ), + FilterOption( + "o_counter", + R.string.stashapp_o_count, + null, + IntCriterionInput::class, + { it.o_counter }, + { filter, value -> filter.copy(o_counter = value) }, + TwoValueCriterionModifiers, + ), FilterOption( "name", R.string.stashapp_name, diff --git a/app/src/main/java/com/github/damontecres/stashapp/image/ImageDetailsFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/image/ImageDetailsFragment.kt index 38d8cb7fb..24cc2baa3 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/image/ImageDetailsFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/image/ImageDetailsFragment.kt @@ -624,7 +624,7 @@ class ImageDetailsFragment : DetailsSupportFragment() { StashCoroutineExceptionHandler( Toast.makeText( requireContext(), - getString(R.string.failed_o_counter), + getString(R.string.failed_update), Toast.LENGTH_SHORT, ), ), diff --git a/app/src/main/java/com/github/damontecres/stashapp/presenters/OCounterPresenter.kt b/app/src/main/java/com/github/damontecres/stashapp/presenters/OCounterPresenter.kt index 0504e67bc..b83ec0a32 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/presenters/OCounterPresenter.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/presenters/OCounterPresenter.kt @@ -12,7 +12,7 @@ class OCounterPresenter( ) { cardView.blackImageBackground = false - val text = cardView.context.getString(R.string.stashapp_o_counter) + val text = cardView.context.getString(R.string.stashapp_o_count) cardView.titleText = "$text (${item.count})" cardView.setMainImageDimensions(ActionPresenter.CARD_WIDTH, ActionPresenter.CARD_HEIGHT) cardView.imageView.setPadding( diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/ComposeUiConfig.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/ComposeUiConfig.kt index f80222abd..1d30a2472 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/ComposeUiConfig.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/ComposeUiConfig.kt @@ -18,6 +18,7 @@ data class ComposeUiConfig( val readOnlyModeEnabled: Boolean, val showCardProgress: Boolean, val playSoundOnFocus: Boolean, + val sfwMode: Boolean, val persistVideoFilters: Boolean = true, val cardSettings: CardUiSettings, ) { @@ -46,6 +47,7 @@ data class ComposeUiConfig( playSoundOnFocus = preferences.interfacePreferences.playMovementSounds, readOnlyModeEnabled = preferences.pinPreferences.readOnlyPin.isNotNullOrBlank(), persistVideoFilters = preferences.playbackPreferences.saveVideoFilters, + sfwMode = server.serverPreferences.sfwMode, ) } } diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/PreviewUtils.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/PreviewUtils.kt index 283f2c127..dfaa80ee9 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/PreviewUtils.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/PreviewUtils.kt @@ -107,6 +107,7 @@ val uiConfigPreview = readOnlyModeEnabled = false, showCardProgress = true, playSoundOnFocus = true, + sfwMode = false, cardSettings = CardUiSettings( maxSearchResults = 25, diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/Cards.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/Cards.kt index 4fa5ba71e..1f94503f7 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/Cards.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/Cards.kt @@ -167,6 +167,7 @@ val iconOrder = @Suppress("ktlint:standard:function-naming") @Composable fun IconRowText( + sfwMode: Boolean, iconMap: EnumMap, oCounter: Int?, modifier: Modifier = Modifier, @@ -186,7 +187,13 @@ fun IconRowText( } additionalIcons?.invoke(this) if (oCounter != null && oCounter > 0) { - appendInlineContent(id = "ocounter", "O") + if (sfwMode) { + withStyle(SpanStyle(fontFamily = FontAwesome)) { + append(stringResource(R.string.fa_thumbs_up)) + } + } else { + appendInlineContent(id = "ocounter", "O") + } append(" $oCounter") } } diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GalleryCard.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GalleryCard.kt index 695dc6339..1afc6e39c 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GalleryCard.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GalleryCard.kt @@ -113,6 +113,7 @@ fun GalleryCard( }, description = { IconRowText( + sfwMode = uiConfig.sfwMode, dataTypeMap, null, Modifier diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GroupCard.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GroupCard.kt index 48fcf86dc..51cca4320 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GroupCard.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/GroupCard.kt @@ -76,8 +76,9 @@ fun GroupCard( description = { focused -> item?.let { IconRowText( + sfwMode = uiConfig.sfwMode, dataTypeMap, - null, + item.o_counter, Modifier .enableMarquee(focused) .align(Alignment.Center), @@ -102,6 +103,9 @@ fun GroupCard( append(stringResource(R.string.fa_arrow_down_long)) } } + if (item.o_counter != null && item.o_counter > 0) { + append(" ") + } } } } diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/ImageCard.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/ImageCard.kt index e67bdb01d..b6e0d56fa 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/ImageCard.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/ImageCard.kt @@ -72,6 +72,7 @@ fun ImageCard( }, description = { IconRowText( + sfwMode = uiConfig.sfwMode, dataTypeMap, item?.o_counter ?: -1, Modifier diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/MarkerCard.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/MarkerCard.kt index 154d0b320..1795847c6 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/MarkerCard.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/MarkerCard.kt @@ -85,6 +85,7 @@ fun MarkerCard( }, description = { IconRowText( + sfwMode = uiConfig.sfwMode, dataTypeMap, null, Modifier diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/PerformerCard.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/PerformerCard.kt index 68c4a822d..1cd81c726 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/PerformerCard.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/PerformerCard.kt @@ -119,6 +119,7 @@ fun PerformerCard( subtitle = { Text(subtitle) }, description = { IconRowText( + sfwMode = uiConfig.sfwMode, dataTypeMap, item?.o_counter ?: -1, Modifier diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/SceneCard.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/SceneCard.kt index 1247c3234..d3baa10ef 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/SceneCard.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/SceneCard.kt @@ -89,6 +89,7 @@ fun SceneCard( }, description = { IconRowText( + sfwMode = uiConfig.sfwMode, dataTypeMap, item?.o_counter ?: -1, Modifier diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/StudioCard.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/StudioCard.kt index b9fb931d3..fb3d1068e 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/StudioCard.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/StudioCard.kt @@ -68,8 +68,9 @@ fun StudioCard( }, description = { IconRowText( + sfwMode = uiConfig.sfwMode, dataTypeMap, - null, + item?.o_counter, Modifier .enableMarquee(it) .align(Alignment.Center), diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/TagCard.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/TagCard.kt index ee9cd32e1..c532c324d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/cards/TagCard.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/cards/TagCard.kt @@ -65,6 +65,7 @@ fun TagCard( }, description = { IconRowText( + sfwMode = uiConfig.sfwMode, dataTypeMap, null, Modifier diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/OCounterButton.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/OCounterButton.kt index 530868e89..6221447dc 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/OCounterButton.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/OCounterButton.kt @@ -18,15 +18,19 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.compose.ui.util.lerp import androidx.tv.material3.Icon import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import com.github.damontecres.stashapp.R +import com.github.damontecres.stashapp.ui.FontAwesome import com.github.damontecres.stashapp.ui.compat.Button import com.github.damontecres.stashapp.ui.components.playback.PlaybackButton +import com.github.damontecres.stashapp.ui.components.playback.PlaybackFaButton import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlin.math.cos @@ -36,6 +40,7 @@ import kotlin.random.Random @Composable fun OCounterButton( + sfwMode: Boolean, oCount: Int, onClick: () -> Unit, onLongClick: () -> Unit, @@ -43,6 +48,7 @@ fun OCounterButton( enabled: Boolean = true, ) { OCounterButtonWrapper( + sfwMode = sfwMode, originalOnClick = onClick, modifier = modifier, ) { newOnClick -> @@ -52,11 +58,20 @@ fun OCounterButton( enabled = enabled, modifier = Modifier, ) { - Icon( - painter = painterResource(R.drawable.sweat_drops), - contentDescription = null, - modifier = Modifier.size(24.dp), - ) + if (sfwMode) { + Text( + text = stringResource(R.string.fa_thumbs_up), + fontSize = 20.sp, + fontFamily = FontAwesome, + style = MaterialTheme.typography.titleSmall, + ) + } else { + Icon( + painter = painterResource(R.drawable.sweat_drops), + contentDescription = null, + modifier = Modifier.size(24.dp), + ) + } Spacer(Modifier.size(8.dp)) Text( text = oCount.toString(), @@ -68,27 +83,39 @@ fun OCounterButton( @Composable fun PlaybackOCountButton( + sfwMode: Boolean, onClick: () -> Unit, onControllerInteraction: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, ) { OCounterButtonWrapper( + sfwMode = sfwMode, originalOnClick = onClick, modifier = modifier, ) { newOnClick -> - PlaybackButton( - iconRes = R.drawable.sweat_drops, - onClick = newOnClick, - enabled = enabled, - onControllerInteraction = onControllerInteraction, - ) + if (sfwMode) { + PlaybackFaButton( + iconRes = R.string.fa_thumbs_up, + onClick = newOnClick, + enabled = enabled, + onControllerInteraction = onControllerInteraction, + ) + } else { + PlaybackButton( + iconRes = R.drawable.sweat_drops, + onClick = newOnClick, + enabled = enabled, + onControllerInteraction = onControllerInteraction, + ) + } } } // Based on https://gist.github.com/ardakazanci/1297a44276d6a0be06c1227cb446dead @Composable private fun OCounterButtonWrapper( + sfwMode: Boolean, originalOnClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable ( @@ -110,25 +137,29 @@ private fun OCounterButtonWrapper( val scale = remember { Animatable(1f) } Box(contentAlignment = Alignment.Center, modifier = modifier) { - floatingList.forEach { id -> - FloatingIcon( - key = id, - config = config, - onAnimationEnd = { floatingList.remove(id) }, - ) - } - content.invoke { - scope.launch { - scale.animateTo(0.85f, tween(100, easing = LinearEasing)) - scale.animateTo( - 1f, - spring(dampingRatio = Spring.DampingRatioHighBouncy), + if (!sfwMode) { + floatingList.forEach { id -> + FloatingIcon( + key = id, + config = config, + onAnimationEnd = { floatingList.remove(id) }, ) } - repeat(count) { index -> + } + content.invoke { + if (!sfwMode) { scope.launch { - delay(index * config.delay) - floatingList.add(index + Random.nextInt(1000)) + scale.animateTo(0.85f, tween(100, easing = LinearEasing)) + scale.animateTo( + 1f, + spring(dampingRatio = Spring.DampingRatioHighBouncy), + ) + } + repeat(count) { index -> + scope.launch { + delay(index * config.delay) + floatingList.add(index + Random.nextInt(1000)) + } } } originalOnClick.invoke() diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageControlsOverlay.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageControlsOverlay.kt index 1c5a627c6..0c030c1e5 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageControlsOverlay.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageControlsOverlay.kt @@ -43,6 +43,7 @@ import kotlinx.coroutines.launch @OptIn(UnstableApi::class) @Composable fun ImageControlsOverlay( + sfwMode: Boolean, isImageClip: Boolean, oCount: Int, onZoom: (Float) -> Unit, @@ -150,6 +151,7 @@ fun ImageControlsOverlay( // O-Counter item { OCounterButton( + sfwMode = sfwMode, oCount = oCount, onClick = oCounterOnClick, onLongClick = oCounterOnLongClick, @@ -214,6 +216,7 @@ fun ImageControlButton( private fun ImageControlsOverlayPreview() { PreviewTheme { ImageControlsOverlay( + sfwMode = false, isImageClip = false, oCount = 10, onZoom = {}, diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageDetailsHeader.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageDetailsHeader.kt index 1f329db25..98eb839d0 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageDetailsHeader.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageDetailsHeader.kt @@ -291,6 +291,7 @@ fun ImageDetailsHeader( oCounterOnLongClick = oCounterOnLongClick, playPauseOnClick = playPauseState::onClick, isPlaying = playPauseState.showPlay, + sfwMode = uiConfig.sfwMode, modifier = Modifier .fillMaxWidth(), diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageOverlay.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageOverlay.kt index bece6f6dd..a4bbf755d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageOverlay.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/image/ImageOverlay.kt @@ -202,7 +202,7 @@ fun ImageOverlay( oCounterOnLongClick = { showDialog = DialogParams( - title = context.getString(R.string.stashapp_o_counter), + title = context.getString(R.string.stashapp_o_count), fromLongClick = true, items = listOf( diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackControls.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackControls.kt index a2d403149..e7b9c4c8b 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackControls.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackControls.kt @@ -6,6 +6,7 @@ import android.util.Log import android.view.Gravity import androidx.annotation.DrawableRes import androidx.annotation.OptIn +import androidx.annotation.StringRes import androidx.compose.foundation.background import androidx.compose.foundation.focusGroup import androidx.compose.foundation.interaction.MutableInteractionSource @@ -47,6 +48,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp @@ -63,6 +65,7 @@ import com.github.damontecres.stashapp.R import com.github.damontecres.stashapp.data.Scene import com.github.damontecres.stashapp.playback.TrackSupport import com.github.damontecres.stashapp.ui.AppColors +import com.github.damontecres.stashapp.ui.FontAwesome import com.github.damontecres.stashapp.ui.PreviewTheme import com.github.damontecres.stashapp.ui.compat.Button import com.github.damontecres.stashapp.ui.compat.ListItem @@ -108,6 +111,7 @@ sealed interface PlaybackAction { @OptIn(UnstableApi::class) @Composable fun PlaybackControls( + sfwMode: Boolean, scene: Scene, captions: List, oCounter: Int, @@ -182,6 +186,7 @@ fun PlaybackControls( showDebugInfo = showDebugInfo, oCount = oCounter, moreButtonOptions = moreButtonOptions, + sfwMode = sfwMode, modifier = Modifier, ) PlaybackButtons( @@ -301,6 +306,7 @@ private val buttonSpacing = 4.dp @Composable fun LeftPlaybackButtons( + sfwMode: Boolean, onControllerInteraction: () -> Unit, onPlaybackActionClick: (PlaybackAction) -> Unit, showDebugInfo: Boolean, @@ -329,6 +335,7 @@ fun LeftPlaybackButtons( verticalAlignment = Alignment.CenterVertically, ) { PlaybackOCountButton( + sfwMode = sfwMode, onClick = { onControllerInteraction.invoke() onPlaybackActionClick.invoke(PlaybackAction.OCount) @@ -582,6 +589,45 @@ fun PlaybackButton( } } +@Composable +fun PlaybackFaButton( + @StringRes iconRes: Int, + onClick: () -> Unit, + onControllerInteraction: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, +) { + val selectedColor = MaterialTheme.colorScheme.border + Button( + enabled = enabled, + onClick = onClick, + shape = ButtonDefaults.shape(CircleShape), + colors = + ButtonDefaults.colors( + containerColor = AppColors.TransparentBlack25, + focusedContainerColor = selectedColor, + ), + contentPadding = PaddingValues(8.dp), + modifier = + modifier + .padding(8.dp) + .size(56.dp, 56.dp) + .onFocusChanged { onControllerInteraction.invoke() }, + ) { + Box( + modifier = Modifier.fillMaxSize(), + ) { + Text( + text = stringResource(iconRes), + fontFamily = FontAwesome, + color = MaterialTheme.colorScheme.onSurface, + fontSize = 28.sp, + modifier = Modifier.align(Alignment.Center), + ) + } + } +} + @Composable private fun BottomDialog( choices: List, @@ -678,18 +724,29 @@ private fun PlaybackButtonsPreview() { @Composable private fun RightPlaybackButtonsPreview() { PreviewTheme { - RightPlaybackButtons( - captions = listOf(), - onControllerInteraction = {}, - onControllerInteractionForDialog = {}, - onPlaybackActionClick = {}, - subtitleIndex = 1, - modifier = Modifier, - audioOptions = listOf(), - audioIndex = null, - playbackSpeed = 1.0f, - scale = ContentScale.Fit, - ) + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + RightPlaybackButtons( + captions = listOf(), + onControllerInteraction = {}, + onControllerInteractionForDialog = {}, + onPlaybackActionClick = {}, + subtitleIndex = 1, + modifier = Modifier, + audioOptions = listOf(), + audioIndex = null, + playbackSpeed = 1.0f, + scale = ContentScale.Fit, + ) + PlaybackFaButton( + iconRes = R.string.fa_thumbs_up, + onClick = {}, + onControllerInteraction = {}, + modifier = Modifier, + enabled = true, + ) + } } } diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackOverlay.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackOverlay.kt index ae9f0d8c0..e502cbe1d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackOverlay.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/playback/PlaybackOverlay.kt @@ -330,6 +330,7 @@ fun PlaybackOverlay( playbackSpeed = playbackSpeed, scale = scale, seekBarIntervals = uiConfig.preferences.playbackPreferences.seekBarSteps, + sfwMode = uiConfig.sfwMode, ) } if (markers.isNotEmpty()) { @@ -634,6 +635,7 @@ private fun PlaybackOverlayPreview() { readOnlyModeEnabled = false, showCardProgress = true, playSoundOnFocus = true, + sfwMode = false, cardSettings = CardUiSettings( maxSearchResults = 25, diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/scene/SceneDetailsHeader.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/scene/SceneDetailsHeader.kt index 32756268a..cdeac0f7d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/scene/SceneDetailsHeader.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/scene/SceneDetailsHeader.kt @@ -164,6 +164,7 @@ fun SceneDetailsHeader( }, alwaysStartFromBeginning = alwaysStartFromBeginning, showEditButton = showEditButton, + sfwMode = uiConfig.sfwMode, modifier = Modifier.padding(vertical = 16.dp), ) } diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/components/scene/ScenePlayButton.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/components/scene/ScenePlayButton.kt index c0a22a022..0c668ad4b 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/components/scene/ScenePlayButton.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/components/scene/ScenePlayButton.kt @@ -38,6 +38,7 @@ import com.github.damontecres.stashapp.ui.components.OCounterButton @OptIn(ExperimentalComposeUiApi::class) @Composable fun PlayButtons( + sfwMode: Boolean, resumePosition: Long, oCount: Int, playOnClick: (position: Long, mode: PlaybackMode) -> Unit, @@ -105,6 +106,7 @@ fun PlayButtons( // O-Counter item { OCounterButton( + sfwMode = sfwMode, oCount = oCount, onClick = oCounterOnClick, onLongClick = oCounterOnLongClick, @@ -190,6 +192,7 @@ private fun PlayButtonsPreview() { focusRequester = remember { FocusRequester() }, alwaysStartFromBeginning = false, showEditButton = true, + sfwMode = false, modifier = Modifier, ) } diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/pages/ChooseThemePage.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/pages/ChooseThemePage.kt index 45ca06a12..c5c1eda90 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/pages/ChooseThemePage.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/pages/ChooseThemePage.kt @@ -139,6 +139,7 @@ fun ChooseThemePage( }, description = { IconRowText( + sfwMode = uiConfig.sfwMode, dataTypeMap, 2, Modifier diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/pages/PerformerPage.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/pages/PerformerPage.kt index f3cd03eb0..9b6afc74c 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/pages/PerformerPage.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/pages/PerformerPage.kt @@ -565,74 +565,78 @@ fun PerformerDetails( ) }, ) - add( - TableRow.from(context, R.string.stashapp_ethnicity, perf.ethnicity) { - navigateTo( - R.string.stashapp_ethnicity, - perf.ethnicity!!, - PerformerFilterType(ethnicity = stringCriterion(perf.ethnicity)), - ) - }, - ) - add( - TableRow.from(context, R.string.stashapp_hair_color, perf.hair_color) { - navigateTo( - R.string.stashapp_hair_color, - perf.hair_color!!, - PerformerFilterType(hair_color = stringCriterion(perf.hair_color)), - ) - }, - ) - add( - TableRow.from(context, R.string.stashapp_eye_color, perf.eye_color) { - navigateTo( - R.string.stashapp_eye_color, - perf.eye_color!!, - PerformerFilterType(eye_color = stringCriterion(perf.eye_color)), - ) - }, - ) - if (perf.height_cm != null) { - val feet = floor(perf.height_cm / 30.48).toInt() - val inches = (perf.height_cm / 2.54 - feet * 12).roundToInt() + if (!uiConfig.sfwMode) { add( - TableRow.from( - context, - R.string.stashapp_height, - "${perf.height_cm} cm ($feet'$inches\")", - ), + TableRow.from(context, R.string.stashapp_ethnicity, perf.ethnicity) { + navigateTo( + R.string.stashapp_ethnicity, + perf.ethnicity!!, + PerformerFilterType(ethnicity = stringCriterion(perf.ethnicity)), + ) + }, ) - } - if (perf.weight != null) { - val pounds = (perf.weight * 2.2).roundToInt() add( - TableRow.from( - context, - R.string.stashapp_weight, - "${perf.weight} kg ($pounds lbs)", - ), + TableRow.from(context, R.string.stashapp_hair_color, perf.hair_color) { + navigateTo( + R.string.stashapp_hair_color, + perf.hair_color!!, + PerformerFilterType(hair_color = stringCriterion(perf.hair_color)), + ) + }, + ) + add( + TableRow.from(context, R.string.stashapp_eye_color, perf.eye_color) { + navigateTo( + R.string.stashapp_eye_color, + perf.eye_color!!, + PerformerFilterType(eye_color = stringCriterion(perf.eye_color)), + ) + }, ) } - if (perf.penis_length != null) { - val inches = round(perf.penis_length / 2.54 * 100) / 100 + if (perf.height_cm != null) { + val feet = floor(perf.height_cm / 30.48).toInt() + val inches = (perf.height_cm / 2.54 - feet * 12).roundToInt() add( TableRow.from( context, - R.string.stashapp_penis_length, - "${perf.penis_length} cm ($inches\")", + R.string.stashapp_height, + "${perf.height_cm} cm ($feet'$inches\")", ), ) } - val circString = - when (perf.circumcised) { - CircumisedEnum.CUT -> context.getString(R.string.stashapp_circumcised_types_CUT) - CircumisedEnum.UNCUT -> context.getString(R.string.stashapp_circumcised_types_UNCUT) - CircumisedEnum.UNKNOWN__, null -> null + if (!uiConfig.sfwMode) { + if (perf.weight != null) { + val pounds = (perf.weight * 2.2).roundToInt() + add( + TableRow.from( + context, + R.string.stashapp_weight, + "${perf.weight} kg ($pounds lbs)", + ), + ) + } + if (perf.penis_length != null) { + val inches = round(perf.penis_length / 2.54 * 100) / 100 + add( + TableRow.from( + context, + R.string.stashapp_penis_length, + "${perf.penis_length} cm ($inches\")", + ), + ) } - add(TableRow.from(context, R.string.stashapp_circumcised, circString)) + val circString = + when (perf.circumcised) { + CircumisedEnum.CUT -> context.getString(R.string.stashapp_circumcised_types_CUT) + CircumisedEnum.UNCUT -> context.getString(R.string.stashapp_circumcised_types_UNCUT) + CircumisedEnum.UNKNOWN__, null -> null + } + add(TableRow.from(context, R.string.stashapp_circumcised, circString)) - add(TableRow.from(context, R.string.stashapp_tattoos, perf.tattoos)) - add(TableRow.from(context, R.string.stashapp_piercings, perf.piercings)) + add(TableRow.from(context, R.string.stashapp_tattoos, perf.tattoos)) + add(TableRow.from(context, R.string.stashapp_piercings, perf.piercings)) + } add(TableRow.from(context, R.string.stashapp_career_length, perf.career_length)) }.filterNotNull() } diff --git a/app/src/main/java/com/github/damontecres/stashapp/ui/pages/SceneDetailsPage.kt b/app/src/main/java/com/github/damontecres/stashapp/ui/pages/SceneDetailsPage.kt index 68ac5c0d0..310095316 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ui/pages/SceneDetailsPage.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ui/pages/SceneDetailsPage.kt @@ -545,7 +545,7 @@ fun SceneDetails( oCounterOnLongClick = { showDialog = DialogParams( - title = context.getString(R.string.stashapp_o_counter), + title = context.getString(R.string.stashapp_o_count), fromLongClick = true, items = listOf( diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/FilterParserExtensions.kt b/app/src/main/java/com/github/damontecres/stashapp/util/FilterParserExtensions.kt index 89075e354..1be26b838 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/FilterParserExtensions.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/FilterParserExtensions.kt @@ -1,8 +1,11 @@ package com.github.damontecres.stashapp.util import com.apollographql.apollo.api.Optional +import com.github.damontecres.stashapp.api.type.FileFilterType +import com.github.damontecres.stashapp.api.type.FolderFilterType import com.github.damontecres.stashapp.api.type.GalleryFilterType import com.github.damontecres.stashapp.api.type.GroupFilterType +import com.github.damontecres.stashapp.api.type.ImageFileFilterInput import com.github.damontecres.stashapp.api.type.ImageFilterType import com.github.damontecres.stashapp.api.type.MovieFilterType import com.github.damontecres.stashapp.api.type.PerformerFilterType @@ -10,6 +13,7 @@ import com.github.damontecres.stashapp.api.type.SceneFilterType import com.github.damontecres.stashapp.api.type.SceneMarkerFilterType import com.github.damontecres.stashapp.api.type.StudioFilterType import com.github.damontecres.stashapp.api.type.TagFilterType +import com.github.damontecres.stashapp.api.type.VideoFileFilterInput fun FilterParser.convertPerformerFilterType(f: Any?): PerformerFilterType? = if (f != null && f is PerformerFilterType) { @@ -54,6 +58,7 @@ fun FilterParser.convertPerformerFilterType(f: Any?): PerformerFilterType? = weight = Optional.presentIfNotNull(convertIntCriterionInput(filter["weight"])), death_year = Optional.presentIfNotNull(convertIntCriterionInput(filter["death_year"])), studios = Optional.presentIfNotNull(convertHierarchicalMultiCriterionInput(filter["studios"])), + groups = Optional.presentIfNotNull(convertHierarchicalMultiCriterionInput(filter["groups"])), performers = Optional.presentIfNotNull(convertMultiCriterionInput(filter["performers"])), ignore_auto_tag = Optional.presentIfNotNull(convertBoolean(filter["ignore_auto_tag"])), birthdate = Optional.presentIfNotNull(convertDateCriterionInput(filter["birthdate"])), @@ -133,6 +138,7 @@ fun FilterParser.convertSceneFilterType(f: Any?): SceneFilterType? = movies_filter = Optional.presentIfNotNull(convertMovieFilterType(filter["movies_filter"])), groups_filter = Optional.presentIfNotNull(convertGroupFilterType(filter["groups_filter"])), markers_filter = Optional.presentIfNotNull(convertSceneMarkerFilterType(filter["markers_filter"])), + files_filter = Optional.presentIfNotNull(convertFileFilterType(filter["files_filter"])), ) } else { null @@ -201,6 +207,7 @@ fun FilterParser.convertTagFilterType(f: Any?): TagFilterType? = parent_count = Optional.presentIfNotNull(convertIntCriterionInput(filter["parent_count"])), child_count = Optional.presentIfNotNull(convertIntCriterionInput(filter["child_count"])), ignore_auto_tag = Optional.presentIfNotNull(convertBoolean(filter["ignore_auto_tag"])), + stash_id_endpoint = Optional.presentIfNotNull(convertStashIDCriterionInput(filter["stash_id_endpoint"])), scenes_filter = Optional.presentIfNotNull(convertSceneFilterType(filter["scenes_filter"])), images_filter = Optional.presentIfNotNull(convertImageFilterType(filter["images_filter"])), galleries_filter = Optional.presentIfNotNull(convertGalleryFilterType(filter["galleries_filter"])), @@ -234,6 +241,7 @@ fun FilterParser.convertGroupFilterType(f: Any?): GroupFilterType? = date = Optional.presentIfNotNull(convertDateCriterionInput(filter["date"])), created_at = Optional.presentIfNotNull(convertTimestampCriterionInput(filter["created_at"])), updated_at = Optional.presentIfNotNull(convertTimestampCriterionInput(filter["updated_at"])), + o_counter = Optional.presentIfNotNull(convertIntCriterionInput(filter["o_counter"])), containing_groups = Optional.presentIfNotNull(convertHierarchicalMultiCriterionInput(filter["containing_groups"])), sub_groups = Optional.presentIfNotNull(convertHierarchicalMultiCriterionInput(filter["sub_groups"])), containing_group_count = Optional.presentIfNotNull(convertIntCriterionInput(filter["containing_group_count"])), @@ -307,6 +315,7 @@ fun FilterParser.convertImageFilterType(f: Any?): ImageFilterType? = performers_filter = Optional.presentIfNotNull(convertPerformerFilterType(filter["performers_filter"])), studios_filter = Optional.presentIfNotNull(convertStudioFilterType(filter["studios_filter"])), tags_filter = Optional.presentIfNotNull(convertTagFilterType(filter["tags_filter"])), + files_filter = Optional.presentIfNotNull(convertFileFilterType(filter["files_filter"])), ) } else { null @@ -354,6 +363,8 @@ fun FilterParser.convertGalleryFilterType(f: Any?): GalleryFilterType? = performers_filter = Optional.presentIfNotNull(convertPerformerFilterType(filter["performers_filter"])), studios_filter = Optional.presentIfNotNull(convertStudioFilterType(filter["studios_filter"])), tags_filter = Optional.presentIfNotNull(convertTagFilterType(filter["tags_filter"])), + files_filter = Optional.presentIfNotNull(convertFileFilterType(filter["files_filter"])), + folders_filter = Optional.presentIfNotNull(convertFolderFilterType(filter["folders_filter"])), ) } else { null @@ -388,3 +399,93 @@ fun FilterParser.convertMovieFilterType(f: Any?): MovieFilterType? = } else { null } + +fun FilterParser.convertFileFilterType(f: Any?): FileFilterType? = + if (f != null && f is FileFilterType) { + f + } else if (f != null) { + val filter = f as Map> + FileFilterType( + AND = Optional.presentIfNotNull(convertFileFilterType(filter["AND"])), + OR = Optional.presentIfNotNull(convertFileFilterType(filter["OR"])), + NOT = Optional.presentIfNotNull(convertFileFilterType(filter["NOT"])), + path = Optional.presentIfNotNull(convertStringCriterionInput(filter["path"])), + basename = Optional.presentIfNotNull(convertStringCriterionInput(filter["basename"])), + dir = Optional.presentIfNotNull(convertStringCriterionInput(filter["dir"])), + parent_folder = Optional.presentIfNotNull(convertHierarchicalMultiCriterionInput(filter["parent_folder"])), + zip_file = Optional.presentIfNotNull(convertMultiCriterionInput(filter["zip_file"])), + mod_time = Optional.presentIfNotNull(convertTimestampCriterionInput(filter["mod_time"])), + duplicated = Optional.presentIfNotNull(convertPHashDuplicationCriterionInput(filter["duplicated"])), + video_file_filter = Optional.presentIfNotNull(convertVideoFileFilterInput(filter["video_file_filter"])), + image_file_filter = Optional.presentIfNotNull(convertImageFileFilterInput(filter["image_file_filter"])), + scene_count = Optional.presentIfNotNull(convertIntCriterionInput(filter["scene_count"])), + image_count = Optional.presentIfNotNull(convertIntCriterionInput(filter["image_count"])), + gallery_count = Optional.presentIfNotNull(convertIntCriterionInput(filter["gallery_count"])), + scenes_filter = Optional.presentIfNotNull(convertSceneFilterType(filter["scenes_filter"])), + images_filter = Optional.presentIfNotNull(convertImageFilterType(filter["images_filter"])), + galleries_filter = Optional.presentIfNotNull(convertGalleryFilterType(filter["galleries_filter"])), + created_at = Optional.presentIfNotNull(convertTimestampCriterionInput(filter["created_at"])), + updated_at = Optional.presentIfNotNull(convertTimestampCriterionInput(filter["updated_at"])), + ) + } else { + null + } + +fun FilterParser.convertFolderFilterType(f: Any?): FolderFilterType? = + if (f != null && f is FolderFilterType) { + f + } else if (f != null) { + val filter = f as Map> + FolderFilterType( + AND = Optional.presentIfNotNull(convertFolderFilterType(filter["AND"])), + OR = Optional.presentIfNotNull(convertFolderFilterType(filter["OR"])), + NOT = Optional.presentIfNotNull(convertFolderFilterType(filter["NOT"])), + path = Optional.presentIfNotNull(convertStringCriterionInput(filter["path"])), + parent_folder = Optional.presentIfNotNull(convertHierarchicalMultiCriterionInput(filter["parent_folder"])), + zip_file = Optional.presentIfNotNull(convertMultiCriterionInput(filter["zip_file"])), + mod_time = Optional.presentIfNotNull(convertTimestampCriterionInput(filter["mod_time"])), + gallery_count = Optional.presentIfNotNull(convertIntCriterionInput(filter["gallery_count"])), + files_filter = Optional.presentIfNotNull(convertFileFilterType(filter["files_filter"])), + galleries_filter = Optional.presentIfNotNull(convertGalleryFilterType(filter["galleries_filter"])), + created_at = Optional.presentIfNotNull(convertTimestampCriterionInput(filter["created_at"])), + updated_at = Optional.presentIfNotNull(convertTimestampCriterionInput(filter["updated_at"])), + ) + } else { + null + } + +fun FilterParser.convertVideoFileFilterInput(f: Any?): VideoFileFilterInput? = + if (f != null && f is VideoFileFilterInput) { + f + } else if (f != null) { + val filter = f as Map> + VideoFileFilterInput( + resolution = Optional.presentIfNotNull(convertResolutionCriterionInput(filter["resolution"])), + orientation = Optional.presentIfNotNull(convertOrientationCriterionInput(filter["orientation"])), + framerate = Optional.presentIfNotNull(convertIntCriterionInput(filter["framerate"])), + bitrate = Optional.presentIfNotNull(convertIntCriterionInput(filter["bitrate"])), + format = Optional.presentIfNotNull(convertStringCriterionInput(filter["format"])), + video_codec = Optional.presentIfNotNull(convertStringCriterionInput(filter["video_codec"])), + audio_codec = Optional.presentIfNotNull(convertStringCriterionInput(filter["audio_codec"])), + duration = Optional.presentIfNotNull(convertIntCriterionInput(filter["duration"])), + captions = Optional.presentIfNotNull(convertStringCriterionInput(filter["captions"])), + interactive = Optional.presentIfNotNull(convertBoolean(filter["interactive"])), + interactive_speed = Optional.presentIfNotNull(convertIntCriterionInput(filter["interactive_speed"])), + ) + } else { + null + } + +fun FilterParser.convertImageFileFilterInput(f: Any?): ImageFileFilterInput? = + if (f != null && f is ImageFileFilterInput) { + f + } else if (f != null) { + val filter = f as Map> + ImageFileFilterInput( + format = Optional.presentIfNotNull(convertStringCriterionInput(filter["format"])), + resolution = Optional.presentIfNotNull(convertResolutionCriterionInput(filter["resolution"])), + orientation = Optional.presentIfNotNull(convertOrientationCriterionInput(filter["orientation"])), + ) + } else { + null + } diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/OCounterLongClickCallBack.kt b/app/src/main/java/com/github/damontecres/stashapp/util/OCounterLongClickCallBack.kt index f3539f7a8..022075e77 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/OCounterLongClickCallBack.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/OCounterLongClickCallBack.kt @@ -22,7 +22,7 @@ fun oCounterAction( StashCoroutineExceptionHandler( Toast.makeText( cardView.context, - cardView.context.getString(R.string.failed_o_counter), + cardView.context.getString(R.string.failed_update), Toast.LENGTH_SHORT, ), ), diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/ServerPreferences.kt b/app/src/main/java/com/github/damontecres/stashapp/util/ServerPreferences.kt index dadfaa1f5..c470abef9 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/ServerPreferences.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/ServerPreferences.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.SharedPreferences import android.util.Log import androidx.core.content.edit +import com.github.damontecres.stashapp.R import com.github.damontecres.stashapp.StashApplication import com.github.damontecres.stashapp.api.ConfigurationQuery import com.github.damontecres.stashapp.data.DataType @@ -59,6 +60,8 @@ class ServerPreferences( val abbreviateCounters get() = preferences.getBoolean(PREF_INTERFACE_ABBREV_COUNTERS, false) + val sfwMode get() = preferences.getBoolean(PREF_INTERFACE_SFW_MODE, false) + val customLocalesEnabled get() = preferences.getBoolean( @@ -114,33 +117,57 @@ class ServerPreferences( config.configuration.`interface`.customLocales .isNotNullOrBlank(), ) + putBoolean(PREF_INTERFACE_SFW_MODE, config.configuration.`interface`.sfwContentMode) } - if (config.configuration.`interface`.customLocalesEnabled == true && - config.configuration.`interface`.customLocales - .isNotNullOrBlank() - ) { + val useRestring = + config.configuration.`interface`.sfwContentMode || + ( + config.configuration.`interface`.customLocalesEnabled == true && + config.configuration.`interface`.customLocales + .isNotNullOrBlank() + ) + if (useRestring) { + Restring.clear() try { - val root = Json.parseToJsonElement(config.configuration.`interface`.customLocales) - if (root is JsonObject) { - val map = - parseDictionary( - root.jsonObject, - listOf("stashapp"), - false, - ).associate { it.key to it.value } - Restring.putStrings(Restring.locale, map) - - if (root.containsKey("StashAppAndroidTV") && root.jsonObject["StashAppAndroidTV"] is JsonObject) { - val appMap = + if (config.configuration.`interface`.sfwContentMode) { + val context = StashApplication.getApplication() + Restring.putStrings( + Restring.locale, + mapOf( + "stashapp_stats_total_o_count" to + context.getString(R.string.stashapp_stats_total_o_count_sfw), + "stashapp_o_count" to + context.getString(R.string.stashapp_o_count_sfw), + "stashapp_last_o_at" to + context.getString(R.string.stashapp_last_o_at_sfw), + ), + ) + } + if (config.configuration.`interface`.customLocalesEnabled == true && + config.configuration.`interface`.customLocales + .isNotNullOrBlank() + ) { + val root = + Json.parseToJsonElement(config.configuration.`interface`.customLocales) + if (root is JsonObject) { + val map = parseDictionary( - root.jsonObject["StashAppAndroidTV"]!!.jsonObject, - listOf(), - true, + root.jsonObject, + listOf("stashapp"), + false, ).associate { it.key to it.value } - Restring.putStrings(Restring.locale, appMap) + Restring.putStrings(Restring.locale, map) + + if (root.containsKey("StashAppAndroidTV") && root.jsonObject["StashAppAndroidTV"] is JsonObject) { + val appMap = + parseDictionary( + root.jsonObject["StashAppAndroidTV"]!!.jsonObject, + listOf(), + true, + ).associate { it.key to it.value } + Restring.putStrings(Restring.locale, appMap) + } } - } else { - Restring.clear() } } catch (ex: Exception) { Log.w(TAG, "Error parsing custom locales", ex) @@ -441,6 +468,7 @@ class ServerPreferences( const val PREF_INTERFACE_STUDIOS_AS_TEXT = "interface.showStudioAsText" const val PREF_INTERFACE_ABBREV_COUNTERS = "interface.abbreviateCounters" const val PREF_INTERFACE_CUSTOM_LOCALES_ENABLED = "interface.customLocalesEnabled" + const val PREF_INTERFACE_SFW_MODE = "interface.sfwContentMode" const val PREF_JOB_SCAN = "app.job.scan" const val PREF_JOB_GENERATE = "app.job.generate" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8a59bf5da..05e7f0ee0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,11 +31,12 @@ + Go to Go to scene - Failed to update o-counter + Failed to update Advanced Settings Home diff --git a/stash-server b/stash-server index 7716c4dd8..b23b0267a 160000 --- a/stash-server +++ b/stash-server @@ -1 +1 @@ -Subproject commit 7716c4dd874512e7937c59ee52d034e452ba2026 +Subproject commit b23b0267adc668bb22390ccc5772e75946aed492