From 28e23098947c69c9603c21627a199313ac5fb5c2 Mon Sep 17 00:00:00 2001 From: Tom Hempel Date: Wed, 24 Jun 2026 12:17:15 +0200 Subject: [PATCH] small changes --- app/build.gradle.kts | 6 ++-- app/proguard-rules.pro | 30 ++++++++----------- app/src/main/AndroidManifest.xml | 4 +-- .../coordinatorapp/network/ApiLog.kt | 11 +++++++ .../coordinatorapp/network/NatAsApiClient.kt | 1 + .../ui/components/CoordinatorBrand.kt | 18 ++++------- .../ui/components/CoordinatorChrome.kt | 11 +------ .../coordinatorapp/ui/theme/Color.kt | 14 ++++----- .../coordinatorapp/ui/theme/Theme.kt | 4 +-- .../viewmodel/CoordinatorViewModel.kt | 23 ++++++++++++-- app/src/main/res/xml/backup_rules.xml | 17 ++++------- .../main/res/xml/data_extraction_rules.xml | 22 +++++--------- 12 files changed, 79 insertions(+), 82 deletions(-) create mode 100644 app/src/main/java/com/tomhempel/coordinatorapp/network/ApiLog.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d9f5a8d..e6a35ad 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -23,7 +23,8 @@ android { buildTypes { release { - isMinifyEnabled = false + isMinifyEnabled = true + isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" @@ -36,6 +37,7 @@ android { } buildFeatures { compose = true + buildConfig = true } } @@ -62,4 +64,4 @@ dependencies { androidTestImplementation(libs.androidx.junit) debugImplementation(libs.androidx.compose.ui.test.manifest) debugImplementation(libs.androidx.compose.ui.tooling) -} \ No newline at end of file +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb43..e5e9f94 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,21 +1,15 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html +# Gson models used for API parsing. +-keep class com.tomhempel.coordinatorapp.data.** { *; } +-keep class com.tomhempel.coordinatorapp.network.** { *; } -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} +# AndroidX Security crypto (EncryptedSharedPreferences / MasterKey) +-keep class androidx.security.crypto.** { *; } -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable +# Gson reflective serialization +-keepattributes Signature +-keepattributes *Annotation* +-dontwarn sun.misc.** -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +# Keep line numbers for crash reports. +-keepattributes SourceFile,LineNumberTable +-renamesourcefileattribute SourceFile diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6530f78..949c3e3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ - \ No newline at end of file + diff --git a/app/src/main/java/com/tomhempel/coordinatorapp/network/ApiLog.kt b/app/src/main/java/com/tomhempel/coordinatorapp/network/ApiLog.kt new file mode 100644 index 0000000..94fc49a --- /dev/null +++ b/app/src/main/java/com/tomhempel/coordinatorapp/network/ApiLog.kt @@ -0,0 +1,11 @@ +package com.tomhempel.coordinatorapp.network + +import android.util.Log +import com.tomhempel.coordinatorapp.BuildConfig + +internal object ApiLog { + fun response(label: String, httpCode: Int, body: String?) { + if (!BuildConfig.DEBUG) return + Log.d("NAT_AS_API", "$label HTTP $httpCode (${body?.length ?: 0} bytes)") + } +} diff --git a/app/src/main/java/com/tomhempel/coordinatorapp/network/NatAsApiClient.kt b/app/src/main/java/com/tomhempel/coordinatorapp/network/NatAsApiClient.kt index bd4a8d5..31435b7 100644 --- a/app/src/main/java/com/tomhempel/coordinatorapp/network/NatAsApiClient.kt +++ b/app/src/main/java/com/tomhempel/coordinatorapp/network/NatAsApiClient.kt @@ -176,6 +176,7 @@ object NatAsApiClient { private inline fun execute(request: Request, parse: (JsonObject) -> T): T { client.newCall(request).execute().use { response -> val bodyText = response.body?.string() + ApiLog.response("${request.method} ${request.url.encodedPath}", response.code, bodyText) val root = parseEnvelope(response.code, bodyText) val data = root.get("data") return when { diff --git a/app/src/main/java/com/tomhempel/coordinatorapp/ui/components/CoordinatorBrand.kt b/app/src/main/java/com/tomhempel/coordinatorapp/ui/components/CoordinatorBrand.kt index 0ddfb14..2a15fb6 100644 --- a/app/src/main/java/com/tomhempel/coordinatorapp/ui/components/CoordinatorBrand.kt +++ b/app/src/main/java/com/tomhempel/coordinatorapp/ui/components/CoordinatorBrand.kt @@ -1,6 +1,7 @@ package com.tomhempel.coordinatorapp.ui.components import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -15,7 +16,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.font.FontWeight @@ -44,25 +44,19 @@ fun CoordinatorBrandBadge( iconSize: Dp = 40.dp, ) { val scheme = MaterialTheme.colorScheme + val shape = RoundedCornerShape(22.dp) Box( modifier = modifier .size(size) - .clip(RoundedCornerShape(22.dp)) - .background( - Brush.linearGradient( - colors = listOf( - scheme.primary, - scheme.primary.copy(alpha = 0.82f), - scheme.primaryContainer, - ), - ), - ), + .clip(shape) + .border(1.dp, scheme.outline, shape) + .background(scheme.surface), contentAlignment = Alignment.Center, ) { Icon( imageVector = ImageVector.vectorResource(R.drawable.ic_coordinator_mark), contentDescription = null, - tint = scheme.onPrimary, + tint = scheme.primary, modifier = Modifier.size(iconSize), ) } diff --git a/app/src/main/java/com/tomhempel/coordinatorapp/ui/components/CoordinatorChrome.kt b/app/src/main/java/com/tomhempel/coordinatorapp/ui/components/CoordinatorChrome.kt index 4dc97ae..9b61b7c 100644 --- a/app/src/main/java/com/tomhempel/coordinatorapp/ui/components/CoordinatorChrome.kt +++ b/app/src/main/java/com/tomhempel/coordinatorapp/ui/components/CoordinatorChrome.kt @@ -25,7 +25,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp @@ -40,15 +39,7 @@ fun CoordinatorBackground( Box( modifier = modifier .fillMaxSize() - .background( - Brush.verticalGradient( - colors = listOf( - scheme.background, - scheme.surfaceVariant, - scheme.background, - ), - ), - ), + .background(scheme.background), ) { content() } diff --git a/app/src/main/java/com/tomhempel/coordinatorapp/ui/theme/Color.kt b/app/src/main/java/com/tomhempel/coordinatorapp/ui/theme/Color.kt index fe1be89..ee4a987 100644 --- a/app/src/main/java/com/tomhempel/coordinatorapp/ui/theme/Color.kt +++ b/app/src/main/java/com/tomhempel/coordinatorapp/ui/theme/Color.kt @@ -3,7 +3,7 @@ package com.tomhempel.coordinatorapp.ui.theme import androidx.compose.ui.graphics.Color val BrandPurple = Color(0xFF6E56CF) -val BrandPurpleDark = Color(0xFF9B8AE8) +val BrandPurpleDark = Color(0xFF8F84B8) val BrandWindowBg = Color(0xFFF4F8F7) val BrandSurface = Color(0xFFFFFFFF) @@ -13,9 +13,9 @@ val BrandStroke = Color(0xFFD5E4E0) val BrandDestructive = Color(0xFFC62828) val BrandAccentContainer = Color(0xFFE6F4F0) -val BrandWindowBgDark = Color(0xFF121A19) -val BrandSurfaceDark = Color(0xFF1E2A28) -val BrandTextLight = Color(0xFFE8F2F0) -val BrandTextMutedDark = Color(0xFF8AA39E) -val BrandStrokeDark = Color(0xFF2D403C) -val BrandAccentContainerDark = Color(0xFF243532) +val BrandWindowBgDark = Color(0xFF0F1413) +val BrandSurfaceDark = Color(0xFF171D1C) +val BrandTextLight = Color(0xFFE4EDEB) +val BrandTextMutedDark = Color(0xFF7F9490) +val BrandStrokeDark = Color(0xFF2B3634) +val BrandAccentContainerDark = Color(0xFF222A28) diff --git a/app/src/main/java/com/tomhempel/coordinatorapp/ui/theme/Theme.kt b/app/src/main/java/com/tomhempel/coordinatorapp/ui/theme/Theme.kt index 90d767a..f0e7b9b 100644 --- a/app/src/main/java/com/tomhempel/coordinatorapp/ui/theme/Theme.kt +++ b/app/src/main/java/com/tomhempel/coordinatorapp/ui/theme/Theme.kt @@ -30,14 +30,14 @@ private val LightColorScheme = lightColorScheme( private val DarkColorScheme = darkColorScheme( primary = BrandPurpleDark, - onPrimary = Color(0xFF1F163D), + onPrimary = BrandWindowBgDark, primaryContainer = BrandAccentContainerDark, onPrimaryContainer = BrandTextLight, background = BrandWindowBgDark, onBackground = BrandTextLight, surface = BrandSurfaceDark, onSurface = BrandTextLight, - surfaceVariant = BrandAccentContainerDark, + surfaceVariant = BrandSurfaceDark, onSurfaceVariant = BrandTextMutedDark, outline = BrandStrokeDark, error = BrandDestructive, diff --git a/app/src/main/java/com/tomhempel/coordinatorapp/viewmodel/CoordinatorViewModel.kt b/app/src/main/java/com/tomhempel/coordinatorapp/viewmodel/CoordinatorViewModel.kt index 8b30b3a..d1b7118 100644 --- a/app/src/main/java/com/tomhempel/coordinatorapp/viewmodel/CoordinatorViewModel.kt +++ b/app/src/main/java/com/tomhempel/coordinatorapp/viewmodel/CoordinatorViewModel.kt @@ -45,13 +45,30 @@ class CoordinatorViewModel(application: Application) : AndroidViewModel(applicat counselors = counselors, ) } - } catch (_: Exception) { - SessionStore.clear(context) - _uiState.update { CoordinatorUiState() } + } catch (e: ApiException) { + if (e.isAuthError()) { + SessionStore.clear(context) + _uiState.update { CoordinatorUiState() } + } else { + _uiState.update { + CoordinatorUiState( + errorMessage = e.message ?: "Could not verify session", + ) + } + } + } catch (e: Exception) { + _uiState.update { + CoordinatorUiState( + errorMessage = e.message ?: "Could not verify session", + ) + } } } } + private fun ApiException.isAuthError(): Boolean = + code == "UNAUTHORIZED" || code == "FORBIDDEN" || code == "PASSWORD_CHANGE_REQUIRED" + fun login(username: String, password: String) { val trimmedUser = username.trim() if (trimmedUser.isBlank() || password.isBlank()) { diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml index 4df9255..76900be 100644 --- a/app/src/main/res/xml/backup_rules.xml +++ b/app/src/main/res/xml/backup_rules.xml @@ -1,13 +1,6 @@ - + - - \ No newline at end of file + + + + diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml index 9ee9997..9b1de60 100644 --- a/app/src/main/res/xml/data_extraction_rules.xml +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -1,19 +1,13 @@ - + - + + + - - \ No newline at end of file +