Skip to content

Commit ddbaa94

Browse files
Complete the debug screen migration to Compose with Nav3
- Completed the UI and logic for pending items of Debug screen using Jetpack Compose. - Implemented a new navigation system for settings using the navigation3 lib as it is now stable. - Reuses the `ScaffoldWithToolbar` composable and removed the previous `Toolbar` composable to avoid redundancy in code. - Refactored the `SettingsViewModel` to use a `BooleanPreference` helper class to reuse and reducing boilerplate for state management. - Created a shared `TextBase` composable to remove duplicated UI logic between `SwitchPreference` and `TextPreference`. - Move the build-variant-dependent logic for LeakCanary and reused it in Compose and Fragment, this logic is used for ensuring the leak canary fields are only enabled in debug variants. - Fixed a layout bug in `SwitchPreference` where long summary text could misalign the switch component and also adjusted the paddings for consistency.
1 parent f5245ea commit ddbaa94

File tree

20 files changed

+675
-366
lines changed

20 files changed

+675
-366
lines changed

app/build.gradle.kts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ android {
4444
defaultConfig {
4545
applicationId = "org.schabi.newpipe"
4646
resValue("string", "app_name", "NewPipe")
47-
minSdk = 21
47+
minSdk = 23
4848
targetSdk = 35
4949

5050
versionCode = System.getProperty("versionCodeOverride")?.toInt() ?: 1005
@@ -262,9 +262,14 @@ dependencies {
262262
implementation(libs.androidx.compose.ui.text) // Needed for parsing HTML to AnnotatedString
263263
implementation(libs.androidx.compose.material.icons.extended)
264264

265+
// Jetpack navigatio3
266+
implementation(libs.androidx.navigation3.ui)
267+
implementation(libs.androidx.navigation3.runtime)
268+
implementation(libs.androidx.navigation3.viewmodel)
269+
265270
// Jetpack Compose related dependencies
266271
implementation(libs.androidx.paging.compose)
267-
implementation(libs.androidx.navigation.compose)
272+
implementation(libs.androidx.hilt.navigation.compose)
268273

269274
// Coroutines interop
270275
implementation(libs.kotlinx.coroutines.rx3)

app/src/debug/java/org/schabi/newpipe/settings/DebugSettingsBVDLeakCanary.java

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.schabi.newpipe.settings
2+
3+
import android.content.Intent
4+
import leakcanary.LeakCanary.newLeakDisplayActivityIntent
5+
6+
/**
7+
* Build variant dependent (BVD) leak canary API implementation for the debug settings fragment.
8+
* This class is loaded via reflection by
9+
* [DebugSettingsBVDLeakCanaryAPI].
10+
*/
11+
@Suppress("unused") // Class is used but loaded via reflection
12+
class DebugSettingsBVDLeakCanary :
13+
14+
DebugSettingsBVDLeakCanaryAPI {
15+
override fun getNewLeakDisplayActivityIntent(): Intent {
16+
return newLeakDisplayActivityIntent()
17+
}
18+
}

app/src/main/java/org/schabi/newpipe/settings/DebugScreen.kt

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.schabi.newpipe.settings
2+
3+
import android.content.Intent
4+
5+
/**
6+
* Build variant dependent (BVD) leak canary API.
7+
* Why is LeakCanary not used directly? Because it can't be assured to be available.
8+
*/
9+
interface DebugSettingsBVDLeakCanaryAPI {
10+
fun getNewLeakDisplayActivityIntent(): Intent
11+
12+
companion object {
13+
const val IMPL_CLASS = "org.schabi.newpipe.settings.DebugSettingsBVDLeakCanary"
14+
}
15+
}

app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.schabi.newpipe.settings;
22

3-
import android.content.Intent;
43
import android.os.Bundle;
54

65
import androidx.preference.Preference;
@@ -88,15 +87,4 @@ private Optional<DebugSettingsBVDLeakCanaryAPI> getBVDLeakCanary() {
8887
return Optional.empty();
8988
}
9089
}
91-
92-
/**
93-
* Build variant dependent (BVD) leak canary API for this fragment.
94-
* Why is LeakCanary not used directly? Because it can't be assured
95-
*/
96-
public interface DebugSettingsBVDLeakCanaryAPI {
97-
String IMPL_CLASS =
98-
"org.schabi.newpipe.settings.DebugSettingsBVDLeakCanary";
99-
100-
Intent getNewLeakDisplayActivityIntent();
101-
}
10290
}

app/src/main/java/org/schabi/newpipe/settings/SettingsScreen.kt

Lines changed: 0 additions & 23 deletions
This file was deleted.

app/src/main/java/org/schabi/newpipe/settings/SettingsV2Activity.kt

Lines changed: 4 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,83 +3,22 @@ package org.schabi.newpipe.settings
33
import android.os.Bundle
44
import androidx.activity.ComponentActivity
55
import androidx.activity.compose.setContent
6-
import androidx.activity.viewModels
7-
import androidx.annotation.StringRes
8-
import androidx.compose.foundation.layout.padding
9-
import androidx.compose.material3.Scaffold
10-
import androidx.compose.runtime.getValue
11-
import androidx.compose.runtime.mutableIntStateOf
12-
import androidx.compose.runtime.remember
13-
import androidx.compose.runtime.setValue
14-
import androidx.compose.ui.Modifier
15-
import androidx.compose.ui.res.stringResource
16-
import androidx.navigation.compose.NavHost
17-
import androidx.navigation.compose.composable
18-
import androidx.navigation.compose.rememberNavController
19-
import androidx.navigation.navArgument
206
import dagger.hilt.android.AndroidEntryPoint
21-
import org.schabi.newpipe.R
22-
import org.schabi.newpipe.settings.viewmodel.SettingsViewModel
23-
import org.schabi.newpipe.ui.Toolbar
7+
import org.schabi.newpipe.settings.navigation.SettingsNavigation
248
import org.schabi.newpipe.ui.theme.AppTheme
259

26-
const val SCREEN_TITLE_KEY = "SCREEN_TITLE_KEY"
27-
2810
@AndroidEntryPoint
2911
class SettingsV2Activity : ComponentActivity() {
3012

31-
private val settingsViewModel: SettingsViewModel by viewModels()
32-
3313
override fun onCreate(savedInstanceState: Bundle?) {
3414
super.onCreate(savedInstanceState)
3515

3616
setContent {
37-
val navController = rememberNavController()
38-
var screenTitle by remember { mutableIntStateOf(SettingsScreenKey.ROOT.screenTitle) }
39-
navController.addOnDestinationChangedListener { _, _, arguments ->
40-
screenTitle =
41-
arguments?.getInt(SCREEN_TITLE_KEY) ?: SettingsScreenKey.ROOT.screenTitle
42-
}
43-
4417
AppTheme {
45-
Scaffold(topBar = {
46-
Toolbar(
47-
title = stringResource(id = screenTitle),
48-
hasSearch = true,
49-
onSearchQueryChange = null // TODO: Add suggestions logic
50-
)
51-
}) { padding ->
52-
NavHost(
53-
navController = navController,
54-
startDestination = SettingsScreenKey.ROOT.name,
55-
modifier = Modifier.padding(padding)
56-
) {
57-
composable(
58-
SettingsScreenKey.ROOT.name,
59-
listOf(createScreenTitleArg(SettingsScreenKey.ROOT.screenTitle))
60-
) {
61-
SettingsScreen(onSelectSettingOption = { screen ->
62-
navController.navigate(screen.name)
63-
})
64-
}
65-
composable(
66-
SettingsScreenKey.DEBUG.name,
67-
listOf(createScreenTitleArg(SettingsScreenKey.DEBUG.screenTitle))
68-
) {
69-
DebugScreen(settingsViewModel)
70-
}
71-
}
72-
}
18+
SettingsNavigation(
19+
onExitSettings = { finish() },
20+
)
7321
}
7422
}
7523
}
7624
}
77-
78-
fun createScreenTitleArg(@StringRes screenTitle: Int) = navArgument(SCREEN_TITLE_KEY) {
79-
defaultValue = screenTitle
80-
}
81-
82-
enum class SettingsScreenKey(@StringRes val screenTitle: Int) {
83-
ROOT(R.string.settings),
84-
DEBUG(R.string.settings_category_debug_title)
85-
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.schabi.newpipe.settings.navigation
2+
3+
import androidx.navigation3.runtime.NavKey
4+
import kotlinx.serialization.Serializable
5+
6+
sealed interface SettingsKey : NavKey
7+
8+
@Serializable
9+
data object SettingsHome : SettingsKey
10+
11+
@Serializable
12+
data object PlayerSettings : SettingsKey
13+
14+
@Serializable
15+
data object BehaviourSettings : SettingsKey
16+
17+
@Serializable
18+
data object DownloadSettings : SettingsKey
19+
20+
@Serializable
21+
data object LookFeelSettings : SettingsKey
22+
23+
@Serializable
24+
data object HistoryCacheSettings : SettingsKey
25+
26+
@Serializable
27+
data object ContentSettings : SettingsKey
28+
29+
@Serializable
30+
data object FeedSettings : SettingsKey
31+
32+
@Serializable
33+
data object ServicesSettings : SettingsKey
34+
35+
@Serializable
36+
data object LanguageSettings : SettingsKey
37+
38+
@Serializable
39+
data object BackupRestoreSettings : SettingsKey
40+
41+
@Serializable
42+
data object UpdateSettings : SettingsKey
43+
44+
@Serializable
45+
data object DebugSettings : SettingsKey
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.schabi.newpipe.settings.navigation
2+
3+
import androidx.compose.material3.Text
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.ui.res.stringResource
6+
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
7+
import androidx.navigation3.runtime.entryProvider
8+
import androidx.navigation3.runtime.rememberNavBackStack
9+
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
10+
import androidx.navigation3.ui.NavDisplay
11+
import org.schabi.newpipe.R
12+
import org.schabi.newpipe.settings.screens.DebugScreen
13+
import org.schabi.newpipe.settings.screens.SettingsHomeScreen
14+
15+
@Composable
16+
fun SettingsNavigation(onExitSettings: () -> Unit) {
17+
val backStack = rememberNavBackStack(SettingsHome)
18+
19+
val handleBack: () -> Unit = {
20+
if (backStack.size > 1) {
21+
backStack.removeLastOrNull()
22+
} else {
23+
onExitSettings()
24+
}
25+
}
26+
27+
NavDisplay(
28+
backStack = backStack,
29+
onBack = handleBack,
30+
entryProvider = entryProvider {
31+
entry<SettingsHome> { SettingsHomeScreen(backStack, handleBack) }
32+
entry<PlayerSettings> { Text(stringResource(id = R.string.settings_category_player_title)) }
33+
entry<BehaviourSettings> { Text(stringResource(id = R.string.settings_category_player_behavior_title)) }
34+
entry<DownloadSettings> { Text(stringResource(id = R.string.settings_category_downloads_title)) }
35+
entry<LookFeelSettings> { Text(stringResource(id = R.string.settings_category_look_and_feel_title)) }
36+
entry<HistoryCacheSettings> { Text(stringResource(id = R.string.settings_category_history_title)) }
37+
entry<ContentSettings> { Text(stringResource(id = R.string.settings_category_content_title)) }
38+
entry<FeedSettings> { Text(stringResource(id = R.string.settings_category_feed_title)) }
39+
entry<ServicesSettings> { Text(stringResource(id = R.string.settings_category_services_title)) }
40+
entry<LanguageSettings> { Text(stringResource(id = R.string.settings_category_language_title)) }
41+
entry<BackupRestoreSettings> { Text(stringResource(id = R.string.settings_category_backup_restore_title)) }
42+
entry<UpdateSettings> { Text(stringResource(id = R.string.settings_category_updates_title)) }
43+
entry<DebugSettings> { DebugScreen(backStack) }
44+
},
45+
entryDecorators = listOf(
46+
rememberSaveableStateHolderNavEntryDecorator(),
47+
rememberViewModelStoreNavEntryDecorator()
48+
)
49+
)
50+
}

0 commit comments

Comments
 (0)