🚧 Works on authentication
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
@@ -27,4 +29,4 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
||||
@@ -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<List<AuthDTO>>(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<AuthDTO>()) }
|
||||
|
||||
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<List<AuthDTO>>()
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,6 @@ fun DropdownList(
|
||||
Text(text = item)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
<string name="go_back">Ísť späť</string>
|
||||
<string name="token">Token</string>
|
||||
<string name="create_auth">Vytvoriť novú auth</string>
|
||||
<string name="bio">Biometrika</string>
|
||||
</resources>
|
||||
@@ -12,6 +12,7 @@
|
||||
<string name="token_key" translatable="false">token</string>
|
||||
<string name="auth_key" translatable="false">auth_id</string>
|
||||
<string name="create_auth">Create new auth</string>
|
||||
<string name="bio">Biometrics</string>
|
||||
<!-- Strings used for fragments for navigation -->
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user