1
1
package com.geode.launcher.preferences
2
2
3
3
import android.content.Context
4
+ import android.content.Intent
5
+ import androidx.activity.compose.LocalActivity
4
6
import androidx.compose.foundation.clickable
5
7
import androidx.compose.foundation.layout.Arrangement
6
8
import androidx.compose.foundation.layout.Box
@@ -19,6 +21,7 @@ import androidx.compose.foundation.verticalScroll
19
21
import androidx.compose.material.icons.Icons
20
22
import androidx.compose.material.icons.filled.Add
21
23
import androidx.compose.material.icons.filled.Clear
24
+ import androidx.compose.material.icons.filled.Delete
22
25
import androidx.compose.material3.AlertDialog
23
26
import androidx.compose.material3.Card
24
27
import androidx.compose.material3.Icon
@@ -32,9 +35,11 @@ import androidx.compose.material3.Text
32
35
import androidx.compose.material3.TextButton
33
36
import androidx.compose.runtime.Composable
34
37
import androidx.compose.runtime.CompositionLocalProvider
35
- import androidx.compose.runtime.compositionLocalOf
38
+ import androidx.compose.runtime.collectAsState
36
39
import androidx.compose.runtime.getValue
40
+ import androidx.compose.runtime.compositionLocalOf
37
41
import androidx.compose.runtime.mutableFloatStateOf
42
+ import androidx.compose.runtime.mutableStateListOf
38
43
import androidx.compose.runtime.mutableStateOf
39
44
import androidx.compose.runtime.remember
40
45
import androidx.compose.runtime.setValue
@@ -55,10 +60,13 @@ import com.geode.launcher.ui.theme.GeodeLauncherTheme
55
60
import com.geode.launcher.ui.theme.Typography
56
61
import com.geode.launcher.utils.LabelledText
57
62
import com.geode.launcher.utils.PreferenceUtils
63
+ import com.geode.launcher.utils.Profile
64
+ import com.geode.launcher.utils.ProfileManager
58
65
import kotlin.math.log10
59
66
import kotlin.math.max
60
67
import kotlin.math.min
61
68
import kotlin.math.roundToInt
69
+ import kotlin.system.exitProcess
62
70
63
71
64
72
fun toggleSetting (context : Context , preferenceKey : PreferenceUtils .Key ): Boolean {
@@ -556,26 +564,234 @@ internal val LocalSelectValue = compositionLocalOf<Any> { 0 }
556
564
internal val LocalSelectSetValue = staticCompositionLocalOf< (Any ) -> Unit > { {} }
557
565
558
566
@Composable
559
- fun <T > SelectOption (name : String , value : T ) {
567
+ fun <T > SelectOption (name : String , value : T , enabled : Boolean = true, leadingContent : @Composable ( Boolean ) -> Unit = {} ) {
560
568
val currentValue = LocalSelectValue .current
561
569
val setValue = LocalSelectSetValue .current
562
570
571
+ val isSelected = currentValue.equals(value)
572
+
563
573
// do not give the row or column padding!! it messes up the selection effect
564
574
Row (
565
575
verticalAlignment = Alignment .CenterVertically ,
566
576
modifier = Modifier
567
577
.fillMaxWidth()
568
578
.clickable(
569
579
onClick = { setValue(value as Any ) },
570
- role = Role .RadioButton
580
+ role = Role .RadioButton ,
581
+ enabled = enabled
571
582
)
572
583
.padding(horizontal = 12 .dp)
573
584
) {
574
- RadioButton (
575
- selected = currentValue.equals(value),
576
- onClick = { setValue(value as Any ) }
577
- )
578
- Text (name, style = Typography .bodyMedium)
585
+ Row (modifier = Modifier .weight(1.0f , true ), verticalAlignment = Alignment .CenterVertically ) {
586
+ RadioButton (
587
+ selected = isSelected,
588
+ onClick = { setValue(value as Any ) },
589
+ enabled = enabled
590
+ )
591
+ Text (name, style = Typography .bodyMedium)
592
+ }
593
+
594
+ leadingContent(isSelected)
595
+ }
596
+ }
597
+
598
+ @Composable
599
+ fun ProfileCreateDialog (onDismissRequest : () -> Unit ) {
600
+ var enteredValue by remember { mutableStateOf(" " ) }
601
+
602
+ val filename = enteredValue.take(16 )
603
+ .lowercase()
604
+ .map {
605
+ if (" qwertyuiopasdfghjklzxcvbnm1234567890-_." .contains(it))
606
+ it else ' _'
607
+ }
608
+ .joinToString(" " )
609
+
610
+
611
+ val context = LocalContext .current
612
+
613
+ val minimumReached = enteredValue.isEmpty() || filename.isEmpty()
614
+
615
+ val profileManager = ProfileManager .get(context)
616
+ val currentProfiles = remember {
617
+ profileManager.getProfiles().map { it.path }.toSet()
618
+ }
619
+
620
+ val isDuplicate = currentProfiles.contains(filename)
621
+
622
+ AlertDialog (
623
+ onDismissRequest = { onDismissRequest() },
624
+ title = {
625
+ Text (stringResource(R .string.preference_profiles_create))
626
+ },
627
+ text = {
628
+ Column {
629
+ Row (
630
+ verticalAlignment = Alignment .CenterVertically ,
631
+ modifier = Modifier .weight(1.0f , false )
632
+ ) {
633
+ OutlinedTextField (
634
+ value = enteredValue,
635
+ onValueChange = {
636
+ enteredValue = it
637
+ },
638
+ singleLine = true ,
639
+ keyboardOptions = KeyboardOptions (
640
+ keyboardType = KeyboardType .Text ,
641
+ ),
642
+ label = {
643
+ Text (stringResource(R .string.preference_profiles_create_name))
644
+ },
645
+ isError = isDuplicate
646
+ )
647
+ }
648
+
649
+ if (isDuplicate) {
650
+ Spacer (Modifier .size(8 .dp))
651
+
652
+ Text (stringResource(R .string.preference_profiles_create_duplicate))
653
+ } else if (! minimumReached) {
654
+ Spacer (Modifier .size(8 .dp))
655
+
656
+ Text (stringResource(R .string.preference_profiles_create_info, filename))
657
+ }
658
+ }
659
+ },
660
+ confirmButton = {
661
+ TextButton (
662
+ onClick = {
663
+ ProfileManager .get(context).storeProfile(Profile (filename, enteredValue))
664
+ onDismissRequest()
665
+ },
666
+ enabled = ! minimumReached && ! isDuplicate
667
+ ) {
668
+ Text (stringResource(R .string.preference_profiles_create_action))
669
+ }
670
+ },
671
+ dismissButton = {
672
+ TextButton (onClick = onDismissRequest) {
673
+ Text (stringResource(R .string.message_box_cancel))
674
+ }
675
+ },
676
+ )
677
+ }
678
+
679
+ @Composable
680
+ fun ProfileCreateCard () {
681
+ var showDialog by remember { mutableStateOf(false ) }
682
+
683
+ OptionsButton (
684
+ title = stringResource(R .string.preference_profiles_create),
685
+ icon = {
686
+ Icon (painterResource(R .drawable.icon_person_add), contentDescription = null )
687
+ }
688
+ ) {
689
+ showDialog = true
690
+ }
691
+
692
+ if (showDialog) {
693
+ ProfileCreateDialog (onDismissRequest = {
694
+ showDialog = false
695
+ })
696
+ }
697
+ }
698
+
699
+ @Composable
700
+ fun ProfileSelectCard () {
701
+ val context = LocalContext .current
702
+ val profileManager = ProfileManager .get(context)
703
+
704
+ val currentProfileId = remember {
705
+ profileManager.getCurrentProfile()
706
+ }
707
+
708
+ var currentProfileName = remember {
709
+ val savedProfile = currentProfileId
710
+ if (savedProfile == null ) {
711
+ " Default"
712
+ } else {
713
+ profileManager.getProfile(savedProfile)
714
+ ?.name ? : savedProfile
715
+ }
716
+ }
717
+
718
+ val clearedProfiles = remember { mutableStateListOf<String >() }
719
+
720
+ val profileList by profileManager.storedProfiles.collectAsState()
721
+
722
+ var showDialog by remember { mutableStateOf(false ) }
723
+
724
+ OptionsButton (
725
+ title = stringResource(R .string.preference_profiles_select),
726
+ description = stringResource(R .string.preference_profiles_current, currentProfileName)
727
+ ) {
728
+ showDialog = true
729
+ }
730
+
731
+ val activity = LocalActivity .current
732
+ if (showDialog) {
733
+ SelectDialog (
734
+ title = stringResource(R .string.preference_profiles_select),
735
+ onDismissRequest = {
736
+ showDialog = false
737
+ },
738
+ onSelect = {
739
+ showDialog = false
740
+
741
+ val selectedProfileId = it.takeIf { it.isNotEmpty() }
742
+
743
+ if (clearedProfiles.isNotEmpty()) {
744
+ profileManager.deleteProfiles(clearedProfiles)
745
+ }
746
+
747
+ if (currentProfileId != selectedProfileId) {
748
+ profileManager
749
+ .setCurrentProfile(selectedProfileId)
750
+
751
+ activity?.run {
752
+ packageManager.getLaunchIntentForPackage(packageName)?.also {
753
+ val mainIntent = Intent .makeRestartActivityTask(it.component)
754
+ startActivity(mainIntent)
755
+ exitProcess(0 )
756
+ }
757
+ }
758
+ }
759
+ },
760
+ initialValue = currentProfileId ? : " " ,
761
+ ) {
762
+ SelectOption (" Default" , " " )
763
+
764
+ profileList.forEach { profile ->
765
+ val path = profile.path
766
+
767
+ SelectOption (
768
+ stringResource(R .string.preference_profiles_select_value, profile.name, path),
769
+ path,
770
+ enabled = ! clearedProfiles.contains(path),
771
+ leadingContent = { selected ->
772
+ IconButton (onClick = {
773
+ if (clearedProfiles.contains(path)) {
774
+ clearedProfiles.remove(path)
775
+ } else {
776
+ clearedProfiles.add(path)
777
+ }
778
+ }, enabled = ! selected) {
779
+ if (clearedProfiles.contains(path)) {
780
+ Icon (
781
+ painterResource(R .drawable.icon_undo),
782
+ contentDescription = stringResource(R .string.preference_profiles_delete_undo_alt)
783
+ )
784
+ } else {
785
+ Icon (
786
+ Icons .Filled .Delete ,
787
+ contentDescription = stringResource(R .string.preference_profiles_delete_alt)
788
+ )
789
+ }
790
+ }
791
+ }
792
+ )
793
+ }
794
+ }
579
795
}
580
796
}
581
797
0 commit comments