Display developer names & links on AboutScreen

This commit is contained in:
Hasinjato 2026-08-24 13:47:09 +03:00
parent 62b1f6784a
commit 0ff426b9ac

View file

@ -3,21 +3,31 @@ package mg.dot.feufaro.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.OpenInNew
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Code
import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.Description
import androidx.compose.material.icons.filled.Language
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
@ -27,12 +37,82 @@ import feufaro.composeapp.generated.resources.icon
import mg.dot.feufaro.di.rememberAppVersion
import mg.dot.feufaro.getPlatform
import org.jetbrains.compose.resources.painterResource
import java.awt.Desktop
import java.net.URI
@OptIn(ExperimentalMaterial3Api::class)
private var lastClickTime = 0L
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
fun AboutDialog(onDismiss: () -> Unit) {
val platform = getPlatform()
val appVersion = rememberAppVersion()
val uriHandler = LocalUriHandler.current
val developers = listOf(
"RANDIMBISOA Mahefa" to "https://github.com/DotMG",
"RAKOTOARIMALALA Hasinjato M." to "https://github.com/Hasinjato",
"RAMANAHADRAY Tsinjo Anjaratahina" to "https://github.com/Tiji-Tahina"
)
fun openWebUrl(url: String, uriHandler: UriHandler) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastClickTime < 1000) return
lastClickTime = currentTime
val isAndroid = getPlatform().name.startsWith("Android")
if (isAndroid) {
try {
uriHandler.openUri(url)
} catch (e: Exception) {
println("Erreur Android : ${e.message}")
}
return
}
// Linux / Windows
Thread {
try {
val os = System.getProperty("os.name")?.lowercase() ?: ""
when {
os.contains("win") -> {
ProcessBuilder("cmd.exe", "/c", "start", url.replace("&", "^&")).start()
}
else -> { // Linux / Unix
val candidates = listOf(
arrayOf("gio", "open", url), // GNOME / GTK
arrayOf("kioclient6", "exec", url), // KDE Plasma 6
arrayOf("kioclient5", "exec", url), // KDE Plasma 5
arrayOf("x-www-browser", url), // Debian / Ubuntu
arrayOf("firefox", url),
arrayOf("google-chrome", url),
arrayOf("zen-browser", url)
)
var success = false
for (cmd in candidates) {
try {
val process = ProcessBuilder(*cmd).start()
if (process.isAlive || process.waitFor() == 0) {
success = true
break
}
} catch (_: Exception) {
}
}
if (!success && Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
Desktop.getDesktop().browse(URI(url))
}
}
}
} catch (e: Exception) {
println("Erreur ouverture URL : ${e.message}")
}
}.start()
}
val devScrollState = rememberScrollState()
Dialog(
onDismissRequest = onDismiss,
properties = DialogProperties(
@ -56,67 +136,123 @@ fun AboutDialog(onDismiss: () -> Unit) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
.padding(10.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.TopEnd) {
IconButton(
onClick = onDismiss,
modifier = Modifier.size(32.dp)
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f, fill = false)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.TopEnd) {
IconButton(
onClick = onDismiss,
modifier = Modifier.size(32.dp)
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = "Fermer",
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Surface(
modifier = Modifier.size(100.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f),
tonalElevation = 4.dp
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = "Fermer",
tint = MaterialTheme.colorScheme.onSurfaceVariant
Image(
painter = painterResource(Res.drawable.icon),
contentDescription = "Logo Feufaro",
modifier = Modifier
.padding(12.dp)
.clip(CircleShape)
)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Feufaro",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.Bold),
color = MaterialTheme.colorScheme.primary
)
Surface(
color = MaterialTheme.colorScheme.secondaryContainer,
shape = RoundedCornerShape(12.dp),
modifier = Modifier.padding(top = 4.dp, bottom = 16.dp)
) {
Text(
text = appVersion,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer
)
}
Text(
text = "L'application pour lire, éditer et analyser vos partitions Solfa. Ecrivez et créez des harmonies, écoutez ses mélodies et organisez votre playlist en toute simplicité.",
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurfaceVariant,
lineHeight = 22.sp
)
Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.height(120.dp)
.clip(RoundedCornerShape(12.dp))
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f))
) {
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(devScrollState),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
developers.forEach { (name, gitUrl) ->
AssistChip(
onClick = { openWebUrl(gitUrl, uriHandler) },
label = {
AutoResizedText(
text = name,
style = MaterialTheme.typography.labelMedium,
minFontSize = 9.sp
)
},
leadingIcon = {
Icon(
imageVector = Icons.Default.Code,
contentDescription = null,
modifier = Modifier.size(16.dp)
)
},
trailingIcon = {
Icon(
imageVector = Icons.AutoMirrored.Default.OpenInNew,
contentDescription = null,
modifier = Modifier.size(14.dp),
tint = MaterialTheme.colorScheme.primary
)
},
shape = RoundedCornerShape(12.dp)
)
}
}
}
}
}
Surface(
modifier = Modifier.size(100.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f),
tonalElevation = 4.dp
) {
Image(
painter = painterResource(Res.drawable.icon),
contentDescription = "Logo Feufaro",
modifier = Modifier
.padding(12.dp)
.clip(CircleShape)
)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Feufaro",
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.Bold),
color = MaterialTheme.colorScheme.primary
)
Surface(
color = MaterialTheme.colorScheme.secondaryContainer,
shape = RoundedCornerShape(12.dp),
modifier = Modifier.padding(top = 4.dp, bottom = 16.dp)
) {
Text(
text = appVersion,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp),
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer
)
}
Text(
text = "L'application pour lire, éditer et analyser vos partitions Solfa. Ecrivez et créez des harmonies, écoutez ses mélodies et organisez votre playlist en toute simplicité.",
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurfaceVariant,
lineHeight = 22.sp
)
Spacer(modifier = Modifier.height(24.dp))
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f))
Spacer(modifier = Modifier.height(16.dp))
@ -124,9 +260,21 @@ fun AboutDialog(onDismiss: () -> Unit) {
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
InfoIconItem(icon = Icons.Default.Language, label = "Site Web", onClick = { })
InfoIconItem(icon = Icons.Default.Email, label = "Contact", onClick = { })
InfoIconItem(icon = Icons.Default.Code, label = "Licence", onClick = { })
InfoIconItem(
icon = Icons.Default.Language,
label = "Site Web",
onClick = { openWebUrl("https://feufar.ooo", uriHandler) }
)
InfoIconItem(
icon = Icons.Default.Code,
label = "Code Source",
onClick = { openWebUrl("https://code.homemade.mg/mahefa/feufaro", uriHandler) }
)
InfoIconItem(
icon = Icons.Default.Description,
label = "Licence",
onClick = { openWebUrl("https://www.gnu.org/licenses/gpl-3.0-standalone.html", uriHandler) }
)
}
Spacer(modifier = Modifier.height(24.dp))
@ -163,4 +311,36 @@ fun InfoIconItem(icon: ImageVector, label: String, onClick: () -> Unit) {
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@Composable
fun AutoResizedText(
text: String,
modifier: Modifier = Modifier,
style: TextStyle = LocalTextStyle.current,
color: Color = Color.Unspecified,
minFontSize: TextUnit = 9.sp
) {
var resizedTextStyle by remember(text) { mutableStateOf(style) }
var readyToDraw by remember(text) { mutableStateOf(false) }
Text(
text = text,
color = color,
modifier = modifier.drawWithContent {
if (readyToDraw) drawContent()
},
style = resizedTextStyle,
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Clip,
onTextLayout = { result ->
if (result.hasVisualOverflow && resizedTextStyle.fontSize > minFontSize) {
resizedTextStyle = resizedTextStyle.copy(
fontSize = (resizedTextStyle.fontSize.value - 1).sp
)
} else {
readyToDraw = true
}
}
)
}