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
|
||||
// strawcore-core (Sulkta-Coop/strawcore) via the UniFFI wrapper; no
|
||||
// NewPipeExtractor in the runtime path.
|
||||
const val STRAW_VERSION_CODE = 48
|
||||
const val STRAW_VERSION_NAME = "0.1.0-BH"
|
||||
const val STRAW_VERSION_CODE = 49
|
||||
const val STRAW_VERSION_NAME = "0.1.0-BI"
|
||||
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_AUTOPLAY_MODE = "autoplay_mode_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) {
|
||||
private val sp: SharedPreferences = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||
|
|
@ -89,6 +90,19 @@ class SettingsStore(context: Context) {
|
|||
)
|
||||
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) {
|
||||
// Atomic toggle via updateAndGet — see AUD-HIGH note in HistoryStore.
|
||||
val next = _sbCategories.updateAndGet { cur ->
|
||||
|
|
@ -137,6 +151,13 @@ class SettingsStore(context: Context) {
|
|||
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> {
|
||||
val raw = sp.getStringSet(KEY_SB_CATS, null)
|
||||
return if (raw == null) {
|
||||
|
|
|
|||
|
|
@ -133,16 +133,19 @@ fun VideoDetailScreen(
|
|||
)
|
||||
}
|
||||
// Inline-play state resets when navigating to a different video.
|
||||
// BUT: if the shared MediaController is already playing this exact
|
||||
// stream — most commonly because the user popped back from
|
||||
// fullscreen Player — default to true so the inline surface picks
|
||||
// up the running playback instead of dropping back to the
|
||||
// thumbnail+Play placeholder. Without this, system back from
|
||||
// fullscreen looked like "the video went to background" — audio
|
||||
// continued via the persistent controller but the video page
|
||||
// re-rendered as a freshly-loaded detail.
|
||||
// Defaults to TRUE when:
|
||||
// * the shared MediaController is already streaming this URL
|
||||
// (back-from-fullscreen — without this the page renders as
|
||||
// "freshly loaded" while audio keeps playing in the
|
||||
// background), or
|
||||
// * the user has Settings → Auto-start playback enabled (cold
|
||||
// open from search / subs / wherever immediately plays).
|
||||
// Off + fresh URL → thumbnail + Play overlay, user taps to start.
|
||||
val autoStart by Settings.get().autoStartPlayback.collectAsState()
|
||||
var inlinePlaying by remember(streamUrl) {
|
||||
mutableStateOf(NowPlaying.current.value?.streamUrl == streamUrl)
|
||||
mutableStateOf(
|
||||
NowPlaying.current.value?.streamUrl == streamUrl || autoStart,
|
||||
)
|
||||
}
|
||||
LaunchedEffect(streamUrl) { vm.load(streamUrl) }
|
||||
|
||||
|
|
|
|||
|
|
@ -242,6 +242,32 @@ fun SettingsScreen() {
|
|||
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))
|
||||
Text(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue