mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2026-10-20 06:22:35 +00:00
dynamic player settings ui per platform
This commit is contained in:
committed by
Aayush Gupta
parent
793a6bc337
commit
4cad0293ca
@@ -7,7 +7,7 @@
|
||||
about-libraries = "15.1.1"
|
||||
acra = "5.13.1"
|
||||
activity = "1.13.0"
|
||||
agp = "9.4.0"
|
||||
agp = "9.4.1"
|
||||
appcompat = "1.8.0"
|
||||
assertj = "3.27.7"
|
||||
autoservice-google = "1.1.1"
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package net.newpipe.app.screen
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.test.ExperimentalTestApi
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
import androidx.compose.ui.test.v2.runComposeUiTest
|
||||
import kotlin.test.Test
|
||||
import newpipe.shared.generated.resources.Res
|
||||
import newpipe.shared.generated.resources.disable_media_tunneling_automatic_info
|
||||
import newpipe.shared.generated.resources.disable_media_tunneling_summary
|
||||
import newpipe.shared.generated.resources.progressive_load_interval_summary
|
||||
import newpipe.shared.generated.resources.progressive_load_interval_title
|
||||
import org.jetbrains.compose.resources.getString
|
||||
|
||||
@Composable
|
||||
private fun Hosted(content: @Composable () -> Unit) {
|
||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) { content() }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalTestApi::class)
|
||||
class ExoPlayerSettingsSectionsTest {
|
||||
|
||||
@Test
|
||||
fun rendersLoadIntervalRow() = runComposeUiTest {
|
||||
setContent { Hosted { ExoPlayerSettingsSectionsContent() } }
|
||||
|
||||
onNodeWithText(getString(Res.string.progressive_load_interval_title)).assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadIntervalSummaryShowsSelectedEntryLabel() = runComposeUiTest {
|
||||
setContent { Hosted { ExoPlayerSettingsSectionsContent() } }
|
||||
|
||||
onNodeWithText(getString(Res.string.progressive_load_interval_summary, "64 KiB"))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mediaTunnelingInfoShownWhenAutoDisabled() = runComposeUiTest {
|
||||
setContent {
|
||||
Hosted { ExoPlayerSettingsSectionsContent(mediaTunnelingAutoDisabled = true) }
|
||||
}
|
||||
|
||||
onNodeWithText(getString(Res.string.disable_media_tunneling_automatic_info), substring = true)
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mediaTunnelingInfoHiddenWhenNotAutoDisabled() = runComposeUiTest {
|
||||
setContent {
|
||||
Hosted { ExoPlayerSettingsSectionsContent(mediaTunnelingAutoDisabled = false) }
|
||||
}
|
||||
|
||||
onNodeWithText(getString(Res.string.disable_media_tunneling_summary), substring = true)
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
onNodeWithText(getString(Res.string.disable_media_tunneling_automatic_info), substring = true)
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package net.newpipe.app.platform
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import net.newpipe.app.screen.ExoPlayerSettingsSections
|
||||
import org.koin.core.annotation.Singleton
|
||||
|
||||
@Singleton(binds = [PlayerSettingsSections::class])
|
||||
class AndroidPlayerSettingsSections : PlayerSettingsSections {
|
||||
override val isAvailable = true
|
||||
|
||||
@Composable
|
||||
override fun Render() = ExoPlayerSettingsSections()
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package net.newpipe.app.screen
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import net.newpipe.app.composable.ListPreference
|
||||
import net.newpipe.app.composable.ListPreferenceEntry
|
||||
import net.newpipe.app.composable.SwitchPreference
|
||||
import net.newpipe.app.preferences.ExoPlayerPreferences
|
||||
import net.newpipe.app.preview.ThemePreviewProvider
|
||||
import net.newpipe.app.viewmodel.settings.PlayerSettingsViewModel
|
||||
import newpipe.shared.generated.resources.Res
|
||||
import newpipe.shared.generated.resources.always_use_exoplayer_set_output_surface_workaround_summary
|
||||
import newpipe.shared.generated.resources.always_use_exoplayer_set_output_surface_workaround_title
|
||||
import newpipe.shared.generated.resources.disable_media_tunneling_automatic_info
|
||||
import newpipe.shared.generated.resources.disable_media_tunneling_summary
|
||||
import newpipe.shared.generated.resources.disable_media_tunneling_title
|
||||
import newpipe.shared.generated.resources.progressive_load_interval_exoplayer_default
|
||||
import newpipe.shared.generated.resources.progressive_load_interval_summary
|
||||
import newpipe.shared.generated.resources.progressive_load_interval_title
|
||||
import newpipe.shared.generated.resources.use_exoplayer_decoder_fallback_summary
|
||||
import newpipe.shared.generated.resources.use_exoplayer_decoder_fallback_title
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@Composable
|
||||
fun ExoPlayerSettingsSections(viewModel: PlayerSettingsViewModel = koinViewModel()) {
|
||||
val progressiveLoadInterval by viewModel.progressiveLoadInterval.collectAsStateWithLifecycle()
|
||||
val useExoplayerDecoderFallback by viewModel.useExoplayerDecoderFallback.collectAsStateWithLifecycle()
|
||||
val disableMediaTunneling by viewModel.disableMediaTunneling.collectAsStateWithLifecycle()
|
||||
val mediaTunnelingAutoDisabled by viewModel.mediaTunnelingAutoDisabled.collectAsStateWithLifecycle()
|
||||
val alwaysUseSetOutputSurfaceWorkaround by viewModel.alwaysUseSetOutputSurfaceWorkaround.collectAsStateWithLifecycle()
|
||||
|
||||
ExoPlayerSettingsSectionsContent(
|
||||
progressiveLoadInterval = progressiveLoadInterval,
|
||||
useExoplayerDecoderFallback = useExoplayerDecoderFallback,
|
||||
disableMediaTunneling = disableMediaTunneling,
|
||||
mediaTunnelingAutoDisabled = mediaTunnelingAutoDisabled,
|
||||
alwaysUseSetOutputSurfaceWorkaround = alwaysUseSetOutputSurfaceWorkaround,
|
||||
onProgressiveLoadIntervalChange = viewModel::setProgressiveLoadInterval,
|
||||
onUseExoplayerDecoderFallbackChange = viewModel::setUseExoplayerDecoderFallback,
|
||||
onDisableMediaTunnelingChange = viewModel::setDisableMediaTunneling,
|
||||
onAlwaysUseSetOutputSurfaceWorkaroundChange = viewModel::setAlwaysUseSetOutputSurfaceWorkaround
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ExoPlayerSettingsSectionsContent(
|
||||
progressiveLoadInterval: String = ExoPlayerPreferences.DEFAULT_PROGRESSIVE_LOAD_INTERVAL,
|
||||
useExoplayerDecoderFallback: Boolean = ExoPlayerPreferences.DEFAULT_USE_EXOPLAYER_DECODER_FALLBACK,
|
||||
disableMediaTunneling: Boolean = ExoPlayerPreferences.DEFAULT_DISABLE_MEDIA_TUNNELING,
|
||||
mediaTunnelingAutoDisabled: Boolean = false,
|
||||
alwaysUseSetOutputSurfaceWorkaround: Boolean = ExoPlayerPreferences.DEFAULT_ALWAYS_USE_SET_OUTPUT_SURFACE_WORKAROUND,
|
||||
onProgressiveLoadIntervalChange: (String) -> Unit = {},
|
||||
onUseExoplayerDecoderFallbackChange: (Boolean) -> Unit = {},
|
||||
onDisableMediaTunnelingChange: (Boolean) -> Unit = {},
|
||||
onAlwaysUseSetOutputSurfaceWorkaroundChange: (Boolean) -> Unit = {}
|
||||
) {
|
||||
val exoPlayerDefaultLabel = stringResource(Res.string.progressive_load_interval_exoplayer_default)
|
||||
|
||||
val loadIntervalEntries = ExoPlayerPreferences.PROGRESSIVE_LOAD_INTERVALS.map { value ->
|
||||
ListPreferenceEntry(
|
||||
value = value,
|
||||
title = if (value == ExoPlayerPreferences.PROGRESSIVE_LOAD_INTERVAL_EXOPLAYER_DEFAULT) exoPlayerDefaultLabel else "$value KiB"
|
||||
)
|
||||
}
|
||||
val selectedLoadIntervalTitle = loadIntervalEntries.firstOrNull { it.value == progressiveLoadInterval }?.title.orEmpty()
|
||||
|
||||
val tunnelingBaseSummary = stringResource(Res.string.disable_media_tunneling_summary)
|
||||
val tunnelingSummary = if (mediaTunnelingAutoDisabled) {
|
||||
tunnelingBaseSummary + " " + stringResource(Res.string.disable_media_tunneling_automatic_info)
|
||||
} else {
|
||||
tunnelingBaseSummary
|
||||
}
|
||||
|
||||
ListPreference(
|
||||
title = stringResource(Res.string.progressive_load_interval_title),
|
||||
entries = loadIntervalEntries,
|
||||
selectedValue = progressiveLoadInterval,
|
||||
onValueSelected = onProgressiveLoadIntervalChange,
|
||||
summary = stringResource(Res.string.progressive_load_interval_summary, selectedLoadIntervalTitle)
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(Res.string.use_exoplayer_decoder_fallback_title),
|
||||
summary = stringResource(Res.string.use_exoplayer_decoder_fallback_summary),
|
||||
checked = useExoplayerDecoderFallback,
|
||||
onCheckedChange = onUseExoplayerDecoderFallbackChange
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(Res.string.disable_media_tunneling_title),
|
||||
summary = tunnelingSummary,
|
||||
checked = disableMediaTunneling,
|
||||
onCheckedChange = onDisableMediaTunnelingChange
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(Res.string.always_use_exoplayer_set_output_surface_workaround_title),
|
||||
summary = stringResource(Res.string.always_use_exoplayer_set_output_surface_workaround_summary),
|
||||
checked = alwaysUseSetOutputSurfaceWorkaround,
|
||||
onCheckedChange = onAlwaysUseSetOutputSurfaceWorkaroundChange
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun ExoPlayerSettingsSectionsPreview() {
|
||||
Column { ExoPlayerSettingsSectionsContent() }
|
||||
}
|
||||
@@ -7,7 +7,6 @@ package net.newpipe.app.navigation
|
||||
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.newpipe.app.model.License
|
||||
|
||||
/**
|
||||
* Destinations for navigation in compose
|
||||
@@ -16,7 +15,7 @@ import net.newpipe.app.model.License
|
||||
sealed interface Destination : NavKey {
|
||||
|
||||
@Serializable
|
||||
data object ExoPlayerSettings : Destination
|
||||
data object PlayerSettings : Destination
|
||||
|
||||
@Serializable
|
||||
data object VideoAudioSettings : Destination
|
||||
|
||||
@@ -44,7 +44,7 @@ fun navModule() = module {
|
||||
VideoAudioSettingsScreen()
|
||||
}
|
||||
|
||||
navigation<Destination.ExoPlayerSettings> {
|
||||
navigation<Destination.PlayerSettings> {
|
||||
PlayerSettingsScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package net.newpipe.app.platform
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
/**
|
||||
* Platform-specific contents of the player settings screen: ExoPlayer settings on Android,
|
||||
* and each other platform's own player settings once it has any.
|
||||
*/
|
||||
interface PlayerSettingsSections {
|
||||
val isAvailable: Boolean
|
||||
|
||||
@Composable
|
||||
fun Render()
|
||||
}
|
||||
+7
-108
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package net.newpipe.app.screen.settings
|
||||
|
||||
@@ -15,95 +15,25 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import net.newpipe.app.composable.ListPreference
|
||||
import net.newpipe.app.composable.ListPreferenceEntry
|
||||
import net.newpipe.app.composable.SwitchPreference
|
||||
import net.newpipe.app.composable.TopAppBar
|
||||
import net.newpipe.app.navigation.Navigator
|
||||
import net.newpipe.app.preferences.ExoPlayerPreferences
|
||||
import net.newpipe.app.preview.ThemePreviewProvider
|
||||
import net.newpipe.app.viewmodel.settings.PlayerSettingsViewModel
|
||||
import net.newpipe.app.platform.PlayerSettingsSections
|
||||
import newpipe.shared.generated.resources.Res
|
||||
import newpipe.shared.generated.resources.always_use_exoplayer_set_output_surface_workaround_summary
|
||||
import newpipe.shared.generated.resources.always_use_exoplayer_set_output_surface_workaround_title
|
||||
import newpipe.shared.generated.resources.disable_media_tunneling_automatic_info
|
||||
import newpipe.shared.generated.resources.disable_media_tunneling_summary
|
||||
import newpipe.shared.generated.resources.disable_media_tunneling_title
|
||||
import newpipe.shared.generated.resources.progressive_load_interval_exoplayer_default
|
||||
import newpipe.shared.generated.resources.progressive_load_interval_summary
|
||||
import newpipe.shared.generated.resources.progressive_load_interval_title
|
||||
import newpipe.shared.generated.resources.settings_category_exoplayer_title
|
||||
import newpipe.shared.generated.resources.use_exoplayer_decoder_fallback_summary
|
||||
import newpipe.shared.generated.resources.use_exoplayer_decoder_fallback_title
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.koin.compose.koinInject
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@Composable
|
||||
fun PlayerSettingsScreen(
|
||||
navigator: Navigator = koinInject(),
|
||||
viewModel: PlayerSettingsViewModel = koinViewModel()
|
||||
sections: PlayerSettingsSections = koinInject()
|
||||
) {
|
||||
val progressiveLoadInterval by viewModel.progressiveLoadInterval.collectAsStateWithLifecycle()
|
||||
val useExoplayerDecoderFallback by viewModel.useExoplayerDecoderFallback.collectAsStateWithLifecycle()
|
||||
val disableMediaTunneling by viewModel.disableMediaTunneling.collectAsStateWithLifecycle()
|
||||
val mediaTunnelingAutoDisabled by viewModel.mediaTunnelingAutoDisabled.collectAsStateWithLifecycle()
|
||||
val alwaysUseSetOutputSurfaceWorkaround by viewModel.alwaysUseSetOutputSurfaceWorkaround.collectAsStateWithLifecycle()
|
||||
|
||||
PlayerSettingsScreenContent(
|
||||
progressiveLoadInterval = progressiveLoadInterval,
|
||||
useExoplayerDecoderFallback = useExoplayerDecoderFallback,
|
||||
disableMediaTunneling = disableMediaTunneling,
|
||||
mediaTunnelingAutoDisabled = mediaTunnelingAutoDisabled,
|
||||
alwaysUseSetOutputSurfaceWorkaround = alwaysUseSetOutputSurfaceWorkaround,
|
||||
onProgressiveLoadIntervalChange = viewModel::setProgressiveLoadInterval,
|
||||
onUseExoplayerDecoderFallbackChange = viewModel::setUseExoplayerDecoderFallback,
|
||||
onDisableMediaTunnelingChange = viewModel::setDisableMediaTunneling,
|
||||
onAlwaysUseSetOutputSurfaceWorkaroundChange = viewModel::setAlwaysUseSetOutputSurfaceWorkaround,
|
||||
onNavigateUp = { navigator.navigateUp() }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlayerSettingsScreenContent(
|
||||
progressiveLoadInterval: String = ExoPlayerPreferences.DEFAULT_PROGRESSIVE_LOAD_INTERVAL,
|
||||
useExoplayerDecoderFallback: Boolean = ExoPlayerPreferences.DEFAULT_USE_EXOPLAYER_DECODER_FALLBACK,
|
||||
disableMediaTunneling: Boolean = ExoPlayerPreferences.DEFAULT_DISABLE_MEDIA_TUNNELING,
|
||||
mediaTunnelingAutoDisabled: Boolean = false,
|
||||
alwaysUseSetOutputSurfaceWorkaround: Boolean = ExoPlayerPreferences.DEFAULT_ALWAYS_USE_SET_OUTPUT_SURFACE_WORKAROUND,
|
||||
onProgressiveLoadIntervalChange: (String) -> Unit = {},
|
||||
onUseExoplayerDecoderFallbackChange: (Boolean) -> Unit = {},
|
||||
onDisableMediaTunnelingChange: (Boolean) -> Unit = {},
|
||||
onAlwaysUseSetOutputSurfaceWorkaroundChange: (Boolean) -> Unit = {},
|
||||
onNavigateUp: () -> Unit = {}
|
||||
) {
|
||||
val exoPlayerDefaultLabel = stringResource(Res.string.progressive_load_interval_exoplayer_default)
|
||||
|
||||
val loadIntervalEntries = ExoPlayerPreferences.PROGRESSIVE_LOAD_INTERVALS.map { value ->
|
||||
ListPreferenceEntry(
|
||||
value = value,
|
||||
title = if (value == ExoPlayerPreferences.PROGRESSIVE_LOAD_INTERVAL_EXOPLAYER_DEFAULT) exoPlayerDefaultLabel else "$value KiB"
|
||||
)
|
||||
}
|
||||
val selectedLoadIntervalTitle = loadIntervalEntries.firstOrNull { it.value == progressiveLoadInterval }?.title.orEmpty()
|
||||
|
||||
val tunnelingBaseSummary = stringResource(Res.string.disable_media_tunneling_summary)
|
||||
val tunnelingSummary = if (mediaTunnelingAutoDisabled) {
|
||||
tunnelingBaseSummary + " " + stringResource(Res.string.disable_media_tunneling_automatic_info)
|
||||
} else {
|
||||
tunnelingBaseSummary
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = stringResource(Res.string.settings_category_exoplayer_title),
|
||||
onNavigateUp = onNavigateUp
|
||||
onNavigateUp = { navigator.navigateUp() }
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
@@ -114,38 +44,7 @@ fun PlayerSettingsScreenContent(
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(WindowInsets.navigationBars.asPaddingValues())
|
||||
) {
|
||||
ListPreference(
|
||||
title = stringResource(Res.string.progressive_load_interval_title),
|
||||
entries = loadIntervalEntries,
|
||||
selectedValue = progressiveLoadInterval,
|
||||
onValueSelected = onProgressiveLoadIntervalChange,
|
||||
summary = stringResource(Res.string.progressive_load_interval_summary, selectedLoadIntervalTitle)
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(Res.string.use_exoplayer_decoder_fallback_title),
|
||||
summary = stringResource(Res.string.use_exoplayer_decoder_fallback_summary),
|
||||
checked = useExoplayerDecoderFallback,
|
||||
onCheckedChange = onUseExoplayerDecoderFallbackChange
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(Res.string.disable_media_tunneling_title),
|
||||
summary = tunnelingSummary,
|
||||
checked = disableMediaTunneling,
|
||||
onCheckedChange = onDisableMediaTunnelingChange
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(Res.string.always_use_exoplayer_set_output_surface_workaround_title),
|
||||
summary = stringResource(Res.string.always_use_exoplayer_set_output_surface_workaround_summary),
|
||||
checked = alwaysUseSetOutputSurfaceWorkaround,
|
||||
onCheckedChange = onAlwaysUseSetOutputSurfaceWorkaroundChange
|
||||
)
|
||||
sections.Render()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun PlayerSettingsScreenPreview() {
|
||||
PlayerSettingsScreenContent()
|
||||
}
|
||||
|
||||
+15
-8
@@ -33,6 +33,7 @@ import net.newpipe.app.composable.SwitchPreference
|
||||
import net.newpipe.app.composable.TopAppBar
|
||||
import net.newpipe.app.navigation.Destination
|
||||
import net.newpipe.app.navigation.Navigator
|
||||
import net.newpipe.app.platform.PlayerSettingsSections
|
||||
import net.newpipe.app.preferences.VideoAudioPreferences
|
||||
import net.newpipe.app.preview.ThemePreviewProvider
|
||||
import net.newpipe.app.viewmodel.settings.VideoAudioSettingsViewModel
|
||||
@@ -117,7 +118,8 @@ import org.koin.compose.viewmodel.koinViewModel
|
||||
@Composable
|
||||
fun VideoAudioSettingsScreen(
|
||||
navigator: Navigator = koinInject(),
|
||||
viewModel: VideoAudioSettingsViewModel = koinViewModel()
|
||||
viewModel: VideoAudioSettingsViewModel = koinViewModel(),
|
||||
playerSettings: PlayerSettingsSections = koinInject()
|
||||
) {
|
||||
val defaultResolution by viewModel.defaultResolution.collectAsStateWithLifecycle()
|
||||
val defaultPopupResolution by viewModel.defaultPopupResolution.collectAsStateWithLifecycle()
|
||||
@@ -144,6 +146,7 @@ fun VideoAudioSettingsScreen(
|
||||
val seekDuration by viewModel.seekDuration.collectAsStateWithLifecycle()
|
||||
val clearQueueConfirmation by viewModel.clearQueueConfirmation.collectAsStateWithLifecycle()
|
||||
val ignoreHardwareMediaButtons by viewModel.ignoreHardwareMediaButtons.collectAsStateWithLifecycle()
|
||||
val playerSettingsAvailable = remember { playerSettings.isAvailable }
|
||||
|
||||
// Option lists are pure functions of their toggles, so derive them instead of storing them.
|
||||
val resolutionValues = remember(showHigherResolutions) {
|
||||
@@ -210,7 +213,8 @@ fun VideoAudioSettingsScreen(
|
||||
onSeekDurationChange = viewModel::setSeekDuration,
|
||||
onClearQueueConfirmationChange = viewModel::setClearQueueConfirmation,
|
||||
onIgnoreHardwareMediaButtonsChange = viewModel::setIgnoreHardwareMediaButtons,
|
||||
onOpenExoPlayerSettings = { navigator.navigateTo(Destination.ExoPlayerSettings) },
|
||||
playerSettingsAvailable = playerSettingsAvailable,
|
||||
onOpenPlayerSettings = { navigator.navigateTo(Destination.PlayerSettings) },
|
||||
onNavigateUp = { navigator.navigateUp() }
|
||||
)
|
||||
}
|
||||
@@ -270,7 +274,8 @@ fun VideoAudioSettingsScreenContent(
|
||||
onSeekDurationChange: (String) -> Unit = {},
|
||||
onClearQueueConfirmationChange: (Boolean) -> Unit = {},
|
||||
onIgnoreHardwareMediaButtonsChange: (Boolean) -> Unit = {},
|
||||
onOpenExoPlayerSettings: () -> Unit = {},
|
||||
playerSettingsAvailable: Boolean = true,
|
||||
onOpenPlayerSettings: () -> Unit = {},
|
||||
onNavigateUp: () -> Unit = {}
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -399,11 +404,13 @@ fun VideoAudioSettingsScreenContent(
|
||||
onCheckedChange = onPreferDescriptiveAudioChange
|
||||
)
|
||||
|
||||
PreferenceRow(
|
||||
title = stringResource(Res.string.settings_category_exoplayer_title),
|
||||
summary = stringResource(Res.string.settings_category_exoplayer_summary),
|
||||
onClick = onOpenExoPlayerSettings
|
||||
)
|
||||
if (playerSettingsAvailable) {
|
||||
PreferenceRow(
|
||||
title = stringResource(Res.string.settings_category_exoplayer_title),
|
||||
summary = stringResource(Res.string.settings_category_exoplayer_summary),
|
||||
onClick = onOpenPlayerSettings
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceCategoryTitle(title = stringResource(Res.string.settings_category_player_title))
|
||||
SwitchPreference(
|
||||
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package net.newpipe.app.screen.settings
|
||||
|
||||
import androidx.compose.ui.test.ExperimentalTestApi
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
import androidx.compose.ui.test.v2.runComposeUiTest
|
||||
import com.russhwolf.settings.MapSettings
|
||||
import com.russhwolf.settings.Settings
|
||||
import kotlin.test.Test
|
||||
import net.newpipe.app.extensions.withKoin
|
||||
import newpipe.shared.generated.resources.Res
|
||||
import newpipe.shared.generated.resources.disable_media_tunneling_automatic_info
|
||||
import newpipe.shared.generated.resources.progressive_load_interval_summary
|
||||
import newpipe.shared.generated.resources.progressive_load_interval_title
|
||||
import newpipe.shared.generated.resources.settings_category_exoplayer_title
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import org.koin.dsl.module
|
||||
|
||||
@OptIn(ExperimentalTestApi::class)
|
||||
class ExoPlayerSettingsScreenTest {
|
||||
|
||||
private val emptySettings = module {
|
||||
single<Settings> { MapSettings() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rendersTitleAndLoadIntervalRow() = runComposeUiTest {
|
||||
withKoin(
|
||||
modules = listOf(emptySettings),
|
||||
content = { PlayerSettingsScreenContent() },
|
||||
onContent = {
|
||||
onNodeWithText(getString(Res.string.settings_category_exoplayer_title)).assertIsDisplayed()
|
||||
onNodeWithText(getString(Res.string.progressive_load_interval_title)).assertIsDisplayed()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadIntervalSummaryShowsSelectedEntryLabel() = runComposeUiTest {
|
||||
withKoin(
|
||||
modules = listOf(emptySettings),
|
||||
content = { PlayerSettingsScreenContent() },
|
||||
onContent = {
|
||||
onNodeWithText(getString(Res.string.progressive_load_interval_summary, "64 KiB")).assertIsDisplayed()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mediaTunnelingInfoShownOnlyWhenAutoDisabled() = runComposeUiTest {
|
||||
withKoin(
|
||||
modules = listOf(emptySettings),
|
||||
content = { PlayerSettingsScreenContent(mediaTunnelingAutoDisabled = true) },
|
||||
onContent = {
|
||||
onNodeWithText(getString(Res.string.disable_media_tunneling_automatic_info), substring = true)
|
||||
.performScrollTo()
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -70,7 +70,7 @@ class VideoAudioSettingsScreenTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun rendersExoPlayerSettingsLink() = runComposeUiTest {
|
||||
fun rendersPlayerSettingsLink() = runComposeUiTest {
|
||||
withKoin(
|
||||
modules = listOf(emptySettings),
|
||||
content = { VideoAudioSettingsScreenContent() },
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package net.newpipe.app.platform
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import org.koin.core.annotation.Singleton
|
||||
|
||||
@Singleton(binds = [PlayerSettingsSections::class])
|
||||
class IOSPlayerSettingsSections : PlayerSettingsSections {
|
||||
override val isAvailable = false
|
||||
|
||||
@Composable
|
||||
override fun Render() = Unit
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 NewPipe e.V. <https://newpipe-ev.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package net.newpipe.app.platform
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import org.koin.core.annotation.Singleton
|
||||
|
||||
@Singleton(binds = [PlayerSettingsSections::class])
|
||||
class JVMPlayerSettingsSections : PlayerSettingsSections {
|
||||
override val isAvailable = false
|
||||
|
||||
@Composable
|
||||
override fun Render() = Unit
|
||||
}
|
||||
Reference in New Issue
Block a user