1
0
Fork 0
mirror of https://github.com/TeamNewPipe/NewPipe.git synced 2025-10-03 09:49:21 +02:00
This commit is contained in:
Siddhesh Naik 2025-09-24 18:36:15 +00:00 committed by GitHub
commit c19355916c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 143 additions and 68 deletions

View file

@ -1,23 +1,31 @@
package org.schabi.newpipe.settings package org.schabi.newpipe.settings
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.ui.SettingsRoutes
import org.schabi.newpipe.ui.TextPreference import org.schabi.newpipe.ui.TextPreference
import org.schabi.newpipe.ui.theme.SizeTokens.SpacingExtraSmall
@Composable @Composable
fun SettingsScreen( fun SettingsScreen(
onSelectSettingOption: (SettingsScreenKey) -> Unit, onSelectSettingOption: (settingsRoute: SettingsRoutes) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Column(modifier = modifier) { Column(modifier = modifier) {
TextPreference( TextPreference(
title = R.string.settings_category_debug_title, title = R.string.settings_category_debug_title,
onClick = { onSelectSettingOption(SettingsScreenKey.DEBUG) } onClick = { onSelectSettingOption(SettingsRoutes.SettingsDebugRoute) }
)
HorizontalDivider(
color = MaterialTheme.colorScheme.onBackground,
thickness = 0.6.dp,
modifier = Modifier.padding(horizontal = SpacingExtraSmall)
) )
HorizontalDivider(color = Color.Black)
} }
} }

View file

@ -10,21 +10,19 @@ import androidx.compose.material3.Scaffold
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.settings.viewmodel.SettingsViewModel import org.schabi.newpipe.settings.viewmodel.SettingsViewModel
import org.schabi.newpipe.ui.SettingsRoutes
import org.schabi.newpipe.ui.Toolbar import org.schabi.newpipe.ui.Toolbar
import org.schabi.newpipe.ui.theme.AppTheme import org.schabi.newpipe.ui.theme.AppTheme
const val SCREEN_TITLE_KEY = "SCREEN_TITLE_KEY"
@AndroidEntryPoint @AndroidEntryPoint
class SettingsV2Activity : ComponentActivity() { class SettingsV2Activity : ComponentActivity() {
@ -35,37 +33,44 @@ class SettingsV2Activity : ComponentActivity() {
setContent { setContent {
val navController = rememberNavController() val navController = rememberNavController()
var screenTitle by remember { mutableIntStateOf(SettingsScreenKey.ROOT.screenTitle) } val navBackStackEntry by navController.currentBackStackEntryAsState()
navController.addOnDestinationChangedListener { _, _, arguments -> @StringRes val screenTitleRes by remember(navBackStackEntry) {
screenTitle = mutableIntStateOf(
arguments?.getInt(SCREEN_TITLE_KEY) ?: SettingsScreenKey.ROOT.screenTitle when (navBackStackEntry?.destination?.route) {
SettingsRoutes.SettingsMainRoute::class.java.canonicalName -> SettingsRoutes.SettingsMainRoute.screenTitleRes
SettingsRoutes.SettingsDebugRoute::class.java.canonicalName -> SettingsRoutes.SettingsDebugRoute.screenTitleRes
else -> R.string.settings
}
)
} }
AppTheme { AppTheme {
Scaffold(topBar = { Scaffold(topBar = {
Toolbar( Toolbar(
title = stringResource(id = screenTitle), title = stringResource(screenTitleRes),
onNavigateBack = {
if (!navController.popBackStack()) {
finish()
}
},
hasSearch = true, hasSearch = true,
onSearchQueryChange = null // TODO: Add suggestions logic onSearch = {
// TODO: Add suggestions logic
},
searchResults = emptyList()
) )
}) { padding -> }) { padding ->
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = SettingsScreenKey.ROOT.name, startDestination = SettingsRoutes.SettingsMainRoute,
modifier = Modifier.padding(padding) modifier = Modifier.padding(padding)
) { ) {
composable( composable<SettingsRoutes.SettingsMainRoute> {
SettingsScreenKey.ROOT.name, SettingsScreen(onSelectSettingOption = { route ->
listOf(createScreenTitleArg(SettingsScreenKey.ROOT.screenTitle)) navController.navigate(route)
) {
SettingsScreen(onSelectSettingOption = { screen ->
navController.navigate(screen.name)
}) })
} }
composable( composable<SettingsRoutes.SettingsDebugRoute> {
SettingsScreenKey.DEBUG.name,
listOf(createScreenTitleArg(SettingsScreenKey.DEBUG.screenTitle))
) {
DebugScreen(settingsViewModel) DebugScreen(settingsViewModel)
} }
} }
@ -74,12 +79,3 @@ class SettingsV2Activity : ComponentActivity() {
} }
} }
} }
fun createScreenTitleArg(@StringRes screenTitle: Int) = navArgument(SCREEN_TITLE_KEY) {
defaultValue = screenTitle
}
enum class SettingsScreenKey(@StringRes val screenTitle: Int) {
ROOT(R.string.settings),
DEBUG(R.string.settings_category_debug_title)
}

View file

@ -0,0 +1,17 @@
package org.schabi.newpipe.ui
import androidx.annotation.StringRes
import kotlinx.serialization.Serializable
import org.schabi.newpipe.R
// Settings screens
@Serializable
sealed class SettingsRoutes(
@get:StringRes
val screenTitleRes: Int
) {
@Serializable
object SettingsMainRoute : SettingsRoutes(R.string.settings)
@Serializable
object SettingsDebugRoute : SettingsRoutes(R.string.settings_category_debug_title)
}

View file

@ -1,46 +1,61 @@
package org.schabi.newpipe.ui package org.schabi.newpipe.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SearchBar import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.traversalIndex
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import org.schabi.newpipe.R import org.schabi.newpipe.R
import org.schabi.newpipe.ui.theme.AppTheme import org.schabi.newpipe.ui.theme.AppTheme
import org.schabi.newpipe.ui.theme.SizeTokens import org.schabi.newpipe.ui.theme.SizeTokens
import org.schabi.newpipe.ui.theme.SizeTokens.SpacingExtraSmall
@Composable @Composable
fun TextAction(text: String, modifier: Modifier = Modifier) { fun TextAction(text: String, modifier: Modifier = Modifier) {
Text(text = text, color = MaterialTheme.colorScheme.onSurface, modifier = modifier) Text(text = text, color = MaterialTheme.colorScheme.onPrimary, modifier = modifier)
} }
@Composable @Composable
fun NavigationIcon() { fun NavigationIcon(navigateBack: () -> Unit) {
IconButton(onClick = navigateBack) {
Icon( Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back", imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back",
modifier = Modifier.padding(horizontal = SizeTokens.SpacingExtraSmall) modifier = Modifier.padding(horizontal = SizeTokens.SpacingExtraSmall)
) )
} }
}
@Composable @Composable
fun SearchSuggestionItem(text: String) { fun SearchSuggestionItem(text: String) {
@ -53,19 +68,27 @@ fun SearchSuggestionItem(text: String) {
fun Toolbar( fun Toolbar(
title: String, title: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
hasNavigationIcon: Boolean = true, onNavigateBack: (() -> Unit)? = null,
hasSearch: Boolean = false, hasSearch: Boolean = false,
onSearchQueryChange: ((String) -> List<String>)? = null, onSearch: (String) -> Unit,
searchResults: List<String>,
actions: @Composable RowScope.() -> Unit = {} actions: @Composable RowScope.() -> Unit = {}
) { ) {
var isSearchActive by remember { mutableStateOf(false) } var isSearchActive by remember { mutableStateOf(false) }
var query by remember { mutableStateOf("") } var expanded by rememberSaveable { mutableStateOf(false) }
val textFieldState = rememberTextFieldState()
Column { Column {
TopAppBar( TopAppBar(
title = { Text(text = title) }, title = { Text(text = title) },
modifier = modifier, modifier = modifier,
navigationIcon = { if (hasNavigationIcon) NavigationIcon() }, colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primary,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
),
navigationIcon = {
onNavigateBack?.let { NavigationIcon(onNavigateBack) }
},
actions = { actions = {
actions() actions()
if (hasSearch) { if (hasSearch) {
@ -73,39 +96,66 @@ fun Toolbar(
Icon( Icon(
painterResource(id = R.drawable.ic_search), painterResource(id = R.drawable.ic_search),
contentDescription = stringResource(id = R.string.search), contentDescription = stringResource(id = R.string.search),
tint = MaterialTheme.colorScheme.onSurface tint = MaterialTheme.colorScheme.onPrimary
) )
} }
} }
} }
) )
if (isSearchActive) { if (isSearchActive) {
SearchBar( Box(
query = query, modifier
onQueryChange = { query = it }, .fillMaxSize()
onSearch = {}, .semantics { isTraversalGroup = true }
placeholder = {
Text(text = stringResource(id = R.string.search))
},
active = true,
onActiveChange = {
isSearchActive = it
}
) { ) {
onSearchQueryChange?.invoke(query)?.takeIf { it.isNotEmpty() } SearchBar(
?.map { suggestionText -> SearchSuggestionItem(text = suggestionText) } modifier = Modifier
?: run { .align(Alignment.TopCenter)
.semantics { traversalIndex = 0f },
inputField = {
SearchBarDefaults.InputField(
query = textFieldState.text.toString(),
onQueryChange = { textFieldState.edit { replace(0, length, it) } },
onSearch = {
onSearch(textFieldState.text.toString())
expanded = false
},
expanded = expanded,
onExpandedChange = { expanded = it },
placeholder = { Text(text = stringResource(id = R.string.search)) },
modifier = Modifier.padding(horizontal = SpacingExtraSmall)
)
},
expanded = expanded,
onExpandedChange = { expanded = it },
) {
if (searchResults.isEmpty()) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxSize()
.fillMaxWidth(), .padding(SpacingExtraSmall),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center,
) { ) {
Column { Column {
Text(text = "╰(°●°╰)") Text(text = "╰(°●°╰)")
Text(text = stringResource(id = R.string.search_no_results)) Text(text = stringResource(id = R.string.search_no_results))
} }
} }
} else {
LazyColumn {
items(searchResults) { result ->
ListItem(
headlineContent = { SearchSuggestionItem(result) },
modifier = Modifier
.clickable {
textFieldState.edit { replace(0, length, result) }
expanded = false
}
.fillMaxWidth()
)
}
}
}
} }
} }
} }
@ -119,7 +169,8 @@ fun ToolbarPreview() {
Toolbar( Toolbar(
title = "Title", title = "Title",
hasSearch = true, hasSearch = true,
onSearchQueryChange = { emptyList() }, onSearch = {},
searchResults = emptyList(),
actions = { actions = {
TextAction(text = "Action1") TextAction(text = "Action1")
TextAction(text = "Action2") TextAction(text = "Action2")

View file

@ -1,8 +1,11 @@
package org.schabi.newpipe.ui.theme package org.schabi.newpipe.ui.theme
// Color.kt is generated using the Material theme builder https://material-foundation.github.io/material-theme-builder/
// TODO: Update the colors to properly match the existing color scheme + also add colors schemes for other services
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
val primaryLight = Color(0xFF904A45) val primaryLight = Color(0xFFE53935)
val onPrimaryLight = Color(0xFFFFFFFF) val onPrimaryLight = Color(0xFFFFFFFF)
val primaryContainerLight = Color(0xFFFFDAD6) val primaryContainerLight = Color(0xFFFFDAD6)
val onPrimaryContainerLight = Color(0xFF3B0908) val onPrimaryContainerLight = Color(0xFF3B0908)
@ -38,8 +41,8 @@ val surfaceContainerLight = Color(0xFFFCEAE8)
val surfaceContainerHighLight = Color(0xFFF6E4E2) val surfaceContainerHighLight = Color(0xFFF6E4E2)
val surfaceContainerHighestLight = Color(0xFFF1DEDC) val surfaceContainerHighestLight = Color(0xFFF1DEDC)
val primaryDark = Color(0xFFFFB3AC) val primaryDark = Color(0xFF992722)
val onPrimaryDark = Color(0xFF571E1B) val onPrimaryDark = Color(0xFFF4D2D2)
val primaryContainerDark = Color(0xFF73332F) val primaryContainerDark = Color(0xFF73332F)
val onPrimaryContainerDark = Color(0xFFFFDAD6) val onPrimaryContainerDark = Color(0xFFFFDAD6)
val secondaryDark = Color(0xFFE7BDB8) val secondaryDark = Color(0xFFE7BDB8)

View file

@ -137,7 +137,7 @@ kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-p
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
kotlinx-coroutines-rx3 = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-rx3", version.ref = "kotlinxCoroutinesRx3" } kotlinx-coroutines-rx3 = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-rx3", version.ref = "kotlinxCoroutinesRx3" }
kotlinx-serialization = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" } kotlinx-serialization = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
lazycolumnscrollbar = { group = "com.github.nanihadesuka", name = "LazyColumnScrollbar", version.ref = "lazycolumnscrollbar" } lazycolumnscrollbar = { group = "com.github.nanihadesuka", name = "LazyColumnScrollbar", version.ref = "lazycolumnscrollbar" }
leakcanary-android-core = { module = "com.squareup.leakcanary:leakcanary-android-core", version.ref = "leakcanary" } leakcanary-android-core = { module = "com.squareup.leakcanary:leakcanary-android-core", version.ref = "leakcanary" }
leakcanary-object-watcher = { group = "com.squareup.leakcanary", name = "leakcanary-object-watcher-android", version.ref = "leakcanary" } leakcanary-object-watcher = { group = "com.squareup.leakcanary", name = "leakcanary-object-watcher-android", version.ref = "leakcanary" }