Keep the programme bar hidden when one programme rolls into the next (0.32.0)
pauljd/davidson-ci: Davidson CI / quality (push) Failing after 1m46s
pauljd/davidson-ci: Davidson CI / ai-review (push) Failing after 1m20s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-11-01 23:15:45 +00:00
co-authored by Claude Opus 5.5
parent 872989eb2a
commit a591a28c74
6 changed files with 28 additions and 8 deletions
+16
View File
@@ -0,0 +1,16 @@
# Orbit TV 0.32.0 verification
The programme bar and green channel number no longer appear when one programme rolls into the next. Paul reported the bar popping up unprompted.
## Changed
- At a programme boundary on the same channel, the bar and number stay hidden; the channel's logo shows instead.
- They still appear when tuning in or changing channel, and OK still brings up the bar at any time.
- Includes 0.31's stream handover, which wasn't installed on its own.
## Checked
- assembleDebug, testDebugUnitTest and lintDebug pass; 138 tests, 0 failures. None of these exercise the rollover on screen.
- APK signature verified with apksigner.
## Not checked
- Not yet run on the box. Both this and the 0.31 handover need Paul's on-TV check.
- The tuning screen still shows briefly between programmes while the next one opens.
+2 -2
View File
@@ -9,8 +9,8 @@ android {
applicationId = "tv.orbit.classic"
minSdk = 26
targetSdk = 36
versionCode = 32
versionName = "0.31.0-seamless-picture"
versionCode = 33
versionName = "0.32.0-quiet-rollover"
}
buildFeatures { compose = true }
compileOptions { sourceCompatibility = JavaVersion.VERSION_17; targetCompatibility = JavaVersion.VERSION_17 }
@@ -41,7 +41,7 @@ internal class HttpsEmbyTransport(internal val deviceId: String = UUID.randomUUI
connection.connectTimeout = 10_000
connection.readTimeout = request.readTimeoutMs ?: if (request.path == "/Sessions/Logout") 5_000 else 15_000
connection.setRequestProperty("Accept", "application/json")
connection.setRequestProperty("X-Emby-Authorization", "Emby Client=\"Orbit TV\", Device=\"Orbit TV\", DeviceId=\"$deviceId\", Version=\"0.31.0\"")
connection.setRequestProperty("X-Emby-Authorization", "Emby Client=\"Orbit TV\", Device=\"Orbit TV\", DeviceId=\"$deviceId\", Version=\"0.32.0\"")
request.token?.let { connection.setRequestProperty("X-Emby-Token", it) }
if (request.method == "POST") {
val bytes = request.body.orEmpty().toByteArray(Charsets.UTF_8)
@@ -38,13 +38,13 @@ import kotlinx.coroutines.withContext
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
@Composable
internal fun EmbyPlaybackDialog(session: EmbySampleSession, plan: EmbyPlaybackPlan, onClose: () -> Unit, scheduled: ScheduledPlayback? = null,
onStep: (Int) -> Unit = {}, onLast: () -> Unit = {}, onNumber: (Int) -> Boolean = { false }, subtitlePreference: MutableState<String?>? = null, runningPlayer: ExoPlayer? = null) {
onStep: (Int) -> Unit = {}, onLast: () -> Unit = {}, onNumber: (Int) -> Boolean = { false }, subtitlePreference: MutableState<String?>? = null, runningPlayer: ExoPlayer? = null, announce: Boolean = true) {
val context = LocalContext.current
var failure by remember { mutableStateOf<String?>(null) }
var tuningNumberVisible by remember { mutableStateOf(true) }
var tuningNumberVisible by remember { mutableStateOf(announce) }
var ready by remember { mutableStateOf(false) }
var playing by remember { mutableStateOf(false) }
var infoVisible by remember { mutableStateOf(true) }
var infoVisible by remember { mutableStateOf(announce) }
var lastInteraction by remember { mutableLongStateOf(SystemClock.elapsedRealtime()) }
fun showInfo() { infoVisible = true; lastInteraction = SystemClock.elapsedRealtime() }
var numberEntry by remember { mutableStateOf<ChannelEntry?>(null) }
@@ -97,6 +97,8 @@ internal fun ScheduledChannelPlayer(session: EmbySampleSession, channel: SampleC
LaunchedEffect(channel, clockOffsetMs) { while (true) { now = System.currentTimeMillis() + clockOffsetMs; delay(250) } }
val shows = channel.guide(now, 2)
val current = shows[0]
// The bar and number greet a tune or channel change. One programme rolling into the next stays clean.
val announcedChannel = remember { intArrayOf(-1) }
val handed = remember {
val stream = handoff?.take()
if (stream != null && stream.channelNumber == channel.number && stream.startMs == current.startMs && clockOffsetMs == 0L) stream
@@ -129,8 +131,10 @@ internal fun ScheduledChannelPlayer(session: EmbySampleSession, channel: SampleC
val plan = prepared?.takeIf { it.item.id == current.media.item.id && preparedSlot == current.startMs && problem == null }
if (plan != null) {
key(current.startMs) {
val announce = remember { announcedChannel[0] != channel.number }
SideEffect { announcedChannel[0] = channel.number }
EmbyPlaybackDialog(session, plan, onClose,
scheduled = ScheduledPlayback(channel, current, shows[1], clockOffsetMs), onStep = onStep, onLast = onLast, onNumber = onNumber, subtitlePreference = subtitlePreference, runningPlayer = handed?.takeIf { it.plan === plan }?.claim())
scheduled = ScheduledPlayback(channel, current, shows[1], clockOffsetMs), onStep = onStep, onLast = onLast, onNumber = onNumber, subtitlePreference = subtitlePreference, runningPlayer = handed?.takeIf { it.plan === plan }?.claim(), announce = announce)
}
} else Dialog(onDismissRequest = onClose, properties = DialogProperties(usePlatformDefaultWidth = false,
decorFitsSystemWindows = false, securePolicy = SecureFlagPolicy.SecureOn)) {
@@ -33,7 +33,7 @@ internal class SecureMediaDataSource(private val session: EmbySampleSession) : B
next.instanceFollowRedirects = false
next.connectTimeout = 10_000
next.readTimeout = 20_000
next.setRequestProperty("User-Agent", "OrbitTV/0.31.0")
next.setRequestProperty("User-Agent", "OrbitTV/0.32.0")
next.setRequestProperty("Accept-Encoding", "identity")
session.mediaHeaders(address).forEach { (key, value) -> next.setRequestProperty(key, value) }
if (dataSpec.position != 0L || dataSpec.length != C.LENGTH_UNSET.toLong()) {