Compare commits
2 Commits
v0.9.0
...
753ab3a3ab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
753ab3a3ab | ||
|
|
f157323626 |
@@ -38,7 +38,7 @@ TV-Fernbedienung oder steuert alles bequem vom Handy aus.
|
|||||||
1. Auf dem Google TV die App **Downloader** installieren (oder einen anderen
|
1. Auf dem Google TV die App **Downloader** installieren (oder einen anderen
|
||||||
Weg nutzen, eine APK-Datei zu öffnen).
|
Weg nutzen, eine APK-Datei zu öffnen).
|
||||||
2. Diese Adresse eingeben:
|
2. Diese Adresse eingeben:
|
||||||
`git.beckm4nn.net/benjamin/castarr/raw/branch/apk/castarr.apk`
|
`git.beckm4nn.net/be-nj/castarr/raw/branch/apk/castarr.apk`
|
||||||
3. Installation bestätigen („Unbekannte Quellen" für Downloader erlauben,
|
3. Installation bestätigen („Unbekannte Quellen" für Downloader erlauben,
|
||||||
wenn der Fernseher fragt).
|
wenn der Fernseher fragt).
|
||||||
4. Castarr starten und den drei Schritten auf dem Bildschirm folgen:
|
4. Castarr starten und den drei Schritten auf dem Bildschirm folgen:
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ android {
|
|||||||
applicationId = "dev.castarr.tv"
|
applicationId = "dev.castarr.tv"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 23
|
versionCode = 25
|
||||||
versionName = "0.9.0"
|
versionName = "0.9.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release signing from environment (see ~/.keys/castarr-release.env on the
|
// Release signing from environment (see ~/.keys/castarr-release.env on the
|
||||||
|
|||||||
@@ -51,6 +51,13 @@ class AppState(
|
|||||||
/** Channel to restore focus to when the list comes back (#13). */
|
/** Channel to restore focus to when the list comes back (#13). */
|
||||||
var lastWatched by mutableStateOf<Channel?>(null)
|
var lastWatched by mutableStateOf<Channel?>(null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True while the list is being restored after playback. The group rail
|
||||||
|
* opens whatever gets focused, so without this the focus landing there
|
||||||
|
* would silently switch the view back to "Alle Sender".
|
||||||
|
*/
|
||||||
|
var restorePending by mutableStateOf(false)
|
||||||
|
|
||||||
private val reentryHandler = android.os.Handler(android.os.Looper.getMainLooper())
|
private val reentryHandler = android.os.Handler(android.os.Looper.getMainLooper())
|
||||||
private var lastStoppedUrl: String = ""
|
private var lastStoppedUrl: String = ""
|
||||||
private var lastStoppedAt: Long = 0L
|
private var lastStoppedAt: Long = 0L
|
||||||
@@ -190,6 +197,7 @@ class AppState(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun stopPlayback() {
|
fun stopPlayback() {
|
||||||
|
restorePending = lastWatched != null
|
||||||
reentryHandler.removeCallbacksAndMessages(null)
|
reentryHandler.removeCallbacksAndMessages(null)
|
||||||
lastStoppedUrl = currentChannel?.url.orEmpty()
|
lastStoppedUrl = currentChannel?.url.orEmpty()
|
||||||
lastStoppedAt = System.currentTimeMillis()
|
lastStoppedAt = System.currentTimeMillis()
|
||||||
|
|||||||
@@ -106,11 +106,12 @@ fun LiveScreen(state: AppState) {
|
|||||||
// A fresh group starts at the top; a return from playback does not.
|
// A fresh group starts at the top; a return from playback does not.
|
||||||
if (restoreIndex == null) listState.scrollToItem(0)
|
if (restoreIndex == null) listState.scrollToItem(0)
|
||||||
}
|
}
|
||||||
LaunchedEffect(restoreIndex, channels.size) {
|
LaunchedEffect(restoreIndex, channels.size, state.restorePending) {
|
||||||
if (restoreIndex != null && !restored) {
|
if (restoreIndex != null && (state.restorePending || !restored)) {
|
||||||
listState.scrollToItem(restoreIndex)
|
listState.scrollToItem(restoreIndex)
|
||||||
runCatching { restoreFocus.requestFocus() }
|
runCatching { restoreFocus.requestFocus() }
|
||||||
restored = true
|
restored = true
|
||||||
|
state.restorePending = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val intoList = if (channels.isEmpty()) Modifier
|
val intoList = if (channels.isEmpty()) Modifier
|
||||||
@@ -135,6 +136,7 @@ fun LiveScreen(state: AppState) {
|
|||||||
modifier = intoList.then(
|
modifier = intoList.then(
|
||||||
if (selected) Modifier.focusRequester(railFocus) else Modifier
|
if (selected) Modifier.focusRequester(railFocus) else Modifier
|
||||||
),
|
),
|
||||||
|
suppressAutoSelect = { state.restorePending },
|
||||||
) {
|
) {
|
||||||
state.favoritesOnly = false
|
state.favoritesOnly = false
|
||||||
state.groupFilter = null
|
state.groupFilter = null
|
||||||
@@ -149,6 +151,7 @@ fun LiveScreen(state: AppState) {
|
|||||||
modifier = intoList.then(
|
modifier = intoList.then(
|
||||||
if (state.favoritesOnly) Modifier.focusRequester(railFocus) else Modifier
|
if (state.favoritesOnly) Modifier.focusRequester(railFocus) else Modifier
|
||||||
),
|
),
|
||||||
|
suppressAutoSelect = { state.restorePending },
|
||||||
) {
|
) {
|
||||||
state.favoritesOnly = true
|
state.favoritesOnly = true
|
||||||
state.groupFilter = null
|
state.groupFilter = null
|
||||||
@@ -172,6 +175,7 @@ fun LiveScreen(state: AppState) {
|
|||||||
modifier = intoList.then(
|
modifier = intoList.then(
|
||||||
if (state.groupFilter == group) Modifier.focusRequester(railFocus) else Modifier
|
if (state.groupFilter == group) Modifier.focusRequester(railFocus) else Modifier
|
||||||
),
|
),
|
||||||
|
suppressAutoSelect = { state.restorePending },
|
||||||
) {
|
) {
|
||||||
state.favoritesOnly = false
|
state.favoritesOnly = false
|
||||||
state.groupFilter = group
|
state.groupFilter = group
|
||||||
@@ -237,6 +241,7 @@ private fun GroupItem(
|
|||||||
count: Int,
|
count: Int,
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
suppressAutoSelect: () -> Boolean = { false },
|
||||||
onSelect: () -> Unit,
|
onSelect: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
@@ -245,7 +250,7 @@ private fun GroupItem(
|
|||||||
onClick = onSelect,
|
onClick = onSelect,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.onFocusChanged { if (it.isFocused) onSelect() },
|
.onFocusChanged { if (it.isFocused && !suppressAutoSelect()) onSelect() },
|
||||||
shape = ClickableSurfaceDefaults.shape(RoundedCornerShape(10.dp)),
|
shape = ClickableSurfaceDefaults.shape(RoundedCornerShape(10.dp)),
|
||||||
scale = ClickableSurfaceDefaults.scale(focusedScale = 1f),
|
scale = ClickableSurfaceDefaults.scale(focusedScale = 1f),
|
||||||
colors = ClickableSurfaceDefaults.colors(
|
colors = ClickableSurfaceDefaults.colors(
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ object UpdateChecker {
|
|||||||
|
|
||||||
private const val TAG = "UpdateChecker"
|
private const val TAG = "UpdateChecker"
|
||||||
private const val LATEST =
|
private const val LATEST =
|
||||||
"https://git.beckm4nn.net/api/v1/repos/benjamin/castarr/releases/latest"
|
"https://git.beckm4nn.net/api/v1/repos/be-nj/castarr/releases/latest"
|
||||||
|
|
||||||
// Release assets need an API token to upload, so the APK is served from
|
// Release assets need an API token to upload, so the APK is served from
|
||||||
// an orphan branch instead — anonymously fetchable on a public repo.
|
// an orphan branch instead — anonymously fetchable on a public repo.
|
||||||
private const val APK_FALLBACK =
|
private const val APK_FALLBACK =
|
||||||
"https://git.beckm4nn.net/benjamin/castarr/raw/branch/apk/castarr.apk"
|
"https://git.beckm4nn.net/be-nj/castarr/raw/branch/apk/castarr.apk"
|
||||||
|
|
||||||
private var apkUrl: String = ""
|
private var apkUrl: String = ""
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ object UpdateChecker {
|
|||||||
return try {
|
return try {
|
||||||
connection.connectTimeout = 10_000
|
connection.connectTimeout = 10_000
|
||||||
connection.readTimeout = 15_000
|
connection.readTimeout = 15_000
|
||||||
connection.setRequestProperty("Accept", "application/vnd.github+json")
|
connection.setRequestProperty("Accept", "application/json")
|
||||||
connection.inputStream.bufferedReader().use { it.readText() }
|
connection.inputStream.bufferedReader().use { it.readText() }
|
||||||
} finally {
|
} finally {
|
||||||
connection.disconnect()
|
connection.disconnect()
|
||||||
|
|||||||
Reference in New Issue
Block a user