ContextualMenu
This commit is contained in:
parent
82f0624095
commit
e261be48f0
19 changed files with 159 additions and 125 deletions
|
|
@ -29,6 +29,7 @@ kotlin {
|
||||||
implementation(compose.preview)
|
implementation(compose.preview)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(libs.koin.android) // Koin Android-specific extensions
|
implementation(libs.koin.android) // Koin Android-specific extensions
|
||||||
|
implementation(libs.koin.androidx.compose)
|
||||||
|
|
||||||
}
|
}
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
|
|
@ -48,6 +49,11 @@ kotlin {
|
||||||
implementation(libs.serialization)
|
implementation(libs.serialization)
|
||||||
api(libs.kmp.observableviewmodel.core)
|
api(libs.kmp.observableviewmodel.core)
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
implementation(libs.androidx.material3)
|
||||||
|
implementation(libs.koin.compose)
|
||||||
|
implementation(libs.koin.compose.viewmodel)
|
||||||
|
implementation(libs.koin.compose.viewmodel.navigation)
|
||||||
|
|
||||||
}
|
}
|
||||||
commonTest.dependencies {
|
commonTest.dependencies {
|
||||||
implementation(libs.kotlin.test)
|
implementation(libs.kotlin.test)
|
||||||
|
|
@ -56,7 +62,6 @@ kotlin {
|
||||||
implementation(compose.desktop.currentOs)
|
implementation(compose.desktop.currentOs)
|
||||||
implementation(libs.kotlinx.coroutinesSwing)
|
implementation(libs.kotlinx.coroutinesSwing)
|
||||||
}
|
}
|
||||||
//androidMain.dependsOn(androidAndJvmMain)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".AndroidApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import org.koin.android.ext.koin.androidLogger
|
||||||
import org.koin.core.logger.Level
|
import org.koin.core.logger.Level
|
||||||
import mg.dot.feufaro.di.androidModule
|
import mg.dot.feufaro.di.androidModule
|
||||||
|
|
||||||
class AndroidApp: Application {
|
class AndroidApp: Application() {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
startKoin {
|
startKoin {
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
package mg.dot.feufaro
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.io.BufferedReader
|
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStreamReader
|
|
||||||
|
|
||||||
// This is just a regular class that implements the common 'FileRepository' interface.
|
|
||||||
// It is NOT an 'actual' declaration of 'FileRepository'.
|
|
||||||
class AndroidFileRepository(private val context: Context) : FileRepository { // IMPORTS AND IMPLEMENTS THE commonMain 'FileRepository' interface
|
|
||||||
override suspend fun readFileLines(filePath: String): List<String> = withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
when {
|
|
||||||
filePath.startsWith("assets://") -> {
|
|
||||||
readAssetFileLines(filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
File(filePath).readLines()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw IOException("Failed to read file or asset '$filePath'")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
override suspend fun readFileContent(filePath: String): String = withContext(Dispatchers.IO) { "" }
|
|
||||||
private fun readAssetFileLines(assetFileName: String): List<String> {
|
|
||||||
return context.assets.open(assetFileName).use { inputStream ->
|
|
||||||
BufferedReader(InputStreamReader(inputStream)).useLines { it.toList() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun readAssetFileContent(assetFileName: String): String {
|
|
||||||
|
|
||||||
return context.assets.open(assetFileName).use { inputStream ->
|
|
||||||
// BufferedReader(InputStreamReader(inputStream)) permet de lire le texte ligne par ligne.
|
|
||||||
BufferedReader(InputStreamReader(inputStream)).use { reader ->
|
|
||||||
reader.readText() // Lit tout le contenu en une seule chaîne.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,6 +6,7 @@ import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|
@ -13,7 +14,8 @@ class MainActivity : ComponentActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
App()
|
val sharedViewModel: SharedViewModel = koinViewModel()
|
||||||
|
App(sharedViewModel = sharedViewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package mg.dot.feufaro.di
|
package mg.dot.feufaro.di
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import mg.dot.feufaro.AndroidFileRepository // Import the actual Android implementation
|
|
||||||
import mg.dot.feufaro.config.AppConfig
|
import mg.dot.feufaro.config.AppConfig
|
||||||
import org.koin.core.module.dsl.singleOf // Import for Koin DSL
|
import org.koin.core.module.dsl.singleOf // Import for Koin DSL
|
||||||
import org.koin.core.module.dsl.bind // Import for Koin DSL
|
import org.koin.core.module.dsl.bind // Import for Koin DSL
|
||||||
|
|
@ -12,15 +11,10 @@ import org.koin.android.ext.koin.androidContext // Import for androidContext()
|
||||||
val androidModule = module {
|
val androidModule = module {
|
||||||
// Koin will automatically provide the Android Context because you called androidContext() in AndroidApp.
|
// Koin will automatically provide the Android Context because you called androidContext() in AndroidApp.
|
||||||
// It then uses this Context to construct AndroidFileRepository.
|
// It then uses this Context to construct AndroidFileRepository.
|
||||||
singleOf(::AndroidFileRepository) { bind<FileRepository>() }
|
|
||||||
single<AppConfig> {
|
single<AppConfig> {
|
||||||
AppConfig(
|
AppConfig(
|
||||||
transposeto = "C",
|
transposeto = "C",
|
||||||
transposeasif = "C",
|
transposeasif = "C",
|
||||||
buttonContainerColorHex = "#ff00f0",
|
|
||||||
buttonContentColorHex = "#ffffff",
|
|
||||||
buttonDisabledContainerColorHex = "#999999",
|
|
||||||
buttonDisabledContentColorHex = "#ffffff",
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package mg.dot.feufaro
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
|
@ -26,6 +27,7 @@ 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.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
@ -41,7 +43,10 @@ import kotlinx.coroutines.launch
|
||||||
import mg.dot.feufaro.solfa.LazyVerticalGridTUO
|
import mg.dot.feufaro.solfa.LazyVerticalGridTUO
|
||||||
import mg.dot.feufaro.solfa.Solfa
|
import mg.dot.feufaro.solfa.Solfa
|
||||||
import org.koin.compose.koinInject
|
import org.koin.compose.koinInject
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
|
import androidx.compose.ui.window.Popup
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
|
|
@ -53,6 +58,8 @@ fun App(sharedViewModel: SharedViewModel = SharedViewModel()
|
||||||
val currentDisplayConfig by displayConfigManager.displayConfig.collectAsState()
|
val currentDisplayConfig by displayConfigManager.displayConfig.collectAsState()
|
||||||
// Load Configurations
|
// Load Configurations
|
||||||
val configScope = CoroutineScope(Dispatchers.Default)
|
val configScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
var showContextualMenu by remember { mutableStateOf(false)}
|
||||||
|
var menuPosition by remember { mutableStateOf(Offset.Zero)}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
configScope.launch {
|
configScope.launch {
|
||||||
|
|
@ -82,7 +89,34 @@ fun App(sharedViewModel: SharedViewModel = SharedViewModel()
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
var showContent by remember { mutableStateOf(false) }
|
var showContent by remember { mutableStateOf(false) }
|
||||||
var gridWidthPx by remember { mutableStateOf(0) }
|
var gridWidthPx by remember { mutableStateOf(0) }
|
||||||
Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(
|
||||||
|
Modifier.fillMaxSize()
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectTapGestures(
|
||||||
|
onDoubleTap = {
|
||||||
|
offset ->
|
||||||
|
menuPosition = offset
|
||||||
|
showContextualMenu = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
if (showContextualMenu) {
|
||||||
|
Popup(
|
||||||
|
alignment = Alignment.TopStart,
|
||||||
|
offset = IntOffset(menuPosition.x.roundToInt(),
|
||||||
|
menuPosition.y.roundToInt()),
|
||||||
|
onDismissRequest = {
|
||||||
|
showContextualMenu = false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
ContextualMenu(onMenuItemClick = { item ->
|
||||||
|
println("Clicked: $item")
|
||||||
|
showContextualMenu = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package mg.dot.feufaro
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.Edit
|
||||||
|
import androidx.compose.material.icons.filled.List
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.ui.unit.TextUnitType
|
||||||
|
@Composable
|
||||||
|
fun ContextualMenu (onMenuItemClick: (String) -> Unit){
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(Color.DarkGray.copy(alpha = 0.8f), RoundedCornerShape(8.dp))
|
||||||
|
.padding(8.dp)
|
||||||
|
) {
|
||||||
|
MenuItem(icon = Icons.Default.Add, text = "+") { onMenuItemClick("Ajouter")}
|
||||||
|
MenuItem(icon = Icons.Default.Edit, text = "!") { onMenuItemClick("Modifier")}
|
||||||
|
MenuItem(icon = Icons.Default.List, text = "-") { onMenuItemClick("Liste")}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MenuItem(icon: androidx.compose.ui.graphics.vector.ImageVector, text: String, onClick: () -> Unit) {
|
||||||
|
IconButton(onClick = onClick) {
|
||||||
|
Column(horizontalAlignment = androidx.compose.ui.Alignment.CenterHorizontally) {
|
||||||
|
Icon(icon, contentDescription = text, tint = Color.White)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,50 @@
|
||||||
package mg.dot.feufaro
|
package mg.dot.feufaro
|
||||||
|
|
||||||
import java.io.IOException // Java.io.IOException est généralement partagée sur JVM/Android
|
import java.io.IOException // Java.io.IOException est généralement partagée sur JVM/Android
|
||||||
|
import feufaro.composeapp.generated.resources.Res
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
// Définissez une expect interface. Elle spécifie le contrat de votre repository.
|
// Définissez une expect interface. Elle spécifie le contrat de votre repository.
|
||||||
// Utilisez 'expect interface' car l'implémentation (actual) variera selon la plateforme.
|
// Utilisez 'expect interface' car l'implémentation (actual) variera selon la plateforme.
|
||||||
expect interface FileRepository {
|
interface FileRepository {
|
||||||
// Lecture de fichier ligne par ligne
|
// Lecture de fichier ligne par ligne
|
||||||
suspend fun readFileLines(filePath: String): List<String>
|
suspend fun readFileLines(filePath: String): List<String>
|
||||||
|
|
||||||
// Lecture de fichier entier en tant que String
|
// Lecture de fichier entier en tant que String
|
||||||
suspend fun readFileContent(filePath: String): String
|
suspend fun readFileContent(filePath: String): String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is just a regular class that implements the common 'FileRepository' interface.
|
||||||
|
// It is NOT an 'actual' declaration of 'FileRepository'.
|
||||||
|
class CommonFileRepository : FileRepository { // IMPORTS AND IMPLEMENTS THE commonMain 'FileRepository' interface
|
||||||
|
override suspend fun readFileLines(filePath: String): List<String> = withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
when {
|
||||||
|
filePath.startsWith("assets://") -> {
|
||||||
|
readAssetFileLines(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
File(filePath).readLines()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw IOException("Failed to read file or asset '$filePath'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override suspend fun readFileContent(filePath: String): String = withContext(Dispatchers.IO) {
|
||||||
|
val lines = readFileLines(filePath)
|
||||||
|
lines.joinToString("\n") { it }
|
||||||
|
}
|
||||||
|
private suspend fun readAssetFileLines(assetFileName: String): List<String> {
|
||||||
|
return try {
|
||||||
|
Res.readBytes("files/"+assetFileName.removePrefix("assets://")).decodeToString().split("\n")
|
||||||
|
} catch (e: IOException) {
|
||||||
|
println("Could not read /"+assetFileName.removePrefix("assets://"))
|
||||||
|
throw IOException("Could not read asset file: $assetFileName", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,6 +29,8 @@ open class SharedViewModel(): ViewModel() {
|
||||||
val tuoList: StateFlow<List<TimeUnitObject>> = _tuoList.asStateFlow()
|
val tuoList: StateFlow<List<TimeUnitObject>> = _tuoList.asStateFlow()
|
||||||
private var _playlist = MutableStateFlow<List<String>>(viewModelScope, emptyList())
|
private var _playlist = MutableStateFlow<List<String>>(viewModelScope, emptyList())
|
||||||
val playlist: StateFlow<List<String>> = _playlist.asStateFlow()
|
val playlist: StateFlow<List<String>> = _playlist.asStateFlow()
|
||||||
|
private var _nextPlayed = MutableStateFlow(viewModelScope, -1)
|
||||||
|
val nextPlayed: StateFlow<Int> = _nextPlayed.asStateFlow()
|
||||||
private val tempTimeUnitObjectList = mutableListOf<TimeUnitObject>()
|
private val tempTimeUnitObjectList = mutableListOf<TimeUnitObject>()
|
||||||
var _hasMarker = MutableStateFlow<Boolean>(viewModelScope, false)
|
var _hasMarker = MutableStateFlow<Boolean>(viewModelScope, false)
|
||||||
val hasMarker: StateFlow<Boolean> = _hasMarker.asStateFlow()
|
val hasMarker: StateFlow<Boolean> = _hasMarker.asStateFlow()
|
||||||
|
|
@ -83,4 +85,12 @@ open class SharedViewModel(): ViewModel() {
|
||||||
_hasMarker.value = theHasMarker
|
_hasMarker.value = theHasMarker
|
||||||
|
|
||||||
}
|
}
|
||||||
|
fun playNext() {
|
||||||
|
val nextIndex = (_nextPlayed.value + 1) % _playlist.value.size
|
||||||
|
_nextPlayed.value = nextIndex
|
||||||
|
}
|
||||||
|
fun currentPlayed(): String {
|
||||||
|
val playlistIndex = _nextPlayed.value
|
||||||
|
return _playlist.value[playlistIndex]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,22 @@
|
||||||
package mg.dot.feufaro.di
|
package mg.dot.feufaro.di
|
||||||
|
|
||||||
|
import mg.dot.feufaro.CommonFileRepository
|
||||||
import mg.dot.feufaro.FileRepository
|
import mg.dot.feufaro.FileRepository
|
||||||
|
import mg.dot.feufaro.SharedViewModel
|
||||||
import mg.dot.feufaro.DisplayConfigManager // Importez DisplayConfigManager
|
import mg.dot.feufaro.DisplayConfigManager // Importez DisplayConfigManager
|
||||||
import mg.dot.feufaro.config.AppConfig
|
import mg.dot.feufaro.config.AppConfig
|
||||||
import mg.dot.feufaro.musicXML.MusicXML
|
import mg.dot.feufaro.musicXML.MusicXML
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
import org.koin.core.module.dsl.singleOf
|
import org.koin.core.module.dsl.singleOf
|
||||||
import org.koin.core.module.Module
|
import org.koin.core.module.Module
|
||||||
|
import org.koin.core.module.dsl.viewModel
|
||||||
|
|
||||||
val commonModule = module {
|
val commonModule = module {
|
||||||
// Déclarez FileRepository comme un singleton.
|
// Déclarez FileRepository comme un singleton.
|
||||||
// L'implémentation concrète (actual) sera résolue par Koin en fonction de la plateforme.
|
// L'implémentation concrète (actual) sera résolue par Koin en fonction de la plateforme.
|
||||||
// Pour Android, Koin injectera le Context que vous avez fourni via androidContext().
|
// Pour Android, Koin injectera le Context que vous avez fourni via androidContext().
|
||||||
singleOf(::MusicXML)
|
singleOf(::MusicXML)
|
||||||
|
single<FileRepository> { CommonFileRepository() }
|
||||||
single { DisplayConfigManager(fileRepository = get())}
|
single { DisplayConfigManager(fileRepository = get())}
|
||||||
|
viewModel { SharedViewModel() }
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +36,6 @@ class Solfa(val sharedViewModel: SharedViewModel, private val fileRepository: Fi
|
||||||
var nextLIndex: Int = -1
|
var nextLIndex: Int = -1
|
||||||
var inGroup: Boolean = false
|
var inGroup: Boolean = false
|
||||||
var templateString: String = ""
|
var templateString: String = ""
|
||||||
var nextPlayed = -1
|
|
||||||
|
|
||||||
private val meta: MutableMap<String, String> = mutableMapOf()
|
private val meta: MutableMap<String, String> = mutableMapOf()
|
||||||
private val lyricsComment: MutableList<String> = mutableListOf()
|
private val lyricsComment: MutableList<String> = mutableListOf()
|
||||||
|
|
@ -94,15 +93,16 @@ class Solfa(val sharedViewModel: SharedViewModel, private val fileRepository: Fi
|
||||||
}
|
}
|
||||||
nextTimeUnitObject()
|
nextTimeUnitObject()
|
||||||
}
|
}
|
||||||
fun loadSolfa(sourceFileName: String) {
|
fun loadSolfa() {
|
||||||
|
val sourceFileName: String = sharedViewModel.currentPlayed()
|
||||||
sharedViewModel.reset()
|
sharedViewModel.reset()
|
||||||
parse(sourceFileName)
|
parse(sourceFileName)
|
||||||
}
|
}
|
||||||
fun loadNextInPlaylist() {
|
fun loadNextInPlaylist() {
|
||||||
val playlist = sharedViewModel?.playlist?.value ?: listOf()
|
val playlist = sharedViewModel.playlist.value
|
||||||
if (playlist.isNotEmpty()) {
|
if (playlist.isNotEmpty()) {
|
||||||
nextPlayed = (nextPlayed + 1) % playlist.size
|
sharedViewModel.playNext()
|
||||||
loadSolfa(playlist[nextPlayed])
|
loadSolfa()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun parse(sourceFile: String) {
|
fun parse(sourceFile: String) {
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.material.LocalTextStyle
|
|
||||||
import androidx.compose.material.Text
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
package mg.dot.feufaro
|
|
||||||
|
|
||||||
import feufaro.composeapp.generated.resources.Res
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
|
|
||||||
// This is just a regular class that implements the common 'FileRepository' interface.
|
|
||||||
// It is NOT an 'actual' declaration of 'FileRepository'.
|
|
||||||
class DesktopFileRepository : FileRepository { // IMPORTS AND IMPLEMENTS THE commonMain 'FileRepository' interface
|
|
||||||
override suspend fun readFileLines(filePath: String): List<String> = withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
when {
|
|
||||||
filePath.startsWith("assets://") -> {
|
|
||||||
readAssetFileLines(filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
File(filePath).readLines()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw IOException("Failed to read file or asset '$filePath'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override suspend fun readFileContent(filePath: String): String = withContext(Dispatchers.IO) {
|
|
||||||
val lines = readFileLines(filePath)
|
|
||||||
lines.joinToString("\n") { it }
|
|
||||||
}
|
|
||||||
private suspend fun readAssetFileLines(assetFileName: String): List<String> {
|
|
||||||
return try {
|
|
||||||
Res.readBytes("files/"+assetFileName.removePrefix("assets://")).decodeToString().split("\n")
|
|
||||||
} catch (e: IOException) {
|
|
||||||
println("Could not read /"+assetFileName.removePrefix("assets://"))
|
|
||||||
throw IOException("Could not read asset file: $assetFileName", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package mg.dot.feufaro
|
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import mg.dot.feufaro.FileRepository
|
|
||||||
|
|
||||||
|
|
||||||
actual interface FileRepository {
|
|
||||||
actual suspend fun readFileLines(filePath: String): List<String>
|
|
||||||
actual suspend fun readFileContent(filePath: String): String
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package mg.dot.feufaro.di
|
package mg.dot.feufaro.di
|
||||||
|
|
||||||
import mg.dot.feufaro.DesktopFileRepository // Import the actual desktop implementation
|
|
||||||
import org.koin.core.module.dsl.singleOf // Import for Koin DSL
|
import org.koin.core.module.dsl.singleOf // Import for Koin DSL
|
||||||
import org.koin.core.module.dsl.bind // Import for Koin DSL
|
import org.koin.core.module.dsl.bind // Import for Koin DSL
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
@ -10,11 +9,10 @@ import mg.dot.feufaro.config.AppConfig
|
||||||
val desktopModule = module {
|
val desktopModule = module {
|
||||||
// When Koin is initialized on Desktop, it will use DesktopFileRepository
|
// When Koin is initialized on Desktop, it will use DesktopFileRepository
|
||||||
// as the implementation for the FileRepository interface.
|
// as the implementation for the FileRepository interface.
|
||||||
singleOf(::DesktopFileRepository) { bind<FileRepository>() }
|
|
||||||
single<AppConfig> {
|
single<AppConfig> {
|
||||||
AppConfig(
|
AppConfig(
|
||||||
transposeto = "C",
|
transposeto = "C",
|
||||||
transposeasif = "C"
|
transposeasif = "C"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import org.koin.core.context.KoinContext
|
||||||
import org.koin.core.logger.Level
|
import org.koin.core.logger.Level
|
||||||
import org.koin.compose.KoinContext
|
import org.koin.compose.KoinContext
|
||||||
|
|
||||||
|
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
startKoin {
|
startKoin {
|
||||||
printLogger(Level.INFO) // Mettez Level.INFO pour voir les logs de Koin
|
printLogger(Level.INFO) // Mettez Level.INFO pour voir les logs de Koin
|
||||||
|
|
@ -24,7 +25,8 @@ fun main() = application {
|
||||||
title = "Feufaro",
|
title = "Feufaro",
|
||||||
) {
|
) {
|
||||||
KoinContext {
|
KoinContext {
|
||||||
App()
|
val sharedViewModel: SharedViewModel = SharedViewModel()
|
||||||
|
App(sharedViewModel = sharedViewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[versions]
|
[versions]
|
||||||
agp = "8.7.3"
|
agp = "8.10.1"
|
||||||
android-compileSdk = "35"
|
android-compileSdk = "35"
|
||||||
android-minSdk = "24"
|
android-minSdk = "24"
|
||||||
android-targetSdk = "35"
|
android-targetSdk = "35"
|
||||||
|
|
@ -20,8 +20,11 @@ koin = "4.0.4"
|
||||||
core = "0.91.1"
|
core = "0.91.1"
|
||||||
kmpObservableviewmodelCore = "1.0.0-BETA-3"
|
kmpObservableviewmodelCore = "1.0.0-BETA-3"
|
||||||
kotlinxSerializationJson = "1.8.1"
|
kotlinxSerializationJson = "1.8.1"
|
||||||
|
material3 = "1.3.2"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" }
|
||||||
|
koin-compose-viewmodel-navigation = { module = "io.insert-koin:koin-compose-viewmodel-navigation", version.ref = "koin" }
|
||||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||||
kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
|
||||||
junit = { module = "junit:junit", version.ref = "junit" }
|
junit = { module = "junit:junit", version.ref = "junit" }
|
||||||
|
|
@ -39,13 +42,15 @@ koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
||||||
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" }
|
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" }
|
||||||
koin-core-viewmodel = { module = "io.insert-koin:koin-core-viewmodel", version.ref = "koin" }
|
koin-core-viewmodel = { module = "io.insert-koin:koin-core-viewmodel", version.ref = "koin" }
|
||||||
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
|
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
|
||||||
#20250704
|
koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin" }
|
||||||
|
|
||||||
core = { module = "io.github.pdvrieze.xmlutil:core", version.ref = "core" }
|
core = { module = "io.github.pdvrieze.xmlutil:core", version.ref = "core" }
|
||||||
#core-android = { module = "io.github.pdvrieze.xmlutil:core-android", version.ref = "core" }
|
#core-android = { module = "io.github.pdvrieze.xmlutil:core-android", version.ref = "core" }
|
||||||
#core-jdk = { module = "io.github.pdvrieze.xmlutil:core-jdk", version.ref = "core" }
|
#core-jdk = { module = "io.github.pdvrieze.xmlutil:core-jdk", version.ref = "core" }
|
||||||
serialization = { module = "io.github.pdvrieze.xmlutil:serialization", version.ref = "core" }
|
serialization = { module = "io.github.pdvrieze.xmlutil:serialization", version.ref = "core" }
|
||||||
kmp-observableviewmodel-core = { module = "com.rickclephas.kmp:kmp-observableviewmodel-core", version.ref = "kmpObservableviewmodelCore" }
|
kmp-observableviewmodel-core = { module = "com.rickclephas.kmp:kmp-observableviewmodel-core", version.ref = "kmpObservableviewmodelCore" }
|
||||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
||||||
|
androidx-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|
|
||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue