1
0

224 lines
8.0 KiB
Kotlin
Raw Normal View History

2025-04-21 17:44:40 +03:00
/*
* Created by sweetbread
* Copyright (c) 2025. All rights reserved.
*/
2025-04-21 15:48:45 +03:00
package ru.risdeveau.geotracker
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.Manifest.permission.POST_NOTIFICATIONS
2025-05-03 21:34:18 +03:00
import android.content.Intent
2025-05-03 17:19:54 +03:00
import android.content.pm.PackageManager
2025-05-03 21:34:18 +03:00
import android.os.Build
2025-04-21 15:48:45 +03:00
import android.os.Bundle
2025-05-03 21:34:18 +03:00
import android.util.Log
2025-04-21 15:48:45 +03:00
import androidx.activity.ComponentActivity
2025-05-03 17:19:54 +03:00
import androidx.activity.compose.rememberLauncherForActivityResult
2025-04-21 15:48:45 +03:00
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
2025-05-03 17:19:54 +03:00
import androidx.activity.result.contract.ActivityResultContracts
2025-04-21 15:48:45 +03:00
import androidx.compose.foundation.layout.Box
2025-04-21 22:32:36 +03:00
import androidx.compose.foundation.layout.Column
2025-04-21 15:48:45 +03:00
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
2025-05-03 17:19:54 +03:00
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Done
import androidx.compose.material3.Button
2025-04-21 17:44:40 +03:00
import androidx.compose.material3.CircularProgressIndicator
2025-05-03 17:19:54 +03:00
import androidx.compose.material3.Icon
2025-04-22 03:11:25 +03:00
import androidx.compose.material3.MaterialTheme
2025-04-21 17:44:40 +03:00
import androidx.compose.material3.OutlinedTextField
2025-04-21 15:48:45 +03:00
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
2025-04-21 17:44:40 +03:00
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
2025-04-21 15:48:45 +03:00
import androidx.compose.ui.Modifier
2025-05-03 17:19:54 +03:00
import androidx.compose.ui.platform.LocalContext
2025-04-22 03:11:25 +03:00
import androidx.compose.ui.text.TextStyle
2025-05-03 17:19:54 +03:00
import androidx.core.content.ContextCompat
import kotlinx.coroutines.delay
2025-04-21 17:44:40 +03:00
import kotlinx.coroutines.launch
2025-04-21 15:48:45 +03:00
import ru.risdeveau.geotracker.ui.theme.GeoTrackerTheme
2025-05-03 17:19:54 +03:00
import splitties.experimental.ExperimentalSplittiesApi
import splitties.init.appCtx
2025-04-22 03:11:25 +03:00
import splitties.resources.appStr
2025-04-21 15:48:45 +03:00
class MainActivity : ComponentActivity() {
2025-05-03 21:34:18 +03:00
2025-04-21 15:48:45 +03:00
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
2025-05-03 21:34:18 +03:00
2025-04-21 15:48:45 +03:00
enableEdgeToEdge()
setContent {
GeoTrackerTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
2025-04-21 17:44:40 +03:00
Box(
Modifier
.padding(innerPadding)
.fillMaxSize()) {
2025-04-21 22:32:36 +03:00
var screen by remember { mutableStateOf<Screen>(Screen.Loading) }
2025-04-21 15:48:45 +03:00
2025-04-21 22:32:36 +03:00
when (screen) {
2025-05-03 17:19:54 +03:00
Screen.Main -> {
2025-05-03 21:34:18 +03:00
LaunchedEffect(Unit) {
Log.d("Thread", "Starting...")
startLocationService()
Log.d("Thread", "Started")
}
2025-05-03 17:19:54 +03:00
Text("Hello world")
}
2025-04-21 22:32:36 +03:00
Screen.Settings -> {
2025-05-03 17:19:54 +03:00
Settings(Modifier.align(Alignment.Center)) {
screen = Screen.Main
}
2025-04-21 22:32:36 +03:00
}
Screen.Loading -> {
var loading by remember { mutableStateOf(true) }
if (loading) {
CircularProgressIndicator(Modifier.align(Alignment.Center))
LaunchedEffect(true) {
launch {
screen = if (
health(SettingsPreferences.url)
&& hasPermission(ACCESS_FINE_LOCATION)
&& (
(Build.VERSION.SDK_INT < 33)
|| hasPermission(POST_NOTIFICATIONS)
)
)
2025-04-21 22:32:36 +03:00
Screen.Main
else
Screen.Settings
}
2025-04-21 17:44:40 +03:00
}
}
}
}
2025-04-21 15:48:45 +03:00
}
}
}
}
}
}
2025-04-21 17:44:40 +03:00
sealed class Screen {
object Main : Screen()
object Settings : Screen()
2025-04-21 22:32:36 +03:00
object Loading : Screen()
2025-04-21 15:48:45 +03:00
}
2025-05-03 17:19:54 +03:00
@OptIn(ExperimentalSplittiesApi::class)
2025-04-21 15:48:45 +03:00
@Composable
2025-05-03 17:19:54 +03:00
fun Settings(modifier: Modifier = Modifier, onConfirm: () -> Unit) {
2025-05-03 21:34:18 +03:00
var username by remember { mutableStateOf(SettingsPreferences.username) }
var url by remember { mutableStateOf(SettingsPreferences.url) }
var urlIsValid by remember { mutableStateOf(false) }
var loading by remember { mutableStateOf(false) }
var fineLoc by remember { mutableStateOf(hasPermission(ACCESS_FINE_LOCATION)) }
var notifications by remember { mutableStateOf(
if (Build.VERSION.SDK_INT >= 33) {
hasPermission(POST_NOTIFICATIONS)
} else true
) }
val hasPerms = fineLoc && notifications
2025-04-21 17:44:40 +03:00
LaunchedEffect(url) {
if (url.isNotEmpty()) {
delay(1000)
loading = true
urlIsValid = health(url)
loading = false
}
}
2025-04-21 17:44:40 +03:00
Column (modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
2025-04-21 17:44:40 +03:00
OutlinedTextField(
value = username,
onValueChange = { username = it },
2025-04-22 03:11:25 +03:00
label = { Text(appStr(R.string.username)) }
2025-04-21 17:44:40 +03:00
)
OutlinedTextField(
value = url,
onValueChange = { url = it },
2025-05-03 17:19:54 +03:00
placeholder = {
Text("https://geo.example.com",
style = TextStyle(color = MaterialTheme.colorScheme.onSurfaceVariant)
)
},
label = { Text(appStr(R.string.server_url)) },
2025-05-03 17:19:54 +03:00
trailingIcon = {
if (loading) CircularProgressIndicator()
else if (urlIsValid) Icon(Icons.Outlined.Done, "Done")
}
2025-04-21 17:44:40 +03:00
)
GetPermission(ACCESS_FINE_LOCATION) { fineLoc = true; }
if (Build.VERSION.SDK_INT >= 33)
GetPermission(POST_NOTIFICATIONS) { notifications = true; }
2025-05-03 17:19:54 +03:00
Button({
SettingsPreferences.username = username.trim()
SettingsPreferences.url = url
onConfirm()
}, enabled = urlIsValid
&& !loading
&& username.trim().isNotEmpty()
&& hasPerms
2025-05-03 17:19:54 +03:00
) {
Text(appStr(R.string.apply))
}
2025-04-21 15:48:45 +03:00
}
2025-05-03 17:19:54 +03:00
}
@Composable
fun GetPermission(permission: String, onSuccess: () -> Unit) {
2025-05-03 17:19:54 +03:00
val context = LocalContext.current
var hasPerm by remember { mutableStateOf(
ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
)}
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted -> hasPerm = granted }
Button(
onClick = { launcher.launch(permission) },
enabled = !hasPerm
) {
if (hasPerm) {
onSuccess()
2025-05-03 17:19:54 +03:00
Icon(Icons.Outlined.Done, "Done")
Text("Разрешение получено")
} else {
Text("Получить разрешение")
2025-05-03 17:19:54 +03:00
}
}
}
fun hasPermission(permission: String): Boolean {
2025-05-03 17:19:54 +03:00
return ContextCompat.checkSelfPermission(
appCtx,
permission
2025-05-03 17:19:54 +03:00
) == PackageManager.PERMISSION_GRANTED
2025-05-03 21:34:18 +03:00
}
fun startLocationService() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
appCtx.startForegroundService(Intent(appCtx, LocationForegroundService::class.java))
Log.d("startLocationService", "startForegroundService")
} else {
appCtx.startService(Intent(appCtx, LocationForegroundService::class.java))
Log.d("startLocationService", "startService")
}
2025-04-21 15:48:45 +03:00
}