From 76f99cadce1bd1ad3fd2dec6d0187b52b356483b Mon Sep 17 00:00:00 2001 From: "hasinarak3@gmail.com" Date: Tue, 5 May 2026 16:13:18 +0300 Subject: [PATCH] Centered songKey & take Included Source & filename on QR Content --- .../src/androidMain/AndroidManifest.xml | 3 +- .../kotlin/mg/dot/feufaro/MainActivity.kt | 12 +- .../kotlin/mg/dot/feufaro/ui/DrawerUI.kt | 48 ++++++-- .../kotlin/mg/dot/feufaro/ui/QRDisplay.kt | 111 ++++++++++++++---- 4 files changed, 135 insertions(+), 39 deletions(-) diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml index 5810c90..99054bb 100644 --- a/composeApp/src/androidMain/AndroidManifest.xml +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -15,7 +15,8 @@ android:theme="@android:style/Theme.Material.Light.NoActionBar"> + android:name=".MainActivity" + android:windowSoftInputMode="adjustResize"> diff --git a/composeApp/src/androidMain/kotlin/mg/dot/feufaro/MainActivity.kt b/composeApp/src/androidMain/kotlin/mg/dot/feufaro/MainActivity.kt index 4462cbb..77f7419 100644 --- a/composeApp/src/androidMain/kotlin/mg/dot/feufaro/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/mg/dot/feufaro/MainActivity.kt @@ -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) diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt index f36100b..6e94e0f 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt @@ -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( diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/QRDisplay.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/QRDisplay.kt index ceca898..ffb8731 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/QRDisplay.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/QRDisplay.kt @@ -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(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() } \ No newline at end of file