Move smart channel buttons to their own scrolling screen (0.51.0)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,8 +9,8 @@ android {
|
||||
applicationId = "tv.orbit.classic"
|
||||
minSdk = 26
|
||||
targetSdk = 36
|
||||
versionCode = 51
|
||||
versionName = "0.50.0-six-channels"
|
||||
versionCode = 52
|
||||
versionName = "0.51.0-channel-list"
|
||||
}
|
||||
buildFeatures { compose = true }
|
||||
compileOptions { sourceCompatibility = JavaVersion.VERSION_17; targetCompatibility = JavaVersion.VERSION_17 }
|
||||
|
||||
@@ -7,6 +7,7 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -118,3 +119,28 @@ internal fun SuggestionsScreen(programming: ChannelProgramming, suggestions: Lis
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Every smart channel in one list that scrolls, however many there are. */
|
||||
@Composable
|
||||
internal fun SmartChannelsScreen(busy: Boolean, message: String?, modifier: Modifier, isOn: (Int) -> Boolean, canStart: (ChannelProgramming) -> Boolean,
|
||||
onSwitch: (ChannelProgramming, Boolean) -> Unit, onSuggestions: (ChannelProgramming) -> Unit, onBack: () -> Unit) {
|
||||
BackHandler(onBack = onBack)
|
||||
val firstFocus = androidx.compose.runtime.remember { androidx.compose.ui.focus.FocusRequester() }
|
||||
androidx.compose.runtime.LaunchedEffect(Unit) { firstFocus.requestFocus() }
|
||||
Column(modifier.fillMaxWidth().background(Navy).padding(14.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Text("Smart channels", color = Color.White, fontSize = 20.sp)
|
||||
message?.let { Text(it, color = Yellow, fontSize = 13.sp, maxLines = 4) }
|
||||
LazyColumn(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
items(SmartChannels.sortedBy { it.number }, key = { it.number }) { programming ->
|
||||
val on = isOn(programming.number)
|
||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
val switch = if (programming == SmartChannels.minByOrNull { it.number }) Modifier.focusRequester(firstFocus) else Modifier
|
||||
OrbitButton("${programming.number} ${programming.name}: smart schedule is ${if (on) "ON. Press for its loop" else "OFF. Press to turn on"}", switch.weight(1f),
|
||||
enabled = !busy && canStart(programming), onClick = { onSwitch(programming, !on) })
|
||||
if (programming.suggestionRules.isNotEmpty()) OrbitButton("Bigger pool: review", enabled = !busy && canStart(programming), onClick = { onSuggestions(programming) })
|
||||
}
|
||||
}
|
||||
}
|
||||
OrbitButton("Back", onClick = onBack)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.50.0\"")
|
||||
connection.setRequestProperty("X-Emby-Authorization", "Emby Client=\"Orbit TV\", Device=\"Orbit TV\", DeviceId=\"$deviceId\", Version=\"0.51.0\"")
|
||||
request.token?.let { connection.setRequestProperty("X-Emby-Token", it) }
|
||||
if (request.method == "POST") {
|
||||
val bytes = request.body.orEmpty().toByteArray(Charsets.UTF_8)
|
||||
|
||||
@@ -99,6 +99,7 @@ internal fun EmbyConnectionScreen(modifier: Modifier = Modifier, visible: Boolea
|
||||
var scheduledChannels by remember { mutableStateOf<Map<Int, ScheduledChannel>>(emptyMap()) }
|
||||
var suggesting by remember { mutableStateOf<ChannelProgramming?>(null) }
|
||||
var suggestions by remember { mutableStateOf<List<Suggestion>>(emptyList()) }
|
||||
var managingChannels by remember { mutableStateOf(false) }
|
||||
var savingSuggestions by remember { mutableStateOf(false) }
|
||||
var tickedSuggestions by remember { mutableStateOf<Set<String>>(emptySet()) }
|
||||
var lastSmartPress by remember { mutableLongStateOf(0L) }
|
||||
@@ -561,6 +562,12 @@ internal fun EmbyConnectionScreen(modifier: Modifier = Modifier, visible: Boolea
|
||||
onBack = { if (!savingSuggestions) { job?.cancel(); busy = false }; suggesting = null })
|
||||
return
|
||||
}
|
||||
if (managingChannels) {
|
||||
SmartChannelsScreen(busy, message, modifier, isOn = { scheduledChannels.containsKey(it) },
|
||||
canStart = { smartLoop(it) != null || ChannelPresets.any { preset -> preset.number == it.number } },
|
||||
onSwitch = { programming, on -> setSmartSchedule(programming, on) }, onSuggestions = { openSuggestions(it) }, onBack = { managingChannels = false })
|
||||
return
|
||||
}
|
||||
if (reviewingSeries) {
|
||||
SeriesReviewScreen(seriesMatches.orEmpty(), approvedSeries, episodeSamples, busy, message, modifier,
|
||||
onToggle = { id ->
|
||||
@@ -698,6 +705,7 @@ internal fun EmbyConnectionScreen(modifier: Modifier = Modifier, visible: Boolea
|
||||
message?.let { Text(it, color = Yellow, fontSize = 14.sp) }
|
||||
Text("${result.items.size} records received${result.total?.let { " • $it available to this account" }.orEmpty()}", color = Color.White, fontSize = 16.sp)
|
||||
Text("Select a title, or create one channel from up to three playable titles.", color = Muted, fontSize = 13.sp)
|
||||
OrbitButton("Smart channels", Modifier.fillMaxWidth(), enabled = !busy && session != null, onClick = { message = null; managingChannels = true })
|
||||
OrbitButton(if (channel == null) "Create sample channel" else "Open TV Guide",
|
||||
Modifier.fillMaxWidth(), enabled = !busy && session != null, onClick = {
|
||||
if (channel != null) { selectedNumber = 101; onGuide() } else {
|
||||
@@ -727,13 +735,6 @@ internal fun EmbyConnectionScreen(modifier: Modifier = Modifier, visible: Boolea
|
||||
OrbitButton("${preset.number} ${preset.name} — ${if (built) "rebuild" else "build"}", Modifier.fillMaxWidth(),
|
||||
enabled = !busy && session != null, onClick = { buildPreset(preset) })
|
||||
}
|
||||
SmartChannels.forEach { programming ->
|
||||
val on = scheduledChannels.containsKey(programming.number)
|
||||
OrbitButton("${programming.number} ${programming.name} — smart schedule is ${if (on) "ON. Press to go back to its loop" else "OFF. Press to turn on"}", Modifier.fillMaxWidth(),
|
||||
enabled = !busy && session != null && (smartLoop(programming) != null || ChannelPresets.any { it.number == programming.number }), onClick = { setSmartSchedule(programming, !on) })
|
||||
if (programming.suggestionRules.isNotEmpty()) OrbitButton("${programming.number} ${programming.name} — bigger pool: review suggestions", Modifier.fillMaxWidth(),
|
||||
enabled = !busy && session != null && smartLoop(programming) != null, onClick = { openSuggestions(programming) })
|
||||
}
|
||||
OrbitButton("Check metadata coverage", Modifier.fillMaxWidth(),
|
||||
enabled = !busy && session != null, onClick = { message = null; reviewingCoverage = true })
|
||||
OrbitButton("Library import test", Modifier.fillMaxWidth(),
|
||||
|
||||
@@ -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.50.0")
|
||||
next.setRequestProperty("User-Agent", "OrbitTV/0.51.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()) {
|
||||
|
||||
Reference in New Issue
Block a user