mia/src/main/kotlin/ui/Profiles.kt

296 lines
11 KiB
Kotlin
Raw Normal View History

2024-10-26 18:07:40 +00:00
package ui
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
2024-10-28 00:46:18 +00:00
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
2024-10-26 18:07:40 +00:00
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
2024-10-28 00:46:18 +00:00
import androidx.compose.foundation.rememberScrollState
2024-10-26 18:07:40 +00:00
import androidx.compose.foundation.shape.CircleShape
2024-10-28 00:46:18 +00:00
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.AlertDialog
import androidx.compose.material.Button
import androidx.compose.material.Icon
2024-10-26 18:07:40 +00:00
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
2024-10-28 00:46:18 +00:00
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Create
2024-10-26 18:07:40 +00:00
import androidx.compose.runtime.Composable
2024-10-28 00:46:18 +00:00
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
2024-10-26 18:07:40 +00:00
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
2024-10-28 00:46:18 +00:00
import androidx.compose.runtime.saveable.rememberSaveable
2024-10-26 18:07:40 +00:00
import androidx.compose.runtime.setValue
2024-10-28 00:46:18 +00:00
import androidx.compose.ui.Alignment
2024-10-26 18:07:40 +00:00
import androidx.compose.ui.Modifier
2024-10-28 00:46:18 +00:00
import androidx.compose.ui.graphics.Color
2024-10-26 18:07:40 +00:00
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
2024-10-28 00:46:18 +00:00
import com.fasterxml.jackson.core.JsonEncoding
import kotlinx.coroutines.delay
2024-10-26 18:07:40 +00:00
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
2024-10-28 00:46:18 +00:00
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
2024-10-26 18:07:40 +00:00
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
2024-10-28 00:46:18 +00:00
import transparentButton
2024-10-26 18:07:40 +00:00
import ui.Capture.Companion.getCaptureDuration
import ui.screens.ProfileDetails
import ui.screens.ProfilesManager
2024-10-28 00:46:18 +00:00
import ui.screens.ProfilesManager.Companion.profileListItem
2024-10-26 18:07:40 +00:00
import ui.screens.Settings
2024-10-28 00:46:18 +00:00
import java.io.File
2024-10-26 18:07:40 +00:00
import java.io.FileInputStream
2024-10-28 00:46:18 +00:00
import java.io.FileNotFoundException
2024-10-26 18:07:40 +00:00
import java.io.FileOutputStream
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.io.path.absolutePathString
class Profiles {
companion object {
2024-10-28 00:46:18 +00:00
val activeProfile = mutableStateOf(getCurrentProfile())
val profiles = mutableStateOf(getProfiles())
fun getCurrentProfile() = load(Settings.userSettings.getProperty("current_profile"))
fun getProfileLocation(profile: String?) = Paths.get("profiles/$profile.profile").absolutePathString()
fun getProfiles(): MutableList<Profile> {
val files = File(Paths.get("profiles/").absolutePathString()).listFiles()
files.ifEmpty {
Settings.userSettings.remove("current_profile")
}
return files.map { file ->
load(file)
} as MutableList<Profile>
}
2024-10-26 18:07:40 +00:00
@OptIn(ExperimentalFoundationApi::class)
@Composable
2024-10-28 00:46:18 +00:00
fun profileList() {
var showActivateDialog by rememberSaveable { mutableStateOf(false) }
var showDeleteDialog by rememberSaveable { mutableStateOf(false) }
var selectedProfile by rememberSaveable { mutableStateOf<Profile>(Profile()) }
val navigator = LocalNavigator.currentOrThrow
profiles.value = getProfiles()
Column(
modifier = Modifier
.padding(horizontal = 25.dp)
.padding(top = 10.dp, bottom = 50.dp)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.End,
) {
profileListItem(
modifier = Modifier
.background(MaterialTheme.colors.onBackground.copy(alpha = 0.05f), CircleShape),
profileName = "Profile Name",
programName = "Program Name",
aiName = "AI Name"
)
var count = 1
profiles.value.forEach { profile ->
val backgroundColor = if (count % 2 == 0) MaterialTheme.colors.onBackground.copy(alpha = 0.05f) else MaterialTheme.colors.background
profileListItem(
modifier = Modifier
.background(backgroundColor, CircleShape)
.combinedClickable(
onClick = {
navigator.push(ProfileDetails(profile))
},
onDoubleClick = {
selectedProfile = profile
showActivateDialog = true
},
onLongClick = {
selectedProfile = profile
showDeleteDialog = true
},
),
profileName = profile.name,
programName = profile.programName,
aiName = profile.aiName,
)
count++
}
if (showActivateDialog) {
profileDialog(
title = "Confirm Activation",
text = "Activate ${selectedProfile.name}?",
confirmClick = {
activeProfile.value = selectedProfile
Settings.userSettings.setProperty("current_profile", selectedProfile.name)
Settings.userSettings.save()
showActivateDialog = false
},
cancelClick = {
showActivateDialog = false
},
)
}
if (showDeleteDialog) {
profileDialog(
title = "Confirm Deletion",
text = "Delete ${selectedProfile.name}?",
confirmClick = {
File(getProfileLocation(selectedProfile.name)).delete()
if (activeProfile.value == selectedProfile) {
Settings.userSettings.remove("current_profile")
Settings.userSettings.save()
activeProfile.value = getCurrentProfile()
}
showDeleteDialog = false
},
cancelClick = {
showDeleteDialog = false
}
)
}
}
}
@Composable
fun profileDialog(title: String, text: String, confirmClick: () -> Unit, cancelClick: () -> Unit) {
AlertDialog(
title = { Text(text = title) },
text = { Text(text = text) },
onDismissRequest = {
cancelClick
},
confirmButton = {
Button(
onClick = confirmClick
) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = "Accept"
)
}
},
dismissButton = {
Button(
onClick = cancelClick
) {
Icon(
imageVector = Icons.Filled.Cancel,
contentDescription = "Cancel"
)
}
}
)
}
@Composable
fun createProfile(modifier: Modifier) {
val navigator = LocalNavigator.currentOrThrow
2024-10-26 18:07:40 +00:00
2024-10-28 00:46:18 +00:00
Box(
modifier = modifier
.background(MaterialTheme.colors.primary, CircleShape)
) {
transparentButton(
modifier = Modifier,
icon = Icons.Filled.Create,
contentDescription = "Create new profile",
onClick = {
navigator.push(ProfileDetails(null))
},
)
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun currentProfile(modifier: Modifier) {
2024-10-26 18:07:40 +00:00
val navigator = LocalNavigator.currentOrThrow
2024-10-28 00:46:18 +00:00
2024-10-26 18:07:40 +00:00
Text(
modifier = modifier
.background(MaterialTheme.colors.primary, CircleShape)
.padding(5.dp)
.width(150.dp)
.combinedClickable(
2024-10-28 00:46:18 +00:00
onClick = { navigator.push(ProfileDetails(activeProfile.value)) },
onDoubleClick = {
if (navigator.lastItem !is ProfilesManager) {
navigator.push(ProfilesManager())
}
}
),
color = MaterialTheme.colors.onPrimary,
textAlign = TextAlign.Center,
text = activeProfile.value.name
2024-10-26 18:07:40 +00:00
)
}
2024-10-28 00:46:18 +00:00
@Serializable
2024-10-26 18:07:40 +00:00
data class Profile(
2024-10-28 00:46:18 +00:00
val name: String = "{NO NAME}",
2024-10-26 18:07:40 +00:00
@SerialName("ai_name")
2024-10-28 00:46:18 +00:00
val aiName: String = "{NO NAME}",
2024-10-26 18:07:40 +00:00
@SerialName("program_name")
2024-10-28 00:46:18 +00:00
val programName: String = "{NO NAME}",
@SerialName("program_path")
val programPath: String = "{NO NAME}",
@SerialName("commands")
val commands: List<Command>? = null,
2024-10-26 18:07:40 +00:00
)
2024-10-28 00:46:18 +00:00
@Serializable
2024-10-26 18:07:40 +00:00
data class Command(
@SerialName("wording_Variants")
2024-10-28 00:46:18 +00:00
val wordingVariants: List<String>? = null,
val triggers: List<String>? = null
2024-10-26 18:07:40 +00:00
)
@OptIn(ExperimentalSerializationApi::class)
2024-10-28 00:46:18 +00:00
fun load(profile: File?): Profile {
var file: Profile
try {
val stream = FileInputStream(profile!!)
file = Json.decodeFromStream<Profile>(stream).copy(name = profile.nameWithoutExtension)
2024-10-26 18:07:40 +00:00
stream.close()
}
2024-10-28 00:46:18 +00:00
catch (ne: FileNotFoundException) {
file = Profile(name = "Not Selected")
}
catch (je: SerializationException) {
file = Profile(name = profile!!.nameWithoutExtension)
2024-10-26 18:07:40 +00:00
}
2024-10-28 00:46:18 +00:00
return file
2024-10-26 18:07:40 +00:00
//TODO: Add logging messages
}
2024-10-28 00:46:18 +00:00
fun load(profile: String?): Profile = load(File(getProfileLocation(profile)))
2024-10-26 18:07:40 +00:00
@OptIn(ExperimentalSerializationApi::class)
fun save(profile: Profile) {
val stream = FileOutputStream(getProfileLocation(profile.name))
Json.encodeToStream(profile, stream)
stream.flush()
stream.close()
}
}
}