Centered songKey & take Included Source & filename on QR Content

This commit is contained in:
hasinarak3@gmail.com 2026-05-05 16:13:18 +03:00
parent 997f5e87b4
commit 76f99cadce
4 changed files with 135 additions and 39 deletions

View file

@ -15,7 +15,8 @@
android:theme="@android:style/Theme.Material.Light.NoActionBar">
<activity
android:exported="true"
android:name=".MainActivity">
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View file

@ -89,12 +89,10 @@ class MainActivity : ComponentActivity() {
println("DeepLink: rawContent = $rawContent")
val fileName = rawContent
.split("|")
.firstOrNull { it.startsWith("t:") }
?.removePrefix("t:")
?.trim()
val fileName = data.getQueryParameter("name") ?: run {
println("DeepLink: paramètre 'name' introuvable")
return
}
println("DeepLink: fileName extrait = $fileName")
if (fileName.isNullOrBlank()) {
@ -111,7 +109,7 @@ class MainActivity : ComponentActivity() {
appDir.mkdirs()
// docsDir.mkdirs()
val file = File(appDir, "$fileName.txt")
val file = File(appDir, fileName)
try {
file.writeText(rawContent)

View file

@ -145,9 +145,10 @@ LaunchedEffect(isPlay, isPos) {
)
}
val favoritePaths by sharedScreenModel.playlistItems.collectAsState()
Scaffold(contentWindowInsets = WindowInsets(0, 0, 0, 0), topBar = {
Scaffold(contentWindowInsets = WindowInsets.safeDrawing, topBar = {
TopAppBar(
modifier = Modifier.height(55.dp).windowInsetsPadding(WindowInsets.statusBars), title = {
modifier = Modifier.height(55.dp).windowInsetsPadding(WindowInsets.safeDrawing),
title = {
Column(
modifier = Modifier.fillMaxSize().verticalScroll(scrollState),
verticalArrangement = Arrangement.Center
@ -169,13 +170,17 @@ LaunchedEffect(isPlay, isPos) {
Icon(Icons.Filled.Menu, contentDescription = "Ouvrir Menu")
}
}, actions = {
Text(
text = songKey,
fontSize = 25.sp,
fontWeight = FontWeight.Black,
)
Spacer(Modifier.width(8.dp))
Box(
modifier = Modifier.fillMaxHeight(),
contentAlignment = Alignment.Center
) {
Text(
text = songKey,
fontSize = 25.sp,
fontWeight = FontWeight.Black,
modifier = Modifier.padding(end = 16.dp)
)
}
}, colors = TopAppBarColors(
containerColor = MaterialTheme.colorScheme.primary,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
@ -228,6 +233,24 @@ LaunchedEffect(isPlay, isPos) {
}
/*AnimatedVisibility(
visible = isExpanded and !showMidiCtrl,
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 }
) {
FloatingActionButton(
onClick = {
sharedScreenModel.toggleEditMode()
sharedScreenModel.setExpandedFAB(false)
}, modifier = Modifier.alpha(0.45f)
) {
Icon(
imageVector = Icons.Filled.Edit,
contentDescription = "null",
tint = Color.Blue
)
}
}*/
AnimatedVisibility(
visible = isExpanded and !showMidiCtrl,
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
@ -345,11 +368,14 @@ LaunchedEffect(isPlay, isPos) {
}) { paddingValues ->
Box(
modifier = Modifier.fillMaxSize().padding(paddingValues).windowInsetsPadding(WindowInsets.ime)
modifier = Modifier.fillMaxSize().padding(paddingValues).consumeWindowInsets(paddingValues).windowInsetsPadding(WindowInsets.ime)
) {
content(PaddingValues(0.dp))
if (sharedScreenModel.isQRCodeVisible.value) {
QRDisplay(sharedScreenModel = sharedScreenModel)
QRDisplay(
sharedScreenModel = sharedScreenModel,
fileRepository = solfaScreenModel.fileRepository
)
} else {
if (filteredSongs.isNotEmpty()) {
Column(

View file

@ -6,42 +6,48 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.produceState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import mg.dot.feufaro.FileRepository
import mg.dot.feufaro.solfa.ColorPrefixA
import mg.dot.feufaro.solfa.ColorPrefixB
import mg.dot.feufaro.solfa.ColorValue
import java.io.ByteArrayOutputStream
import java.net.URLEncoder
import java.util.zip.GZIPOutputStream
import kotlin.io.encoding.Base64
@Composable
fun QRDisplay(sharedScreenModel: SharedScreenModel) {
fun QRDisplay(sharedScreenModel: SharedScreenModel, fileRepository: FileRepository) {
val content by sharedScreenModel.fileContent
val path by sharedScreenModel.activeFilePath
val fileName = path.substringAfterLast('/').substringAfterLast(':')
fun compressAndEncode(content: String?): String {
val bos = ByteArrayOutputStream()
GZIPOutputStream(bos).use { it.write(content?.toByteArray(Charsets.UTF_8)) }
val compressedBytes = bos.toByteArray()
return Base64.UrlSafe.encode(compressedBytes).trim('=')
}
val qrCodeImage by produceState<ImageBitmap?>(initialValue = null, content, path) {
val expandedSource = expandInclusions(
content = content,
currentFilePath = path,
fileRepository = fileRepository
)
if(expandedSource.isNotEmpty()) {
val bos = ByteArrayOutputStream()
GZIPOutputStream(bos).use { it.write(expandedSource.toByteArray(Charsets.UTF_8)) }
val compressedBytes = bos.toByteArray()
val compressedData = compressAndEncode(content)
val uri = "feufaro://song?file=$compressedData"
// val encodedContent = URLEncoder.encode(content, "UTF-8")
// val uri = "feufaro://song?file=$encodedContent"
val compressedData = Base64.UrlSafe.encode(compressedBytes).trim('=')
val qrCodeImage = remember(content) {
if (content != null) {
generateQRCode(uri, size = 800)
} else {
null
val uri = "feufaro://song?file=${compressedData}&name=$fileName"
value = generateQRCode(uri, size = 800)
}
}
@ -64,14 +70,79 @@ fun QRDisplay(sharedScreenModel: SharedScreenModel) {
Text("Scanner pour ouvrir ce partition")
if (qrCodeImage != null) {
Image(
bitmap = qrCodeImage,
bitmap = qrCodeImage!!,
contentDescription = "Code QR du fichier actif",
modifier = Modifier.size(400.dp)
)
} else {
Text("Chargement du Code QR ou contenu vide...", modifier = Modifier.padding(16.dp))
Box(
modifier = Modifier
.size(400.dp)
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(48.dp),
color = ColorPrefixA,
strokeWidth = 4.dp,
trackColor = ColorPrefixB.copy(alpha = 0.2f)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Génération du QR Code...",
style = MaterialTheme.typography.bodyMedium.copy(
color = ColorValue,
fontWeight = FontWeight.Medium
)
)
}
}
}
}
}
}
}
private suspend fun expandInclusions(
content: String?,
currentFilePath: String,
fileRepository: FileRepository
): String {
if(content.isNullOrEmpty()) return ""
val lines = content.split("\n")
val result = StringBuilder()
val lastSlash = currentFilePath.lastIndexOf('/')
val directory = if (lastSlash != -1) currentFilePath.substring(0, lastSlash + 1) else ""
lines.forEach { line ->
if (line.startsWith("I0:")) {
try {
val parts = line.substring(3).split(":")
val fileName = parts[0]
val ignorePattern = if (parts.size > 1) parts[1] else ""
val fullPath = directory + fileName
val includedLines = fileRepository.readFileLines(fullPath)
val regexIgnore = if (ignorePattern.isNotEmpty()) Regex(ignorePattern) else null
includedLines.forEach { incLine ->
if (regexIgnore == null || !regexIgnore.containsMatchIn(incLine)) {
result.append(incLine).append("\n")
}
}
} catch (e: Exception) {
result.append("// Erreur inclusion: ${e.message}\n")
}
} else {
result.append(line).append("\n")
}
}
return result.toString().trimEnd()
}