vc=49: Auto-start playback setting (cold-open autoplay)
Settings → Autoplay section now has a third toggle: Auto-start playback (default on). When on, opening a fresh video starts playing immediately on the detail page. When off, the page renders with the thumbnail + Play overlay and you tap to start. Independent of the end-of-queue autoplay mode and the back-from- fullscreen behavior (that already auto-resumes because the controller is mid-stream — preserved). Implementation: a single OR into the initial inlinePlaying state in VideoDetailScreen.
This commit is contained in:
parent
62cc18c940
commit
0f946d8b4e
4 changed files with 61 additions and 11 deletions
|
|
@ -55,6 +55,6 @@ const val NEWPIPE_APPLICATION_ID_NEW = "net.newpipe.app"
|
||||||
// vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via
|
// vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via
|
||||||
// strawcore-core (Sulkta-Coop/strawcore) via the UniFFI wrapper; no
|
// strawcore-core (Sulkta-Coop/strawcore) via the UniFFI wrapper; no
|
||||||
// NewPipeExtractor in the runtime path.
|
// NewPipeExtractor in the runtime path.
|
||||||
const val STRAW_VERSION_CODE = 48
|
const val STRAW_VERSION_CODE = 49
|
||||||
const val STRAW_VERSION_NAME = "0.1.0-BH"
|
const val STRAW_VERSION_NAME = "0.1.0-BI"
|
||||||
const val STRAW_APPLICATION_ID = "com.sulkta.straw"
|
const val STRAW_APPLICATION_ID = "com.sulkta.straw"
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ private const val KEY_THEME = "theme_mode_v1"
|
||||||
private const val KEY_CACHE_ENABLED = "cache_enabled_v1"
|
private const val KEY_CACHE_ENABLED = "cache_enabled_v1"
|
||||||
private const val KEY_AUTOPLAY_MODE = "autoplay_mode_v1"
|
private const val KEY_AUTOPLAY_MODE = "autoplay_mode_v1"
|
||||||
private const val KEY_AUTOPLAY_SKIP_WATCHED = "autoplay_skip_watched_v1"
|
private const val KEY_AUTOPLAY_SKIP_WATCHED = "autoplay_skip_watched_v1"
|
||||||
|
private const val KEY_AUTOSTART_PLAYBACK = "autostart_playback_v1"
|
||||||
|
|
||||||
class SettingsStore(context: Context) {
|
class SettingsStore(context: Context) {
|
||||||
private val sp: SharedPreferences = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
private val sp: SharedPreferences = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||||
|
|
@ -89,6 +90,19 @@ class SettingsStore(context: Context) {
|
||||||
)
|
)
|
||||||
val autoplaySkipWatched: StateFlow<Boolean> = _autoplaySkipWatched.asStateFlow()
|
val autoplaySkipWatched: StateFlow<Boolean> = _autoplaySkipWatched.asStateFlow()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Open a video → it starts playing immediately." Default on —
|
||||||
|
* matches YT/NewPipe. When off, opening a fresh video lands you
|
||||||
|
* on the detail page with the thumbnail + Play overlay; you tap
|
||||||
|
* to start. Doesn't affect back-from-fullscreen (that's a
|
||||||
|
* separate path in VideoDetailScreen that defaults to true when
|
||||||
|
* the shared controller is already streaming the URL).
|
||||||
|
*/
|
||||||
|
private val _autoStartPlayback = MutableStateFlow(
|
||||||
|
sp.getBoolean(KEY_AUTOSTART_PLAYBACK, true),
|
||||||
|
)
|
||||||
|
val autoStartPlayback: StateFlow<Boolean> = _autoStartPlayback.asStateFlow()
|
||||||
|
|
||||||
fun toggle(cat: SbCategory) {
|
fun toggle(cat: SbCategory) {
|
||||||
// Atomic toggle via updateAndGet — see AUD-HIGH note in HistoryStore.
|
// Atomic toggle via updateAndGet — see AUD-HIGH note in HistoryStore.
|
||||||
val next = _sbCategories.updateAndGet { cur ->
|
val next = _sbCategories.updateAndGet { cur ->
|
||||||
|
|
@ -137,6 +151,13 @@ class SettingsStore(context: Context) {
|
||||||
sp.edit().putBoolean(KEY_AUTOPLAY_SKIP_WATCHED, skip).apply()
|
sp.edit().putBoolean(KEY_AUTOPLAY_SKIP_WATCHED, skip).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setAutoStartPlayback(autoStart: Boolean) {
|
||||||
|
val before = _autoStartPlayback.value
|
||||||
|
if (before == autoStart) return
|
||||||
|
_autoStartPlayback.value = autoStart
|
||||||
|
sp.edit().putBoolean(KEY_AUTOSTART_PLAYBACK, autoStart).apply()
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadCategories(): Set<SbCategory> {
|
private fun loadCategories(): Set<SbCategory> {
|
||||||
val raw = sp.getStringSet(KEY_SB_CATS, null)
|
val raw = sp.getStringSet(KEY_SB_CATS, null)
|
||||||
return if (raw == null) {
|
return if (raw == null) {
|
||||||
|
|
|
||||||
|
|
@ -133,16 +133,19 @@ fun VideoDetailScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Inline-play state resets when navigating to a different video.
|
// Inline-play state resets when navigating to a different video.
|
||||||
// BUT: if the shared MediaController is already playing this exact
|
// Defaults to TRUE when:
|
||||||
// stream — most commonly because the user popped back from
|
// * the shared MediaController is already streaming this URL
|
||||||
// fullscreen Player — default to true so the inline surface picks
|
// (back-from-fullscreen — without this the page renders as
|
||||||
// up the running playback instead of dropping back to the
|
// "freshly loaded" while audio keeps playing in the
|
||||||
// thumbnail+Play placeholder. Without this, system back from
|
// background), or
|
||||||
// fullscreen looked like "the video went to background" — audio
|
// * the user has Settings → Auto-start playback enabled (cold
|
||||||
// continued via the persistent controller but the video page
|
// open from search / subs / wherever immediately plays).
|
||||||
// re-rendered as a freshly-loaded detail.
|
// Off + fresh URL → thumbnail + Play overlay, user taps to start.
|
||||||
|
val autoStart by Settings.get().autoStartPlayback.collectAsState()
|
||||||
var inlinePlaying by remember(streamUrl) {
|
var inlinePlaying by remember(streamUrl) {
|
||||||
mutableStateOf(NowPlaying.current.value?.streamUrl == streamUrl)
|
mutableStateOf(
|
||||||
|
NowPlaying.current.value?.streamUrl == streamUrl || autoStart,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
LaunchedEffect(streamUrl) { vm.load(streamUrl) }
|
LaunchedEffect(streamUrl) { vm.load(streamUrl) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,32 @@ fun SettingsScreen() {
|
||||||
onCheckedChange = { store.setAutoplaySkipWatched(it) },
|
onCheckedChange = { store.setAutoplaySkipWatched(it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val autoStartPlayback by store.autoStartPlayback.collectAsState()
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 6.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
Text(
|
||||||
|
"Auto-start playback",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
"Open a video → it starts immediately. Off: tap " +
|
||||||
|
"the thumbnail to start.",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Switch(
|
||||||
|
checked = autoStartPlayback,
|
||||||
|
onCheckedChange = { store.setAutoStartPlayback(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
Text(
|
Text(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue