diff --git a/app/src/main/java/net/jeena/jtubetv/ui/screens/ChannelsScreen.kt b/app/src/main/java/net/jeena/jtubetv/ui/screens/ChannelsScreen.kt index 5decf11..0ee6ab5 100644 --- a/app/src/main/java/net/jeena/jtubetv/ui/screens/ChannelsScreen.kt +++ b/app/src/main/java/net/jeena/jtubetv/ui/screens/ChannelsScreen.kt @@ -49,6 +49,7 @@ import androidx.tv.material3.Border import androidx.tv.material3.Card import androidx.tv.material3.CardDefaults import coil.compose.AsyncImage +import android.util.Log import kotlinx.coroutines.delay import kotlinx.coroutines.launch import net.jeena.jtubetv.AppGraph @@ -149,6 +150,11 @@ class ChannelsViewModel(private val store: TokenStore) : ViewModel() { val known = all.map { it.name to it.host }.toSet() all += page.first.filter { c -> (c.name to c.host) !in known } start += 100 + android.util.Log.w( + "JTube", + "channels page " + (start / 100) + ": total=" + total + " all=" + all.size + + " locals=" + all.count { it.host == host }, + ) allChannels = all rebuildRows(host) } @@ -156,6 +162,8 @@ class ChannelsViewModel(private val store: TokenStore) : ViewModel() { // stats (videos + plays) for every local channel: throttled parallel-ish val locals = all.filter { it.host == host } + Log.w("JTube", "pages done: all=" + all.size + " locals=" + locals.size + + " userChannels=" + userChannels.size) channelStatsLoop(locals, host) } catch (e: Exception) { error = e.message ?: "Could not load channels" @@ -171,7 +179,9 @@ class ChannelsViewModel(private val store: TokenStore) : ViewModel() { for (channel in channels) { val key = channel.name + "@" + channel.host if (statsMap.containsKey(key)) continue - val cs = repo.channelStats(key).getOrNull() ?: continue + val cs = repo.channelStats(key).getOrNull() + if (cs == null) { android.util.Log.w("JTube", "stats FAIL " + key); continue } + android.util.Log.w("JTube", "stats " + key + " videos=" + cs.videos + " plays=" + cs.plays) statsMap[key] = cs stats = statsMap.toMap() rebuildRows(host) @@ -230,6 +240,14 @@ class ChannelsViewModel(private val store: TokenStore) : ViewModel() { .thenByDescending { g -> g.channels.sumOf { statsMap[it.name + "@" + it.host]?.videos ?: 0 } } .thenByDescending { g -> g.channels.sumOf { it.followersCount } }, ) + Log.w( + "JTube", + "order now: " + userRows.take(6).joinToString(" | ") { g -> + g.ownerName + "(" + g.channels.size + "ch, " + + g.channels.sumOf { statsMap[it.name + "@" + it.host]?.videos ?: 0 } + "v" + + (if (g.channels.any { it.name in ownNames }) ", OWN" else "") + ")" + }, + ) } private fun ownerKey(c: VideoChannelDto) = c.ownerAccount?.name ?: "" diff --git a/app/src/main/java/net/jeena/jtubetv/ui/screens/HomeScreen.kt b/app/src/main/java/net/jeena/jtubetv/ui/screens/HomeScreen.kt index 5cdefeb..a9d790b 100644 --- a/app/src/main/java/net/jeena/jtubetv/ui/screens/HomeScreen.kt +++ b/app/src/main/java/net/jeena/jtubetv/ui/screens/HomeScreen.kt @@ -76,7 +76,8 @@ fun HomeScreen(viewModel: AppViewModel = androidx.lifecycle.viewmodel.compose.vi val epoch by viewModel.sessionEpoch.collectAsState() var destination by rememberSaveable { mutableStateOf(Destination.Home) } var channelDetails by rememberSaveable { mutableStateOf(null) } // handle - var playingVideo by rememberSaveable { mutableStateOf(null) } // shortUUID + // NOT saveable: launching the app should never resume a half-watched player session. + var playingVideo by remember { mutableStateOf(null) } // shortUUID var restoreToken by rememberSaveable { mutableStateOf(0) } // increments when playback ends var drawerOpen by rememberSaveable { mutableStateOf(false) } val context = androidx.compose.ui.platform.LocalContext.current @@ -89,35 +90,29 @@ fun HomeScreen(viewModel: AppViewModel = androidx.lifecycle.viewmodel.compose.vi if (playingVideo == null && restoreToken >= 1) restoreToken++ } - // BACK order: player (own BackHandler) → drawer close → channel drilldown → - // BACK (on content) opens the drawer; a double-BACK with the drawer open exits. - BackHandler(enabled = playingVideo == null && channelDetails == null) { - val now = System.currentTimeMillis() - if (drawerOpen) { + // BACK order (TV standard): player (own handler) → drawer close → channel + // drilldown → non-Home destination → Home; from Home: double-press exits. + BackHandler(enabled = drawerOpen && playingVideo == null) { + drawerOpen = false + runCatching { searchFocus.requestFocus() } + } + BackHandler(enabled = !drawerOpen && playingVideo == null && channelDetails != null) { + channelDetails = null + } + BackHandler(enabled = !drawerOpen && playingVideo == null && channelDetails == null) { + if (destination != Destination.Home) { + channelDetails = null + destination = Destination.Home + } else { + val now = System.currentTimeMillis() if (now - lastBackAt < 2500) { (context as? android.app.Activity)?.finish() return@BackHandler } lastBackAt = now android.widget.Toast.makeText(context, "Press BACK again to exit", android.widget.Toast.LENGTH_SHORT).show() - } else { - lastBackAt = now - drawerOpen = true } } - BackHandler(enabled = playingVideo == null && channelDetails != null && !drawerOpen) { - channelDetails = null - } - BackHandler(enabled = drawerOpen && playingVideo == null) { - val now = System.currentTimeMillis() - if (now - lastBackAt < 2500) { - (context as? android.app.Activity)?.finish() - return@BackHandler - } - lastBackAt = now - drawerOpen = false - runCatching { searchFocus.requestFocus() } - } Box( modifier = Modifier