Files
NovaSonic/android/app/build.gradle.kts
T
marcandClaude Opus 5 b6e98463ee feat(web+android): Lyrics-Mitlaufen umschaltbar, echtes Mischen, Wiederholung am Queue-Ende
Lyrics: Schalter "Mitlaufen" im Lyrics-Reiter, Vorgabe AUS. Hervorgehoben wird die
laufende Zeile immer, gescrollt nur bei eingeschaltetem Mitlaufen. Nur bei synchronen
Texten sichtbar. Android speichert den Schluessel einzeln, damit das Speichern im
Einstellungsbildschirm ihn nicht auf die Vorgabe zuruecksetzt.

Mischen (Web): sort(() => Math.random() - 0.5) ist keine Mischung. Gemessen blieb der
erste Titel drei- bis fuenfmal zu oft vorn (18,8 % statt 10 % bei n=10, 0,23 % statt
0,05 % bei n=2000). Ersetzt durch Fisher-Yates in queueMath.mischeListe.

Queue-Ende (Web): ein beendetes <audio> meldet sich als pausiert, die Wiederaufnahme
nach unfreiwilligem Hintergrund-Stop rief play() und spielte den letzten Titel erneut.
Jetzt Abbruch bei audio.ended.

Shuffle (Android): das Mischen macht jetzt der Player (playSongs(liste, -1)), damit die
ungemischte Liste erhalten bleibt — vorher leuchtete das Symbol nicht und "Shuffle aus"
konnte die Albumreihenfolge nicht wiederherstellen.

Radio-Modus (beide): der laufende Titel ist aus der letzten Rueckfallebene ausgenommen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-01 17:13:38 +02:00

135 lines
5.0 KiB
Kotlin

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
}
android {
namespace = "com.novasonic.app"
compileSdk = 35
defaultConfig {
applicationId = "com.novasonic.app"
minSdk = 26
targetSdk = 35
versionCode = 29
versionName = "1.26"
// 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.
val host1 = (project.findProperty("novasonicHost") as String?) ?: "music.example.com"
// Zweiter Host fuer einen Domainwechsel: solange beide Adressen erreichbar sind, oeffnen
// Links von der alten UND der neuen in der App. Ohne Angabe wird host1 wiederholt — ein
// doppelter Eintrag ist harmlos, ein Platzhalter-Host waere es NICHT: auf Android 11 und
// aelter verifiziert das System nur, wenn ALLE angegebenen Hosts eine gueltige
// assetlinks.json ausliefern, ein toter Host wuerde also alle blockieren.
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\"")
// Vorgabe-Adressen fuer Backend und Scrobble-Dienst. Sie stehen ohnehin in der
// docker-compose.yml — sie zusaetzlich auf jedem Geraet einzutippen ist Arbeit ohne Nutzen.
// Leer gelassen verhaelt sich die App wie bisher (Felder sichtbar, nichts vorbelegt).
// Zugangsdaten bleiben bewusst aussen vor: die gehoeren pro Person, nicht ins APK.
val backendUrl = (project.findProperty("novasonicBackend") as String?) ?: ""
val scrobbleUrl = (project.findProperty("novasonicScrobble") as String?) ?: ""
val navidromeUrl= (project.findProperty("novasonicNavidrome") as String?) ?: ""
buildConfigField("String", "DEFAULT_BACKEND_URL", "\"$backendUrl\"")
buildConfigField("String", "DEFAULT_SCROBBLE_URL", "\"$scrobbleUrl\"")
buildConfigField("String", "DEFAULT_ND_URL", "\"$navidromeUrl\"")
}
buildFeatures {
compose = true
buildConfig = true
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
}
dependencies {
// Core
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.splashscreen)
// Compose
implementation(platform(libs.compose.bom))
implementation(libs.compose.ui)
implementation(libs.compose.ui.tooling.preview)
implementation(libs.compose.foundation)
implementation(libs.compose.material3)
implementation(libs.compose.icons)
debugImplementation(libs.compose.ui.tooling)
// Navigation
implementation(libs.navigation.compose)
// Lifecycle / ViewModel
implementation(libs.lifecycle.viewmodel.compose)
implementation(libs.lifecycle.runtime.compose)
// Media3
implementation(libs.media3.exoplayer)
implementation(libs.media3.session)
implementation(libs.media3.common)
implementation(libs.media3.datasource)
implementation(libs.media3.ui)
// Ktor
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.android)
implementation(libs.ktor.client.content.neg)
implementation(libs.ktor.serialization.json)
implementation(libs.ktor.client.logging)
// Kotlinx
implementation(libs.kotlinx.serialization.json)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlin.reflect)
// Room
implementation(libs.room.runtime)
implementation(libs.room.ktx)
ksp(libs.room.compiler)
// WorkManager
implementation(libs.work.runtime.ktx)
// DataStore
implementation(libs.datastore.preferences)
// Coil
implementation(libs.coil.compose)
// Palette (dominant cover color extraction in NowPlaying)
implementation("androidx.palette:palette-ktx:1.0.0")
// MediaStyle for ABS notification (already transitive via media3-session, made explicit)
implementation("androidx.media:media:1.7.0")
// Google Fonts (Outfit)
implementation(libs.compose.ui.google.fonts)
}