diff --git a/shared/src/commonMain/composeResources/values/strings.xml b/shared/src/commonMain/composeResources/values/strings.xml index d56afc50b..c738a1c13 100644 --- a/shared/src/commonMain/composeResources/values/strings.xml +++ b/shared/src/commonMain/composeResources/values/strings.xml @@ -34,4 +34,17 @@ Open in browser Done Not available + + + Settings + Search + Content + Notifications + Video and audio + Download + Appearance + History and cache + Updates + Backup and restore + Debug diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/composable/PreferenceRow.kt b/shared/src/commonMain/kotlin/net/newpipe/app/composable/PreferenceRow.kt index f6d2235bb..7e983c3d0 100644 --- a/shared/src/commonMain/kotlin/net/newpipe/app/composable/PreferenceRow.kt +++ b/shared/src/commonMain/kotlin/net/newpipe/app/composable/PreferenceRow.kt @@ -1,2 +1,62 @@ +/* +* SPDX-FileCopyrightText: 2026 NewPipe e.V. +* SPDX-License-Identifier: GPL-3.0-or-later +*/ + package net.newpipe.app.composable +import androidx.compose.foundation.clickable +import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewWrapper +import net.newpipe.app.preview.ThemePreviewProvider +import newpipe.shared.generated.resources.Res +import newpipe.shared.generated.resources.ic_headset +import newpipe.shared.generated.resources.settings_category_video_audio_title +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource + +/** + * A row that mirrors the standard AndroidX preference item used in the legacy + * settings screens: 24dp leading icon, title, optional summary, ripple on tap. + * + * @param title Title text of the preference + * @param modifier Modifier applied to the row + * @param icon Leading icon painter; null hides the leading slot (parity with + * `app:iconSpaceReserved="false"`) + * @param summary Optional secondary line under the title (used on sub-screens; + * the settings home currently shows none) + * @param onClick Action executed when the row is tapped + */ +@Composable +fun PreferenceRow( + title: String, + modifier: Modifier = Modifier, + icon: Painter? = null, + summary: String? = null, + onClick: () -> Unit = {} +) { + ListItem( + modifier = modifier.clickable(onClick = onClick), + headlineContent = { Text(text = title) }, + supportingContent = summary?.let { text -> { Text(text = text) } }, + leadingContent = icon?.let { painter -> + { Icon(painter = painter, contentDescription = null) } + } + ) +} + +@PreviewWrapper(ThemePreviewProvider::class) +@PreviewLightDark +@Composable +private fun PreferenceRowPreview() { + PreferenceRow( + title = stringResource(Res.string.settings_category_video_audio_title), + icon = painterResource(Res.drawable.ic_headset) + ) +} diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/navigation/NavModule.kt b/shared/src/commonMain/kotlin/net/newpipe/app/navigation/NavModule.kt index b528d23a1..7e3728464 100644 --- a/shared/src/commonMain/kotlin/net/newpipe/app/navigation/NavModule.kt +++ b/shared/src/commonMain/kotlin/net/newpipe/app/navigation/NavModule.kt @@ -8,6 +8,7 @@ package net.newpipe.app.navigation import androidx.compose.runtime.mutableStateListOf import co.touchlab.kermit.Logger import net.newpipe.app.screen.about.AboutScreen +import net.newpipe.app.screen.settings.SettingsHomeScreen import org.koin.core.annotation.KoinExperimentalAPI import org.koin.core.annotation.Provided import org.koin.core.annotation.Singleton diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/screen/settings/SettingsHomeScreen.kt b/shared/src/commonMain/kotlin/net/newpipe/app/screen/settings/SettingsHomeScreen.kt index 46c1fe5ff..7da0d5984 100644 --- a/shared/src/commonMain/kotlin/net/newpipe/app/screen/settings/SettingsHomeScreen.kt +++ b/shared/src/commonMain/kotlin/net/newpipe/app/screen/settings/SettingsHomeScreen.kt @@ -1,2 +1,125 @@ +/* +* SPDX-FileCopyrightText: 2026 NewPipe e.V. +* SPDX-License-Identifier: GPL-3.0-or-later +*/ + package net.newpipe.app.screen.settings +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.PreviewWrapper +import net.newpipe.app.composable.PreferenceRow +import net.newpipe.app.composable.TopAppBar +import net.newpipe.app.navigation.Navigator +import net.newpipe.app.preview.ThemePreviewProvider +import newpipe.shared.generated.resources.Res +import newpipe.shared.generated.resources.content +import newpipe.shared.generated.resources.ic_bug_report +import newpipe.shared.generated.resources.ic_cloud_download +import newpipe.shared.generated.resources.ic_file_download +import newpipe.shared.generated.resources.ic_headset +import newpipe.shared.generated.resources.ic_history +import newpipe.shared.generated.resources.ic_language +import newpipe.shared.generated.resources.ic_notifications +import newpipe.shared.generated.resources.ic_palette +import newpipe.shared.generated.resources.ic_search +import newpipe.shared.generated.resources.ic_settings_backup_restore +import newpipe.shared.generated.resources.notifications +import newpipe.shared.generated.resources.search +import newpipe.shared.generated.resources.settings +import newpipe.shared.generated.resources.settings_category_appearance_title +import newpipe.shared.generated.resources.settings_category_backup_restore_title +import newpipe.shared.generated.resources.settings_category_debug_title +import newpipe.shared.generated.resources.settings_category_downloads_title +import newpipe.shared.generated.resources.settings_category_history_title +import newpipe.shared.generated.resources.settings_category_updates_title +import newpipe.shared.generated.resources.settings_category_video_audio_title +import org.jetbrains.compose.resources.DrawableResource +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource +import org.koin.compose.koinInject + +data class SettingsCategory( + val title: StringResource, + val icon: DrawableResource, + // TODO: Replace with a Destination once sub-screens are migrated + val onClick: () -> Unit = {} +) + +@Composable +fun SettingsHomeScreen(navigator: Navigator = koinInject()) { + SettingsHomeScreenContent( + onNavigateUp = { navigator.navigateUp() } + ) +} + +@Composable +fun SettingsHomeScreenContent( + categories: List = defaultCategories(), + onNavigateUp: () -> Unit = {}, + onSearchClick: () -> Unit = {} +) { + Scaffold( + topBar = { + TopAppBar( + title = stringResource(Res.string.settings), + onNavigateUp = onNavigateUp, + actions = { + IconButton(onClick = onSearchClick) { + Icon( + painter = painterResource(Res.drawable.ic_search), + contentDescription = stringResource(Res.string.search) + ) + } + } + ) + } + ) { paddingValues -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(top = paddingValues.calculateTopPadding()), + contentPadding = WindowInsets.navigationBars.asPaddingValues() + ) { + items(items = categories) { category -> + PreferenceRow( + title = stringResource(category.title), + icon = painterResource(category.icon), + onClick = category.onClick + ) + } + } + } +} + +@Composable +private fun defaultCategories(): List = listOf( + SettingsCategory(Res.string.settings_category_video_audio_title, Res.drawable.ic_headset), + SettingsCategory(Res.string.settings_category_downloads_title, Res.drawable.ic_file_download), + SettingsCategory(Res.string.settings_category_appearance_title, Res.drawable.ic_palette), + SettingsCategory(Res.string.settings_category_history_title, Res.drawable.ic_history), + SettingsCategory(Res.string.content, Res.drawable.ic_language), + SettingsCategory(Res.string.notifications, Res.drawable.ic_notifications), + SettingsCategory(Res.string.settings_category_updates_title, Res.drawable.ic_cloud_download), + SettingsCategory(Res.string.settings_category_backup_restore_title, Res.drawable.ic_settings_backup_restore), + SettingsCategory(Res.string.settings_category_debug_title, Res.drawable.ic_bug_report) +) + +@PreviewWrapper(ThemePreviewProvider::class) +@PreviewLightDark +@Composable +private fun SettingsHomeScreenPreview() { + SettingsHomeScreenContent() +}