refactor(android): Web-Adresse aus BuildConfig statt als vierte Einstellung

Der Host stand ohnehin schon beim Bauen fest, weil die App-Links ihn im Manifest
brauchen. Ihn zusaetzlich als Einstellung zu fuehren hiess: eine vierte Adresse
pflegen, die vom Manifest abweichen kann, ohne dass es jemand merkt — und der
Teilen-Eintrag versteckte sich, solange sie leer war, was beim Suchen schlicht
wie ein fehlendes Feature aussah.

Jetzt kommt beides aus -PnovasonicHost=…: das Manifest fuer eingehende Links,
BuildConfig.SHARE_HOST fuer ausgehende. Die Einstellung samt Speicherfeld ist
entfernt, Teilen ist immer sichtbar. versionCode 5 / 1.2, APK hochgeladen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-09-06 20:27:27 +02:00
1 parent dea0485a54
commit 83570b22b5
6 files changed
+23 -32

No files matched your search

+5 -2
View File
@@ -150,6 +150,10 @@ Versioning: two-part `MAJOR.MINOR` — each release bumps the minor (features an
mutiert, um unter Windows das Neuaufbauen der Medienkachel zu vermeiden. Über MPRIS kam davon
nichts an: Plasma-Widgets zeigten dauerhaft den zuerst gespielten Titel. Bei echtem Titelwechsel
wird jetzt ein neues `MediaMetadata` zugewiesen, sonst weiter in-place (Cover-Nachladen).
- **Android 1.2 (versionCode 5)** — die Web-Adresse ist keine Einstellung mehr, sondern kommt aus
`BuildConfig.SHARE_HOST`, also aus **derselben** Angabe (`-PnovasonicHost=…`), die ohnehin für die
App-Links ins Manifest wandert. Damit gibt es keine vierte Adresse zu pflegen, „Teilen" ist immer
sichtbar, und ein- und ausgehende Links können nicht mehr auseinanderlaufen.
- **Android 1.1 (versionCode 4)** — erste Version mit Teilen-Link, Songlöschen, Playlist aus dem
Import und Duell-Anzeigetafel. Wichtig für den In-App-Updater: er vergleicht den `versionCode` aus
der Gitea-Release-Beschreibung mit dem der installierten App. Wird die Version nicht angehoben
@@ -163,8 +167,7 @@ Versioning: two-part `MAJOR.MINOR` — each release bumps the minor (features an
nicht erneut losspielt. Als installierte PWA landet der Link dank `launch_handler` im bereits
offenen Fenster. Android teilt über den Systemdialog (`ACTION_SEND`) und empfängt Links per
App-Link-Intent-Filter; der Host kommt beim Bauen aus `-PnovasonicHost=…`, die Web-Adresse für
ausgehende Links aus den Einstellungen — **ohne diese Einstellung erscheint „Teilen" im
Kontextmenü nicht**, weil es ohne Adresse keinen Link zu bauen gäbe. Für das Öffnen ohne Nachfrage muss der SHA-256 des
ausgehende Links aus derselben Angabe (seit 1.2, vorher eine eigene Einstellung). Für das Öffnen ohne Nachfrage muss der SHA-256 des
Release-Schlüssels in `public/.well-known/assetlinks.json` stehen — Anleitung in
`docs/share-links.md`. Ohne diesen Schritt öffnet der Link die Web-App im Browser.
- **Android: Songs von der Festplatte löschen** — im Song-Kontextmenü, unten abgesetzt und rot, mit
+6 -2
View File
@@ -14,8 +14,8 @@ android {
applicationId = "com.novasonic.app"
minSdk = 26
targetSdk = 35
versionCode = 4
versionName = "1.1"
versionCode = 5
versionName = "1.2"
// Host der eigenen NovaSonic-Web-App — Ziel der geteilten Links. Einmal in
// gradle.properties setzen (novasonicHost=music.example.com); ohne Angabe bleibt der
// Platzhalter stehen und der App-Link greift schlicht nicht.
@@ -28,6 +28,10 @@ android {
val host2 = (project.findProperty("novasonicHost2") as String?) ?: host1
manifestPlaceholders["shareHost"] = host1
manifestPlaceholders["shareHost2"] = host2
// Derselbe Host auch fuer AUSGEHENDE Links. Er steht ohnehin schon fest im Manifest, eine
// zusaetzliche Einstellung in der App waere also nur eine weitere Adresse zum Pflegen —
// und eine, die vom Manifest abweichen koennte, ohne dass es jemand merkt.
buildConfigField("String", "SHARE_HOST", "\"$host1\"")
}
buildFeatures {
@@ -19,8 +19,6 @@ class PrefsStore(private val context: Context) {
val KEY_LASTFM_KEY = stringPreferencesKey("lastfm_key")
val KEY_BACKEND_URL = stringPreferencesKey("backend_url")
val KEY_SCROBBLE_URL = stringPreferencesKey("scrobble_url")
// Adresse der eigenen NovaSonic-Web-App — Basis der geteilten Links.
val KEY_WEB_URL = stringPreferencesKey("web_url")
val KEY_SCROBBLE_MODE = stringPreferencesKey("scrobble_mode")
val KEY_SHOW_COUNT = booleanPreferencesKey("show_play_count")
val KEY_THEME = stringPreferencesKey("theme")
@@ -44,7 +42,6 @@ class PrefsStore(private val context: Context) {
val lastFmKey: String = "",
val backendUrl: String = "",
val scrobbleUrl: String = "",
val webUrl: String = "",
val scrobbleMode: String = "30s",
val showPlayCount: Boolean = false,
val theme: String = "dark-ocean",
@@ -76,7 +73,6 @@ class PrefsStore(private val context: Context) {
lastFmKey = prefs[KEY_LASTFM_KEY] ?: "",
backendUrl = prefs[KEY_BACKEND_URL] ?: "",
scrobbleUrl = prefs[KEY_SCROBBLE_URL] ?: "",
webUrl = prefs[KEY_WEB_URL] ?: "",
scrobbleMode = prefs[KEY_SCROBBLE_MODE] ?: "30s",
showPlayCount = prefs[KEY_SHOW_COUNT] ?: false,
theme = prefs[KEY_THEME] ?: "dark-ocean",
@@ -112,7 +108,6 @@ class PrefsStore(private val context: Context) {
prefs[KEY_LASTFM_KEY] = settings.lastFmKey
prefs[KEY_BACKEND_URL] = settings.backendUrl
prefs[KEY_SCROBBLE_URL] = settings.scrobbleUrl
prefs[KEY_WEB_URL] = settings.webUrl.trimEnd('/')
prefs[KEY_SCROBBLE_MODE] = settings.scrobbleMode
prefs[KEY_SHOW_COUNT] = settings.showPlayCount
prefs[KEY_THEME] = settings.theme
@@ -609,19 +609,18 @@ fun AppNavigation() {
contextMenuSong = null
},
// Link auf die eigene Web-App, den der Empfaenger mit SEINEN Zugangsdaten oeffnet —
// uebertragen wird nur die Song-Id, kein Geheimnis. Ohne konfigurierten Host waere
// der Link unbrauchbar, deshalb erscheint der Eintrag dann gar nicht erst.
onShare = appSettings.webUrl.takeIf { it.isNotBlank() }?.let { host ->
{
val link = "${host.trimEnd('/')}/?song=${song.id}"
val text = listOfNotNull(song.title, song.artist).joinToString(" — ")
context.startActivity(android.content.Intent.createChooser(
android.content.Intent(android.content.Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(android.content.Intent.EXTRA_SUBJECT, text)
putExtra(android.content.Intent.EXTRA_TEXT, "$text\n$link")
}, "Song teilen"))
}
// uebertragen wird nur die Song-Id, kein Geheimnis. Der Host kommt aus BuildConfig
// (dieselbe Angabe wie fuer die App-Links im Manifest), damit es keine vierte
// Adresse zum Pflegen gibt und beide Richtungen nie auseinanderlaufen koennen.
onShare = {
val link = "https://${com.novasonic.app.BuildConfig.SHARE_HOST}/?song=${song.id}"
val text = listOfNotNull(song.title, song.artist).joinToString(" — ")
context.startActivity(android.content.Intent.createChooser(
android.content.Intent(android.content.Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(android.content.Intent.EXTRA_SUBJECT, text)
putExtra(android.content.Intent.EXTRA_TEXT, "$text\n$link")
}, "Song teilen"))
},
// Nur mit Backend: das Loeschen laeuft ueber /api/delete-song, Navidrome allein
// kann keine Dateien entfernen.
@@ -210,12 +210,6 @@ fun SettingsScreen(onOpenStats: () -> Unit = {}, onOpenCompare: () -> Unit = {})
)
ConnStatusRow(status = state.scrobbleStatus, label = "Nicht konfiguriert", onTest = vm::testScrobble)
NsTextField(
value = state.webUrl, onValueChange = vm::onWebUrl,
label = "NovaSonic Web-Adresse",
hint = "Basis der geteilten Links, z. B. https://music.example.com",
keyboardType = KeyboardType.Uri,
)
Spacer(Modifier.height(4.dp))
@@ -30,7 +30,6 @@ data class SettingsUiState(
val lastFmKey: String = "",
val backendUrl: String = "",
val scrobbleUrl: String = "",
val webUrl: String = "",
val scrobbleMode: String = "30s",
val showPlayCount: Boolean = false,
val theme: String = "dark-ocean",
@@ -63,7 +62,6 @@ class SettingsViewModel(app: Application) : AndroidViewModel(app) {
lastFmKey = s.lastFmKey,
backendUrl = s.backendUrl,
scrobbleUrl = s.scrobbleUrl,
webUrl = s.webUrl,
scrobbleMode = s.scrobbleMode,
showPlayCount = s.showPlayCount,
theme = s.theme,
@@ -81,7 +79,6 @@ class SettingsViewModel(app: Application) : AndroidViewModel(app) {
fun onLastFmKey(v: String) = update { it.copy(lastFmKey = v, savedOk = false) }
fun onBackendUrl(v: String) = update { it.copy(backendUrl = v, savedOk = false, backendStatus = ConnStatus.Idle) }
fun onScrobbleUrl(v: String) = update { it.copy(scrobbleUrl = v, savedOk = false, scrobbleStatus = ConnStatus.Idle) }
fun onWebUrl(v: String) = update { it.copy(webUrl = v, savedOk = false) }
fun onScrobbleMode(v: String) = update { it.copy(scrobbleMode = v, savedOk = false) }
fun onShowPlayCount(v: Boolean) = update { it.copy(showPlayCount = v, savedOk = false) }
fun onAbsUrl(v: String) = update { it.copy(absUrl = v, savedOk = false, absStatus = ConnStatus.Idle) }
@@ -174,7 +171,6 @@ class SettingsViewModel(app: Application) : AndroidViewModel(app) {
lastFmKey = s.lastFmKey.trim(),
backendUrl = s.backendUrl.trimEnd('/'),
scrobbleUrl = s.scrobbleUrl.trimEnd('/'),
webUrl = s.webUrl.trimEnd('/'),
scrobbleMode = s.scrobbleMode,
showPlayCount = s.showPlayCount,
theme = s.theme,