Grund: Compose-app med login mot gym-API:t, VS Code-task och CI-release
Some checks failed
release / build-release (push) Failing after 1m42s

- Kotlin + Jetpack Compose + Material 3, minSdk 31 (Android 12)
- GraphQL-klient (OkHttp) med login/refresh/validate/logout som webben
- Tokenlagring i DataStore, auto-refresh av JWT
- Signering via gitignorerad signing/ lokalt och Actions-secrets i CI
- VS Code build-task -> build/FitnessDroid.apk
- Gitea Actions: bygger APK på Pi5 (arm64, aapt2-override) och publicerar
  rullande latest-release

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kf4MAbZ3c3B4Xy5XfuGsCP
This commit is contained in:
2026-07-23 11:59:21 +02:00
commit 777f627888
33 changed files with 1816 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
package eu.brassepc.fitnessdroid.data
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit
class GraphQlException(val messages: List<String>) : Exception(messages.joinToString("; "))
/**
* Minimal GraphQL-klient över OkHttp. Samma request-form som webbklienten
* (gymTrackerApi.js): POST {query, variables} + "Authorization: Bearer <jwt>".
*/
class GraphQlClient(private val urlProvider: suspend () -> String) {
private val http = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
val json = Json { ignoreUnknownKeys = true }
suspend fun execute(
query: String,
variables: JsonObject = buildJsonObject {},
token: String? = null,
): JsonObject = withContext(Dispatchers.IO) {
val body = buildJsonObject {
put("query", query)
put("variables", variables)
}
val request = Request.Builder()
.url(urlProvider())
.post(body.toString().toRequestBody("application/json".toMediaType()))
.apply { if (token != null) header("Authorization", "Bearer $token") }
.build()
http.newCall(request).execute().use { response ->
val text = response.body?.string().orEmpty()
val root = runCatching { json.parseToJsonElement(text).jsonObject }.getOrNull()
?: throw GraphQlException(listOf("HTTP ${response.code}: oväntat svar från servern"))
root["errors"]?.jsonArray?.takeIf { it.isNotEmpty() }?.let { errors ->
throw GraphQlException(
errors.map {
it.jsonObject["message"]?.jsonPrimitive?.content ?: "Okänt fel"
}
)
}
root["data"]?.jsonObject
?: throw GraphQlException(listOf("HTTP ${response.code}: tomt svar från servern"))
}
}
}