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
+