diff --git a/backend/gradle.properties b/backend/gradle.properties
index ef6192d..020c737 100644
--- a/backend/gradle.properties
+++ b/backend/gradle.properties
@@ -3,4 +3,4 @@ exposed_version=0.61.0
h2_version=2.3.232
kotlin_version=2.1.10
ktor_version=3.1.3
-logback_version=1.4.14
+logback_version=1.5.13
diff --git a/frontend/app/src/main/AndroidManifest.xml b/frontend/app/src/main/AndroidManifest.xml
index f583f25..c6e2329 100644
--- a/frontend/app/src/main/AndroidManifest.xml
+++ b/frontend/app/src/main/AndroidManifest.xml
@@ -2,6 +2,8 @@
+
+
-
\ No newline at end of file
+
diff --git a/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt b/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt
index 169431b..6321e15 100644
--- a/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt
+++ b/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt
@@ -4,34 +4,13 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.filled.Add
-import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.FloatingActionButton
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextField
-import androidx.compose.material3.TopAppBar
-import androidx.compose.material3.TopAppBarDefaults
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -45,20 +24,26 @@ import dev.svitan.antifed.ui.components.DropdownList
import dev.svitan.antifed.ui.theme.AntiFedTheme
import io.ktor.client.call.body
import io.ktor.client.request.get
+import io.ktor.client.request.headers
import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.delay
@OptIn(ExperimentalMaterial3Api::class)
class AuthActivity : ComponentActivity() {
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
+
setContent {
AntiFedTheme {
+
var serverUrl by remember { mutableStateOf("") }
var token by remember { mutableStateOf("") }
- var authIndex by remember { mutableIntStateOf(-1) }
+
var authId by remember { mutableStateOf("") }
+ var authIndex by remember { mutableIntStateOf(-1) }
+ var auths by remember { mutableStateOf>(emptyList()) }
var needToCheckUrl by remember { mutableStateOf(false) }
var checkingUrl by remember { mutableStateOf(false) }
@@ -68,110 +53,99 @@ class AuthActivity : ComponentActivity() {
var checkingToken by remember { mutableStateOf(false) }
var isTokenOk by remember { mutableStateOf(false) }
- var auths by remember { mutableStateOf(listOf()) }
-
- val prefs = LocalContext.current.getSharedPreferences(
- stringResource(R.string.settings_prefs_key),
- MODE_PRIVATE
+ val context = LocalContext.current
+ val prefs = context.getSharedPreferences(
+ stringResource(R.string.settings_prefs_key), MODE_PRIVATE
)
- val loadedServerUrl = prefs.getString(getString(R.string.server_url_key), "") ?: ""
- if (loadedServerUrl.isNotBlank()) {
- serverUrl = loadedServerUrl
+
+ LaunchedEffect(Unit) {
+ serverUrl = prefs.getString(getString(R.string.server_url_key), "") ?: ""
+ token = prefs.getString(getString(R.string.token_key), "") ?: ""
+ authId = prefs.getString(getString(R.string.auth_key), "") ?: ""
+ isServerUrlOk = prefs.getBoolean(getString(R.string.server_url_okay_key), false)
}
- val loadedToken = prefs.getString(getString(R.string.token_key), "") ?: ""
- if (loadedToken.isNotBlank()) {
- token = loadedToken
- }
- authId = prefs.getString(getString(R.string.auth_key), "") ?: ""
- isServerUrlOk = prefs.getBoolean(getString(R.string.server_url_okay_key), false)
fun serverUrlMatches(): Boolean {
- var re = Regex(
- """https?://[a-zA-Z0-9\\.]+\\.[a-zA-Z0-9]+""",
- RegexOption.DOT_MATCHES_ALL
- )
- return re.containsMatchIn(serverUrl)
+ val regex = Regex("""https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(:\d+)?""")
+ return regex.matches(serverUrl)
}
LaunchedEffect(serverUrl) {
if (!needToCheckUrl) return@LaunchedEffect
- else {
- isServerUrlOk = false
- prefs.edit {
- putBoolean(getString(R.string.server_url_okay_key), false)
- }
- }
+ needToCheckUrl = false
- if (serverUrl.isBlank()) return@LaunchedEffect
- if (!serverUrlMatches()) return@LaunchedEffect
+ isServerUrlOk = false
+ prefs.edit {
+ putBoolean(getString(R.string.server_url_okay_key), false)
+ }
+ if (serverUrl.isBlank() || !serverUrlMatches()) return@LaunchedEffect
delay(1000)
checkingUrl = true
- val response = client.get(serverUrl)
+
+ isServerUrlOk = try {
+ val response = client.get(serverUrl)
+ response.status == HttpStatusCode.OK
+ } catch (_: Exception) {
+ false
+ }
checkingUrl = false
- needToCheckUrl = false
- isServerUrlOk = response.status == HttpStatusCode.OK
prefs.edit {
- putBoolean(getString(R.string.server_url_okay_key), isServerUrlOk)
+ putBoolean(
+ getString(R.string.server_url_okay_key), isServerUrlOk
+ )
}
}
LaunchedEffect(token) {
if (!needToCheckToken) return@LaunchedEffect
- else isTokenOk = false
+ needToCheckToken = false
+ isTokenOk = false
- if (!isServerUrlOk) return@LaunchedEffect
- if (token.isBlank()) return@LaunchedEffect
+ if (!isServerUrlOk || token.isBlank()) return@LaunchedEffect
delay(1000)
checkingToken = true
- val response = client.get("$serverUrl/auth")
- if (response.status != HttpStatusCode.OK) {
- checkingToken = false
- needToCheckToken = false
- isTokenOk = false
- return@LaunchedEffect
- }
+ try {
+ val response = client.get("$serverUrl/auth") {
+ headers {
+ append("Authorization", "Bearer $token")
+ }
+ }
- auths = response.body>()
- if (authId.isNotBlank()) {
- authIndex = auths.indexOfFirst { it.id == authId }
+ if (response.status == HttpStatusCode.OK) {
+ auths = response.body()
+ authIndex = auths.indexOfFirst { it.id == authId }
+ isTokenOk = true
+ }
+ } catch (_: Exception) {
+ isTokenOk = false
}
checkingToken = false
- needToCheckToken = false
- isTokenOk = true
}
Scaffold(
topBar = {
TopAppBar(
- colors = TopAppBarDefaults.topAppBarColors(
- titleContentColor = MaterialTheme.colorScheme.primary
- ),
- title = {
- Text(stringResource(R.string.auth))
- },
+ title = { Text(stringResource(R.string.auth)) },
navigationIcon = {
IconButton(onClick = { finish() }) {
Icon(
- imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
+ ( Icons.AutoMirrored.Outlined.ArrowBack,
contentDescription = stringResource(R.string.go_back)
)
}
- }
- )
- },
- modifier = Modifier.fillMaxSize()
- ) { innerPadding ->
+ })
+ }) { padding ->
Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center,
modifier = Modifier
- .padding(innerPadding)
- .fillMaxSize()
+ .padding(padding)
+ .fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
) {
Text(stringResource(R.string.auth), fontSize = 24.sp)
Spacer(modifier = Modifier.height(16.dp))
@@ -179,69 +153,83 @@ class AuthActivity : ComponentActivity() {
TextField(
value = serverUrl,
onValueChange = {
- needToCheckUrl = true
serverUrl = it
+ needToCheckUrl = true
prefs.edit {
- putString(getString(R.string.server_url_key), it)
+ putString(
+ getString(R.string.server_url_key), it
+ )
}
},
label = { Text(stringResource(R.string.server_url)) },
singleLine = true,
- isError = !isServerUrlOk,
+ isError = !isServerUrlOk && serverUrl.isNotBlank(),
trailingIcon = {
- if (checkingUrl)
+ if (checkingUrl) {
CircularProgressIndicator(
- modifier = Modifier.width(36.dp),
- color = MaterialTheme.colorScheme.secondary,
- trackColor = MaterialTheme.colorScheme.surfaceVariant
+ modifier = Modifier.size(24.dp)
)
- }
- )
+ }
+ })
+
Spacer(modifier = Modifier.height(12.dp))
TextField(
value = token,
onValueChange = {
- needToCheckToken = true
token = it
+ needToCheckToken = true
prefs.edit {
- putString(getString(R.string.token_key), it)
+ putString(
+ getString(R.string.token_key), it
+ )
}
},
label = { Text(stringResource(R.string.token)) },
singleLine = true,
- isError = isTokenOk,
+ enabled = isServerUrlOk,
+ isError = !isTokenOk && token.isNotBlank(),
visualTransformation = PasswordVisualTransformation(),
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Password
+ ),
trailingIcon = {
if (checkingToken) {
CircularProgressIndicator(
- modifier = Modifier.width(36.dp),
- color = MaterialTheme.colorScheme.secondary,
- trackColor = MaterialTheme.colorScheme.surfaceVariant
+ modifier = Modifier.size(24.dp)
)
}
- }
- )
+ })
+
Spacer(modifier = Modifier.height(12.dp))
- if (auths.isNotEmpty())
+ if (auths.isNotEmpty()) {
DropdownList(
- itemList = auths.map { it -> it.name },
- selectedIndex = 0,
- modifier = Modifier,
+ itemList = auths.map { it.name },
+ selectedIndex = authIndex.coerceAtLeast(0),
onItemClick = {
authIndex = it
+ authId = auths[it].id
prefs.edit {
- putInt(getString(R.string.auth_key), it)
+ putString(
+ getString(R.string.auth_key), authId
+ )
}
- }
+ },
+ modifier = Modifier
)
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
FloatingActionButton(
- onClick = { println("creating new auth!!") }
- ) {
- Icon(Icons.Default.Add, stringResource(R.string.create_auth))
+ onClick = {
+ println("creating new auth!!")
+ }) {
+ Icon(
+ Icons.Default.Add,
+ contentDescription = stringResource(R.string.create_auth)
+ )
}
}
}
diff --git a/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt b/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt
index a5f2142..91376ef 100644
--- a/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt
+++ b/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt
@@ -83,7 +83,6 @@ fun DropdownList(
Text(text = item)
}
}
-
}
}
}
diff --git a/frontend/app/src/main/res/values-sk/strings.xml b/frontend/app/src/main/res/values-sk/strings.xml
index 30ca4de..90e3d43 100644
--- a/frontend/app/src/main/res/values-sk/strings.xml
+++ b/frontend/app/src/main/res/values-sk/strings.xml
@@ -7,4 +7,5 @@
Ísť späť
Token
Vytvoriť novú auth
+ Biometrika
\ No newline at end of file
diff --git a/frontend/app/src/main/res/values/strings.xml b/frontend/app/src/main/res/values/strings.xml
index b38d3cb..a9cd420 100644
--- a/frontend/app/src/main/res/values/strings.xml
+++ b/frontend/app/src/main/res/values/strings.xml
@@ -12,6 +12,7 @@
token
auth_id
Create new auth
+ Biometrics
\ No newline at end of file
diff --git a/frontend/gradle.properties b/frontend/gradle.properties
index 20e2a01..4540b75 100644
--- a/frontend/gradle.properties
+++ b/frontend/gradle.properties
@@ -20,4 +20,14 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
\ No newline at end of file
+android.nonTransitiveRClass=true
+android.defaults.buildfeatures.resvalues=true
+android.sdk.defaultTargetSdkToCompileSdkIfUnset=false
+android.enableAppCompileTimeRClass=false
+android.usesSdkInManifest.disallowed=false
+android.uniquePackageNames=false
+android.dependency.useConstraints=true
+android.r8.strictFullModeForKeepRules=false
+android.r8.optimizedResourceShrinking=false
+android.builtInKotlin=false
+android.newDsl=false
\ No newline at end of file
diff --git a/frontend/gradle/gradle-daemon-jvm.properties b/frontend/gradle/gradle-daemon-jvm.properties
new file mode 100644
index 0000000..5c34300
--- /dev/null
+++ b/frontend/gradle/gradle-daemon-jvm.properties
@@ -0,0 +1,13 @@
+#This file is generated by updateDaemonJvm
+toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect
+toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect
+toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect
+toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect
+toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e99bae143b75f9a10ead10248f02055e/redirect
+toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/04e088f8677de3b384108493cc9481d0/redirect
+toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect
+toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect
+toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e55dccbfe27cb97945148c61a39c89c5/redirect
+toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/dbd05c4936d573642f94cd149e1356c8/redirect
+toolchainVendor=JETBRAINS
+toolchainVersion=21
diff --git a/frontend/gradle/libs.versions.toml b/frontend/gradle/libs.versions.toml
index c4bee55..7e1dc6c 100644
--- a/frontend/gradle/libs.versions.toml
+++ b/frontend/gradle/libs.versions.toml
@@ -1,6 +1,6 @@
[versions]
-agp = "8.10.0"
-kotlin = "2.0.21"
+agp = "9.1.1"
+kotlin = "2.2.10"
coreKtx = "1.10.1"
junit = "4.13.2"
junitVersion = "1.1.5"
diff --git a/frontend/gradle/wrapper/gradle-wrapper.properties b/frontend/gradle/wrapper/gradle-wrapper.properties
index 61dfb22..c04b16a 100644
--- a/frontend/gradle/wrapper/gradle-wrapper.properties
+++ b/frontend/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed May 14 20:14:06 CEST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/frontend/settings.gradle.kts b/frontend/settings.gradle.kts
index ee91deb..f8c75df 100644
--- a/frontend/settings.gradle.kts
+++ b/frontend/settings.gradle.kts
@@ -11,6 +11,9 @@ pluginManagement {
gradlePluginPortal()
}
}
+plugins {
+ id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
+}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {