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.interaction.MutableInteractionSource
27+ import androidx.compose.foundation.layout.Arrangement
28+ import androidx.compose.foundation.layout.Box
29+ import androidx.compose.foundation.layout.Column
30+ import androidx.compose.foundation.layout.Row
31+ import androidx.compose.foundation.layout.fillMaxWidth
32+ import androidx.compose.foundation.layout.padding
33+ import androidx.compose.foundation.shape.RoundedCornerShape
34+ import androidx.compose.material3.HorizontalDivider
35+ import androidx.compose.material3.Text
36+ import androidx.compose.runtime.Composable
37+ import androidx.compose.runtime.mutableStateOf
38+ import androidx.compose.runtime.remember
39+ import androidx.compose.ui.Modifier
40+ import androidx.compose.ui.draw.clip
41+ import androidx.compose.ui.graphics.Color
42+ import androidx.compose.ui.res.stringResource
43+ import androidx.compose.ui.layout.onGloballyPositioned
44+ import androidx.compose.ui.platform.LocalDensity
45+ import androidx.compose.ui.text.TextStyle
46+ import androidx.compose.ui.text.font.Font
47+ import androidx.compose.ui.text.font.FontFamily
48+ import androidx.compose.ui.text.font.FontWeight
49+ import androidx.compose.ui.unit.Dp
50+ import androidx.compose.ui.unit.dp
51+ import androidx.compose.ui.unit.sp
52+ import androidx.navigation.NavController
53+ import me.kavishdevar.librepods.R
54+ import me.kavishdevar.librepods.composables.NavigationButton
55+ import me.kavishdevar.librepods.services.ServiceManager
56+ import kotlin.io.encoding.ExperimentalEncodingApi
57+
58+ @Composable
59+ fun AboutCard (navController : NavController ) {
60+ val isDarkTheme = isSystemInDarkTheme()
61+ val textColor = if (isDarkTheme) Color .White else Color .Black
62+ val service = ServiceManager .getService()
63+ if (service == null ) return
64+ val airpodsInstance = service.airpodsInstance
65+ if (airpodsInstance == null ) return
66+ val backgroundColor = if (isDarkTheme) Color (0xFF1C1C1E ) else Color (0xFFFFFFFF )
67+
68+ Box (
69+ modifier = Modifier
70+ .background(if (isDarkTheme) Color (0xFF000000 ) else Color (0xFFF2F2F7 ))
71+ .padding(horizontal = 16 .dp, vertical = 4 .dp)
72+ ){
73+ Text (
74+ text = stringResource(R .string.about),
75+ style = TextStyle (
76+ fontSize = 14 .sp,
77+ fontWeight = FontWeight .Bold ,
78+ color = textColor.copy(alpha = 0.6f )
79+ )
80+ )
81+ }
82+
83+ val rowHeight = remember { mutableStateOf(0 .dp) }
84+ val density = LocalDensity .current
85+
86+ Column (
87+ modifier = Modifier
88+ .clip(RoundedCornerShape (28 .dp))
89+ .fillMaxWidth()
90+ .background(backgroundColor, RoundedCornerShape (28 .dp))
91+ .padding(top = 2 .dp)
92+ ) {
93+ Row (
94+ modifier = Modifier
95+ .fillMaxWidth()
96+ .padding(16 .dp)
97+ .onGloballyPositioned { coordinates ->
98+ rowHeight.value = with (density) { coordinates.size.height.toDp() }
99+ },
100+ horizontalArrangement = Arrangement .SpaceBetween ,
101+ ) {
102+ Text (
103+ text = stringResource(R .string.model_name),
104+ style = TextStyle (
105+ fontSize = 16 .sp,
106+ color = textColor,
107+ fontFamily = FontFamily (Font (R .font.sf_pro))
108+ )
109+ )
110+ Text (
111+ text = airpodsInstance.model.displayName,
112+ style = TextStyle (
113+ fontSize = 16 .sp,
114+ color = if (isDarkTheme) Color .White .copy(alpha = 0.6f ) else Color .Black .copy(alpha = 0.8f ),
115+ fontFamily = FontFamily (Font (R .font.sf_pro))
116+ )
117+ )
118+ }
119+ HorizontalDivider (
120+ thickness = 1 .dp,
121+ color = Color (0x40888888 ),
122+ modifier = Modifier
123+ .padding(horizontal = 12 .dp)
124+ )
125+ Row (
126+ modifier = Modifier
127+ .fillMaxWidth()
128+ .padding(16 .dp),
129+ horizontalArrangement = Arrangement .SpaceBetween ,
130+ ) {
131+ Text (
132+ text = stringResource(R .string.model_name),
133+ style = TextStyle (
134+ fontSize = 16 .sp,
135+ color = textColor,
136+ fontFamily = FontFamily (Font (R .font.sf_pro))
137+ )
138+ )
139+ Text (
140+ text = airpodsInstance.actualModelNumber,
141+ style = TextStyle (
142+ fontSize = 16 .sp,
143+ color = if (isDarkTheme) Color .White .copy(alpha = 0.6f ) else Color .Black .copy(alpha = 0.8f ),
144+ fontFamily = FontFamily (Font (R .font.sf_pro))
145+ )
146+ )
147+ }
148+ HorizontalDivider (
149+ thickness = 1 .dp,
150+ color = Color (0x40888888 ),
151+ modifier = Modifier
152+ .padding(horizontal = 12 .dp)
153+ )
154+ val serialNumbers = listOf (
155+ airpodsInstance.serialNumber? : " " ,
156+ " ${airpodsInstance.leftSerialNumber} " ,
157+ " ${airpodsInstance.rightSerialNumber} "
158+ )
159+ val serialNumber = remember { mutableStateOf(0 ) }
160+ Row (
161+ modifier = Modifier
162+ .fillMaxWidth()
163+ .padding(16 .dp),
164+ horizontalArrangement = Arrangement .SpaceBetween ,
165+ ) {
166+ Text (
167+ text = stringResource(R .string.serial_number),
168+ style = TextStyle (
169+ fontSize = 16 .sp,
170+ color = textColor,
171+ fontFamily = FontFamily (Font (R .font.sf_pro))
172+ ),
173+ )
174+ Text (
175+ text = serialNumbers[serialNumber.value],
176+ style = TextStyle (
177+ fontSize = 16 .sp,
178+ color = if (isDarkTheme) Color .White .copy(alpha = 0.6f ) else Color .Black .copy(alpha = 0.8f ),
179+ fontFamily = FontFamily (Font (R .font.sf_pro))
180+ ),
181+ modifier = Modifier
182+ .clickable(
183+ interactionSource = remember { MutableInteractionSource () },
184+ indication = null
185+ ) {
186+ serialNumber.value = (serialNumber.value + 1 ) % serialNumbers.size
187+ }
188+ )
189+ }
190+ HorizontalDivider (
191+ thickness = 1 .dp,
192+ color = Color (0x40888888 ),
193+ modifier = Modifier
194+ .padding(horizontal = 12 .dp)
195+ )
196+ NavigationButton (
197+ to = " version_info" ,
198+ navController = navController,
199+ name = stringResource(R .string.version),
200+ currentState = airpodsInstance.version3,
201+ independent = false ,
202+ height = rowHeight.value + 32 .dp
203+ )
204+ }
205+ }
0 commit comments