|
1 | | -/* |
2 | | - * LibrePods - AirPods liberated from Apple’s ecosystem |
3 | | - * |
4 | | - * Copyright (C) 2025 LibrePods contributors |
5 | | - * |
6 | | - * This program is free software: you can redistribute it and/or modify |
7 | | - * it under the terms of the GNU Affero General Public License as published |
8 | | - * by the Free Software Foundation, either version 3 of the License. |
9 | | - * |
10 | | - * This program is distributed in the hope that it will be useful, |
11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | - * GNU Affero General Public License for more details. |
14 | | - * |
15 | | - * You should have received a copy of the GNU Affero General Public License |
16 | | - * along with this program. If not, see <https://www.gnu.org/licenses/>. |
17 | | - */ |
18 | | - |
19 | | -@file:OptIn(ExperimentalEncodingApi::class) |
20 | | - |
21 | | -package me.kavishdevar.librepods.composables |
22 | | - |
23 | | -import androidx.compose.foundation.background |
24 | | -import androidx.compose.foundation.clickable |
25 | | -import androidx.compose.foundation.isSystemInDarkTheme |
26 | | -import androidx.compose.foundation.layout.Box |
27 | | -import androidx.compose.foundation.layout.Column |
28 | | -import androidx.compose.foundation.layout.fillMaxWidth |
29 | | -import androidx.compose.foundation.layout.padding |
30 | | -import androidx.compose.foundation.shape.RoundedCornerShape |
31 | | -import androidx.compose.material3.DropdownMenu |
32 | | -import androidx.compose.material3.DropdownMenuItem |
33 | | -import androidx.compose.material3.Text |
34 | | -import androidx.compose.runtime.Composable |
35 | | -import androidx.compose.runtime.getValue |
36 | | -import androidx.compose.runtime.mutableStateOf |
37 | | -import androidx.compose.runtime.remember |
38 | | -import androidx.compose.runtime.setValue |
39 | | -import androidx.compose.ui.Modifier |
40 | | -import androidx.compose.ui.graphics.Color |
41 | | -import androidx.compose.ui.res.stringResource |
42 | | -import androidx.compose.ui.text.TextStyle |
43 | | -import androidx.compose.ui.text.font.FontWeight |
44 | | -import androidx.compose.ui.tooling.preview.Preview |
45 | | -import androidx.compose.ui.unit.dp |
46 | | -import androidx.compose.ui.unit.sp |
47 | | -import me.kavishdevar.librepods.R |
48 | | -import me.kavishdevar.librepods.services.ServiceManager |
49 | | -import me.kavishdevar.librepods.utils.AACPManager |
50 | | -import kotlin.io.encoding.ExperimentalEncodingApi |
51 | | - |
52 | | -@Composable |
53 | | -fun AccessibilitySettings() { |
54 | | - val isDarkTheme = isSystemInDarkTheme() |
55 | | - val textColor = if (isDarkTheme) Color.White else Color.Black |
56 | | - val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) |
57 | | - val service = ServiceManager.getService()!! |
58 | | - Text( |
59 | | - text = stringResource(R.string.accessibility).uppercase(), |
60 | | - style = TextStyle( |
61 | | - fontSize = 14.sp, |
62 | | - fontWeight = FontWeight.Light, |
63 | | - color = textColor.copy(alpha = 0.6f) |
64 | | - ), |
65 | | - modifier = Modifier.padding(8.dp, bottom = 2.dp) |
66 | | - ) |
67 | | - |
68 | | - Column( |
69 | | - modifier = Modifier |
70 | | - .fillMaxWidth() |
71 | | - .background(backgroundColor, RoundedCornerShape(14.dp)) |
72 | | - .padding(top = 2.dp) |
73 | | - ) { |
74 | | - Column( |
75 | | - modifier = Modifier |
76 | | - .fillMaxWidth() |
77 | | - .padding(12.dp) |
78 | | - ) { |
79 | | - Text( |
80 | | - text = stringResource(R.string.tone_volume), |
81 | | - modifier = Modifier |
82 | | - .padding(end = 8.dp, bottom = 2.dp, start = 2.dp) |
83 | | - .fillMaxWidth(), |
84 | | - style = TextStyle( |
85 | | - fontSize = 16.sp, |
86 | | - fontWeight = FontWeight.Medium, |
87 | | - color = textColor |
88 | | - ) |
89 | | - ) |
90 | | - |
91 | | - ToneVolumeSlider() |
92 | | - } |
93 | | - |
94 | | - val pressSpeedOptions = mapOf( |
95 | | - 0.toByte() to "Default", |
96 | | - 1.toByte() to "Slower", |
97 | | - 2.toByte() to "Slowest" |
98 | | - ) |
99 | | - val selectedPressSpeedValue = service.aacpManager.controlCommandStatusList.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.DOUBLE_CLICK_INTERVAL }?.value?.takeIf { it.isNotEmpty() }?.get(0) |
100 | | - var selectedPressSpeed by remember { mutableStateOf(pressSpeedOptions[selectedPressSpeedValue] ?: pressSpeedOptions[0]) } |
101 | | - DropdownMenuComponent( |
102 | | - label = "Press Speed", |
103 | | - options = pressSpeedOptions.values.toList(), |
104 | | - selectedOption = selectedPressSpeed.toString(), |
105 | | - onOptionSelected = { newValue -> |
106 | | - selectedPressSpeed = newValue |
107 | | - service.aacpManager.sendControlCommand( |
108 | | - identifier = AACPManager.Companion.ControlCommandIdentifiers.DOUBLE_CLICK_INTERVAL.value, |
109 | | - value = pressSpeedOptions.filterValues { it == newValue }.keys.firstOrNull() ?: 0.toByte() |
110 | | - ) |
111 | | - }, |
112 | | - textColor = textColor |
113 | | - ) |
114 | | - |
115 | | - val pressAndHoldDurationOptions = mapOf( |
116 | | - 0.toByte() to "Default", |
117 | | - 1.toByte() to "Slower", |
118 | | - 2.toByte() to "Slowest" |
119 | | - ) |
120 | | - |
121 | | - val selectedPressAndHoldDurationValue = service.aacpManager.controlCommandStatusList.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.CLICK_HOLD_INTERVAL }?.value?.takeIf { it.isNotEmpty() }?.get(0) |
122 | | - var selectedPressAndHoldDuration by remember { mutableStateOf(pressAndHoldDurationOptions[selectedPressAndHoldDurationValue] ?: pressAndHoldDurationOptions[0]) } |
123 | | - DropdownMenuComponent( |
124 | | - label = "Press and Hold Duration", |
125 | | - options = pressAndHoldDurationOptions.values.toList(), |
126 | | - selectedOption = selectedPressAndHoldDuration.toString(), |
127 | | - onOptionSelected = { newValue -> |
128 | | - selectedPressAndHoldDuration = newValue |
129 | | - service.aacpManager.sendControlCommand( |
130 | | - identifier = AACPManager.Companion.ControlCommandIdentifiers.CLICK_HOLD_INTERVAL.value, |
131 | | - value = pressAndHoldDurationOptions.filterValues { it == newValue }.keys.firstOrNull() ?: 0.toByte() |
132 | | - ) |
133 | | - }, |
134 | | - textColor = textColor |
135 | | - ) |
136 | | - |
137 | | - val volumeSwipeSpeedOptions = mapOf( |
138 | | - 1.toByte() to "Default", |
139 | | - 2.toByte() to "Longer", |
140 | | - 3.toByte() to "Longest" |
141 | | - ) |
142 | | - val selectedVolumeSwipeSpeedValue = service.aacpManager.controlCommandStatusList.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_INTERVAL }?.value?.takeIf { it.isNotEmpty() }?.get(0) |
143 | | - var selectedVolumeSwipeSpeed by remember { mutableStateOf(volumeSwipeSpeedOptions[selectedVolumeSwipeSpeedValue] ?: volumeSwipeSpeedOptions[1]) } |
144 | | - DropdownMenuComponent( |
145 | | - label = "Volume Swipe Speed", |
146 | | - options = volumeSwipeSpeedOptions.values.toList(), |
147 | | - selectedOption = selectedVolumeSwipeSpeed.toString(), |
148 | | - onOptionSelected = { newValue -> |
149 | | - selectedVolumeSwipeSpeed = newValue |
150 | | - service.aacpManager.sendControlCommand( |
151 | | - identifier = AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_INTERVAL.value, |
152 | | - value = volumeSwipeSpeedOptions.filterValues { it == newValue }.keys.firstOrNull() ?: 1.toByte() |
153 | | - ) |
154 | | - }, |
155 | | - textColor = textColor |
156 | | - ) |
157 | | - } |
158 | | -} |
159 | | - |
160 | | -@Composable |
161 | | -fun DropdownMenuComponent( |
162 | | - label: String, |
163 | | - options: List<String>, |
164 | | - selectedOption: String, |
165 | | - onOptionSelected: (String) -> Unit, |
166 | | - textColor: Color |
167 | | -) { |
168 | | - var expanded by remember { mutableStateOf(false) } |
169 | | - |
170 | | - Column ( |
171 | | - modifier = Modifier |
172 | | - .fillMaxWidth() |
173 | | - .padding(horizontal = 12.dp) |
174 | | - ) { |
175 | | - Text( |
176 | | - text = label, |
177 | | - style = TextStyle( |
178 | | - fontSize = 16.sp, |
179 | | - fontWeight = FontWeight.Medium, |
180 | | - color = textColor |
181 | | - ) |
182 | | - ) |
183 | | - |
184 | | - Box( |
185 | | - modifier = Modifier |
186 | | - .fillMaxWidth() |
187 | | - .clickable { expanded = true } |
188 | | - .padding(8.dp) |
189 | | - ) { |
190 | | - Text( |
191 | | - text = selectedOption, |
192 | | - modifier = Modifier.padding(16.dp), |
193 | | - color = textColor |
194 | | - ) |
195 | | - } |
196 | | - |
197 | | - DropdownMenu( |
198 | | - expanded = expanded, |
199 | | - onDismissRequest = { expanded = false } |
200 | | - ) { |
201 | | - options.forEach { option -> |
202 | | - DropdownMenuItem( |
203 | | - onClick = { |
204 | | - onOptionSelected(option) |
205 | | - expanded = false |
206 | | - }, |
207 | | - text = { Text(text = option) } |
208 | | - ) |
209 | | - } |
210 | | - } |
211 | | - } |
212 | | -} |
213 | | - |
214 | | -@Preview |
215 | | -@Composable |
216 | | -fun AccessibilitySettingsPreview() { |
217 | | - AccessibilitySettings() |
218 | | -} |
0 commit comments