Add SettingsColumn composable and clean up formatting

This commit is contained in:
Niels van Velzen
2025-12-14 13:05:44 +01:00
committed by Niels van Velzen
parent c6e0badbae
commit d714d58fef
12 changed files with 227 additions and 371 deletions

View File

@@ -0,0 +1,17 @@
package org.jellyfin.androidtv.ui.settings.composable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun SettingsColumn(content: LazyListScope.() -> Unit) = LazyColumn(
modifier = Modifier
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
content = content,
)

View File

@@ -1,20 +1,13 @@
package org.jellyfin.androidtv.ui.settings.screen
import android.text.format.Formatter
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import coil3.ImageLoader
import org.jellyfin.androidtv.BuildConfig
import org.jellyfin.androidtv.R
@@ -25,6 +18,7 @@ import org.jellyfin.androidtv.ui.base.form.Checkbox
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.settings.compat.rememberPreference
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.jellyfin.androidtv.util.isTvDevice
import org.koin.compose.koinInject
@@ -36,30 +30,29 @@ fun SettingsDeveloperScreen() {
val isTvDevice = remember(context) { context.isTvDevice() }
val isDeveloperBuild = BuildConfig.DEVELOPMENT
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
ListSection(
overlineContent = { Text(stringResource(R.string.settings).uppercase()) },
headingContent = { Text(stringResource(R.string.pref_developer_link)) },
captionContent = { Text(stringResource(R.string.pref_developer_link_description)) },
)
SettingsColumn {
item {
ListSection(
overlineContent = { Text(stringResource(R.string.settings).uppercase()) },
headingContent = { Text(stringResource(R.string.pref_developer_link)) },
captionContent = { Text(stringResource(R.string.pref_developer_link_description)) },
)
}
// Legacy debug flag
// Not in use by much components anymore
var debuggingEnabled by rememberPreference(userPreferences, UserPreferences.debuggingEnabled)
ListButton(
headingContent = { Text(stringResource(R.string.lbl_enable_debug)) },
trailingContent = { Checkbox(checked = debuggingEnabled) },
captionContent = { Text(stringResource(R.string.desc_debug)) },
onClick = { debuggingEnabled = !debuggingEnabled }
)
item {
// Legacy debug flag
// Not in use by much components anymore
var debuggingEnabled by rememberPreference(userPreferences, UserPreferences.debuggingEnabled)
ListButton(
headingContent = { Text(stringResource(R.string.lbl_enable_debug)) },
trailingContent = { Checkbox(checked = debuggingEnabled) },
captionContent = { Text(stringResource(R.string.desc_debug)) },
onClick = { debuggingEnabled = !debuggingEnabled }
)
}
// UI Mode toggle
if (!isTvDevice) {
if (!isTvDevice) item {
var disableUiModeWarning by rememberPreference(systemPreferences, SystemPreferences.disableUiModeWarning)
ListButton(
headingContent = { Text(stringResource(R.string.disable_ui_mode_warning)) },
@@ -69,7 +62,7 @@ fun SettingsDeveloperScreen() {
}
// Playback rewrite - only show in debug mode
if (isDeveloperBuild) {
if (isDeveloperBuild) item {
var playbackRewriteVideoEnabled by rememberPreference(userPreferences, UserPreferences.playbackRewriteVideoEnabled)
ListButton(
// String is hardcoded because it's for development only
@@ -80,42 +73,48 @@ fun SettingsDeveloperScreen() {
)
}
// Trick play
var trickPlayEnabled by rememberPreference(userPreferences, UserPreferences.trickPlayEnabled)
ListButton(
headingContent = { Text(stringResource(R.string.preference_enable_trickplay)) },
trailingContent = { Checkbox(checked = trickPlayEnabled) },
captionContent = { Text(stringResource(R.string.enable_playback_module_description)) },
onClick = { trickPlayEnabled = !trickPlayEnabled }
)
item {
// Trick play
var trickPlayEnabled by rememberPreference(userPreferences, UserPreferences.trickPlayEnabled)
ListButton(
headingContent = { Text(stringResource(R.string.preference_enable_trickplay)) },
trailingContent = { Checkbox(checked = trickPlayEnabled) },
captionContent = { Text(stringResource(R.string.enable_playback_module_description)) },
onClick = { trickPlayEnabled = !trickPlayEnabled }
)
}
// FFmpeg audio extension
var preferExoPlayerFfmpeg by rememberPreference(userPreferences, UserPreferences.preferExoPlayerFfmpeg)
ListButton(
headingContent = { Text(stringResource(R.string.prefer_exoplayer_ffmpeg)) },
trailingContent = { Checkbox(checked = preferExoPlayerFfmpeg) },
captionContent = { Text(stringResource(R.string.prefer_exoplayer_ffmpeg_content)) },
onClick = { preferExoPlayerFfmpeg = !preferExoPlayerFfmpeg }
)
item {
// FFmpeg audio extension
var preferExoPlayerFfmpeg by rememberPreference(userPreferences, UserPreferences.preferExoPlayerFfmpeg)
ListButton(
headingContent = { Text(stringResource(R.string.prefer_exoplayer_ffmpeg)) },
trailingContent = { Checkbox(checked = preferExoPlayerFfmpeg) },
captionContent = { Text(stringResource(R.string.prefer_exoplayer_ffmpeg_content)) },
onClick = { preferExoPlayerFfmpeg = !preferExoPlayerFfmpeg }
)
}
// Image cache
val imageLoader = koinInject<ImageLoader>()
var imageCacheSize by remember { mutableLongStateOf(imageLoader.diskCache?.size ?: 0L) }
ListButton(
headingContent = { Text(stringResource(R.string.clear_image_cache)) },
captionContent = {
Text(
stringResource(
R.string.clear_image_cache_content,
Formatter.formatFileSize(context, imageCacheSize)
item {
// Image cache
val imageLoader = koinInject<ImageLoader>()
var imageCacheSize by remember { mutableLongStateOf(imageLoader.diskCache?.size ?: 0L) }
ListButton(
headingContent = { Text(stringResource(R.string.clear_image_cache)) },
captionContent = {
Text(
stringResource(
R.string.clear_image_cache_content,
Formatter.formatFileSize(context, imageCacheSize)
)
)
)
},
onClick = {
imageLoader.memoryCache?.clear()
imageLoader.diskCache?.clear()
imageCacheSize = imageLoader.diskCache?.size ?: 0L
}
)
},
onClick = {
imageLoader.memoryCache?.clear()
imageLoader.diskCache?.clear()
imageCacheSize = imageLoader.diskCache?.size ?: 0L
}
)
}
}
}

View File

@@ -1,16 +1,9 @@
package org.jellyfin.androidtv.ui.settings.screen
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.ui.base.Icon
import org.jellyfin.androidtv.ui.base.Text
@@ -19,88 +12,70 @@ import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.ActivityDestinations
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.Routes
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
@Composable
fun SettingsMainScreen() {
val context = LocalContext.current
val router = LocalRouter.current
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
ListSection(
overlineContent = { Text(stringResource(R.string.app_name).uppercase()) },
headingContent = { Text(stringResource(R.string.settings)) },
captionContent = { Text(stringResource(R.string.settings_description)) },
)
SettingsColumn {
item {
ListSection(
overlineContent = { Text(stringResource(R.string.app_name).uppercase()) },
headingContent = { Text(stringResource(R.string.settings)) },
captionContent = { Text(stringResource(R.string.settings_description)) },
)
}
ListButton(
leadingContent = {
Icon(
painterResource(R.drawable.ic_users),
contentDescription = null
)
},
headingContent = { Text(stringResource(R.string.pref_login)) },
captionContent = { Text(stringResource(R.string.pref_login_description)) },
onClick = { router.push(Routes.AUTHENTICATION) }
)
item {
ListButton(
leadingContent = { Icon(painterResource(R.drawable.ic_users), contentDescription = null) },
headingContent = { Text(stringResource(R.string.pref_login)) },
captionContent = { Text(stringResource(R.string.pref_login_description)) },
onClick = { router.push(Routes.AUTHENTICATION) }
)
}
ListButton(
leadingContent = {
Icon(
painterResource(R.drawable.ic_adjust),
contentDescription = null
)
},
headingContent = { Text(stringResource(R.string.pref_customization)) },
captionContent = { Text(stringResource(R.string.pref_customization_description)) },
onClick = {
context.startActivity(ActivityDestinations.customizationPreferences(context))
}
)
item {
ListButton(
leadingContent = { Icon(painterResource(R.drawable.ic_adjust), contentDescription = null) },
headingContent = { Text(stringResource(R.string.pref_customization)) },
captionContent = { Text(stringResource(R.string.pref_customization_description)) },
onClick = { context.startActivity(ActivityDestinations.customizationPreferences(context)) }
)
}
ListButton(
leadingContent = {
Icon(
painterResource(R.drawable.ic_next),
contentDescription = null
)
},
headingContent = { Text(stringResource(R.string.pref_playback)) },
captionContent = { Text(stringResource(R.string.pref_playback_description)) },
onClick = {
context.startActivity(ActivityDestinations.playbackPreferences(context))
}
)
item {
ListButton(
leadingContent = { Icon(painterResource(R.drawable.ic_next), contentDescription = null) },
headingContent = { Text(stringResource(R.string.pref_playback)) },
captionContent = { Text(stringResource(R.string.pref_playback_description)) },
onClick = { context.startActivity(ActivityDestinations.playbackPreferences(context)) }
)
}
ListButton(
leadingContent = {
Icon(
painterResource(R.drawable.ic_error),
contentDescription = null
)
},
headingContent = { Text(stringResource(R.string.pref_telemetry_category)) },
captionContent = { Text(stringResource(R.string.pref_telemetry_description)) },
onClick = { router.push(Routes.TELEMETRY) }
)
item {
ListButton(
leadingContent = { Icon(painterResource(R.drawable.ic_error), contentDescription = null) },
headingContent = { Text(stringResource(R.string.pref_telemetry_category)) },
captionContent = { Text(stringResource(R.string.pref_telemetry_description)) },
onClick = { router.push(Routes.TELEMETRY) }
)
ListButton(
leadingContent = {
Icon(
painterResource(R.drawable.ic_flask),
contentDescription = null
)
},
headingContent = { Text(stringResource(R.string.pref_developer_link)) },
captionContent = { Text(stringResource(R.string.pref_developer_link_description)) },
onClick = { router.push(Routes.DEVELOPER) }
)
}
SettingsMainScreenAbout()
item {
ListButton(
leadingContent = { Icon(painterResource(R.drawable.ic_flask), contentDescription = null) },
headingContent = { Text(stringResource(R.string.pref_developer_link)) },
captionContent = { Text(stringResource(R.string.pref_developer_link_description)) },
onClick = { router.push(Routes.DEVELOPER) }
)
}
settingsAboutItems(
openLicenses = { router.push(Routes.LICENSES) }
)
}
}

View File

@@ -1,7 +1,7 @@
package org.jellyfin.androidtv.ui.settings.screen
import android.os.Build
import androidx.compose.runtime.Composable
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import org.jellyfin.androidtv.BuildConfig
@@ -10,48 +10,34 @@ import org.jellyfin.androidtv.ui.base.Icon
import org.jellyfin.androidtv.ui.base.Text
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.Routes
@Composable
fun SettingsMainScreenAbout() {
val router = LocalRouter.current
fun LazyListScope.settingsAboutItems(
openLicenses: () -> Unit,
) {
item { ListSection(headingContent = { Text(stringResource(R.string.pref_about_title)) }) }
ListSection(
headingContent = { Text(stringResource(R.string.pref_about_title)) },
)
item {
ListSection(
leadingContent = { Icon(painterResource(R.drawable.ic_jellyfin), contentDescription = null) },
headingContent = { Text("Jellyfin app version") },
captionContent = { Text("jellyfin-androidtv ${BuildConfig.VERSION_NAME} ${BuildConfig.BUILD_TYPE}") },
)
}
ListSection(
leadingContent = {
Icon(
painterResource(R.drawable.ic_jellyfin),
contentDescription = null
)
},
headingContent = { Text("Jellyfin app version") },
captionContent = { Text("jellyfin-androidtv ${BuildConfig.VERSION_NAME} ${BuildConfig.BUILD_TYPE}") },
)
item {
ListSection(
leadingContent = { Icon(painterResource(R.drawable.ic_tv), contentDescription = null) },
headingContent = { Text(stringResource(R.string.pref_device_model)) },
captionContent = { Text("${Build.MANUFACTURER} ${Build.MODEL}") },
)
}
ListSection(
leadingContent = {
Icon(
painterResource(R.drawable.ic_tv),
contentDescription = null
)
},
headingContent = { Text(stringResource(R.string.pref_device_model)) },
captionContent = { Text("${Build.MANUFACTURER} ${Build.MODEL}") },
)
ListButton(
leadingContent = {
Icon(
painterResource(R.drawable.ic_guide),
contentDescription = null
)
},
headingContent = { Text(stringResource(R.string.licenses_link)) },
captionContent = { Text(stringResource(R.string.licenses_link_description)) },
onClick = { router.push(Routes.LICENSES) }
)
item {
ListButton(
leadingContent = { Icon(painterResource(R.drawable.ic_guide), contentDescription = null) },
headingContent = { Text(stringResource(R.string.licenses_link)) },
captionContent = { Text(stringResource(R.string.licenses_link_description)) },
onClick = { openLicenses() }
)
}
}

View File

@@ -1,16 +1,9 @@
package org.jellyfin.androidtv.ui.settings.screen
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.preference.TelemetryPreferences
import org.jellyfin.androidtv.ui.base.Text
@@ -18,58 +11,46 @@ import org.jellyfin.androidtv.ui.base.form.Checkbox
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.settings.compat.rememberPreference
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.koin.compose.koinInject
@Composable
fun SettingsTelemetryScreen() {
val telemetryPreferences = koinInject<TelemetryPreferences>()
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
ListSection(
overlineContent = { Text(stringResource(R.string.settings).uppercase()) },
headingContent = { Text(stringResource(R.string.pref_telemetry_category)) },
captionContent = { Text(stringResource(R.string.pref_telemetry_description)) },
)
SettingsColumn {
item {
ListSection(
overlineContent = { Text(stringResource(R.string.settings).uppercase()) },
headingContent = { Text(stringResource(R.string.pref_telemetry_category)) },
captionContent = { Text(stringResource(R.string.pref_telemetry_description)) },
)
}
var crashReportEnabled by rememberPreference(telemetryPreferences, TelemetryPreferences.crashReportEnabled)
ListButton(
headingContent = { Text(stringResource(R.string.pref_crash_reports)) },
trailingContent = {
Checkbox(
checked = crashReportEnabled,
)
},
captionContent = {
if (crashReportEnabled) {
Text(stringResource(R.string.pref_crash_reports_enabled))
} else {
Text(stringResource(R.string.pref_crash_reports_disabled))
}
},
onClick = {
crashReportEnabled = !crashReportEnabled
}
)
item {
var crashReportEnabled by rememberPreference(telemetryPreferences, TelemetryPreferences.crashReportEnabled)
ListButton(
headingContent = { Text(stringResource(R.string.pref_crash_reports)) },
trailingContent = { Checkbox(checked = crashReportEnabled) },
captionContent = {
if (crashReportEnabled) Text(stringResource(R.string.pref_crash_reports_enabled))
else Text(stringResource(R.string.pref_crash_reports_disabled))
},
onClick = { crashReportEnabled = !crashReportEnabled }
)
}
var crashReportIncludeLogs by rememberPreference(telemetryPreferences, TelemetryPreferences.crashReportIncludeLogs)
ListButton(
headingContent = { Text(stringResource(R.string.pref_crash_report_logs)) },
trailingContent = { Checkbox(checked = crashReportIncludeLogs) },
captionContent = {
if (crashReportIncludeLogs) {
Text(stringResource(R.string.pref_crash_report_logs_enabled))
} else {
Text(stringResource(R.string.pref_crash_report_logs_disabled))
}
},
onClick = {
crashReportIncludeLogs = !crashReportIncludeLogs
}
)
item {
var crashReportIncludeLogs by rememberPreference(telemetryPreferences, TelemetryPreferences.crashReportIncludeLogs)
ListButton(
headingContent = { Text(stringResource(R.string.pref_crash_report_logs)) },
trailingContent = { Checkbox(checked = crashReportIncludeLogs) },
captionContent = {
if (crashReportIncludeLogs) Text(stringResource(R.string.pref_crash_report_logs_enabled))
else Text(stringResource(R.string.pref_crash_report_logs_disabled))
},
onClick = { crashReportIncludeLogs = !crashReportIncludeLogs }
)
}
}
}

View File

@@ -1,11 +1,8 @@
package org.jellyfin.androidtv.ui.settings.screen.authentication
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -35,6 +32,7 @@ import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.compat.rememberPreference
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.koin.compose.koinInject
@Composable
@@ -54,11 +52,7 @@ fun SettingsAuthenticationAutoSignInScreen() {
val storedServers by serverRepository.storedServers.collectAsState()
LazyColumn(
modifier = Modifier
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
SettingsColumn {
item {
ListSection(
overlineContent = { Text(stringResource(R.string.pref_login).uppercase()) },
@@ -91,11 +85,7 @@ fun SettingsAuthenticationAutoSignInScreen() {
}
for (server in storedServers) {
item {
ListSection(
headingContent = { Text(server.name) },
)
}
item { ListSection(headingContent = { Text(server.name) }) }
val users = serverUserRepository.getStoredServerUsers(server)
val serverId = server.id.toString()
@@ -108,10 +98,7 @@ fun SettingsAuthenticationAutoSignInScreen() {
ListButton(
leadingContent = {
if (!userImageVisible) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_user),
contentDescription = null,
)
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_user), contentDescription = null)
} else {
Image(
painter = userImagePainter,

View File

@@ -1,8 +1,5 @@
package org.jellyfin.androidtv.ui.settings.screen.authentication
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -10,10 +7,8 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.auth.repository.ServerRepository
import org.jellyfin.androidtv.auth.repository.ServerUserRepository
@@ -27,7 +22,8 @@ import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.Routes
import org.jellyfin.androidtv.ui.settings.compat.rememberPreference
import org.jellyfin.androidtv.ui.settings.screen.SettingsMainScreenAbout
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.jellyfin.androidtv.ui.settings.screen.settingsAboutItems
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
import org.koin.compose.koinInject
@@ -38,17 +34,11 @@ fun SettingsAuthenticationScreen(launchedFromLogin: Boolean = false) {
val serverUserRepository = koinInject<ServerUserRepository>()
val authenticationPreferences = koinInject<AuthenticationPreferences>()
LaunchedEffect(serverRepository) {
serverRepository.loadStoredServers()
}
LaunchedEffect(serverRepository) { serverRepository.loadStoredServers() }
val storedServers by serverRepository.storedServers.collectAsState()
LazyColumn(
modifier = Modifier
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
SettingsColumn {
if (launchedFromLogin) item {
ListSection(
overlineContent = { Text(stringResource(R.string.app_name).uppercase()) },
@@ -98,15 +88,11 @@ fun SettingsAuthenticationScreen(launchedFromLogin: Boolean = false) {
}
if (storedServers.isNotEmpty()) {
item {
ListSection(
headingContent = { Text(stringResource(R.string.lbl_manage_servers)) },
)
}
item { ListSection(headingContent = { Text(stringResource(R.string.lbl_manage_servers)) }) }
items(storedServers) { server ->
ListButton(
leadingContent = { Icon(painterResource(R.drawable.ic_house), contentDescription = server.name) },
leadingContent = { Icon(painterResource(R.drawable.ic_house), contentDescription = null) },
headingContent = { Text(server.name) },
captionContent = { Text(server.address) },
onClick = {
@@ -124,11 +110,7 @@ fun SettingsAuthenticationScreen(launchedFromLogin: Boolean = false) {
// Disallow changing the "always authenticate" option from the login screen
// because that could allow a kid to disable the function to access a parent's account
if (!launchedFromLogin) {
item {
ListSection(
headingContent = { Text(stringResource(R.string.advanced_settings)) },
)
}
item { ListSection(headingContent = { Text(stringResource(R.string.advanced_settings)) }) }
item {
var alwaysAuthenticate by rememberPreference(authenticationPreferences, AuthenticationPreferences.alwaysAuthenticate)
@@ -141,8 +123,8 @@ fun SettingsAuthenticationScreen(launchedFromLogin: Boolean = false) {
}
}
if (launchedFromLogin) item {
SettingsMainScreenAbout()
}
if (launchedFromLogin) settingsAboutItems(
openLicenses = { router.push(Routes.LICENSES) }
)
}
}

View File

@@ -1,11 +1,8 @@
package org.jellyfin.androidtv.ui.settings.screen.authentication
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -37,6 +34,7 @@ import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.Routes
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.koin.compose.koinInject
import java.time.Instant
import java.time.LocalDateTime
@@ -53,23 +51,15 @@ fun SettingsAuthenticationServerScreen(serverId: UUID) {
val serverUserRepository = koinInject<ServerUserRepository>()
val authenticationRepository = koinInject<AuthenticationRepository>()
LaunchedEffect(serverRepository) {
serverRepository.loadStoredServers()
}
LaunchedEffect(serverRepository) { serverRepository.loadStoredServers() }
val server by remember(serverRepository.storedServers) {
serverRepository.storedServers.map { it.find { server -> server.id == serverId } }
}.collectAsState(null)
val users = remember(server) {
server?.let(serverUserRepository::getStoredServerUsers)
}
val users = remember(server) { server?.let(serverUserRepository::getStoredServerUsers) }
LazyColumn(
modifier = Modifier
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
SettingsColumn {
item {
ListSection(
overlineContent = { Text(stringResource(R.string.pref_login).uppercase()) },
@@ -79,11 +69,7 @@ fun SettingsAuthenticationServerScreen(serverId: UUID) {
}
if (!users.isNullOrEmpty()) {
item {
ListSection(
headingContent = { Text(stringResource(R.string.pref_accounts)) },
)
}
item { ListSection(headingContent = { Text(stringResource(R.string.pref_accounts)) }) }
items(users) { user ->
val userImagePainter = rememberAsyncImagePainter(authenticationRepository.getUserImageUrl(server!!, user))
@@ -93,10 +79,7 @@ fun SettingsAuthenticationServerScreen(serverId: UUID) {
ListButton(
leadingContent = {
if (!userImageVisible) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_user),
contentDescription = null,
)
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_user), contentDescription = null)
} else {
Image(
painter = userImagePainter,
@@ -136,20 +119,11 @@ fun SettingsAuthenticationServerScreen(serverId: UUID) {
}
}
item {
ListSection(
headingContent = { Text(stringResource(R.string.lbl_server)) },
)
}
item { ListSection(headingContent = { Text(stringResource(R.string.lbl_server)) }) }
item {
ListButton(
leadingContent = {
Icon(
painterResource(R.drawable.ic_delete),
contentDescription = stringResource(R.string.lbl_remove_server)
)
},
leadingContent = { Icon(painterResource(R.drawable.ic_delete), contentDescription = null) },
headingContent = { Text(stringResource(R.string.lbl_remove_server)) },
captionContent = { Text(stringResource(R.string.lbl_remove_users)) },
onClick = {

View File

@@ -1,17 +1,12 @@
package org.jellyfin.androidtv.ui.settings.screen.authentication
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.map
@@ -25,6 +20,7 @@ import org.jellyfin.androidtv.ui.base.Text
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.koin.compose.koinInject
import java.util.UUID
@@ -36,23 +32,15 @@ fun SettingsAuthenticationServerUserScreen(serverId: UUID, userId: UUID) {
val serverUserRepository = koinInject<ServerUserRepository>()
val authenticationRepository = koinInject<AuthenticationRepository>()
LaunchedEffect(serverRepository) {
serverRepository.loadStoredServers()
}
LaunchedEffect(serverRepository) { serverRepository.loadStoredServers() }
val server by remember(serverRepository.storedServers) {
serverRepository.storedServers.map { it.find { server -> server.id == serverId } }
}.collectAsState(null)
val user = remember(server) {
server?.let(serverUserRepository::getStoredServerUsers)?.find { user -> user.id == userId }
}
val user = remember(server) { server?.let(serverUserRepository::getStoredServerUsers)?.find { user -> user.id == userId } }
LazyColumn(
modifier = Modifier
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
SettingsColumn {
item {
ListSection(
overlineContent = { Text(server?.name?.uppercase().orEmpty()) },
@@ -62,12 +50,7 @@ fun SettingsAuthenticationServerUserScreen(serverId: UUID, userId: UUID) {
item {
ListButton(
leadingContent = {
Icon(
painterResource(R.drawable.ic_logout),
contentDescription = stringResource(R.string.lbl_sign_out)
)
},
leadingContent = { Icon(painterResource(R.drawable.ic_logout), contentDescription = null) },
headingContent = { Text(stringResource(R.string.lbl_sign_out)) },
captionContent = { Text(stringResource(R.string.lbl_sign_out_content)) },
onClick = {
@@ -81,12 +64,7 @@ fun SettingsAuthenticationServerUserScreen(serverId: UUID, userId: UUID) {
item {
ListButton(
leadingContent = {
Icon(
painterResource(R.drawable.ic_delete),
contentDescription = stringResource(R.string.lbl_remove)
)
},
leadingContent = { Icon(painterResource(R.drawable.ic_delete), contentDescription = null) },
headingContent = { Text(stringResource(R.string.lbl_remove)) },
captionContent = { Text(stringResource(R.string.lbl_remove_user_content)) },
onClick = {

View File

@@ -1,15 +1,10 @@
package org.jellyfin.androidtv.ui.settings.screen.authentication
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.auth.model.AuthenticationSortBy
import org.jellyfin.androidtv.auth.store.AuthenticationPreferences
@@ -19,6 +14,7 @@ import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.compat.rememberPreference
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.koin.compose.koinInject
@Composable
@@ -27,11 +23,7 @@ fun SettingsAuthenticationSortByScreen() {
val authenticationPreferences = koinInject<AuthenticationPreferences>()
var sortBy by rememberPreference(authenticationPreferences, AuthenticationPreferences.sortBy)
LazyColumn(
modifier = Modifier
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
SettingsColumn {
item {
ListSection(
overlineContent = { Text(stringResource(R.string.pref_login).uppercase()) },

View File

@@ -3,18 +3,13 @@ package org.jellyfin.androidtv.ui.settings.screen.license
import android.content.ClipData
import android.os.Build
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.mikepenz.aboutlibraries.Libs
@@ -24,6 +19,7 @@ import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.ui.base.Text
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
@Composable
fun SettingsLicenseScreen(artifactId: String) {
@@ -53,11 +49,8 @@ fun SettingsLicenseScreen(artifactId: String) {
library.developers.forEach { developer -> add(stringResource(R.string.license_author) to developer.name) }
library.licenses.forEach { license -> add(stringResource(R.string.license_license) to license.name) }
}
LazyColumn(
modifier = Modifier
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
SettingsColumn {
item {
ListSection(
overlineContent = { Text(stringResource(R.string.licenses_link).uppercase()) },

View File

@@ -1,15 +1,10 @@
package org.jellyfin.androidtv.ui.settings.screen.license
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.util.withContext
import org.jellyfin.androidtv.R
@@ -18,6 +13,7 @@ import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.Routes
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
@Composable
fun SettingsLicensesScreen() {
@@ -32,11 +28,7 @@ fun SettingsLicensesScreen() {
libs.libraries.sortedBy { it.name.lowercase() }
}
LazyColumn(
modifier = Modifier
.padding(6.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
SettingsColumn {
item {
ListSection(
overlineContent = { Text(stringResource(R.string.settings).uppercase()) },