Generate qrCode using DeepLink
This commit is contained in:
parent
1d35b9227d
commit
cf4046e202
12 changed files with 248 additions and 28 deletions
|
|
@ -21,6 +21,12 @@
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter android:autoVerify="false">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="feufaro" android:host="song" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,14 @@ class AndroidFileRepository(private val context: Context) : FileRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
File(filePath).readLines()
|
val file = File(filePath)
|
||||||
|
println("AndroidRead: Tentative sur ${file.absolutePath}")
|
||||||
|
println("AndroidRead: Existe=${file.exists()} | Lisible=${file.canRead()}")
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw IOException("Le fichier n'existe pas : $filePath")
|
||||||
|
}
|
||||||
|
file.bufferedReader(Charsets.UTF_8).useLines { it.toList() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
|
|
@ -77,32 +84,37 @@ class AndroidFileRepository(private val context: Context) : FileRepository {
|
||||||
if (!file.exists()) throw IOException("File not found: ${file.absolutePath}")
|
if (!file.exists()) throw IOException("File not found: ${file.absolutePath}")
|
||||||
file.readBytes()
|
file.readBytes()
|
||||||
}*/
|
}*/
|
||||||
override suspend fun saveLocalFile(filePath: String, data: ByteArray) {
|
override suspend fun saveLocalFile(filePath: String, data: ByteArray) = withContext(Dispatchers.IO) {
|
||||||
val cleanPath = if (filePath.contains("/storage/emulated/0/")) {
|
val file = if (filePath.contains("/") ) {
|
||||||
filePath.substring(filePath.indexOf("/storage/emulated/0/"))
|
File(filePath)
|
||||||
} else {
|
} else {
|
||||||
filePath
|
File(getAppPublicFolder(), filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
val file = File(cleanPath)
|
|
||||||
try {
|
try {
|
||||||
file.parentFile?.mkdirs()
|
file.parentFile?.mkdirs()
|
||||||
file.writeBytes(data)
|
file.writeBytes(data)
|
||||||
println("Fichier sauvegardé avec succès dans : ${file.absolutePath}")
|
println("Fichier sauvegardé dans : ${file.absolutePath}")
|
||||||
openPdfFile(context = context, filePath = file.absolutePath)
|
|
||||||
|
if (file.extension.lowercase() == "pdf") {
|
||||||
|
openPdfFile(context = context, filePath = file.absolutePath)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Internal files
|
private fun getAppPublicFolder(): File {
|
||||||
/*override fun getFileName(shortName: String): String {
|
val publicDocsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
|
||||||
return File(context.filesDir, shortName).absolutePath
|
val appDir = File(publicDocsDir, "Feufaro")
|
||||||
}*/
|
if (!appDir.exists()) {
|
||||||
|
appDir.mkdirs()
|
||||||
|
}
|
||||||
|
return appDir
|
||||||
|
}
|
||||||
|
|
||||||
override fun pickSavePath(defaultName: String): String? {
|
override fun pickSavePath(defaultName: String): String? {
|
||||||
val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
return File(getAppPublicFolder(), defaultName).absolutePath
|
||||||
return File(downloadDir, defaultName).absolutePath
|
|
||||||
}
|
}
|
||||||
suspend fun openPdfFile(context: Context, filePath: String) {
|
suspend fun openPdfFile(context: Context, filePath: String) {
|
||||||
val file = File(filePath)
|
val file = File(filePath)
|
||||||
|
|
@ -115,10 +127,9 @@ class AndroidFileRepository(private val context: Context) : FileRepository {
|
||||||
file
|
file
|
||||||
)
|
)
|
||||||
|
|
||||||
// uvrir un PDF
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
setDataAndType(uri, "application/pdf")
|
setDataAndType(uri, "application/pdf")
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // Important !
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
package mg.dot.feufaro
|
package mg.dot.feufaro
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
import android.app.ComponentCaller
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
|
import android.util.Base64
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
|
@ -13,6 +19,8 @@ import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import org.koin.androidx.compose.KoinAndroidContext
|
import org.koin.androidx.compose.KoinAndroidContext
|
||||||
|
import java.io.File
|
||||||
|
import java.util.zip.GZIPInputStream
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|
@ -26,6 +34,7 @@ class MainActivity : ComponentActivity() {
|
||||||
setFilePickerActivity(this)
|
setFilePickerActivity(this)
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
hideSystemBar()
|
hideSystemBar()
|
||||||
|
intent?.let { handleDeepLinkIntent(it) }
|
||||||
|
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
|
|
@ -34,6 +43,93 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@Suppress("OVERRIDE_DEPRECATION")
|
||||||
|
override fun onNewIntent(intent: Intent) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
setIntent(intent)
|
||||||
|
handleDeepLinkIntent(intent)
|
||||||
|
}
|
||||||
|
override fun onNewIntent(intent: Intent, caller: ComponentCaller) {
|
||||||
|
super.onNewIntent(intent, caller)
|
||||||
|
setIntent(intent)
|
||||||
|
handleDeepLinkIntent(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decodeAndDecompress(base64Data: String): String {
|
||||||
|
return try {
|
||||||
|
val compressedBytes = Base64.decode(base64Data, Base64.URL_SAFE)
|
||||||
|
java.util.zip.GZIPInputStream(compressedBytes.inputStream()).bufferedReader(Charsets.UTF_8).use {
|
||||||
|
it.readText()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("Decompression Error: ${e.message}")
|
||||||
|
base64Data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun handleDeepLinkIntent(intent: Intent) {
|
||||||
|
println("DeepLink: intent reçu = ${intent.data}")
|
||||||
|
|
||||||
|
val data: Uri = intent.data ?: run {
|
||||||
|
println("DeepLink: data est null")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
println("DeepLink: scheme=${data.scheme} host=${data.host}")
|
||||||
|
|
||||||
|
if (data.scheme != "feufaro" || data.host != "song") {
|
||||||
|
println("DeepLink: scheme/host incorrect")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val encodedData = data.getQueryParameter("file") ?: run {
|
||||||
|
println("DeepLink: paramètre 'file' introuvable")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val rawContent = decodeAndDecompress(encodedData)
|
||||||
|
|
||||||
|
println("DeepLink: rawContent = $rawContent")
|
||||||
|
|
||||||
|
val fileName = rawContent
|
||||||
|
.split("|")
|
||||||
|
.firstOrNull { it.startsWith("t:") }
|
||||||
|
?.removePrefix("t:")
|
||||||
|
?.trim()
|
||||||
|
|
||||||
|
println("DeepLink: fileName extrait = $fileName")
|
||||||
|
|
||||||
|
if (fileName.isNullOrBlank()) {
|
||||||
|
println("DeepLink: fileName vide ou null")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val docsDir = Environment.getExternalStoragePublicDirectory(
|
||||||
|
Environment.DIRECTORY_DOCUMENTS
|
||||||
|
)
|
||||||
|
println("DeepLink: docsDir = ${docsDir.absolutePath}, exists=${docsDir.exists()}")
|
||||||
|
|
||||||
|
val appDir = File(docsDir, "Feufaro")
|
||||||
|
appDir.mkdirs()
|
||||||
|
// docsDir.mkdirs()
|
||||||
|
|
||||||
|
val file = File(appDir, "$fileName.txt")
|
||||||
|
|
||||||
|
try {
|
||||||
|
file.writeText(rawContent)
|
||||||
|
println("DeepLink: fichier créé → ${file.absolutePath}")
|
||||||
|
DeepLinkHandler.hasConsumedDeepLink = true
|
||||||
|
DeepLinkHandler.handleSongContent(rawContent, file.absolutePath)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("DeepLink: ERREUR écriture fichier → ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractFileName(content: String): String? {
|
||||||
|
return content
|
||||||
|
.split("|")
|
||||||
|
.firstOrNull { it.startsWith("H:") }
|
||||||
|
?.removePrefix("H:")
|
||||||
|
?.trim()
|
||||||
|
}
|
||||||
private fun hideSystemBar() {
|
private fun hideSystemBar() {
|
||||||
// Pour les versions d'Android plus récentes (API 30+)
|
// Pour les versions d'Android plus récentes (API 30+)
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
|
||||||
|
|
|
||||||
6
composeApp/src/androidMain/res/xml/file_paths.xml
Normal file
6
composeApp/src/androidMain/res/xml/file_paths.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths>
|
||||||
|
<external-files-path name="external_files" path="." />
|
||||||
|
<files-path name="internal_files" path="." />
|
||||||
|
<external-path name="external_files" path="Documents/Feufaro" />
|
||||||
|
</paths>
|
||||||
|
|
@ -43,11 +43,17 @@ fun App() {
|
||||||
|
|
||||||
LaunchedEffect(currentDisplayConfig) {
|
LaunchedEffect(currentDisplayConfig) {
|
||||||
if (currentDisplayConfig.playlist.isNotEmpty()) {
|
if (currentDisplayConfig.playlist.isNotEmpty()) {
|
||||||
try {
|
if (DeepLinkHandler.hasConsumedDeepLink) {
|
||||||
sharedScreenModel.setPlaylist(currentDisplayConfig.playlist)
|
sharedScreenModel.setPlaylist(currentDisplayConfig.playlist)
|
||||||
solfaScreenModel.reload()
|
} else {
|
||||||
} catch (e: Exception) {
|
if(sharedScreenModel.activeFilePath.value.isEmpty()) {
|
||||||
println("Error loading playlist : ${e.message}")
|
try {
|
||||||
|
sharedScreenModel.setPlaylist(currentDisplayConfig.playlist)
|
||||||
|
solfaScreenModel.reload()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("Error loading playlist : ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
|
@ -45,6 +47,7 @@ import mg.dot.feufaro.ui.MainScreenWithDrawer
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
import cafe.adriel.voyager.koin.koinScreenModel
|
import cafe.adriel.voyager.koin.koinScreenModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import mg.dot.feufaro.data.GridTUOData
|
import mg.dot.feufaro.data.GridTUOData
|
||||||
import mg.dot.feufaro.ui.MyVerticalScrollbar
|
import mg.dot.feufaro.ui.MyVerticalScrollbar
|
||||||
|
|
@ -56,7 +59,6 @@ object ScreenSolfa : Screen {
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val solfaScreenModel = koinScreenModel<SolfaScreenModel>()
|
val solfaScreenModel = koinScreenModel<SolfaScreenModel>()
|
||||||
var menuPosition by remember { mutableStateOf(Offset.Zero) }
|
var menuPosition by remember { mutableStateOf(Offset.Zero) }
|
||||||
var showContextualMenu by remember { mutableStateOf(false) }
|
|
||||||
var gridWidthPx by rememberSaveable { mutableStateOf(0) }
|
var gridWidthPx by rememberSaveable { mutableStateOf(0) }
|
||||||
val sharedScreenModel = koinScreenModel<SharedScreenModel>()
|
val sharedScreenModel = koinScreenModel<SharedScreenModel>()
|
||||||
val tuoList by sharedScreenModel.tuoList.collectAsState()
|
val tuoList by sharedScreenModel.tuoList.collectAsState()
|
||||||
|
|
@ -69,7 +71,20 @@ object ScreenSolfa : Screen {
|
||||||
|
|
||||||
var isScanning by remember { mutableStateOf(false) }
|
var isScanning by remember { mutableStateOf(false) }
|
||||||
var qrCodeResult by remember { mutableStateOf("Aucun code scanné") }
|
var qrCodeResult by remember { mutableStateOf("Aucun code scanné") }
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
DeepLinkHandler.onSongReceived = { content, filePath ->
|
||||||
|
println("2DeepLink: LaunchedEffect reçoit → $filePath")
|
||||||
|
sharedScreenModel.reset()
|
||||||
|
solfaScreenModel.loadFromFile(filePath)
|
||||||
|
}
|
||||||
|
// DeepLinkHandler.consumePending("")
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
onDispose {
|
||||||
|
DeepLinkHandler.onSongReceived = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MainScreenWithDrawer(
|
MainScreenWithDrawer(
|
||||||
solfaScreenModel,
|
solfaScreenModel,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package mg.dot.feufaro
|
||||||
|
|
||||||
|
object DeepLinkHandler {
|
||||||
|
var hasConsumedDeepLink = false
|
||||||
|
private var pendingData: Pair<String, String>? = null
|
||||||
|
|
||||||
|
var onSongReceived: ((fileContent: String, filePath: String) -> Unit)? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
if (value != null) {
|
||||||
|
consumePending()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleSongContent(fileContent: String, filePath: String) {
|
||||||
|
var hasConsumedDeepLink = true
|
||||||
|
val callback = onSongReceived
|
||||||
|
if (callback != null) {
|
||||||
|
println("DeepLink: callback dispo → appel immédiat")
|
||||||
|
callback(fileContent, filePath)
|
||||||
|
} else {
|
||||||
|
println("DeepLink: callback null → mise en attente de $filePath")
|
||||||
|
pendingData = Pair(fileContent, filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun consumePending() {
|
||||||
|
pendingData?.let { (content, path) ->
|
||||||
|
println("DeepLink: consommation du contenu en attente pour $path")
|
||||||
|
onSongReceived?.invoke(content, path)
|
||||||
|
pendingData = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -175,7 +175,11 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun resetShared() {
|
||||||
|
sharedScreenModel.reset()
|
||||||
|
}
|
||||||
fun parse(sourceFile: String) {
|
fun parse(sourceFile: String) {
|
||||||
|
println("37: lFL bien reçu $sourceFile")
|
||||||
currentFile = sourceFile
|
currentFile = sourceFile
|
||||||
val parseScope = CoroutineScope(Dispatchers.Default)
|
val parseScope = CoroutineScope(Dispatchers.Default)
|
||||||
parseScope.launch {
|
parseScope.launch {
|
||||||
|
|
@ -224,7 +228,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
val midiWriter = MidiWriterKotlin(fileRepository)
|
val midiWriter = MidiWriterKotlin(fileRepository)
|
||||||
midiWriter.process(pitches)
|
midiWriter.process(pitches)
|
||||||
midiWriter.save("whawyd3.mid")
|
midiWriter.save("whawyd3.mid")
|
||||||
|
sharedScreenModel.setStanza(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,8 +93,11 @@ LaunchedEffect(isPlay, isPos) {
|
||||||
focusRequester.requestFocus()
|
focusRequester.requestFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val fileContent = sharedScreenModel.fileContent.value ?: ""
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
sharedScreenModel.loadNewSong("$midiFile")
|
if(fileContent == "") {
|
||||||
|
sharedScreenModel.loadNewSong("$midiFile")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ModalNavigationDrawer(drawerState = drawerState, drawerContent = {
|
ModalNavigationDrawer(drawerState = drawerState, drawerContent = {
|
||||||
SimpleDrawerContent(
|
SimpleDrawerContent(
|
||||||
|
|
@ -134,6 +137,7 @@ LaunchedEffect(isPlay, isPos) {
|
||||||
if (showPrintSettings) {
|
if (showPrintSettings) {
|
||||||
PrintSettingsDialog(
|
PrintSettingsDialog(
|
||||||
initialSettings = defaultPrintSettings(),
|
initialSettings = defaultPrintSettings(),
|
||||||
|
// nbStanza = nbStanzas,
|
||||||
onDismiss = { showPrintSettings = false },
|
onDismiss = { showPrintSettings = false },
|
||||||
onConfirm = { settings ->
|
onConfirm = { settings ->
|
||||||
showPrintSettings = false
|
showPrintSettings = false
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,31 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.util.zip.GZIPOutputStream
|
||||||
|
import kotlin.io.encoding.Base64
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun QRDisplay(sharedScreenModel: SharedScreenModel) {
|
fun QRDisplay(sharedScreenModel: SharedScreenModel) {
|
||||||
|
|
||||||
val content by sharedScreenModel.fileContent
|
val content by sharedScreenModel.fileContent
|
||||||
|
|
||||||
|
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 compressedData = compressAndEncode(content)
|
||||||
|
val uri = "feufaro://song?file=$compressedData"
|
||||||
|
// val encodedContent = URLEncoder.encode(content, "UTF-8")
|
||||||
|
// val uri = "feufaro://song?file=$encodedContent"
|
||||||
|
|
||||||
val qrCodeImage = remember(content) {
|
val qrCodeImage = remember(content) {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
generateQRCode(content!!, size = 800)
|
generateQRCode(uri, size = 800)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +61,7 @@ fun QRDisplay(sharedScreenModel: SharedScreenModel) {
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
modifier = Modifier.padding(24.dp)
|
modifier = Modifier.padding(24.dp)
|
||||||
) {
|
) {
|
||||||
Text("Scanner ceci: ")
|
Text("Scanner pour ouvrir ce partition")
|
||||||
if (qrCodeImage != null) {
|
if (qrCodeImage != null) {
|
||||||
Image(
|
Image(
|
||||||
bitmap = qrCodeImage,
|
bitmap = qrCodeImage,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import mg.dot.feufaro.DeepLinkHandler
|
||||||
import mg.dot.feufaro.FileRepository
|
import mg.dot.feufaro.FileRepository
|
||||||
import mg.dot.feufaro.data.DrawerItem
|
import mg.dot.feufaro.data.DrawerItem
|
||||||
import mg.dot.feufaro.data.GridTUOData
|
import mg.dot.feufaro.data.GridTUOData
|
||||||
|
|
@ -94,6 +95,14 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
||||||
fun toggleQRCodeVisibility() {
|
fun toggleQRCodeVisibility() {
|
||||||
_isQRCodeVisible.value = !_isQRCodeVisible.value
|
_isQRCodeVisible.value = !_isQRCodeVisible.value
|
||||||
}
|
}
|
||||||
|
val qrCodeContent: State<String?>
|
||||||
|
get() = mutableStateOf(
|
||||||
|
_activeFilePath.value.takeIf { it.isNotBlank() }?.let { path ->
|
||||||
|
val fileName = path.substringAfterLast("/").substringAfterLast("\\")
|
||||||
|
"feufaro://song?file=${fileName}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
fun updateSearchTxt(searchValue: String) {
|
fun updateSearchTxt(searchValue: String) {
|
||||||
_searchTitle.value = searchValue
|
_searchTitle.value = searchValue
|
||||||
}
|
}
|
||||||
|
|
@ -341,7 +350,6 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
||||||
fun loadNewSong(newMidiFile: String) {
|
fun loadNewSong(newMidiFile: String) {
|
||||||
_mediaPlayer?.stop()
|
_mediaPlayer?.stop()
|
||||||
_mediaPlayer?.release()
|
_mediaPlayer?.release()
|
||||||
_stanza.value = 1
|
|
||||||
_mediaPlayer = null
|
_mediaPlayer = null
|
||||||
_isPos.value = true
|
_isPos.value = true
|
||||||
_isPlay.value = false
|
_isPlay.value = false
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,28 @@
|
||||||
package mg.dot.feufaro.viewmodel
|
package mg.dot.feufaro.viewmodel
|
||||||
|
|
||||||
import cafe.adriel.voyager.core.model.ScreenModel
|
import cafe.adriel.voyager.core.model.ScreenModel
|
||||||
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
|
import mg.dot.feufaro.DeepLinkHandler
|
||||||
import mg.dot.feufaro.FileRepository
|
import mg.dot.feufaro.FileRepository
|
||||||
import mg.dot.feufaro.solfa.Solfa
|
import mg.dot.feufaro.solfa.Solfa
|
||||||
|
|
||||||
class SolfaScreenModel(
|
class SolfaScreenModel(
|
||||||
val fileRepository: FileRepository,
|
val fileRepository: FileRepository,
|
||||||
private val solfa: Solfa
|
val solfa: Solfa
|
||||||
) : ScreenModel {
|
) : ScreenModel {
|
||||||
init {}
|
init {
|
||||||
|
DeepLinkHandler.onSongReceived = { fileContent, filePath ->
|
||||||
|
println("QR Scan: Chemin reçu, lancement du chargement...")
|
||||||
|
loadExternalFile(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadExternalFile(path: String) {
|
||||||
|
solfa.resetShared()
|
||||||
|
solfa.parse(path)
|
||||||
|
|
||||||
|
println("QR Scan: Chargement terminé pour $path")
|
||||||
|
}
|
||||||
|
|
||||||
fun loadNextInPlaylist() {
|
fun loadNextInPlaylist() {
|
||||||
solfa.loadNextInPlaylist()
|
solfa.loadNextInPlaylist()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue