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