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.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons 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.Close
import androidx.compose.material.icons.filled.Code 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.material.icons.filled.Language
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip 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.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.font.FontWeight
import androidx.compose.ui.text.style.TextAlign 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.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog 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.di.rememberAppVersion
import mg.dot.feufaro.getPlatform import mg.dot.feufaro.getPlatform
import org.jetbrains.compose.resources.painterResource 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 @Composable
fun AboutDialog(onDismiss: () -> Unit) { fun AboutDialog(onDismiss: () -> Unit) {
val platform = getPlatform() val platform = getPlatform()
val appVersion = rememberAppVersion() 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( Dialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
properties = DialogProperties( properties = DialogProperties(
@ -56,67 +136,123 @@ fun AboutDialog(onDismiss: () -> Unit) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(24.dp), .padding(10.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.TopEnd) { Column(
IconButton( modifier = Modifier
onClick = onDismiss, .fillMaxWidth()
modifier = Modifier.size(32.dp) .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( Image(
imageVector = Icons.Default.Close, painter = painterResource(Res.drawable.icon),
contentDescription = "Fermer", contentDescription = "Logo Feufaro",
tint = MaterialTheme.colorScheme.onSurfaceVariant 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)) HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f))
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
@ -124,9 +260,21 @@ fun AboutDialog(onDismiss: () -> Unit) {
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly horizontalArrangement = Arrangement.SpaceEvenly
) { ) {
InfoIconItem(icon = Icons.Default.Language, label = "Site Web", onClick = { }) InfoIconItem(
InfoIconItem(icon = Icons.Default.Email, label = "Contact", onClick = { }) icon = Icons.Default.Language,
InfoIconItem(icon = Icons.Default.Code, label = "Licence", onClick = { }) 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)) Spacer(modifier = Modifier.height(24.dp))
@ -164,3 +312,35 @@ fun InfoIconItem(icon: ImageVector, label: String, onClick: () -> Unit) {
) )
} }
} }
@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
}
}
)
}