diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 725061e..af2f83b 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -16,15 +16,22 @@ jobs: - name: Setup JDK uses: actions/setup-java@v1.4.3 with: - java-version: '11' + java-version: '17' - name: Setup secret.properties env: ADMOB_APPLICATION_ID: ${{ secrets.ADMOB_APPLICATION_ID }} ADMOB_HOME_BANNER_ID: ${{ secrets.ADMOB_HOME_BANNER_ID }} + ADMOB_TRANSPOSER_BANNER_ID: ${{ secrets.ADMOB_TRANSPOSER_BANNER_ID }} FAKE_ADMOB_HOME_BANNER_ID: ${{ secrets.FAKE_ADMOB_HOME_BANNER_ID }} - run: echo "ADMOB_APPLICATION_ID=\"$ADMOB_APPLICATION_ID\"" > ./secret.properties | - echo "ADMOB_HOME_BANNER_ID=\"$ADMOB_HOME_BANNER_ID\"" >> ./secret.properties + GITHUB_PACKAGES_USER: ${{ secrets.PACKAGES_USER }} + GITHUB_PACKAGES_TOKEN: ${{ secrets.PACKAGES_TOKEN }} + run: | + echo "ADMOB_APPLICATION_ID=\"$ADMOB_APPLICATION_ID\"" >> ./secret.properties | + echo "ADMOB_HOME_BANNER_ID=\"$ADMOB_HOME_BANNER_ID\"" >> ./secret.properties | + echo "ADMOB_TRANSPOSER_BANNER_ID=\"$ADMOB_TRANSPOSER_BANNER_ID\"" >> ./secret.properties | + echo "GITHUB_PACKAGES_USER=\"$GITHUB_PACKAGES_USER\"" >> ./secret.properties | + echo "GITHUB_PACKAGES_TOKEN=\"$GITHUB_PACKAGES_TOKEN\"" >> ./secret.properties - name: Setup firebase auth env: diff --git a/.idea/.name b/.idea/.name index c4b6c50..de63484 100644 --- a/.idea/.name +++ b/.idea/.name @@ -1 +1 @@ -Chords Dictionary \ No newline at end of file +Guitar Kit \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index d9259be..969c039 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -7,19 +7,16 @@ - diff --git a/.idea/misc.xml b/.idea/misc.xml index 0bbed6d..fe387b3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - @@ -95,7 +94,7 @@ - + diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ed0b374..91e4ed2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,14 +6,13 @@ plugins { } android { - compileSdk = Versions.Sdk.compile + compileSdk = 33 defaultConfig { applicationId = "com.dosei.music.scoreconverter" - minSdk = Versions.Sdk.minimum - targetSdk = Versions.Sdk.target - versionCode = Versions.App.code - versionName = Versions.App.name - buildToolsVersion = Versions.Sdk.buildTools + minSdk = 21 + targetSdk = 33 + versionCode = 7 + versionName = "1.3.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } @@ -35,6 +34,7 @@ android { resValue("string", "admob_application_id", secrets.getString(SecretsKeys.adMobAppId)) resValue("string", "admob_home_banner_id", BuildConstants.adMobFakeBannerId) resValue("string", "admob_transposer_banner_id", BuildConstants.adMobFakeBannerId) + resValue("string", "admob_dictionary_banner_id", BuildConstants.adMobFakeBannerId) } getByName("release") { isMinifyEnabled = true @@ -58,10 +58,15 @@ android { "admob_transposer_banner_id", secrets.getString(SecretsKeys.adMobTransposerBannerId) ) + resValue( + "string", + "admob_dictionary_banner_id", + secrets.getString(SecretsKeys.adMobDictionaryBannerId) + ) } } composeOptions { - kotlinCompilerExtensionVersion = "1.1.0-rc01" + kotlinCompilerExtensionVersion = libs.versions.compiler.kotlin.ext.get() } buildFeatures { compose = true @@ -71,38 +76,59 @@ android { resources.excludes.add("META-INF/AL2.0") resources.excludes.add("META-INF/LGPL2.1") } + namespace = "com.dosei.music.scoreconverter" +} + +kotlin { + jvmToolchain(8) } dependencies { implementation(project(path = ":ui")) implementation(project(path = ":player")) - implementation(project(path = ":arpeggio")) implementation(libs.kotlin.stdlib) implementation(libs.app.compat) implementation(libs.core.ktx) implementation(libs.constraint.layout) implementation(libs.recycler.view) + + implementation(platform(libs.firebase.bom)) implementation(libs.firebase.analytics) implementation(libs.firebase.crashlytics.core) + + implementation(platform(libs.koin.bom)) implementation(libs.koin.android) - implementation(libs.koin.scope) - implementation(libs.koin.view.model) + implementation(libs.koin.compose) implementation(libs.play.services.ads) implementation(libs.android.material) - implementation(libs.bundles.compose) implementation(libs.lifecycle.runtime) + + //region UI + + implementation(platform(libs.compose.bom)) + implementation(libs.compose.ui) + implementation(libs.compose.material) + implementation(libs.compose.graphics) + implementation(libs.compose.preview) + implementation(libs.activity.compose) + + debugImplementation(libs.compose.tooling.ui) + debugImplementation(libs.compose.test.manifest) + + androidTestImplementation(platform(libs.compose.bom)) + androidTestImplementation(libs.android.test.ext) + androidTestImplementation(libs.espresso.core) + androidTestImplementation(libs.compose.junit) + + //endregion + implementation(libs.hilt.compose) implementation(libs.navigation.compose) implementation(libs.ktransposer) + implementation(libs.arpeggio) testImplementation(libs.junit.core) androidTestImplementation(libs.android.test.ext) - androidTestImplementation(libs.espresso.core) - androidTestImplementation(libs.compose.junit) - androidTestImplementation(libs.espresso.compose) - - debugImplementation(libs.compose.test.manifest) - debugImplementation(libs.compose.tooling.ui) } diff --git a/app/src/androidTest/java/com/dosei/music/scoreconverter/main/ComposableConverterKtTest.kt b/app/src/androidTest/java/com/dosei/music/scoreconverter/main/ComposableConverterKtTest.kt deleted file mode 100644 index 63a751d..0000000 --- a/app/src/androidTest/java/com/dosei/music/scoreconverter/main/ComposableConverterKtTest.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.dosei.music.scoreconverter.main - -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.ui.Modifier -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.performTouchInput -import androidx.compose.ui.test.swipeDown -import org.junit.Rule -import org.junit.Test - -class ComposableConverterKtTest { - - @get:Rule - val composeTestRule = createComposeRule() - - @Test - fun testScroll() { - composeTestRule.setContent { - ScoreToTablature(Modifier.fillMaxSize()) - } - - composeTestRule.onNodeWithTag("score").performTouchInput { - swipeDown( - startY = 0f, - endY = 150f, - durationMillis = 3000 - ) - } - - Thread.sleep(3000) - - } -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d4e2ab0..22ceb1e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,26 +1,21 @@ + xmlns:tools="http://schemas.android.com/tools"> + android:theme="@style/Minuet" + tools:replace="android:theme"> - - - Row( - Modifier - .fillMaxWidth() - .height(250.dp) - .padding(horizontal = 16.dp, vertical = 8.dp) - ) { - DrawChord(chord = pairOfChords.getOrNull(0)) - DrawChord(chord = pairOfChords.getOrNull(1)) - } - } - } - } -} - -@Composable -private fun RowScope.DrawChord(chord: Chord?) { - if (chord != null) { - ChordDiagram( - modifier = Modifier.weight(1f), - name = chord.name, - components = chord.components - ) - } else { - Spacer(modifier = Modifier.weight(1f)) - } -} - -@Preview(showBackground = true) -@Composable -private fun PreviewChordsDictionary() { - Surface(modifier = Modifier.fillMaxSize(), color = Color.White) { - ChordsDictionary( - modifier = Modifier - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/GuitarThumbnailTheme.kt b/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/GuitarThumbnailTheme.kt deleted file mode 100644 index b641a06..0000000 --- a/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/GuitarThumbnailTheme.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.dosei.music.scoreconverter.chords.dictionary - -import androidx.compose.material.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.dosei.music.arpeggio.theme.ArpeggioTheme -import com.dosei.music.arpeggio.theme.FormatInitialFret -import com.dosei.music.arpeggio.theme.Sizes -import com.dosei.music.arpeggio.theme.Typography - -@Composable -fun GuitarThumbnailTheme( - content: @Composable () -> Unit -) { - ArpeggioTheme( - typography = Typography( - name = TextStyle().copy(fontSize = 18.sp, fontWeight = FontWeight.Bold), - firstFretIndicator = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.SemiBold), - fingerIndicator = TextStyle(fontSize = 10.sp, color = MaterialTheme.colors.onPrimary) - ), - sizes = Sizes(position = 16.dp), - content = content, - formatInitialFret = FormatInitialFret.ptBr() - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/di/DependencyInjection.kt b/app/src/main/java/com/dosei/music/scoreconverter/di/DependencyInjection.kt index 3e60054..ea06210 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/di/DependencyInjection.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/di/DependencyInjection.kt @@ -3,13 +3,18 @@ package com.dosei.music.scoreconverter.di import com.dosei.music.ktransposer.TransposeSong import com.dosei.music.scoreconverter.converter.MIDINoteConverter import com.dosei.music.scoreconverter.domain.Guitar +import com.dosei.music.scoreconverter.feature.chords.dictionary.ChordsDictionaryViewModel +import com.dosei.music.scoreconverter.feature.chords.dictionary.data.ChordParser +import com.dosei.music.scoreconverter.feature.chords.dictionary.data.ChordsRepository +import com.dosei.music.scoreconverter.feature.chords.dictionary.data.LocalChordsRepository import com.dosei.music.scoreconverter.io.SharedPreferencesClient import com.dosei.music.scoreconverter.player.PlayerDependencyInjection import com.dosei.music.scoreconverter.toolbox.BeautifySong import com.dosei.music.scoreconverter.toolbox.CopyToClipboard -import com.dosei.music.scoreconverter.transposer.TransposerViewModel +import com.dosei.music.scoreconverter.feature.chords.transposer.TransposerViewModel +import com.dosei.music.scoreconverter.toolbox.AssetsReader import org.koin.android.ext.koin.androidContext -import org.koin.android.viewmodel.dsl.viewModel +import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.module object DependencyInjection { @@ -17,10 +22,19 @@ object DependencyInjection { single { Guitar.default() } factory { SharedPreferencesClient(androidContext()) } factory { PlayerDependencyInjection.createPlayer() } + single { + LocalChordsRepository( + assetsReader = get(), + chordParser = get() + ) + } + factory { ChordParser() } + factory { AssetsReader(androidContext()) } factory { MIDINoteConverter() } factory { CopyToClipboard(get()) } factory { BeautifySong() } factory { TransposeSong() } viewModel { TransposerViewModel(get(), get(), get()) } + viewModel { ChordsDictionaryViewModel(get()) } } } \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/ChordsDictionary.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/ChordsDictionary.kt new file mode 100644 index 0000000..9c89be0 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/ChordsDictionary.kt @@ -0,0 +1,158 @@ +package com.dosei.music.scoreconverter.feature.chords.dictionary + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement.spacedBy +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SearchBar +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.dosei.music.arpeggio.ChordDiagram +import com.dosei.music.scoreconverter.R +import com.dosei.music.scoreconverter.feature.chords.dictionary.data.Chords +import com.dosei.music.scoreconverter.toolbox.AdvertView +import com.dosei.music.scoreconverter.ui.view.MenuButton + +@Composable +fun ChordsDictionaryScreen( + modifier: Modifier = Modifier, + viewModel: ChordsDictionaryViewModel, + onMenuClick: () -> Unit +) { + val state by viewModel.state.collectAsState(initial = ChordsDictionaryState()) + ChordsDictionaryContent( + modifier, + state, + viewModel::onSearch, + onMenuClick, + ) +} + +@Composable +private fun ChordsDictionaryContent( + modifier: Modifier, + state: ChordsDictionaryState, + onSearch: (String) -> Unit, + onMenuClick: () -> Unit +) { + Scaffold( + modifier = modifier, + contentWindowInsets = WindowInsets(8.dp, 16.dp, 8.dp, 16.dp) + ) { padding -> + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Search(onMenuClick, onSearch) + Spacer(modifier = Modifier.height(8.dp)) + GuitarThumbnailTheme { + LazyVerticalGrid( + modifier = modifier.weight(1f).fillMaxWidth(), + verticalArrangement = spacedBy(8.dp), + horizontalArrangement = spacedBy(8.dp), + columns = GridCells.Fixed(2), + contentPadding = padding + ) { + items(state.content) { chord -> + ChordDiagram( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(.85f), + name = chord.name, + components = chord.components + ) + } + } + } + AdvertView( + modifier = Modifier.padding(16.dp), + unitId = R.string.admob_dictionary_banner_id + ) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun Search( + onMenuClick: () -> Unit, + onSearch: (String) -> Unit +) { + var query by remember { mutableStateOf("") } + val history = remember { mutableListOf() } + var isSearchActive by remember { mutableStateOf(false) } + SearchBar( + modifier = Modifier, + leadingIcon = { MenuButton(onClick = onMenuClick) }, + trailingIcon = if (query.isNotEmpty()) { + { + IconButton(onClick = { onSearch("") }) { + Icon( + imageVector = Icons.Default.Clear, + contentDescription = "Clear" + ) + } + } + } else null, + query = query, + onQueryChange = { query = it }, + onSearch = { + onSearch(it) + isSearchActive = false + history.add(it) + }, + active = isSearchActive, + onActiveChange = { isSearchActive = it }, + placeholder = { Text(stringResource(R.string.search_chords)) }, + windowInsets = WindowInsets(top = 8.dp) + ) { + history.reversed().forEach { historyItem -> + ListItem( + modifier = Modifier.clickable { + query = historyItem + isSearchActive = false + }, + leadingContent = { + Icon( + painter = painterResource(id = R.drawable.baseline_history_24), + contentDescription = stringResource(R.string.history_item) + ) + }, + headlineContent = { Text(text = historyItem) } + ) + } + } +} + +@Preview(showBackground = true, locale = "pt-rBR") +@Composable +private fun PreviewChordsDictionary() { + Surface(modifier = Modifier.fillMaxSize(), color = Color.White) { + ChordsDictionaryContent( + modifier = Modifier, + state = ChordsDictionaryState( + content = Chords.all.take(7) + ), + {}, {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/ChordsDictionaryState.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/ChordsDictionaryState.kt new file mode 100644 index 0000000..dcc7905 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/ChordsDictionaryState.kt @@ -0,0 +1,7 @@ +package com.dosei.music.scoreconverter.feature.chords.dictionary + +import com.dosei.music.scoreconverter.feature.chords.dictionary.data.Chord + +data class ChordsDictionaryState( + val content: List = emptyList(), +) \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/ChordsDictionaryViewModel.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/ChordsDictionaryViewModel.kt new file mode 100644 index 0000000..4f9a7ab --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/ChordsDictionaryViewModel.kt @@ -0,0 +1,37 @@ +package com.dosei.music.scoreconverter.feature.chords.dictionary + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.dosei.music.scoreconverter.feature.chords.dictionary.data.ChordsRepository +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch + +class ChordsDictionaryViewModel( + repository: ChordsRepository +) : ViewModel() { + + private val _searchQuery = MutableStateFlow("") + + val state = combine( + _searchQuery, + repository.allChords.onStart { emit(emptyList()) } + ) { query, chords -> + + val filteredChords = if (query.isEmpty()) { + chords + } else { + chords.filter { it.name.contains(query) } + } + + ChordsDictionaryState( + content = filteredChords, + ) + } + + fun onSearch(query: String) { + viewModelScope.launch { _searchQuery.emit(query) } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/GuitarThumbnailTheme.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/GuitarThumbnailTheme.kt new file mode 100644 index 0000000..a3d9fa1 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/GuitarThumbnailTheme.kt @@ -0,0 +1,45 @@ +package com.dosei.music.scoreconverter.feature.chords.dictionary + +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.dosei.music.arpeggio.ChordDiagram +import com.dosei.music.arpeggio.Component +import com.dosei.music.arpeggio.theme.ArpeggioTheme +import com.dosei.music.arpeggio.theme.Colors +import com.dosei.music.arpeggio.theme.FormatInitialFret +import com.dosei.music.arpeggio.theme.Sizes +import com.dosei.music.arpeggio.theme.Typography +import com.dosei.music.scoreconverter.feature.chords.dictionary.data.Chords +import com.dosei.music.scoreconverter.ui.theme.AppTheme + +@Composable +fun GuitarThumbnailTheme( + content: @Composable () -> Unit +) { + ArpeggioTheme( + typography = Typography( + name = TextStyle().copy(fontSize = 18.sp, fontWeight = FontWeight.Bold), + firstFretIndicator = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.SemiBold), + fingerIndicator = TextStyle( + fontSize = 10.sp, + color = MaterialTheme.colorScheme.onPrimary + ) + ), + sizes = Sizes(position = 16.dp), + content = content, + formatInitialFret = FormatInitialFret.ptBr(), + colors = Colors( + grid = MaterialTheme.colorScheme.onSurface, + position = MaterialTheme.colorScheme.onSurface, + stringUsageIndicator = MaterialTheme.colorScheme.onSurface, + ) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Shapes.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/Shapes.kt similarity index 98% rename from app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Shapes.kt rename to app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/Shapes.kt index d50fb44..05cec66 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Shapes.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/Shapes.kt @@ -1,7 +1,8 @@ -package com.dosei.music.scoreconverter.chords.dictionary +package com.dosei.music.scoreconverter.feature.chords.dictionary import androidx.compose.ui.text.AnnotatedString import com.dosei.music.arpeggio.* +import com.dosei.music.scoreconverter.feature.chords.dictionary.data.Chord object Shapes { diff --git a/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Utility.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/Utility.kt similarity index 93% rename from app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Utility.kt rename to app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/Utility.kt index f0ff079..91547bb 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Utility.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/Utility.kt @@ -1,4 +1,4 @@ -package com.dosei.music.scoreconverter.chords.dictionary +package com.dosei.music.scoreconverter.feature.chords.dictionary import com.dosei.music.arpeggio.* diff --git a/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Chord.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/Chord.kt similarity index 71% rename from app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Chord.kt rename to app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/Chord.kt index 34e0934..ca9e156 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Chord.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/Chord.kt @@ -1,4 +1,4 @@ -package com.dosei.music.scoreconverter.chords.dictionary +package com.dosei.music.scoreconverter.feature.chords.dictionary.data import androidx.compose.ui.text.AnnotatedString import com.dosei.music.arpeggio.Component diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/ChordParser.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/ChordParser.kt new file mode 100644 index 0000000..6b47e91 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/ChordParser.kt @@ -0,0 +1,91 @@ +package com.dosei.music.scoreconverter.feature.chords.dictionary.data + +import androidx.compose.ui.text.AnnotatedString +import com.dosei.music.arpeggio.Barre +import com.dosei.music.arpeggio.Finger +import com.dosei.music.arpeggio.OpenString +import com.dosei.music.arpeggio.Position + +class ChordParser { + + fun parse(pattern: String): Chord { + val split = pattern.split(GROUP_DELIMITER) + val name = split.getOrNull(SLOT_NAME).orEmpty() + + val positions = split.subList(SLOT_FIRST_POSITION, split.size) + .mapNotNull { parseGroup(it) } + + val positionsWithBarres = positions + .groupBy { (it as? Position)?.finger } + .map { (finger, positions) -> + when { + finger == null -> positions + positions.size == 1 -> positions + else -> { + val minString = positions.minOf { it.string } + val maxString = positions.maxOf { it.string } + val barre = Barre( + fret = positions.first().fret, + strings = minString..maxString, + finger = finger + ) + listOf(barre) + } + } + } + .flatten() + .map { if (it is Position && it.fret == 0) OpenString(it.string) else it } + + return Chord(AnnotatedString(name), positionsWithBarres) + } + + private fun parseGroup(group: String): Position? { + return when { + REGEX_POSITION_WITH_FINGER.matches(group) -> { + val values = group.split(VALUE_DELIMITER) + Position( + fret = values[SLOT_POSITION_FRET].toInt(), + string = values[SLOT_POSITION_STRING].toInt(), + finger = values[SLOT_POSITION_FINGER].toFinger() + ) + } + REGEX_POSITION.matches(group) -> { + val values = group.split(VALUE_DELIMITER) + Position( + fret = values[SLOT_POSITION_FRET].toInt(), + string = values[SLOT_POSITION_STRING].toInt(), + finger = null + ) + } + else -> null + } + } + + private fun String.toFinger() = when (this) { + FINGER_INDEX -> Finger.Index + FINGER_MIDDLE -> Finger.Middle + FINGER_RING -> Finger.Ring + FINGER_PINKY -> Finger.Pinky + else -> null + } + + companion object { + const val GROUP_DELIMITER: String = "," + const val VALUE_DELIMITER: String = "." + + private const val SLOT_NAME = 0 + private const val SLOT_FIRST_POSITION = 1 + + private const val FINGER_INDEX = "i" + private const val FINGER_MIDDLE = "m" + private const val FINGER_RING = "r" + private const val FINGER_PINKY = "p" + + private const val SLOT_POSITION_STRING = 0 + private const val SLOT_POSITION_FRET = 1 + private const val SLOT_POSITION_FINGER = 2 + + private val REGEX_POSITION_WITH_FINGER = "\\d+\\.\\d+\\.[imrp]".toRegex() + private val REGEX_POSITION = "\\d+\\.\\d+".toRegex() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Chords.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/Chords.kt similarity index 93% rename from app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Chords.kt rename to app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/Chords.kt index 278cc20..b432208 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/chords/dictionary/Chords.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/Chords.kt @@ -1,10 +1,16 @@ -package com.dosei.music.scoreconverter.chords.dictionary +package com.dosei.music.scoreconverter.feature.chords.dictionary.data import androidx.compose.ui.text.AnnotatedString import com.dosei.music.arpeggio.Barre import com.dosei.music.arpeggio.Component import com.dosei.music.arpeggio.OpenString import com.dosei.music.arpeggio.Position +import com.dosei.music.scoreconverter.feature.chords.dictionary.Shapes +import com.dosei.music.scoreconverter.feature.chords.dictionary.i +import com.dosei.music.scoreconverter.feature.chords.dictionary.m +import com.dosei.music.scoreconverter.feature.chords.dictionary.o +import com.dosei.music.scoreconverter.feature.chords.dictionary.p +import com.dosei.music.scoreconverter.feature.chords.dictionary.r object Chords { /* diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/ChordsRepository.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/ChordsRepository.kt new file mode 100644 index 0000000..9d3f77a --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/ChordsRepository.kt @@ -0,0 +1,8 @@ +package com.dosei.music.scoreconverter.feature.chords.dictionary.data + +import com.dosei.music.scoreconverter.feature.chords.dictionary.data.Chord +import kotlinx.coroutines.flow.Flow + +interface ChordsRepository { + val allChords: Flow> +} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/LocalChordsRepository.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/LocalChordsRepository.kt new file mode 100644 index 0000000..9279b16 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/LocalChordsRepository.kt @@ -0,0 +1,25 @@ +package com.dosei.music.scoreconverter.feature.chords.dictionary.data + +import com.dosei.music.scoreconverter.toolbox.AssetsReader +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.withContext + +class LocalChordsRepository( + private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO), + private val assetsReader: AssetsReader, + private val chordParser: ChordParser +) : ChordsRepository { + + override val allChords: Flow> = flow { + val chords = withContext(scope.coroutineContext) { + assetsReader + .readLines("chords.csv") + .map { line -> chordParser.parse(line) } + .toList() + } + emit(chords) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/transposer/Transposer.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/transposer/Transposer.kt new file mode 100644 index 0000000..9b736e8 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/transposer/Transposer.kt @@ -0,0 +1,160 @@ +package com.dosei.music.scoreconverter.feature.chords.transposer + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.* +import androidx.compose.material3.BottomAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.dosei.music.scoreconverter.R +import com.dosei.music.scoreconverter.main.Feature +import com.dosei.music.scoreconverter.toolbox.AdvertView +import com.dosei.music.scoreconverter.ui.view.MenuButton +import org.koin.java.KoinJavaComponent.get + +@Composable +fun TransposerScreen( + modifier: Modifier = Modifier, + onMenuClick: () -> Unit +) { + val viewModel: TransposerViewModel = get(TransposerViewModel::class.java) + val song = remember { mutableStateOf(TextFieldValue("")) } + val semitones = remember { mutableStateOf(0) } + + TransposerScreenContent( + modifier = modifier, + song = song.value, + onChangeSong = { song.value = it }, + onAddSemitone = { semitones.value += 1 }, + onRemoveSemitone = { semitones.value -= 1 }, + onBeautify = { + val formatted = viewModel.onBeautify(song.value.annotatedString) + song.value = song.value.copy(annotatedString = formatted) + }, + onCopy = { viewModel.onCopy(song.value.annotatedString) }, + onTranspose = { + val transposed = viewModel.onTranspose(song.value.annotatedString, semitones.value) + song.value = song.value.copy(annotatedString = transposed) + semitones.value = 0 + }, + onMenuClick = onMenuClick + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TransposerScreenContent( + modifier: Modifier = Modifier, + song: TextFieldValue, + onChangeSong: (TextFieldValue) -> Unit, + onAddSemitone: () -> Unit, + onRemoveSemitone: () -> Unit, + onBeautify: () -> Unit, + onCopy: () -> Unit, + onTranspose: () -> Unit, + onMenuClick: () -> Unit +) { + Scaffold( + topBar = { + TopAppBar( + navigationIcon = { MenuButton(onClick = onMenuClick) }, + title = { Text(text = stringResource(id = Feature.Transposer.nameRes)) } + ) + }, + contentWindowInsets = WindowInsets(16.dp, 16.dp, 16.dp, 16.dp), + bottomBar = { + BottomAppBar( + actions = { + IconButton(onClick = onBeautify) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_auto_fix_high_24), + contentDescription = stringResource(id = R.string.transposer_beautify_button) + ) + } + IconButton(onClick = onCopy) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_content_copy_24), + contentDescription = stringResource(id = R.string.transposer_copy_button) + ) + } + IconButton( + onClick = { onRemoveSemitone(); onTranspose() } + ) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_remove_24), + contentDescription = stringResource(id = R.string.transposer_remove_semitone_button) + ) + } + IconButton(onClick = { onAddSemitone(); onTranspose() }) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_add_24), + contentDescription = stringResource(id = R.string.transposer_add_semitone_button) + ) + } + } + ) + } + ) { padding -> + Column(modifier.padding(padding)) { + OutlinedTextField( + modifier = Modifier + .fillMaxSize() + .weight(1f), + textStyle = LocalTextStyle + .current + .copy(fontFamily = FontFamily.Monospace), + placeholder = { + Text( + fontFamily = FontFamily.Monospace, + text = stringResource(id = R.string.transposer_your_chords_here) + ) + }, + value = song, + onValueChange = { content -> onChangeSong(content) }, + ) + Spacer(Modifier.height(8.dp)) + AdvertView(unitId = R.string.admob_transposer_banner_id) + Spacer(Modifier.height(8.dp)) + } + } +} + +@Preview(showBackground = true, locale = "pt-rBR") +@Composable +private fun PreviewTransposer() { + Surface(modifier = Modifier.fillMaxSize(), color = Color.White) { + TransposerScreenContent( + modifier = Modifier, + song = TextFieldValue(), + onChangeSong = {}, + onAddSemitone = {}, + onRemoveSemitone = {}, + onBeautify = {}, + onCopy = {}, + onTranspose = {}, + onMenuClick = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/transposer/TransposerViewModel.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/transposer/TransposerViewModel.kt similarity index 94% rename from app/src/main/java/com/dosei/music/scoreconverter/transposer/TransposerViewModel.kt rename to app/src/main/java/com/dosei/music/scoreconverter/feature/chords/transposer/TransposerViewModel.kt index 37a6647..1ab2b0e 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/transposer/TransposerViewModel.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/chords/transposer/TransposerViewModel.kt @@ -1,4 +1,4 @@ -package com.dosei.music.scoreconverter.transposer +package com.dosei.music.scoreconverter.feature.chords.transposer import androidx.compose.ui.text.AnnotatedString import androidx.lifecycle.LiveData diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/converter/tablature/ScoreToTablature.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/converter/tablature/ScoreToTablature.kt new file mode 100644 index 0000000..f9ed897 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/converter/tablature/ScoreToTablature.kt @@ -0,0 +1,184 @@ +package com.dosei.music.scoreconverter.feature.converter.tablature + +import android.content.res.Configuration +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.PlainTooltipBox +import androidx.compose.material3.PlainTooltipState +import androidx.compose.material3.RichTooltipBox +import androidx.compose.material3.RichTooltipState +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringArrayResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.ParagraphStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextIndent +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.dosei.music.arpeggio.Score +import com.dosei.music.arpeggio.ScoreNote +import com.dosei.music.arpeggio.ScoreNoteDecoration +import com.dosei.music.arpeggio.Tablature +import com.dosei.music.scoreconverter.R +import com.dosei.music.scoreconverter.domain.Guitar +import com.dosei.music.scoreconverter.main.Feature +import com.dosei.music.scoreconverter.main.toNote +import com.dosei.music.scoreconverter.toolbox.AdvertView +import com.dosei.music.scoreconverter.ui.theme.AppTheme +import com.dosei.music.scoreconverter.ui.view.MenuButton +import kotlinx.coroutines.launch + +@Composable +fun ScoreToTablatureScreen( + modifier: Modifier = Modifier, + onMenuClick: () -> Unit +) { + ScoreToTablatureContent(modifier, onMenuClick) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ScoreToTablatureContent( + modifier: Modifier = Modifier, + onMenuClick: () -> Unit +) { + val guitar = Guitar.default() + var currentNote by remember { mutableStateOf(ScoreNote.E2) } + var decorator by remember { mutableStateOf(ScoreNoteDecoration.NATURAL) } + val interactionSource = remember { MutableInteractionSource() } + val helpState = remember { RichTooltipState() } + val scope = rememberCoroutineScope() + + Scaffold( + topBar = { + TopAppBar( + navigationIcon = { MenuButton(onClick = onMenuClick) }, + title = { Text(stringResource(id = Feature.ScoreToTablature.nameRes)) }, + actions = { + RichTooltipBox( + modifier = Modifier, + tooltipState = helpState, + title = { Text(stringResource(id = R.string.action_help)) }, + text = { Text(buildHelpText()) }, + action = { + Button( + onClick = { scope.launch { helpState.dismiss() } }, + content = { Text(text = stringResource(R.string.understood)) } + ) + } + ) { + IconButton(onClick = { scope.launch { helpState.show() } }) { + Icon( + painter = painterResource(id = R.drawable.outline_help_outline_24), + contentDescription = stringResource(R.string.action_help) + ) + } + } + } + ) + }, + contentWindowInsets = WindowInsets(16.dp, 16.dp, 16.dp, 16.dp) + ) { padding -> + Column(modifier.padding(padding)) { + val note = currentNote.index.toNote(decorator) + Text( + fontSize = 18.sp, + text = stringResource(id = R.string.current_note, note?.toString().orEmpty()), + style = MaterialTheme.typography.labelMedium + ) + Spacer(modifier = Modifier.height(16.dp)) + Tablature( + modifier = Modifier.padding(8.dp), + positions = guitar.tuning.run { + if (note != null) { + mapOf( + 1 to string1.positionOf(note), + 2 to string2.positionOf(note), + 3 to string3.positionOf(note), + 4 to string4.positionOf(note), + 5 to string5.positionOf(note), + 6 to string6.positionOf(note), + ) + } else { + mapOf() + } + } + ) + Spacer(modifier = Modifier.weight(1f)) + Score( + modifier = Modifier + .padding(horizontal = 20.dp) + .testTag("score") + .clickable(interactionSource = interactionSource, indication = null) { + decorator = when (decorator) { + ScoreNoteDecoration.FLAT -> ScoreNoteDecoration.NATURAL + ScoreNoteDecoration.NATURAL -> ScoreNoteDecoration.SHARP + ScoreNoteDecoration.SHARP -> ScoreNoteDecoration.FLAT + } + }, + currentNote = currentNote, + showSupplementaryLines = false, + onUpdateNoteIndex = { currentNote = ScoreNote.getByIndex(it) ?: ScoreNote.E2 }, + noteDecoration = decorator + ) + Spacer(modifier = Modifier.height(16.dp)) + AdvertView( + modifier = Modifier.padding(top = 16.dp), + unitId = R.string.admob_home_banner_id + ) + } + } + +} + +@Composable +private fun buildHelpText(): AnnotatedString { + val bullet = "\u2022" + val items = stringArrayResource(id = R.array.score_to_tablature_help) + val paragraphStyle = ParagraphStyle( + textIndent = TextIndent(restLine = 15.sp), + ) + return buildAnnotatedString { + items.forEach { item -> + withStyle(paragraphStyle) { + append(bullet) + append("\t\t") + append(item) + } + } + } +} + +@Preview(showBackground = true, locale = "pt-rBR") +@Preview(showBackground = true, locale = "pt-rBR", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewComposableConverter() { + AppTheme { + Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { + ScoreToTablatureContent( + modifier = Modifier, {} + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/converter/tablature/ScoreToTablatureHelp.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/converter/tablature/ScoreToTablatureHelp.kt new file mode 100644 index 0000000..af2f896 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/converter/tablature/ScoreToTablatureHelp.kt @@ -0,0 +1,52 @@ +package com.dosei.music.scoreconverter.feature.converter.tablature + +import androidx.compose.foundation.layout.width +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.PlainTooltipBox +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringArrayResource +import androidx.compose.ui.text.ParagraphStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextIndent +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.dosei.music.scoreconverter.R + +@Composable +fun ScoreToTablatureHelp(modifier: Modifier = Modifier) { + ScoreToTablatureHelpContent(modifier) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ScoreToTablatureHelpContent(modifier: Modifier = Modifier) { + val bullet = "\u2022" + val items = stringArrayResource(id = R.array.score_to_tablature_help) + val paragraphStyle = ParagraphStyle( + textIndent = TextIndent(restLine = 15.sp), + ) + val text = buildAnnotatedString { + items.forEach { item -> + withStyle(paragraphStyle) { + append(bullet) + append("\t\t") + append(item) + } + } + } + PlainTooltipBox(modifier = modifier, tooltip = { Text(text) }) { + + } +} + +@Preview(locale = "pt-rBR") +@Composable +private fun PreviewScoreToTablatureHelp() { + ScoreToTablatureHelp( + modifier = Modifier.width(300.dp) + ) +} diff --git a/app/src/main/java/com/dosei/music/scoreconverter/feature/home/Home.kt b/app/src/main/java/com/dosei/music/scoreconverter/feature/home/Home.kt new file mode 100644 index 0000000..6d5f8c1 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/feature/home/Home.kt @@ -0,0 +1,86 @@ +package com.dosei.music.scoreconverter.feature.home + +import android.content.res.Configuration +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Info +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LargeTopAppBar +import androidx.compose.material3.ListItem +import androidx.compose.material3.MediumTopAppBar +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.dosei.music.scoreconverter.R +import com.dosei.music.scoreconverter.main.Feature +import com.dosei.music.scoreconverter.ui.theme.AppTheme + +@Composable +fun HomeScreen( + modifier: Modifier = Modifier, + onSelectFeature: (Feature) -> Unit, + onAboutClicked: () -> Unit +) { + HomeContent( + modifier = modifier, + onSelectFeature = onSelectFeature, + onAboutClicked = onAboutClicked + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HomeContent( + modifier: Modifier = Modifier, + onSelectFeature: (Feature) -> Unit, + onAboutClicked: () -> Unit +) { + Scaffold( + modifier = modifier, + topBar = { + MediumTopAppBar( + title = { Text(stringResource(id = R.string.app_name)) }, + ) + }, + contentWindowInsets = WindowInsets(0.dp, 16.dp, 0.dp, 16.dp) + ) { padding -> + LazyColumn(contentPadding = padding) { + items(Feature.values()) { feature -> + ListItem( + modifier = Modifier.clickable { onSelectFeature(feature) }, + headlineContent = { Text(text = stringResource(feature.nameRes)) }, + ) + Divider(Modifier.padding(horizontal = 16.dp)) + } + } + } +} + +@Preview(showBackground = true, locale = "pt-rBR") +@Preview(showBackground = true, locale = "pt-rBR", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewHome() { + AppTheme { + Surface(modifier = Modifier.fillMaxSize(), color = Color.White) { + HomeContent( + modifier = Modifier, + onSelectFeature = {}, + onAboutClicked = {} + ) + } + } +} diff --git a/app/src/main/java/com/dosei/music/scoreconverter/main/Feature.kt b/app/src/main/java/com/dosei/music/scoreconverter/main/Feature.kt index 3d32b37..0599cac 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/main/Feature.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/main/Feature.kt @@ -3,8 +3,8 @@ package com.dosei.music.scoreconverter.main import androidx.annotation.StringRes import com.dosei.music.scoreconverter.R -enum class Feature(@StringRes val nameRes: Int) { - ScoreToTablature(nameRes = R.string.menu_item_converter), - ChordsDictionary(nameRes = R.string.menu_item_chords_dictionary), - Transposer(nameRes = R.string.menu_item_transposer) +enum class Feature(@StringRes val nameRes: Int, val route: String) { + ScoreToTablature(R.string.menu_item_converter, "converter_tab"), + ChordsDictionary(R.string.menu_item_chords_dictionary, "dictionary"), + Transposer(R.string.menu_item_transposer, "transposer") } \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/main/FeatureMenu.kt b/app/src/main/java/com/dosei/music/scoreconverter/main/FeatureMenu.kt index 972d2ab..0d981e6 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/main/FeatureMenu.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/main/FeatureMenu.kt @@ -4,11 +4,10 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.* -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.material.Text +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.Bottom import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier @@ -23,7 +22,7 @@ fun FeatureMenu( columnScope: ColumnScope, activeFeature: Feature, features: List = Feature.values().toList(), - onClickFeature: (Feature) -> Unit + onClickFeature: (Feature) -> Unit, ) { columnScope.run { Row( @@ -36,7 +35,7 @@ fun FeatureMenu( Text( modifier = Modifier.fillMaxWidth().align(Bottom), text = stringResource(id = R.string.app_name), - style = MaterialTheme.typography.h6 + style = MaterialTheme.typography.headlineSmall ) } features.forEach { currentFeature -> @@ -58,7 +57,7 @@ fun FeatureMenu( @Composable fun ActiveMenuRow(text: String, onClick: () -> Unit) { val color = - if (isSystemInDarkTheme()) MaterialTheme.colors.onSurface else MaterialTheme.colors.primary + if (isSystemInDarkTheme()) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.primary Row( Modifier .height(48.dp) @@ -76,7 +75,7 @@ fun ActiveMenuRow(text: String, onClick: () -> Unit) { .padding(horizontal = 11.dp), text = text, color = color, - style = MaterialTheme.typography.subtitle2 + style = MaterialTheme.typography.labelMedium ) } } @@ -95,8 +94,8 @@ fun InactiveMenuRow(text: String, onClick: () -> Unit) { .align(CenterVertically) .padding(horizontal = 11.dp), text = text, - color = MaterialTheme.colors.onSurface, - style = MaterialTheme.typography.subtitle2 + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.labelMedium ) } } diff --git a/app/src/main/java/com/dosei/music/scoreconverter/main/MainActivity.kt b/app/src/main/java/com/dosei/music/scoreconverter/main/MainActivity.kt index a82111e..1e0f443 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/main/MainActivity.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/main/MainActivity.kt @@ -1,41 +1,18 @@ package com.dosei.music.scoreconverter.main -import android.content.Intent import android.os.Bundle -import android.view.Menu -import android.view.MenuItem import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Menu -import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import com.dosei.music.scoreconverter.R -import com.dosei.music.scoreconverter.about.AboutActivity -import com.dosei.music.scoreconverter.chords.dictionary.ChordsDictionary +import com.dosei.music.scoreconverter.navigation.Navigation import com.dosei.music.scoreconverter.toolbox.URL_PLAY_STORE -import com.dosei.music.scoreconverter.toolbox.goToPlayStore -import com.dosei.music.scoreconverter.toolbox.sendEmail import com.dosei.music.scoreconverter.toolbox.shareText -import com.dosei.music.scoreconverter.transposer.TransposerLoader import com.dosei.music.scoreconverter.ui.theme.AppTheme import com.google.android.gms.ads.MobileAds -import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { @@ -47,47 +24,6 @@ class MainActivity : AppCompatActivity() { MobileAds.initialize(this) {} } - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.menu_main, menu) - return true - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.rate_the_app -> rateThisApp() - R.id.share -> shareApp() - R.id.about -> redirectToAboutScreen() - R.id.contact_us -> redirectToEmailContact() - else -> return false - } - return true - } - - private fun rateThisApp() { - goToPlayStore( - activity = this, - appPackage = applicationContext.packageName - ) - } - - private fun redirectToAboutScreen() { - startActivity( - Intent(this, AboutActivity::class.java) - ) - } - - private fun redirectToEmailContact() { - val contactEmail = getString(R.string.saturn_dev_email) - val subject = getString(R.string.app_name) - val chooserTitle = getString(R.string.contact_chooser_title) - sendEmail( - activity = this, - recipients = arrayOf(contactEmail), - subject = subject, - chooserTitle = chooserTitle - ) - } - private fun shareApp() { val appName = getString(R.string.app_name) val appPlayStorePath = URL_PLAY_STORE + applicationContext.packageName @@ -101,67 +37,18 @@ class MainActivity : AppCompatActivity() { chooserTitle = getString(R.string.share_chooser_title) ) } - - companion object { - private const val SCORE_CONVERTER_TAG = "ScoreConverter" - } } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun MainContent() { - val selectedFeature = remember { mutableStateOf(Feature.ScoreToTablature) } - val scaffoldState = rememberScaffoldState() - val scope = rememberCoroutineScope() AppTheme { Surface { - Scaffold( - modifier = Modifier.fillMaxSize(), - scaffoldState = scaffoldState, - topBar = { - TopBar( - onClickMenu = { scope.launch { scaffoldState.drawerState.open() } } - ) - }, - drawerContent = { - FeatureMenu( - columnScope = this, - activeFeature = selectedFeature.value, - onClickFeature = { clickedFeature -> - scope.launch { - selectedFeature.value = clickedFeature - scaffoldState.drawerState.close() - } - } - ) - } - ) { - when (selectedFeature.value) { - Feature.ScoreToTablature -> ScoreToTablature(modifier = Modifier.fillMaxSize()) - Feature.ChordsDictionary -> ChordsDictionary(modifier = Modifier.fillMaxSize()) - Feature.Transposer -> TransposerLoader(modifier = Modifier.fillMaxSize()) - } - - } + Navigation() } } } -@Composable -private fun TopBar(onClickMenu: () -> Unit) { - TopAppBar( - title = { Text(stringResource(id = R.string.app_name)) }, - navigationIcon = { - Icon( - imageVector = Icons.Default.Menu, - modifier = Modifier - .padding(start = 20.dp) - .clickable(onClick = onClickMenu), - contentDescription = stringResource(id = R.string.menu) - ) - } - ) -} - @Preview(showBackground = true) @Composable fun PreviewMainContent() { diff --git a/app/src/main/java/com/dosei/music/scoreconverter/main/NoteMapper.kt b/app/src/main/java/com/dosei/music/scoreconverter/main/NoteMapper.kt index ff362b3..1fb825a 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/main/NoteMapper.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/main/NoteMapper.kt @@ -1,10 +1,10 @@ package com.dosei.music.scoreconverter.main +import com.dosei.music.arpeggio.ScoreNoteDecoration import com.dosei.music.scoreconverter.domain.Note import com.dosei.music.scoreconverter.domain.NoteModifier import com.dosei.music.scoreconverter.domain.OctavedNote import com.dosei.music.scoreconverter.ui.view.NotationNotes -import com.dosei.music.scoreconverter.ui.view.ScoreNoteDecoration fun Int.toNote(decoration: ScoreNoteDecoration): OctavedNote? { val modifier = when(decoration) { diff --git a/app/src/main/java/com/dosei/music/scoreconverter/main/ScoreToTablature.kt b/app/src/main/java/com/dosei/music/scoreconverter/main/ScoreToTablature.kt deleted file mode 100644 index f430f99..0000000 --- a/app/src/main/java/com/dosei/music/scoreconverter/main/ScoreToTablature.kt +++ /dev/null @@ -1,118 +0,0 @@ -package com.dosei.music.scoreconverter.main - -import androidx.compose.foundation.layout.* -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.dosei.music.scoreconverter.R -import com.dosei.music.scoreconverter.domain.Guitar -import com.dosei.music.scoreconverter.toolbox.AdvertView -import com.dosei.music.scoreconverter.ui.view.* - -@Composable -fun ScoreToTablature(modifier: Modifier = Modifier) { - val guitar = Guitar.default() - val noteIndex = remember { mutableStateOf(NotationNotes.E2.index) } - val selectedDecorationIndex = remember { mutableStateOf(1) } - val decorator = remember { mutableStateOf(ScoreNoteDecoration.NATURAL) } - Column(modifier.padding(16.dp)) { - Score( - modifier = Modifier - .padding(horizontal = 20.dp) - .weight(1f) - .testTag("score"), - noteIndex = noteIndex.value, - onUpdateNoteIndex = { noteIndex.value = it }, - noteDecoration = decorator.value - ) - val note = noteIndex.value.toNote(decorator.value) - Tablature( - modifier = Modifier.padding(8.dp), - positions = guitar.tuning.run { - if (note != null) { - mapOf( - 1 to string1.positionOf(note), - 2 to string2.positionOf(note), - 3 to string3.positionOf(note), - 4 to string4.positionOf(note), - 5 to string5.positionOf(note), - 6 to string6.positionOf(note), - ) - } else { - mapOf() - } - } - ) - Row( - modifier = Modifier - .fillMaxWidth() - .padding(top = 8.dp) - ) { - Text( - modifier = Modifier - .weight(1f) - .align(Alignment.CenterVertically), - fontSize = 18.sp, - fontWeight = FontWeight.Bold, - text = buildString { - append(stringResource(id = R.string.current_note)) - append(note?.toString().orEmpty()) - } - ) - - ToggleIconRow( - modifier = Modifier.padding(start = 8.dp), - selectedIndex = selectedDecorationIndex.value, - onSelectIndex = { - selectedDecorationIndex.value = it - when (it) { - 0 -> decorator.value = ScoreNoteDecoration.FLAT - 1 -> decorator.value = ScoreNoteDecoration.NATURAL - 2 -> decorator.value = ScoreNoteDecoration.SHARP - } - }, - iconSize = 24.dp, - icons = listOf( - Icon( - painter = painterResource(id = R.drawable.ic_flat_black), - description = stringResource(id = R.string.flat_button) - ), - Icon( - painter = painterResource(id = R.drawable.ic_natural_note), - description = stringResource(id = R.string.natural_button) - ), - Icon( - painter = painterResource(id = R.drawable.ic_sharp_black), - description = stringResource(id = R.string.sharp_button) - ) - ) - ) - } - AdvertView( - modifier = Modifier.padding(top = 16.dp), - unitId = R.string.admob_home_banner_id - ) - } -} - -@Preview(showBackground = true) -@Composable -private fun PreviewComposableConverter() { - Surface(modifier = Modifier.fillMaxSize(), color = Color.White) { - ScoreToTablature( - modifier = Modifier - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/navigation/AppDrawer.kt b/app/src/main/java/com/dosei/music/scoreconverter/navigation/AppDrawer.kt new file mode 100644 index 0000000..52d5834 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/navigation/AppDrawer.kt @@ -0,0 +1,90 @@ +package com.dosei.music.scoreconverter.navigation + +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.DrawerState +import androidx.compose.material3.DrawerValue +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalDrawerSheet +import androidx.compose.material3.ModalNavigationDrawer +import androidx.compose.material3.NavigationDrawerItem +import androidx.compose.material3.NavigationDrawerItemDefaults +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.dosei.music.scoreconverter.BuildConfig +import com.dosei.music.scoreconverter.R +import com.dosei.music.scoreconverter.main.Feature +import kotlinx.coroutines.launch + +@Composable +fun AppDrawer( + modifier: Modifier = Modifier, + drawerState: DrawerState, + currentFeature: Feature?, + onSelect: (Feature) -> Unit, + content: @Composable () -> Unit, +) { + val scope = rememberCoroutineScope() + val features = remember { Feature.values() } + ModalNavigationDrawer( + modifier = modifier, + drawerState = drawerState, + drawerContent = { + ModalDrawerSheet { + Spacer(Modifier.height(24.dp)) + Text( + modifier = Modifier.padding(horizontal = 28.dp), + style = MaterialTheme.typography.titleSmall, + text = stringResource(id = R.string.app_name) + ) + Spacer(Modifier.height(16.dp)) + features.forEach { item -> + NavigationDrawerItem( + modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding), + label = { Text(stringResource(item.nameRes)) }, + selected = item == currentFeature, + onClick = { + scope.launch { drawerState.close() } + onSelect(item) + } + ) + } + Spacer(Modifier.weight(1f)) + Text( + modifier = Modifier.padding(horizontal = 28.dp), + text = buildString { + append("v") + append(BuildConfig.VERSION_NAME) + }, + style = MaterialTheme.typography.labelMedium + ) + Spacer(Modifier.height(16.dp)) + } + }, + content = content + ) +} + +@Preview(showBackground = true, locale = "pt-rBR") +@Composable +private fun PreviewAppDrawer() { + Surface(modifier = Modifier.fillMaxSize(), color = Color.White) { + AppDrawer( + modifier = Modifier, + drawerState = DrawerState(DrawerValue.Open), + currentFeature = Feature.ScoreToTablature, + onSelect = {}, + content = {} + ) + } +} diff --git a/app/src/main/java/com/dosei/music/scoreconverter/navigation/Navigation.kt b/app/src/main/java/com/dosei/music/scoreconverter/navigation/Navigation.kt index 553609b..6ee823a 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/navigation/Navigation.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/navigation/Navigation.kt @@ -1,11 +1,25 @@ package com.dosei.music.scoreconverter.navigation +import androidx.compose.material3.DrawerValue +import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import com.dosei.music.scoreconverter.feature.chords.dictionary.ChordsDictionaryScreen +import com.dosei.music.scoreconverter.feature.chords.transposer.TransposerScreen +import com.dosei.music.scoreconverter.feature.converter.tablature.ScoreToTablatureScreen +import com.dosei.music.scoreconverter.feature.home.HomeScreen +import com.dosei.music.scoreconverter.main.Feature +import kotlinx.coroutines.launch +import org.koin.androidx.compose.koinViewModel @Composable fun Navigation(modifier: Modifier = Modifier) { @@ -15,9 +29,65 @@ fun Navigation(modifier: Modifier = Modifier) { @Composable private fun MainNavigation(navController: NavHostController) { - NavHost(navController = navController, startDestination = "profile") { - composable("converter") { } - composable("chordList") { } - /*...*/ + val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) + var selectedFeature by remember { mutableStateOf(null) } + val scope = rememberCoroutineScope() + + NavHost(navController = navController, startDestination = "home") { + composable("home") { + HomeScreen( + onSelectFeature = { + navController.navigate(it.route) + selectedFeature = it + }, + onAboutClicked = {} + ) + } + composable("about") { + + } + composable("converter_tab") { + AppDrawer( + drawerState = drawerState, + currentFeature = selectedFeature, + onSelect = { + navController.navigate(it.route) + selectedFeature = it + } + ) { + ScoreToTablatureScreen( + onMenuClick = { scope.launch { drawerState.open() } } + ) + } + } + composable("dictionary") { + AppDrawer( + drawerState = drawerState, + currentFeature = selectedFeature, + onSelect = { + navController.navigate(it.route) + selectedFeature = it + } + ) { + ChordsDictionaryScreen( + viewModel = koinViewModel(), + onMenuClick = { scope.launch { drawerState.open() } } + ) + } + } + composable("transposer") { + AppDrawer( + drawerState = drawerState, + currentFeature = selectedFeature, + onSelect = { + navController.navigate(it.route) + selectedFeature = it + } + ) { + TransposerScreen( + onMenuClick = { scope.launch { drawerState.open() } } + ) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/toolbox/AdvertView.kt b/app/src/main/java/com/dosei/music/scoreconverter/toolbox/AdvertView.kt index b40cae4..b6bc720 100644 --- a/app/src/main/java/com/dosei/music/scoreconverter/toolbox/AdvertView.kt +++ b/app/src/main/java/com/dosei/music/scoreconverter/toolbox/AdvertView.kt @@ -3,7 +3,7 @@ package com.dosei.music.scoreconverter.toolbox import androidx.compose.foundation.background import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -35,7 +35,7 @@ fun AdvertView(modifier: Modifier = Modifier, unitId: Int) { modifier = modifier.fillMaxWidth(), factory = { context -> AdView(context).apply { - adSize = AdSize.BANNER + setAdSize(AdSize.BANNER) adUnitId = context.getString(unitId) loadAd(AdRequest.Builder().build()) } diff --git a/app/src/main/java/com/dosei/music/scoreconverter/toolbox/AssetsReader.kt b/app/src/main/java/com/dosei/music/scoreconverter/toolbox/AssetsReader.kt new file mode 100644 index 0000000..f67a519 --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/toolbox/AssetsReader.kt @@ -0,0 +1,11 @@ +package com.dosei.music.scoreconverter.toolbox + +import android.content.Context + +class AssetsReader( + private val context: Context +) { + + fun readLines(filename: String): Sequence = + context.assets.open(filename).bufferedReader().lineSequence() +} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/toolbox/TonesUtil.kt b/app/src/main/java/com/dosei/music/scoreconverter/toolbox/TonesUtil.kt new file mode 100644 index 0000000..b89fdef --- /dev/null +++ b/app/src/main/java/com/dosei/music/scoreconverter/toolbox/TonesUtil.kt @@ -0,0 +1,32 @@ +package com.dosei.music.scoreconverter.toolbox + +import androidx.compose.runtime.Composable +import java.text.DecimalFormat +import java.util.Locale + +private const val SEMITONES_IN_TONE = 2 + +@Composable +fun Int.formatSemitonesToTones(): String { + val semitones = this.toDouble() + val tones = semitones / SEMITONES_IN_TONE + return DecimalFormat.getInstance(Locale.getDefault()) + .run { + maximumFractionDigits = 1 + format(tones) + } + .let { if (tones > 0.0) "+$it" else it } + .orEmpty() +} + +@Composable +fun Int.formatTonesShort(): String { + val semitones = this.toDouble() + val tones = semitones / SEMITONES_IN_TONE + return DecimalFormat.getInstance(Locale.getDefault()) + .run { + maximumFractionDigits = 1 + format(tones) + } + .let { if (tones > 0.0) "+${it}T" else "${it}T" } +} \ No newline at end of file diff --git a/app/src/main/java/com/dosei/music/scoreconverter/transposer/Transposer.kt b/app/src/main/java/com/dosei/music/scoreconverter/transposer/Transposer.kt deleted file mode 100644 index 9299d68..0000000 --- a/app/src/main/java/com/dosei/music/scoreconverter/transposer/Transposer.kt +++ /dev/null @@ -1,179 +0,0 @@ -package com.dosei.music.scoreconverter.transposer - -import android.widget.Toast -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.dosei.music.scoreconverter.R -import com.dosei.music.scoreconverter.toolbox.AdvertView -import com.dosei.music.scoreconverter.ui.view.Icon -import com.dosei.music.scoreconverter.ui.view.ToggleIconRow -import org.koin.java.KoinJavaComponent.get - -@Composable -fun TransposerLoader( - modifier: Modifier = Modifier -) { - val viewModel: TransposerViewModel = get(TransposerViewModel::class.java) - val song = remember { mutableStateOf(TextFieldValue("")) } - val semitones = remember { mutableStateOf(0) } - - Transposer( - modifier = modifier, - song = song.value, - toastMessage = null, - semitones = semitones.value, - onChangeSong = { song.value = it }, - onAddSemitone = { semitones.value += 1 }, - onRemoveSemitone = { semitones.value -= 1 }, - onBeautify = { - val formatted = viewModel.onBeautify(song.value.annotatedString) - song.value = song.value.copy(annotatedString = formatted) - }, - onCopy = { viewModel.onCopy(song.value.annotatedString) }, - onTranspose = { - val transposed = viewModel.onTranspose(song.value.annotatedString, semitones.value) - song.value = song.value.copy(annotatedString = transposed) - semitones.value = 0 - } - ) -} - -@Composable -fun Transposer( - modifier: Modifier = Modifier, - song: TextFieldValue, - toastMessage: Unit?, - semitones: Int, - onChangeSong: (TextFieldValue) -> Unit, - onAddSemitone: () -> Unit, - onRemoveSemitone: () -> Unit, - onBeautify: () -> Unit, - onCopy: () -> Unit, - onTranspose: () -> Unit, -) { - Column(modifier.padding(16.dp)) { - AdvertView(unitId = R.string.admob_transposer_banner_id) - - OutlinedTextField( - modifier = Modifier - .padding(top = 8.dp) - .fillMaxSize() - .weight(1f), - textStyle = LocalTextStyle - .current - .copy(fontFamily = FontFamily.Monospace), - placeholder = { - Text( - fontFamily = FontFamily.Monospace, - text = stringResource(id = R.string.transposer_your_chords_here) - ) - }, - value = song, - onValueChange = { content -> onChangeSong(content) } - ) - - Text( - modifier = Modifier.align(Alignment.End), - fontSize = 16.sp, - text = buildAnnotatedString { - withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { - if (semitones >= 0) append('+') - append(semitones.toString()) - } - append(' ') - append(stringResource(id = R.string.transposer_semitones_diff)) - } - ) - - ToggleIconRow( - modifier = Modifier - .align(Alignment.CenterHorizontally) - .padding(top = 16.dp), - selectedIndex = -1, - onSelectIndex = { - when (it) { - 0 -> onBeautify() - 1 -> onCopy() - 2 -> onRemoveSemitone() - 3 -> onAddSemitone() - } - }, - cornerRadius = 4.dp, - iconSize = 24.dp, - icons = listOf( - Icon( - painter = painterResource(id = R.drawable.ic_baseline_auto_fix_high_24), - description = stringResource(id = R.string.transposer_beautify_button) - ), - Icon( - painter = painterResource(id = R.drawable.ic_baseline_content_copy_24), - description = stringResource(id = R.string.transposer_copy_button) - ), - Icon( - painter = painterResource(id = R.drawable.ic_baseline_remove_24), - description = stringResource(id = R.string.transposer_remove_semitone_button) - ), - Icon( - painter = painterResource(id = R.drawable.ic_baseline_add_24), - description = stringResource(id = R.string.transposer_add_semitone_button) - ), - ) - ) - - Button( - modifier = Modifier - .fillMaxWidth() - .padding(top = 8.dp), - onClick = onTranspose - ) { - Text( - fontSize = 18.sp, - text = stringResource(id = R.string.transposer_transpose_button) - ) - } - } - - toastMessage?.let { - Toast.makeText(LocalContext.current, "Value Copied!", Toast.LENGTH_LONG).show() - } -} - -@Preview(showBackground = true) -@Composable -private fun PreviewTransposer() { - Surface(modifier = Modifier.fillMaxSize(), color = Color.White) { - Transposer( - modifier = Modifier, - song = TextFieldValue(), - toastMessage = null, - semitones = 3, - onChangeSong = {}, - onAddSemitone = {}, - onRemoveSemitone = {}, - onBeautify = {}, - onCopy = {}, - onTranspose = {} - ) - } -} \ No newline at end of file diff --git a/app/src/main/res/drawable/baseline_history_24.xml b/app/src/main/res/drawable/baseline_history_24.xml new file mode 100644 index 0000000..a474997 --- /dev/null +++ b/app/src/main/res/drawable/baseline_history_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_loop_24.xml b/app/src/main/res/drawable/ic_baseline_loop_24.xml deleted file mode 100644 index c2f773a..0000000 --- a/app/src/main/res/drawable/ic_baseline_loop_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_menu_24.xml b/app/src/main/res/drawable/ic_baseline_menu_24.xml deleted file mode 100644 index 4350ba9..0000000 --- a/app/src/main/res/drawable/ic_baseline_menu_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_info_outline_black_24dp.xml b/app/src/main/res/drawable/ic_info_outline_black_24dp.xml deleted file mode 100644 index 9c51a4f..0000000 --- a/app/src/main/res/drawable/ic_info_outline_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_mail_outline_black_24dp.xml b/app/src/main/res/drawable/ic_mail_outline_black_24dp.xml deleted file mode 100644 index efd9eb3..0000000 --- a/app/src/main/res/drawable/ic_mail_outline_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_more_vert_white_24dp.xml b/app/src/main/res/drawable/ic_more_vert_white_24dp.xml deleted file mode 100644 index 3983cd4..0000000 --- a/app/src/main/res/drawable/ic_more_vert_white_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_share_black_24dp.xml b/app/src/main/res/drawable/ic_share_black_24dp.xml deleted file mode 100644 index e81a92b..0000000 --- a/app/src/main/res/drawable/ic_share_black_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_star_border_black_24dp.xml b/app/src/main/res/drawable/ic_star_border_black_24dp.xml deleted file mode 100644 index c452e47..0000000 --- a/app/src/main/res/drawable/ic_star_border_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/outline_help_outline_24.xml b/app/src/main/res/drawable/outline_help_outline_24.xml new file mode 100644 index 0000000..d2f9639 --- /dev/null +++ b/app/src/main/res/drawable/outline_help_outline_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout-land/fragment_score_converter.xml b/app/src/main/res/layout-land/fragment_score_converter.xml deleted file mode 100644 index 98dc47e..0000000 --- a/app/src/main/res/layout-land/fragment_score_converter.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml deleted file mode 100644 index dd00d3c..0000000 --- a/app/src/main/res/layout/activity_about.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 091dc90..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_score_converter.xml b/app/src/main/res/layout/fragment_score_converter.xml deleted file mode 100644 index 5bd6319..0000000 --- a/app/src/main/res/layout/fragment_score_converter.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml deleted file mode 100644 index f5d84c8..0000000 --- a/app/src/main/res/menu/menu_main.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 61b7e9c..c753217 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1,7 +1,7 @@ - Partitura para Tablatura - Nota: + Kit Violão + Nota selecionada: %s Partitura Tablatura @@ -18,21 +18,44 @@ Olá! Dê uma olhada neste aplicativo incrível!\n\n%1s\n%2s Enviar e-mail para: Sobre o app - Esta ferramenta foi feita com o objetivo de auxiliar estudantes de violão erudito na memorização das posições de notas de partitura no braço de um violão.\n\nSugestões são bem vindas!\nSe você tem alguma funcionalidade ou melhoria que gostaria de ver neste app, entre em contato conosco clicando aqui. + Esta ferramenta foi feita com o objetivo de auxiliar estudantes de violão na memorização das posições de notas de partitura no braço de um violão.\n\nSugestões são bem vindas!\nSe você tem alguma funcionalidade ou melhoria que gostaria de ver neste app, entre em contato conosco clicando aqui. Versão %s Botão Sustenido Botão Natural Botão Bemol Menu + Partitura para Tablatura + Partitura para Posições no Braço Dicionário de Acordes - Tranpositor de Cifras + Transpositor de Cifras - Sua cifra aqui... + "D7 C7 G7\nAdicione sua cifra aqui!!" Botão remover semitom Botão adicionar semitom Transpor semitons Botão copiar Botão formatar + Funcionalidades + Sobre + Ajuda + Entendi! + Pesquisar + Pesquisar acordes + Item do histórico + Transpor cifra + Cifra + + + Selecione uma nota arrastando a partitura verticalmente + Altere para sustenido ou bemol clicando na partitura + Veja as posições da nota selecionada na tablatura + + + + %s tom + %s tons + %s tons + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d3d85b0..d0e819d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ - Score to Tablature - Note: + Guitar Kit + Selected note: %s Score Tablature @@ -25,16 +25,37 @@ Natural Button Flat Button Menu + Score to Tablature + Score to Guitar Fretboard Chords Dictionary - Tranposer + Chords Tranposer - Your song chords here... + "D7 C7 G7\nPlace your lyrics like this!" Remove semitone button Add semitone button Transpose semitones Copy button Beautify button + Features + About + Help + Understood! + Search + Search chords + History item + Transpose + Lyrics and Chords + + Selecione a nota a ser convertida arrastando a partitura verticalmente; + Altere os modificadores (sustenido e bemol) clicando na partitura; + Veja as posições disponíveis para a nota selecionada na tablatura. + + + + %s tone + %s tones + diff --git a/app/src/test/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/ChordParserTest.kt b/app/src/test/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/ChordParserTest.kt new file mode 100644 index 0000000..58ae341 --- /dev/null +++ b/app/src/test/java/com/dosei/music/scoreconverter/feature/chords/dictionary/data/ChordParserTest.kt @@ -0,0 +1,80 @@ +package com.dosei.music.scoreconverter.feature.chords.dictionary.data + +import com.dosei.music.arpeggio.Barre +import com.dosei.music.arpeggio.Finger +import com.dosei.music.arpeggio.OpenString +import com.dosei.music.arpeggio.Position +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test + +class ChordParserTest { + + private lateinit var parser: ChordParser + + @Before + fun before() { + parser = ChordParser() + } + + @Test + fun `parse valid chord with barre`() { + val pattern = "C#m,5.4.i,4.6.r,3.6.p,2.5.m,1.4.i,6.0" + + with(parser.parse(pattern)) { + assertEquals("C#m", name.toString()) + assertEquals(5, components.size) + + assert(Barre(4, 1..5, Finger.Index) in components) + assert(Position(6, 4, Finger.Ring) in components) + assert(Position(6, 3, Finger.Pinky) in components) + assert(Position(5, 2, Finger.Middle) in components) + assert(OpenString(6) in components) + } + } + + @Test + fun `parse valid chord with empty finger slots`() { + val pattern = "Am,6.5.i,5.7.r,4.7.p,3.5.i,2.5.i,1.5.i" + + with(parser.parse(pattern)) { + assertEquals("Am", name.toString()) + assertEquals(3, components.size) + assert(Barre(5, 1..6, Finger.Index) in components) + assert(Position(7, 5, Finger.Ring) in components) + assert(Position(7, 4, Finger.Pinky) in components) + } + } + + @Test + fun `parse valid chord with multiple open chords`() { + val pattern = "C,5.3.r,4.2.m,3.0,2.1.i,1.0" + + with(parser.parse(pattern)) { + assertEquals("C", name.toString()) + assertEquals(5, components.size) + assert(Position(3, 5, Finger.Ring) in components) + assert(Position(2, 4, Finger.Middle) in components) + assert(Position(1, 2, Finger.Index) in components) + assert(OpenString(3) in components) + assert(OpenString(1) in components) + } + } + + @Test + fun `parse chord with all strings`() { + val pattern = "Em,6.0,5.2.m,4.2.r,3.0,2.0,1.0" + + with(parser.parse(pattern)) { + assertEquals("Em", name.toString()) + assertEquals(6, components.size) + assert(OpenString(6) in components) + assert(OpenString(3) in components) + assert(OpenString(2) in components) + assert(OpenString(1) in components) + assert(Position(2, 4, Finger.Ring) in components) + assert(Position(2, 5, Finger.Middle) in components) + } + } + +} \ No newline at end of file diff --git a/arpeggio/.gitignore b/arpeggio/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/arpeggio/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/arpeggio/build.gradle b/arpeggio/build.gradle deleted file mode 100644 index 19d03fe..0000000 --- a/arpeggio/build.gradle +++ /dev/null @@ -1,48 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - compileSdk 31 - - defaultConfig { - minSdk 21 - targetSdk 31 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - kotlinOptions { - jvmTarget = '1.8' - } - buildFeatures { - compose = true - } - - composeOptions { - kotlinCompilerExtensionVersion = "1.1.0-rc01" - } -} - -dependencies { - implementation libs.core.ktx - implementation libs.app.compat - implementation libs.android.material - implementation libs.bundles.compose - implementation libs.lifecycle.runtime - - testImplementation libs.junit.core - androidTestImplementation libs.android.test.ext - androidTestImplementation libs.espresso.core - androidTestImplementation libs.compose.junit - debugImplementation libs.compose.tooling.ui -} \ No newline at end of file diff --git a/arpeggio/proguard-rules.pro b/arpeggio/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/arpeggio/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/arpeggio/src/androidTest/java/com/dosei/music/arpeggio/ExampleInstrumentedTest.kt b/arpeggio/src/androidTest/java/com/dosei/music/arpeggio/ExampleInstrumentedTest.kt deleted file mode 100644 index 3ca855d..0000000 --- a/arpeggio/src/androidTest/java/com/dosei/music/arpeggio/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.dosei.music.arpeggio - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.dosei.music.arpeggio", appContext.packageName) - } -} \ No newline at end of file diff --git a/arpeggio/src/main/AndroidManifest.xml b/arpeggio/src/main/AndroidManifest.xml deleted file mode 100644 index eb87b96..0000000 --- a/arpeggio/src/main/AndroidManifest.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/ChordDiagram.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/ChordDiagram.kt deleted file mode 100644 index 5dc0cee..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/ChordDiagram.kt +++ /dev/null @@ -1,108 +0,0 @@ -package com.dosei.music.arpeggio - -import androidx.compose.foundation.layout.* -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.dosei.music.arpeggio.grid.Grid -import com.dosei.music.arpeggio.theme.DiagramTheme - -@Composable -fun ChordDiagram( - modifier: Modifier = Modifier, - name: String, - components: List -) { - ChordDiagram( - modifier = modifier, - name = AnnotatedString(name), - components = components - ) -} - -@Composable -fun ChordDiagram( - modifier: Modifier = Modifier, - name: AnnotatedString, - components: List -) { - val typography = DiagramTheme.typography - val formatInitialFret = DiagramTheme.formatInitialFret - val initialFretRange = DiagramTheme.initialFretRange - - val firstFret = if (components.fitsInFretRange(initialFretRange)) { - DefaultInitialFret - } else { - components.firstFret() - } - Column( - modifier = modifier - .defaultMinSize(minWidth = MinWidth, minHeight = MinHeight) - ) { - Text( - text = name, - style = typography.name, - modifier = Modifier.align(Alignment.CenterHorizontally) - ) - Row(modifier = Modifier.padding(top = 8.dp)) { - if (firstFret != DefaultInitialFret) { - Text( - modifier = Modifier.width(40.dp), - textAlign = TextAlign.Center, - text = formatInitialFret(firstFret), - style = typography.firstFretIndicator - ) - } else { - Spacer(modifier = Modifier.width(40.dp)) - } - Grid( - modifier = Modifier.weight(1f), - initialFret = firstFret - ) { - components.forEach { draw(it) } - } - Spacer(modifier = Modifier.width(40.dp)) - } - } -} - -@Preview(showBackground = true) -@Composable -private fun PreviewDiagram() { - Surface(color = Color.White, modifier = Modifier.fillMaxSize()) { - ChordDiagram( - modifier = Modifier.padding(16.dp), - name = "Bm", - components = listOf( - Barre(fret = 2, strings = 1..5, finger = Finger.Index), - Position(fret = 3, string = 4, finger = Finger.Middle), - Position(fret = 4, string = 3, finger = Finger.Pinky), - Position(fret = 5, string = 2, finger = Finger.Ring), - ) - ) - } -} - -@Preview(showBackground = true) -@Composable -private fun PreviewInitialFret() { - Surface(color = Color.White, modifier = Modifier.fillMaxSize()) { - ChordDiagram( - modifier = Modifier.padding(16.dp), - name = "B", - components = listOf( - Barre(fret = 7, strings = 1..6, finger = Finger.Index), - Position(fret = 8, string = 3, finger = Finger.Middle), - Position(fret = 9, string = 4, finger = Finger.Pinky), - Position(fret = 9, string = 5, finger = Finger.Ring), - ) - ) - } -} \ No newline at end of file diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/Component.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/Component.kt deleted file mode 100644 index cdb485e..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/Component.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.dosei.music.arpeggio - -sealed class Component - -class Barre( - val fret: Int, - val strings: IntRange, - val finger: Finger? = null -) : Component() - -class Position( - val fret: Int, - val string: Int, - val finger: Finger? = null -) : Component() - -class OpenString(val string: Int) : Component() - -internal fun List.fitsInFretRange(fretRange: IntRange): Boolean = - fretRange.run { - contains(firstFret()) and contains(lastFret()) - } - -internal fun List.firstFret(): Int = - minOf { - when (it) { - is Barre -> it.fret - is Position -> it.fret - else -> Int.MAX_VALUE - } - } - -internal fun List.lastFret(): Int = - maxOf { - when (it) { - is Barre -> it.fret - is Position -> it.fret - else -> 0 - } - } \ No newline at end of file diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/Constants.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/Constants.kt deleted file mode 100644 index 63eed8d..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/Constants.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.dosei.music.arpeggio - -import androidx.compose.ui.unit.dp - -const val DefaultFrets = 5 -const val DefaultStrings = 6 - -internal const val DefaultInitialFret = 1 -internal val MinWidth = 180.dp -internal val MinHeight = 180.dp diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/Finger.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/Finger.kt deleted file mode 100644 index 70e825b..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/Finger.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.dosei.music.arpeggio - -enum class Finger(val number: Int) { - Index(1), - Middle(2), - Ring(3), - Pinky(4), -} \ No newline at end of file diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/canvas/DrawScopeExtensions.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/canvas/DrawScopeExtensions.kt deleted file mode 100644 index 7a971eb..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/canvas/DrawScopeExtensions.kt +++ /dev/null @@ -1,176 +0,0 @@ -package com.dosei.music.arpeggio.canvas - -import android.graphics.Typeface -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Paint -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.graphics.drawscope.drawIntoCanvas -import androidx.compose.ui.graphics.nativeCanvas -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.dp -import com.dosei.music.arpeggio.Finger - -internal fun DrawScope.extractGeometry( - inset: Float, - positionSize: Dp, - strokeWidth: Dp, - frets: Int, - strings: Int -): Geometry { - val gridSize = Size( - width = size.width - positionSize.toPx(), - height = size.height - (positionSize.toPx() * 1.5f + 8.dp.toPx()) - ) - return Geometry( - strokeWidth = strokeWidth.toPx(), - fretSpaceHeight = gridSize.height / frets, - stringSpaceWidth = gridSize.width / strings.dec(), - canvasSize = size, - inset = inset, - positionSize = positionSize.toPx(), - gridSize = gridSize, - gridStart = Offset(inset, inset), - gridEnd = Offset(inset + gridSize.width, inset + gridSize.height), - bottomInset = size.height - (positionSize * 1.5f).toPx(), - playIndicatorPadding = inset, - playIndicatorSize = positionSize.toPx() - ) -} - -internal fun DrawScope.drawGrid( - frets: Int, - strings: Int, - columnWidth: Float, - rowHeight: Float, - color: Color, - strokeWidth: Dp -) { - drawRect( - color = color, - style = Stroke(width = strokeWidth.toPx()) - ) - - for (fret in 1 until frets) { - drawHorizontalLine(fret * rowHeight, color, strokeWidth) - } - - for (string in 1 until strings.dec()) { - drawVerticalLine(string * columnWidth, color, strokeWidth) - } -} - -internal fun DrawScope.drawHorizontalLine(y: Float, color: Color, strokeWidth: Dp) = - drawLine( - color = color, - start = Offset(x = 0f, y = y), - end = Offset(x = size.width, y = y), - strokeWidth = strokeWidth.toPx() - ) - -internal fun DrawScope.drawVerticalLine(x: Float, color: Color, strokeWidth: Dp) = - drawLine( - color = color, - start = Offset(x = x, y = 0f), - end = Offset(x = x, y = size.height), - strokeWidth = strokeWidth.toPx() - ) - -internal fun DrawScope.drawPosition( - fretCenter: Float, - stringLine: Float, - color: Color, - positionSize: Dp -) { - drawCircle( - color = color, - radius = positionSize.toPx() / 2f, - center = Offset(x = stringLine, y = fretCenter) - ) -} - -internal fun DrawScope.drawFingerIndicator( - finger: Finger, - fretCenter: Float, - stringCenter: Float, - color: Color, - positionSize: Dp, - textSize: TextUnit -) { - val paint = Paint().asFrameworkPaint().apply { - this.color = color.hashCode() - this.textSize = textSize.toPx() - this.textAlign = android.graphics.Paint.Align.CENTER - this.typeface = Typeface.DEFAULT_BOLD - } - - drawIntoCanvas { - it.nativeCanvas.drawText( - finger.number.toString(), - stringCenter, - (fretCenter + positionSize.toPx() * 0.2f), - paint - ) - } -} - -internal fun DrawScope.drawClosedStringIndicator( - start: Offset, - end: Offset, - color: Color, - strokeWidth: Dp -) { - drawLine( - color = color, - strokeWidth = strokeWidth.toPx(), - start = start, - end = end - ) - drawLine( - color = color, - strokeWidth = strokeWidth.toPx(), - start = Offset(x = start.x, y = end.y), - end = Offset(x = end.x, y = start.y) - ) -} - -internal fun DrawScope.drawOpenStringIndicator( - center: Offset, - color: Color, - strokeWidth: Dp, - positionSize: Dp -) { - drawCircle( - color = color, - style = Stroke(width = strokeWidth.toPx()), - radius = (positionSize.toPx() / 2f) - strokeWidth.toPx(), - center = center - ) -} - -internal fun DrawScope.drawBarre( - initialStringCenter: Float, - finalStringCenter: Float, - fretCenter: Float, - color: Color, - positionSize: Dp -) { - val halfPositionSize = positionSize.toPx() / 2f - val initialX = initialStringCenter - halfPositionSize - drawRoundRect( - color = color, - topLeft = Offset( - x = initialX, - y = fretCenter - halfPositionSize - ), - size = Size( - width = finalStringCenter - initialX + halfPositionSize, - height = positionSize.toPx() - ), - cornerRadius = CornerRadius(x = halfPositionSize, y = halfPositionSize) - ) -} \ No newline at end of file diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/canvas/Geometry.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/canvas/Geometry.kt deleted file mode 100644 index d07e17b..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/canvas/Geometry.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.dosei.music.arpeggio.canvas - -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size - -internal data class Geometry( - val strokeWidth: Float, - val fretSpaceHeight: Float, - val stringSpaceWidth: Float, - val canvasSize: Size, - val inset: Float, - val positionSize: Float, - val gridSize: Size, - val gridStart: Offset, - val gridEnd: Offset, - val bottomInset: Float, - val playIndicatorPadding: Float, - val playIndicatorSize: Float -) { - fun centerOfFret(fret: Int): Float { - val centerDiff = fretSpaceHeight / 2f - return fretSpaceHeight * fret - centerDiff + inset - } - - fun centerOfString(string: Int): Float { - return stringSpaceWidth * string + inset - } - - fun centerOfStringIndicator(string: Int): Offset { - return Offset( - x = centerOfString(string), - y = canvasSize.height - positionSize / 2f - ) - } - - fun topLeftOfStringIndicator(string: Int): Offset { - return Offset( - x = centerOfString(string) - positionSize / 2f + strokeWidth, - y = canvasSize.height - positionSize + strokeWidth - ) - } - - fun bottomRightOfStringIndicator(string: Int): Offset { - return Offset( - x = centerOfString(string) + positionSize / 2f - strokeWidth, - y = canvasSize.height - strokeWidth - ) - } -} \ No newline at end of file diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/grid/Grid.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/grid/Grid.kt deleted file mode 100644 index bed0f8c..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/grid/Grid.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.dosei.music.arpeggio.grid - -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.inset -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.dosei.music.arpeggio.Barre -import com.dosei.music.arpeggio.DefaultInitialFret -import com.dosei.music.arpeggio.Finger -import com.dosei.music.arpeggio.Position -import com.dosei.music.arpeggio.canvas.drawGrid -import com.dosei.music.arpeggio.canvas.extractGeometry -import com.dosei.music.arpeggio.theme.DiagramTheme - -@Composable -internal fun Grid( - modifier: Modifier = Modifier, - initialFret: Int = DefaultInitialFret, - scope: GridScope.() -> Unit -) { - val sizes = DiagramTheme.sizes - val colors = DiagramTheme.colors - val typography = DiagramTheme.typography - val frets = DiagramTheme.frets - val strings = DiagramTheme.strings - - Canvas(modifier = modifier.fillMaxSize()) { - val inset = sizes.position.toPx() / 2f - val geometry = extractGeometry( - inset = inset, - positionSize = sizes.position, - strokeWidth = sizes.strokeWidth, - frets = frets, - strings = strings - ) - inset(horizontal = inset - sizes.strokeWidth.toPx()) { - drawRect(colors.grid, size = Size(width = size.width, height = inset)) - } - inset( - left = inset, - right = inset, - top = inset, - bottom = inset * 2 + 8.dp.toPx() - ) { - drawGrid( - frets = frets, - strings = strings, - columnWidth = geometry.stringSpaceWidth, - rowHeight = geometry.fretSpaceHeight, - color = colors.grid, - strokeWidth = sizes.strokeWidth - ) - } - GridScope(initialFret, colors, geometry, sizes, typography, strings, this) - .apply(scope) - .commit() - } -} - -@Preview(showBackground = true) -@Composable -private fun PreviewChordThumbnail() { - Surface(modifier = Modifier.size(600.dp), color = Color.White) { - Grid( - modifier = Modifier.padding(50.dp), - scope = { - draw( - Barre( - fret = 2, - strings = 1..5, - finger = Finger.Index - ) - ) - draw(Position(fret = 3, string = 2, finger = Finger.Middle)) - draw(Position(fret = 4, string = 3, finger = Finger.Pinky)) - draw(Position(fret = 4, string = 4, finger = Finger.Ring)) - }, - initialFret = 1 - ) - } -} \ No newline at end of file diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/grid/GridScope.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/grid/GridScope.kt deleted file mode 100644 index ccd774a..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/grid/GridScope.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.dosei.music.arpeggio.grid - -import androidx.compose.ui.graphics.drawscope.DrawScope -import com.dosei.music.arpeggio.Barre -import com.dosei.music.arpeggio.Component -import com.dosei.music.arpeggio.OpenString -import com.dosei.music.arpeggio.Position -import com.dosei.music.arpeggio.canvas.* -import com.dosei.music.arpeggio.theme.Colors -import com.dosei.music.arpeggio.theme.Sizes -import com.dosei.music.arpeggio.theme.Typography - -internal class GridScope( - initialFret: Int, - private val colors: Colors, - private val geometry: Geometry, - private val sizes: Sizes, - private val typography: Typography, - private val strings: Int, - private val drawScope: DrawScope -) { - private val stringUsage = (0 until strings).map { it to false }.toMap().toMutableMap() - private val fretDiff = initialFret.dec() - - fun commit() { - stringUsage.forEach { entry -> - val string = entry.key - val isUsed = entry.value - if (isUsed) { - drawScope.run { - drawOpenStringIndicator( - geometry.centerOfStringIndicator(string), - colors.stringUsageIndicator, - sizes.strokeWidth, - sizes.position - ) - } - } else { - drawScope.run { - drawClosedStringIndicator( - geometry.topLeftOfStringIndicator(string), - geometry.bottomRightOfStringIndicator(string), - colors.stringUsageIndicator, - sizes.strokeWidth - ) - } - } - } - } - - fun draw(component: Component) { - when (component) { - is Barre -> draw(barre = component) - is OpenString -> draw(openString = component) - is Position -> draw(position = component) - } - } - - fun draw(barre: Barre) { - drawScope.drawBarre( - initialStringCenter = geometry.centerOfString(barre.strings.last.toCanvasPosition()), - finalStringCenter = geometry.centerOfString(barre.strings.first.toCanvasPosition()), - fretCenter = geometry.centerOfFret(barre.fret.relativeToInitialFret()), - color = colors.position, - positionSize = sizes.position - ) - - barre.finger?.let { - drawScope.drawFingerIndicator( - finger = it, - fretCenter = geometry.centerOfFret(barre.fret.relativeToInitialFret()), - stringCenter = geometry.centerOfString(barre.strings.last.toCanvasPosition()), - color = typography.fingerIndicator.color, - positionSize = sizes.position, - textSize = typography.fingerIndicator.fontSize - ) - } - stringUsage.putAll(barre.strings.map { it.toCanvasPosition() to true }) - } - - fun draw(position: Position) { - val fretCenter = geometry.centerOfFret(position.fret.relativeToInitialFret()) - val stringCenter = geometry.centerOfString(position.string.toCanvasPosition()) - - drawScope.drawPosition( - fretCenter = fretCenter, - stringLine = stringCenter, - color = colors.position, - positionSize = sizes.position - ) - - position.finger?.let { - drawScope.drawFingerIndicator( - finger = it, - fretCenter = fretCenter, - stringCenter = stringCenter, - color = typography.fingerIndicator.color, - positionSize = sizes.position, - textSize = typography.fingerIndicator.fontSize - ) - } - stringUsage[position.string.toCanvasPosition()] = true - } - - fun draw(openString: OpenString) { - stringUsage[openString.string.toCanvasPosition()] = true - } - - private fun Int.relativeToInitialFret(): Int = minus(fretDiff) - - private fun Int.toCanvasPosition(): Int = strings - this -} diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/ArpeggioTheme.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/ArpeggioTheme.kt deleted file mode 100644 index fb00e98..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/ArpeggioTheme.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.dosei.music.arpeggio.theme - -import androidx.compose.runtime.* -import com.dosei.music.arpeggio.DefaultFrets -import com.dosei.music.arpeggio.DefaultInitialFret -import com.dosei.music.arpeggio.DefaultStrings - -@Composable -fun ArpeggioTheme( - frets: Int = DefaultFrets, - strings: Int = DefaultStrings, - colors: Colors = Colors(), - typography: Typography = Typography(), - sizes: Sizes = Sizes(), - formatInitialFret: FormatInitialFret = FormatInitialFret.enUs(), - content: @Composable () -> Unit -) { - val rememberedColors = remember { colors.copy() }.apply { updateColorsFrom(colors) } - CompositionLocalProvider( - LocalColors provides rememberedColors, - LocalTypography provides typography.copy(), - LocalSizes provides sizes.copy(), - LocalFormatInitialFret provides formatInitialFret, - LocalFrets provides frets, - LocalStrings provides strings, - content = content - ) -} - -internal val LocalFrets = staticCompositionLocalOf { DefaultFrets } -internal val LocalStrings = staticCompositionLocalOf { DefaultStrings } - -object DiagramTheme { - - val colors: Colors - @Composable - @ReadOnlyComposable - get() = LocalColors.current - - val typography: Typography - @Composable - @ReadOnlyComposable - get() = LocalTypography.current - - val sizes: Sizes - @Composable - @ReadOnlyComposable - get() = LocalSizes.current - - val formatInitialFret: FormatInitialFret - @Composable - @ReadOnlyComposable - get() = LocalFormatInitialFret.current - - val frets: Int - @Composable - @ReadOnlyComposable - get() = LocalFrets.current - - val strings: Int - @Composable - @ReadOnlyComposable - get() = LocalStrings.current - - val initialFretRange: IntRange - @Composable - @ReadOnlyComposable - get() = DefaultInitialFret..frets -} \ No newline at end of file diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/Colors.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/Colors.kt deleted file mode 100644 index d9f3656..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/Colors.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.dosei.music.arpeggio.theme - -import androidx.compose.runtime.* -import androidx.compose.ui.graphics.Color - -class Colors( - grid: Color = Color.Black, - position: Color = Color.Black, - stringUsageIndicator: Color = Color.Black -) { - var grid by mutableStateOf(grid, structuralEqualityPolicy()) - internal set - var position by mutableStateOf(position, structuralEqualityPolicy()) - internal set - var stringUsageIndicator by mutableStateOf(stringUsageIndicator, structuralEqualityPolicy()) - internal set - - fun copy( - grid: Color = this.grid, - position: Color = this.position, - stringUsageIndicator: Color = this.stringUsageIndicator - ) = Colors( - grid, - position, - stringUsageIndicator - ) -} - -internal fun Colors.updateColorsFrom(other: Colors) { - this.grid = other.grid - this.position = other.position - this.stringUsageIndicator = other.stringUsageIndicator -} - -internal val LocalColors = staticCompositionLocalOf { Colors() } diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/FormatInitialFret.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/FormatInitialFret.kt deleted file mode 100644 index 090080f..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/FormatInitialFret.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.dosei.music.arpeggio.theme - -import androidx.compose.runtime.ProvidableCompositionLocal -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.BaselineShift -import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.text.withStyle - -interface FormatInitialFret { - operator fun invoke(fret: Int): AnnotatedString - - companion object { - - fun enUs(): FormatInitialFret = EnUsFormatInitialFret() - fun ptBr(): FormatInitialFret = PtBrFormatInitialFret() - } -} - -class EnUsFormatInitialFret : FormatInitialFret { - - override fun invoke(fret: Int): AnnotatedString = - buildAnnotatedString { - append(fret.toString()) - withStyle(style = SpanStyle(baselineShift = BaselineShift.Superscript)) { - val ordinal = when (fret) { - 1 -> "st" - 2 -> "nd" - 3 -> "rd" - else -> "th" - } - append(ordinal) - } - } -} - -class PtBrFormatInitialFret : FormatInitialFret { - - override fun invoke(fret: Int): AnnotatedString = - buildAnnotatedString { - append(fret.toString()) - withStyle( - style = SpanStyle( - baselineShift = BaselineShift.Superscript, - textDecoration = TextDecoration.Underline - ) - ) { - append("a") - } - } -} - -internal val LocalFormatInitialFret: ProvidableCompositionLocal = - staticCompositionLocalOf { EnUsFormatInitialFret() } \ No newline at end of file diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/Sizes.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/Sizes.kt deleted file mode 100644 index 71d00a7..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/Sizes.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.dosei.music.arpeggio.theme - -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -data class Sizes( - val position: Dp = 40.dp, - val strokeWidth: Dp = 1.dp -) - -internal val LocalSizes = staticCompositionLocalOf { Sizes() } \ No newline at end of file diff --git a/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/Typography.kt b/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/Typography.kt deleted file mode 100644 index 0a96835..0000000 --- a/arpeggio/src/main/java/com/dosei/music/arpeggio/theme/Typography.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.dosei.music.arpeggio.theme - -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp - -data class Typography( - val name: TextStyle = TextStyle( - color = Color.Black, - fontSize = 40.sp, - fontWeight = FontWeight.Bold - ), - val firstFretIndicator: TextStyle = TextStyle( - color = Color.Black, - fontSize = 24.sp, - fontWeight = FontWeight.Bold - ), - val fingerIndicator: TextStyle = TextStyle(color = Color.White, fontSize = 20.sp) -) - -internal val LocalTypography = staticCompositionLocalOf { Typography() } diff --git a/arpeggio/src/main/res/drawable-v24/ic_launcher_foreground.xml b/arpeggio/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/arpeggio/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/arpeggio/src/main/res/drawable/ic_launcher_background.xml b/arpeggio/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/arpeggio/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/arpeggio/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/arpeggio/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cf..0000000 --- a/arpeggio/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/arpeggio/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/arpeggio/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index eca70cf..0000000 --- a/arpeggio/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/arpeggio/src/main/res/mipmap-hdpi/ic_launcher.webp b/arpeggio/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78..0000000 Binary files a/arpeggio/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/arpeggio/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/arpeggio/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d..0000000 Binary files a/arpeggio/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/arpeggio/src/main/res/mipmap-mdpi/ic_launcher.webp b/arpeggio/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d6..0000000 Binary files a/arpeggio/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/arpeggio/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/arpeggio/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 62b611d..0000000 Binary files a/arpeggio/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/arpeggio/src/main/res/mipmap-xhdpi/ic_launcher.webp b/arpeggio/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a307..0000000 Binary files a/arpeggio/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/arpeggio/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/arpeggio/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a695..0000000 Binary files a/arpeggio/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/arpeggio/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/arpeggio/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77..0000000 Binary files a/arpeggio/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/arpeggio/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/arpeggio/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9287f50..0000000 Binary files a/arpeggio/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/arpeggio/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/arpeggio/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d642..0000000 Binary files a/arpeggio/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/arpeggio/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/arpeggio/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9126ae3..0000000 Binary files a/arpeggio/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/arpeggio/src/main/res/values-night/themes.xml b/arpeggio/src/main/res/values-night/themes.xml deleted file mode 100644 index c5a808b..0000000 --- a/arpeggio/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/arpeggio/src/main/res/values/colors.xml b/arpeggio/src/main/res/values/colors.xml deleted file mode 100644 index f8c6127..0000000 --- a/arpeggio/src/main/res/values/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/arpeggio/src/main/res/values/strings.xml b/arpeggio/src/main/res/values/strings.xml deleted file mode 100644 index 56ec6ad..0000000 --- a/arpeggio/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - arpeggio - \ No newline at end of file diff --git a/arpeggio/src/main/res/values/themes.xml b/arpeggio/src/main/res/values/themes.xml deleted file mode 100644 index 66511b6..0000000 --- a/arpeggio/src/main/res/values/themes.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/arpeggio/src/test/java/com/dosei/music/arpeggio/ExampleUnitTest.kt b/arpeggio/src/test/java/com/dosei/music/arpeggio/ExampleUnitTest.kt deleted file mode 100644 index 4d506c6..0000000 --- a/arpeggio/src/test/java/com/dosei/music/arpeggio/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.dosei.music.arpeggio - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index eadfd7b..ca5ce0b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,24 +1,22 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - val kotlin_version by extra("1.3.72") repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:7.0.3") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0") - classpath(Dependencies.googleServices) - classpath(Dependencies.Firebase.crashlyticsGradlePlugin) - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") + classpath(libs.gradle.plugin) + classpath(libs.kotlin.gradle) + classpath(libs.google.play.services) + classpath(libs.firebase.crashlytics.gradle) } } allprojects { repositories { google() - jcenter() + mavenCentral() maven(url = "https://jitpack.io") maven(url = "https://maven.pkg.github.com/JoaoGeniselli/ktransposer") { val secrets = SecretsAPI.forProject(rootProject) @@ -27,6 +25,13 @@ allprojects { password = secrets.getString("GITHUB_PACKAGES_TOKEN") } } + maven(url = "https://maven.pkg.github.com/JoaoGeniselli/arpeggio") { + val secrets = SecretsAPI.forProject(rootProject) + credentials { + username = secrets.getString("GITHUB_PACKAGES_USER") + password = secrets.getString("GITHUB_PACKAGES_TOKEN") + } + } } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index c697204..f155244 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,11 +1,7 @@ repositories { - jcenter() + mavenCentral() } plugins { `kotlin-dsl` -} - -kotlinDslPluginOptions { - experimentalWarning.set(false) } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt deleted file mode 100644 index 91b6541..0000000 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ /dev/null @@ -1,30 +0,0 @@ -object Dependencies { - - object AndroidX { - const val appCompat = "androidx.appcompat:appcompat:1.1.0" - const val constraintLayout = "androidx.constraintlayout:constraintlayout:1.1.3" - const val coreKtx = "androidx.core:core-ktx:1.3.0" - const val espresso = "androidx.test.espresso:espresso-core:3.2.0" - const val junitExtensions = "androidx.test.ext:junit:1.1.1" - const val recyclerView = "androidx.recyclerview:recyclerview:1.1.0" - } - - object Firebase { - const val analytics = "com.google.firebase:firebase-analytics:17.5.0" - const val crashlytics = "com.google.firebase:firebase-crashlytics:17.2.1" - const val crashlyticsGradlePlugin = "com.google.firebase:firebase-crashlytics-gradle:2.2.0" - } - - const val googleServices = "com.google.gms:google-services:4.3.3" - const val gradlePlugin = "com.android.tools.build:gradle:3.6.2" - const val junit = "junit:junit:4.12" - const val koin = "org.koin:koin-android:${Versions.koin}" - const val koinScope = "org.koin:koin-android-scope:${Versions.koin}" - const val koinViewModel = "org.koin:koin-android-viewmodel:${Versions.koin}" - const val kotlin = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}" - const val kotlinGradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}" - const val playServicesAds = "com.google.android.gms:play-services-ads:19.3.0" - const val tapTargetView = "com.getkeepsafe.taptargetview:taptargetview:1.13.0" - const val midiDriver = "com.github.billthefarmer:mididriver:v1.20" - const val mockk = "io.mockk:mockk:1.11.0" -} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/SecretsKeys.kt b/buildSrc/src/main/kotlin/SecretsKeys.kt index 9ad02c6..782790d 100644 --- a/buildSrc/src/main/kotlin/SecretsKeys.kt +++ b/buildSrc/src/main/kotlin/SecretsKeys.kt @@ -2,4 +2,5 @@ object SecretsKeys { const val adMobAppId = "ADMOB_APPLICATION_ID" const val adMobHomeBannerId = "ADMOB_HOME_BANNER_ID" const val adMobTransposerBannerId = "ADMOB_TRANSPOSER_BANNER_ID" + const val adMobDictionaryBannerId = "ADMOB_DICTIONARY_BANNER_ID" } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt deleted file mode 100644 index 76b85e9..0000000 --- a/buildSrc/src/main/kotlin/Versions.kt +++ /dev/null @@ -1,16 +0,0 @@ -object Versions { - object App { - const val name = "1.2.0" - const val code = 6 - } - - object Sdk { - const val minimum = 21 - const val target = 32 - const val compile = 32 - const val buildTools = "30.0.2" - } - - const val koin = "2.1.6" - const val kotlin = "1.6.0" -} diff --git a/gradle.properties b/gradle.properties index 23339e0..9e73ebc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,3 +19,6 @@ android.useAndroidX=true android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 50280ca..52aaee2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,57 +1,58 @@ [versions] -compose = '1.1.0-rc01' -kotlin = '1.6.20' -koin = '2.1.6' +kotlin = '1.8.10' +koin-bom = '3.5.1' +compiler-kotlin-ext = "1.4.3" +firebase-bom = "32.2.3" +compose-bom = "2023.06.01" [libraries] -core-ktx = 'androidx.core:core-ktx:1.7.0' -app-compat = 'androidx.appcompat:appcompat:1.4.0' -android-material = 'com.google.android.material:material:1.4.0' -compose-ui = { module = 'androidx.compose.ui:ui', version.ref = 'compose' } -compose-material = { module = 'androidx.compose.material:material', version.ref = 'compose' } -compose-preview = { module = 'androidx.compose.ui:ui-tooling-preview', version.ref = 'compose' } -compose-junit = { module = 'androidx.compose.ui:ui-test-junit4', version.ref = 'compose' } -compose-livedata = { module = 'androidx.compose.runtime:runtime-livedata', version.ref = 'compose' } +google-play-services = "com.google.gms:google-services:4.3.15" + +firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase-bom" } +firebase-crashlytics-gradle = { module = "com.google.firebase:firebase-crashlytics-gradle", version = "2.9.9" } +firebase-analytics = { module = "com.google.firebase:firebase-analytics" } +firebase-crashlytics-core = { module = "com.google.firebase:firebase-crashlytics" } +firebase-crashlytics-plugin = { module = "com.google.firebase:firebase-crashlytics-gradle" } + +core-ktx = 'androidx.core:core-ktx:1.9.0' +app-compat = 'androidx.appcompat:appcompat:1.6.1' +android-material = 'com.google.android.material:material:1.9.0' + +compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } +compose-ui = { group = "androidx.compose.ui", name = "ui" } +compose-material = { group = "androidx.compose.material3", name = "material3" } +compose-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } +compose-junit = { group = "androidx.compose.ui", name = "ui-test-junit4" } +compose-livedata = { module = 'androidx.compose.runtime:runtime-livedata' } +compose-tooling-ui = { group = "androidx.compose.ui", name = "ui-tooling" } +compose-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } +compose-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } +hilt-compose = "androidx.hilt:hilt-navigation-compose:1.0.0" +navigation-compose = "androidx.hilt:hilt-navigation-compose:1.0.0" +activity-compose = 'androidx.activity:activity-compose:1.7.2' ktransposer = 'com.dosei.music:ktransposer:1.1.0' +arpeggio = 'com.dosei.music:arpeggio:1.3.1' -compose-tooling-ui = { module = 'androidx.compose.ui:ui-tooling', version.ref = 'compose' } -lifecycle-runtime = 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' -activity-compose = 'androidx.activity:activity-compose:1.4.0' +lifecycle-runtime = 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' junit-core = 'junit:junit:4.13.2' -android-test-ext = 'androidx.test.ext:junit:1.1.3' -espresso-core = 'androidx.test.espresso:espresso-core:3.4.0' -constraint-layout = "androidx.constraintlayout:constraintlayout:1.1.3" -recycler-view = "androidx.recyclerview:recyclerview:1.1.0" -firebase-analytics = "com.google.firebase:firebase-analytics:17.5.0" -firebase-crashlytics-core = "com.google.firebase:firebase-crashlytics:17.2.1" -firebase-crashlytics-plugin = "com.google.firebase:firebase-crashlytics-gradle:2.2.0" - -hilt-compose = "androidx.hilt:hilt-navigation-compose:1.0.0" -navigation-compose = "androidx.hilt:hilt-navigation-compose:1.0.0" +android-test-ext = 'androidx.test.ext:junit:1.1.5' +espresso-core = 'androidx.test.espresso:espresso-core:3.5.1' +constraint-layout = "androidx.constraintlayout:constraintlayout:2.1.4" +recycler-view = "androidx.recyclerview:recyclerview:1.3.1" -espresso-compose = { module = "androidx.compose.ui:ui-test-junit4", version.ref = 'compose' } -compose-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = 'compose' } +google-services = "com.google.gms:google-services:4.3.15" +gradle-plugin = "com.android.tools.build:gradle:8.1.1" -google-services = "com.google.gms:google-services:4.3.3" -gradle-plugin = "com.android.tools.build:gradle:3.6.2" +koin-bom = { module = "io.insert-koin:koin-bom", version.ref = 'koin-bom' } +koin-android = { module = "io.insert-koin:koin-android" } +koin-compose = { module = "io.insert-koin:koin-androidx-compose" } -koin-android = { module = "org.koin:koin-android", version.ref = 'koin' } -koin-scope = { module = "org.koin:koin-android-scope", version.ref = 'koin' } -koin-view-model = { module = "org.koin:koin-android-viewmodel", version.ref = 'koin' } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = 'kotlin' } kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = 'kotlin' } -play-services-ads = "com.google.android.gms:play-services-ads:19.3.0" -tap-target-view = "com.getkeepsafe.taptargetview:taptargetview:1.13.0" +play-services-ads = "com.google.android.gms:play-services-ads:22.3.0" midi-driver = "com.github.billthefarmer:mididriver:v1.20" mockk = "io.mockk:mockk:1.11.0" [bundles] -compose = [ - 'activity-compose', - 'compose-material', - 'compose-preview', - 'compose-ui', - 'compose-livedata' -] \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af2ed0..004d5a6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip diff --git a/player/build.gradle.kts b/player/build.gradle.kts index dc839ab..cda42d5 100644 --- a/player/build.gradle.kts +++ b/player/build.gradle.kts @@ -4,12 +4,11 @@ plugins { } android { - compileSdk = Versions.Sdk.compile + compileSdk = 33 defaultConfig { - minSdk = Versions.Sdk.minimum - buildToolsVersion = Versions.Sdk.buildTools - targetSdk = Versions.Sdk.target + minSdk = 21 + targetSdk = 33 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } @@ -26,6 +25,7 @@ android { kotlinOptions { jvmTarget = "1.8" } + namespace = "com.dosei.music.scoreconverter.player" } dependencies { diff --git a/player/src/main/AndroidManifest.xml b/player/src/main/AndroidManifest.xml index 645524b..44008a4 100644 --- a/player/src/main/AndroidManifest.xml +++ b/player/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 2f47343..1727501 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,7 +1,4 @@ +rootProject.name = "Guitar Kit" include(":player") -rootProject.name = "Chords Dictionary" include(":app") -include(":ui") -include(":uitest") -include(":arpeggio") -enableFeaturePreview("VERSION_CATALOGS") \ No newline at end of file +include(":ui") \ No newline at end of file diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index 0e36346..ce26b49 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -4,12 +4,11 @@ plugins { } android { - compileSdk = Versions.Sdk.compile + compileSdk = 33 defaultConfig { - minSdk = Versions.Sdk.minimum - targetSdk = Versions.Sdk.target - buildToolsVersion = Versions.Sdk.buildTools + minSdk = 21 + targetSdk = 33 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } @@ -24,27 +23,47 @@ android { } } composeOptions { - kotlinCompilerExtensionVersion = "1.1.0-rc01" + kotlinCompilerExtensionVersion = libs.versions.compiler.kotlin.ext.get() } buildFeatures { compose = true viewBinding = true } + namespace = "com.dosei.music.scoreconverter.ui" +} + +kotlin { + jvmToolchain(8) } dependencies { - implementation(Dependencies.kotlin) - implementation(Dependencies.tapTargetView) - implementation(Dependencies.AndroidX.constraintLayout) + implementation(libs.kotlin.stdlib) + implementation(libs.constraint.layout) implementation(libs.core.ktx) implementation(libs.app.compat) implementation(libs.android.material) - implementation(libs.bundles.compose) implementation(libs.lifecycle.runtime) testImplementation(libs.junit.core) + + //region UI + + implementation(platform(libs.compose.bom)) + implementation(libs.compose.ui) + implementation(libs.compose.material) + implementation(libs.compose.graphics) + implementation(libs.compose.preview) + implementation(libs.activity.compose) + + debugImplementation(libs.compose.tooling.ui) + debugImplementation(libs.compose.test.manifest) + + androidTestImplementation(platform(libs.compose.bom)) androidTestImplementation(libs.android.test.ext) androidTestImplementation(libs.espresso.core) androidTestImplementation(libs.compose.junit) - debugImplementation(libs.compose.tooling.ui) + + //endregion + + implementation(libs.arpeggio) } diff --git a/ui/src/main/AndroidManifest.xml b/ui/src/main/AndroidManifest.xml index 898fbca..cc947c5 100644 --- a/ui/src/main/AndroidManifest.xml +++ b/ui/src/main/AndroidManifest.xml @@ -1 +1 @@ - + diff --git a/ui/src/main/java/com/dosei/music/scoreconverter/ui/theme/AppTheme.kt b/ui/src/main/java/com/dosei/music/scoreconverter/ui/theme/AppTheme.kt index 9c4b99a..85d6e0a 100644 --- a/ui/src/main/java/com/dosei/music/scoreconverter/ui/theme/AppTheme.kt +++ b/ui/src/main/java/com/dosei/music/scoreconverter/ui/theme/AppTheme.kt @@ -1,53 +1,112 @@ package com.dosei.music.scoreconverter.ui.theme +import android.app.Activity +import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material.MaterialTheme -import androidx.compose.material.darkColors -import androidx.compose.material.lightColors +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat -val PrimaryColor = Color(0xff283593) -val PrimaryColorDark = Color(0xff001064) - -val SecondaryColor = Color(0xff424242) -val SecondaryColorDark = Color(0xff1b1b1b) - -val ErrorColor = Color(0xffc62828) - -val LightColors = lightColors( - primary = PrimaryColor, - primaryVariant = PrimaryColorDark, - secondary = SecondaryColor, - secondaryVariant = SecondaryColorDark, - background = Color.White, - surface = Color.White, - error = ErrorColor, - onPrimary = Color.White, - onSecondary = Color.White, - onBackground = Color.Black, - onSurface = Color.Black, - onError = Color.White +private val LightColors = lightColorScheme( + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + primaryContainer = md_theme_light_primaryContainer, + onPrimaryContainer = md_theme_light_onPrimaryContainer, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + secondaryContainer = md_theme_light_secondaryContainer, + onSecondaryContainer = md_theme_light_onSecondaryContainer, + tertiary = md_theme_light_tertiary, + onTertiary = md_theme_light_onTertiary, + tertiaryContainer = md_theme_light_tertiaryContainer, + onTertiaryContainer = md_theme_light_onTertiaryContainer, + error = md_theme_light_error, + errorContainer = md_theme_light_errorContainer, + onError = md_theme_light_onError, + onErrorContainer = md_theme_light_onErrorContainer, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, + surfaceVariant = md_theme_light_surfaceVariant, + onSurfaceVariant = md_theme_light_onSurfaceVariant, + outline = md_theme_light_outline, + inverseOnSurface = md_theme_light_inverseOnSurface, + inverseSurface = md_theme_light_inverseSurface, + inversePrimary = md_theme_light_inversePrimary, + surfaceTint = md_theme_light_surfaceTint, + outlineVariant = md_theme_light_outlineVariant, + scrim = md_theme_light_scrim, ) -val DarkColors = darkColors( - primary = PrimaryColor, - primaryVariant = PrimaryColorDark, - secondary = SecondaryColor, - secondaryVariant = SecondaryColorDark, - error = ErrorColor, - onPrimary = Color.White, - onSecondary = Color.White, - onError = Color.White + +private val DarkColors = darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + errorContainer = md_theme_dark_errorContainer, + onError = md_theme_dark_onError, + onErrorContainer = md_theme_dark_onErrorContainer, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + outline = md_theme_dark_outline, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = md_theme_dark_inverseSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim, ) @Composable fun AppTheme( darkTheme: Boolean = isSystemInDarkTheme(), - content: @Composable () -> Unit + dynamicColor: Boolean = false, + content: @Composable() () -> Unit ) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColors + else -> LightColors + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme + } + } + MaterialTheme( - colors = if (darkTheme) DarkColors else LightColors, + colorScheme = colorScheme, + typography = Typography, content = content ) } \ No newline at end of file diff --git a/ui/src/main/java/com/dosei/music/scoreconverter/ui/theme/Color.kt b/ui/src/main/java/com/dosei/music/scoreconverter/ui/theme/Color.kt new file mode 100644 index 0000000..4cdde98 --- /dev/null +++ b/ui/src/main/java/com/dosei/music/scoreconverter/ui/theme/Color.kt @@ -0,0 +1,68 @@ +package com.dosei.music.scoreconverter.ui.theme + +import androidx.compose.ui.graphics.Color + +val md_theme_light_primary = Color(0xFF006874) +val md_theme_light_onPrimary = Color(0xFFFFFFFF) +val md_theme_light_primaryContainer = Color(0xFF97F0FF) +val md_theme_light_onPrimaryContainer = Color(0xFF001F24) +val md_theme_light_secondary = Color(0xFF4A6267) +val md_theme_light_onSecondary = Color(0xFFFFFFFF) +val md_theme_light_secondaryContainer = Color(0xFFCDE7EC) +val md_theme_light_onSecondaryContainer = Color(0xFF051F23) +val md_theme_light_tertiary = Color(0xFF525E7D) +val md_theme_light_onTertiary = Color(0xFFFFFFFF) +val md_theme_light_tertiaryContainer = Color(0xFFDAE2FF) +val md_theme_light_onTertiaryContainer = Color(0xFF0E1B37) +val md_theme_light_error = Color(0xFFBA1A1A) +val md_theme_light_errorContainer = Color(0xFFFFDAD6) +val md_theme_light_onError = Color(0xFFFFFFFF) +val md_theme_light_onErrorContainer = Color(0xFF410002) +val md_theme_light_background = Color(0xFFFAFDFD) +val md_theme_light_onBackground = Color(0xFF191C1D) +val md_theme_light_surface = Color(0xFFFAFDFD) +val md_theme_light_onSurface = Color(0xFF191C1D) +val md_theme_light_surfaceVariant = Color(0xFFDBE4E6) +val md_theme_light_onSurfaceVariant = Color(0xFF3F484A) +val md_theme_light_outline = Color(0xFF6F797A) +val md_theme_light_inverseOnSurface = Color(0xFFEFF1F1) +val md_theme_light_inverseSurface = Color(0xFF2E3132) +val md_theme_light_inversePrimary = Color(0xFF4FD8EB) +val md_theme_light_shadow = Color(0xFF000000) +val md_theme_light_surfaceTint = Color(0xFF006874) +val md_theme_light_outlineVariant = Color(0xFFBFC8CA) +val md_theme_light_scrim = Color(0xFF000000) + +val md_theme_dark_primary = Color(0xFF4FD8EB) +val md_theme_dark_onPrimary = Color(0xFF00363D) +val md_theme_dark_primaryContainer = Color(0xFF004F58) +val md_theme_dark_onPrimaryContainer = Color(0xFF97F0FF) +val md_theme_dark_secondary = Color(0xFFB1CBD0) +val md_theme_dark_onSecondary = Color(0xFF1C3438) +val md_theme_dark_secondaryContainer = Color(0xFF334B4F) +val md_theme_dark_onSecondaryContainer = Color(0xFFCDE7EC) +val md_theme_dark_tertiary = Color(0xFFBAC6EA) +val md_theme_dark_onTertiary = Color(0xFF24304D) +val md_theme_dark_tertiaryContainer = Color(0xFF3B4664) +val md_theme_dark_onTertiaryContainer = Color(0xFFDAE2FF) +val md_theme_dark_error = Color(0xFFFFB4AB) +val md_theme_dark_errorContainer = Color(0xFF93000A) +val md_theme_dark_onError = Color(0xFF690005) +val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) +val md_theme_dark_background = Color(0xFF191C1D) +val md_theme_dark_onBackground = Color(0xFFE1E3E3) +val md_theme_dark_surface = Color(0xFF191C1D) +val md_theme_dark_onSurface = Color(0xFFE1E3E3) +val md_theme_dark_surfaceVariant = Color(0xFF3F484A) +val md_theme_dark_onSurfaceVariant = Color(0xFFBFC8CA) +val md_theme_dark_outline = Color(0xFF899294) +val md_theme_dark_inverseOnSurface = Color(0xFF191C1D) +val md_theme_dark_inverseSurface = Color(0xFFE1E3E3) +val md_theme_dark_inversePrimary = Color(0xFF006874) +val md_theme_dark_shadow = Color(0xFF000000) +val md_theme_dark_surfaceTint = Color(0xFF4FD8EB) +val md_theme_dark_outlineVariant = Color(0xFF3F484A) +val md_theme_dark_scrim = Color(0xFF000000) + + +val seed = Color(0xFFFFFFFF) \ No newline at end of file diff --git a/ui/src/main/java/com/dosei/music/scoreconverter/ui/theme/Type.kt b/ui/src/main/java/com/dosei/music/scoreconverter/ui/theme/Type.kt new file mode 100644 index 0000000..ee25ba0 --- /dev/null +++ b/ui/src/main/java/com/dosei/music/scoreconverter/ui/theme/Type.kt @@ -0,0 +1,32 @@ +package com.dosei.music.scoreconverter.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ), + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) +) \ No newline at end of file diff --git a/ui/src/main/java/com/dosei/music/scoreconverter/ui/tutorial/Tutorial.kt b/ui/src/main/java/com/dosei/music/scoreconverter/ui/tutorial/Tutorial.kt deleted file mode 100644 index 12c4801..0000000 --- a/ui/src/main/java/com/dosei/music/scoreconverter/ui/tutorial/Tutorial.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.dosei.music.scoreconverter.ui.tutorial - -import android.app.Activity -import android.view.View -import com.getkeepsafe.taptargetview.TapTarget -import com.getkeepsafe.taptargetview.TapTargetSequence - -class Tutorial { - - private var onFinishListener: () -> Unit = {} - private var onCancelListener: () -> Unit = {} - private val steps = mutableListOf() - - fun addStep(target: View, description: CharSequence) { - steps.add(Step(target, description)) - } - - fun onFinish(listener: () -> Unit) { - onFinishListener = listener - } - - fun onCancel(listener: () -> Unit) { - onCancelListener = listener - } - - fun start(activity: Activity) { - if (steps.isEmpty()) return - TapTargetSequence(activity).apply { - targets(steps.map { it.toTarget() }) - listener(makeListener()) - start() - } - } - - private fun Step.toTarget(): TapTarget = TapTarget - .forView(target, description) - .tintTarget(false) - .cancelable(false) - - private fun makeListener() = object : TapTargetSequence.Listener { - override fun onSequenceCanceled(lastTarget: TapTarget?) = onCancelListener() - override fun onSequenceFinish() = onFinishListener() - override fun onSequenceStep(lastTarget: TapTarget?, targetClicked: Boolean) = Unit - } - - private data class Step( - val target: View, - val description: CharSequence - ) - - companion object { - - fun create(def: Tutorial.() -> Unit): Tutorial = Tutorial().apply(def) - - fun start(activity: Activity, def: Tutorial.() -> Unit) = create(def).start(activity) - } -} - diff --git a/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/Fretboard.kt b/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/Fretboard.kt new file mode 100644 index 0000000..2287f98 --- /dev/null +++ b/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/Fretboard.kt @@ -0,0 +1,156 @@ +package com.dosei.music.scoreconverter.ui.view + +import android.content.res.Configuration +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.drawscope.Fill +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.text.ExperimentalTextApi +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.drawText +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.dosei.music.arpeggio.GuitarStringAndFret +import com.dosei.music.scoreconverter.ui.theme.AppTheme + +@OptIn(ExperimentalTextApi::class) +@Composable +fun GuitarNeck( + modifier: Modifier = Modifier, + highlights: GuitarStringAndFret = emptyMap(), + fretboardColor: Color = MaterialTheme.colorScheme.onSurface, + highlightColor: Color = MaterialTheme.colorScheme.error, + fretMarkStyle: TextStyle = MaterialTheme.typography.labelMedium.copy(color = MaterialTheme.colorScheme.onSurface) +) { + val measurer = rememberTextMeasurer() + + Canvas(modifier = modifier.fillMaxHeight()) { + val padding = 16.dp.toPx() + val style = Stroke(4.dp.toPx()) + + val fretboardLeading = padding / 2f + 24.dp.toPx() + val fretboardTrailing = size.width - padding / 2f + + val nutHeight = 8.dp.toPx() + val nutBottom = nutHeight + val nutPath = Path().apply { + moveTo(fretboardLeading, nutBottom) + lineTo(fretboardLeading, 0f) + lineTo(fretboardTrailing, 0f) + lineTo(fretboardTrailing, nutBottom) + } + drawPath(path = nutPath, color = fretboardColor, style = style) + + val fretboardBottom = size.height + val fretboardWidth = fretboardTrailing - fretboardLeading + val fretboardHeight = fretboardBottom - nutBottom + + val stringXs = (0..5).map { (fretboardWidth / 5) * it + fretboardLeading } + + var refFretY = fretboardHeight * 0.07f + var lastFretY = nutBottom + + val fretYs = (1..19).map { fret -> + (refFretY + lastFretY).also { calc -> + refFretY -= refFretY * 0.037f + lastFretY = calc + } + } + + val markedFrets = listOf(3, 5, 7, 9, 12, 15, 17, 19) + val fretMarks = markedFrets.map { fret -> + val index = fret.dec() + fret to (fretYs[index] - fretYs[index.dec()]) / 2 + fretYs[index.dec()] + } + val fretboardPath = Path().apply { + moveTo(fretboardLeading, nutBottom) + lineTo(fretboardTrailing, nutBottom) + moveTo(fretboardTrailing, fretboardBottom) + lineTo(fretboardLeading, fretboardBottom) + + fretYs.forEach { fretY -> + moveTo(fretboardLeading, fretY) + lineTo(fretboardTrailing, fretY) + } + + stringXs.forEach { stringX -> + moveTo(stringX, nutBottom) + lineTo(stringX, fretboardBottom) + } + + fretMarks.forEach { (fret, fretY) -> + moveTo(fretboardLeading, fretY) + addOval( + oval = Rect( + center = Offset(fretboardWidth / 2f + fretboardLeading, fretY), + 2.dp.toPx() + ) + ) + drawText( + textMeasurer = measurer, + text = fret.toString(), + topLeft = Offset(0f, fretY - 10.sp.toPx()), + style = fretMarkStyle + ) + } + } + drawPath(path = fretboardPath, color = fretboardColor, style = style) + + val highlightsPath = Path().apply { + highlights.forEach { (guitarString, fret) -> + val t = stringXs.size + val hX = stringXs[t - guitarString] + + val t2 = fretYs.size + val fretIndex = t2 - (t2 - (fret?.dec() ?: 0)) + val hY = (fretYs[fretIndex] - fretYs[fretIndex.dec()]) / 2 + fretYs[fretIndex.dec()] + + addOval( + oval = Rect( + center = Offset(hX, hY), + 9.dp.toPx() + ), + ) + } + } + drawPath(highlightsPath, color = highlightColor, style = Fill) + } +} + +@Preview(showBackground = true) +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewGuitarNeck() { + AppTheme { + Surface(modifier = Modifier.fillMaxSize()) { + Box(contentAlignment = Alignment.Center) { + GuitarNeck( + modifier = Modifier + .padding(16.dp) + .width(156.dp), + highlights = mapOf( + 1 to 3, + 2 to 8, + 3 to 12, + ) + ) + } + } + } +} \ No newline at end of file diff --git a/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/MenuButton.kt b/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/MenuButton.kt new file mode 100644 index 0000000..2e65e6d --- /dev/null +++ b/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/MenuButton.kt @@ -0,0 +1,31 @@ +package com.dosei.music.scoreconverter.ui.view + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.dosei.music.scoreconverter.ui.R + +@Composable +fun MenuButton(modifier: Modifier = Modifier, onClick: () -> Unit) { + IconButton(modifier = modifier, onClick = onClick) { + Icon(imageVector = Icons.Default.Menu, contentDescription = stringResource(R.string.menu)) + } +} + +@Preview(showBackground = true) +@Composable +private fun PreviewMenuButton() { + Surface(modifier = Modifier.fillMaxSize(), color = Color.White) { + MenuButton( + modifier = Modifier, {} + ) + } +} diff --git a/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/Score.kt b/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/Score.kt deleted file mode 100644 index 3204362..0000000 --- a/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/Score.kt +++ /dev/null @@ -1,240 +0,0 @@ -package com.dosei.music.scoreconverter.ui.view - -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.draggable -import androidx.compose.foundation.gestures.rememberDraggableState -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.inset -import androidx.compose.ui.graphics.drawscope.rotate -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.graphics.vector.VectorPainter -import androidx.compose.ui.graphics.vector.rememberVectorPainter -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.dosei.music.scoreconverter.ui.R -import com.dosei.music.scoreconverter.ui.theme.AppTheme -import com.dosei.music.scoreconverter.ui.view.NotationNotes.* -import com.dosei.music.scoreconverter.ui.view.ScoreNoteDecoration.* -import kotlin.math.roundToInt - -@Composable -fun Score( - modifier: Modifier = Modifier, - noteIndex: Int = G3.index, - noteDecoration: ScoreNoteDecoration = NATURAL, - onUpdateNoteIndex: (Int) -> Unit -) { - val noteRange = D6.index..E2.index - val notes = NotationNotes.getAll(noteRange) - - val decoratorPainter = when(noteDecoration) { - FLAT -> rememberVector(id = R.drawable.ic_flat_black) - SHARP -> rememberVector(id = R.drawable.ic_sharp_black) - else -> null - } - val clefPainter = rememberVector(id = R.drawable.ic_treble_clef) - - val height = 16.dp * (notes.count { it.isLine.not() } - 0.5f) - val heightInPx = LocalDensity.current.run { height.toPx() } - val noteSizeInPx = LocalDensity.current.run { 8.dp.toPx() } - - val offsetY = remember { mutableStateOf(noteIndex * heightInPx) } - - val lineColor = MaterialTheme.colors.onSurface - val supplementaryLineColor = MaterialTheme.colors.onSurface.copy(alpha = 0.4f) - - Canvas(modifier = modifier - .fillMaxWidth() - .height(height + 24.dp) - .draggable( - orientation = Orientation.Vertical, - state = rememberDraggableState { delta -> - val updated = (offsetY.value + delta) - val valueInRange = maxOf(0f, minOf(updated, heightInPx)) - offsetY.value = valueInRange - val newNoteIndex = (valueInRange / noteSizeInPx).roundToInt() - onUpdateNoteIndex(newNoteIndex) - } - ) - ) { - val noteSize = 16.dp.toPx() - val stroke = 2.dp.toPx() - var yCursor = 0f - - notes.sortedBy { note -> note.index }.forEach { note -> - val yPosition = yCursor + (noteSize / 2f) - if (note.isLine) { - drawHorizontalLine( - color = if (note.isMainLine) lineColor else supplementaryLineColor, - stroke = stroke, - startX = 0f, - endX = size.width, - y = yPosition - ) - } - yCursor = yPosition - } - - drawVerticalLine( - color = lineColor, - stroke = stroke, - startY = 6.5f * noteSize, - endY = 10.5f * noteSize, - x = 0f - ) - - drawVerticalLine( - color = lineColor, - stroke = stroke, - startY = 6.5f * noteSize, - endY = 10.5f * noteSize, - x = size.width - ) - - drawClef( - color = lineColor, - noteSize = noteSize, - painter = clefPainter - ) - - drawNote( - color = lineColor, - noteIndex = noteIndex, - stroke = stroke, - noteSizeInPx = noteSizeInPx, - noteSize = noteSize, - noteDecoration = noteDecoration, - decorationPainter = decoratorPainter - ) - } -} - -@Composable -private fun rememberVector(id: Int) = - rememberVectorPainter(image = ImageVector.vectorResource(id = id)) - -private fun DrawScope.drawClef( - color: Color, - noteSize: Float, - painter: VectorPainter -) = inset(vertical = noteSize * 5.4f, horizontal = 8.dp.toPx()) { - with(painter) { - draw( - colorFilter = ColorFilter.tint(color), - size = painter.intrinsicSize - ) - } -} - -private fun DrawScope.drawHorizontalLine( - color: Color, - stroke: Float, - startX: Float, - endX: Float, - y: Float -) = drawLine( - color = color, - strokeWidth = stroke, - start = Offset(x = startX, y = y), - end = Offset(x = endX, y = y) -) - - -private fun DrawScope.drawVerticalLine( - color: Color, - stroke: Float, - startY: Float, - endY: Float, - x: Float -) = drawLine( - color = color, - strokeWidth = stroke, - start = Offset(x = x, y = startY), - end = Offset(x = x, y = endY) -) - -private fun DrawScope.drawNote( - color: Color, - noteIndex: Int, - stroke: Float, - noteSizeInPx: Float, - noteSize: Float, - noteDecoration: ScoreNoteDecoration?, - decorationPainter: VectorPainter? -) { - val note = NotationNotes.getByIndex(noteIndex) ?: return - val width = noteSize + 4.dp.toPx() - val startX = size.width / 2f - val endX = size.width / 2f + width - val noteY = noteIndex.toFloat() * noteSizeInPx - - rotate(degrees = -25f, Offset(x = startX + width / 2f, y = noteY + noteSize / 2f)) { - drawOval( - color = color, - topLeft = Offset(x = startX, y = noteY), - size = Size(width = width, height = noteSize) - ) - } - drawVerticalLine( - color = color, - stroke = stroke, - startY = noteY + noteSizeInPx, - endY = (note.tailIndex.inc() * noteSizeInPx), - x = if (note.tailInStart) startX + stroke / 2f else endX - stroke / 2f - ) - note.supplementaryLines.forEach { - drawHorizontalLine( - color = color, - stroke = stroke, - startX = startX - 8.dp.toPx(), - endX = endX + 8.dp.toPx(), - y = it.inc() * noteSizeInPx - ) - } - decorationPainter?.run { - val decoratorSize = intrinsicSize.scale(0.8f) - val decoratorX = startX - decoratorSize.width - 10.dp.toPx() - val decoratorY = noteY - decoratorSize.height * (noteDecoration?.topPaddingDiff ?: 1f) - inset(left = decoratorX, top = decoratorY, right = 0f, bottom = 0f) { - draw( - colorFilter = ColorFilter.tint(color), - size = decoratorSize - ) - } - } -} - -private fun Size.scale(multiplier: Float) = - Size(this.width * multiplier, this.height * multiplier) - -@Preview(showBackground = true) -@Composable -private fun PreviewComposableScore() { - AppTheme(darkTheme = true) { - Surface { - Score( - modifier = Modifier - .padding(16.dp), - noteIndex = C4.index, - onUpdateNoteIndex = {}, - noteDecoration = SHARP - ) - } - } -} \ No newline at end of file diff --git a/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/ScoreNoteDecoration.kt b/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/ScoreNoteDecoration.kt deleted file mode 100644 index 0d29add..0000000 --- a/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/ScoreNoteDecoration.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.dosei.music.scoreconverter.ui.view - -import androidx.annotation.DrawableRes -import com.dosei.music.scoreconverter.ui.R - -enum class ScoreNoteDecoration( - @DrawableRes val resource: Int, - val topPaddingDiff: Float -) { - NATURAL(R.drawable.ic_natural_note, 0.5f), - FLAT(R.drawable.ic_flat_black, 0.5f), - SHARP(R.drawable.ic_sharp_black, 0.3f); -} \ No newline at end of file diff --git a/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/Tablature.kt b/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/Tablature.kt deleted file mode 100644 index 3754e7d..0000000 --- a/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/Tablature.kt +++ /dev/null @@ -1,146 +0,0 @@ -package com.dosei.music.scoreconverter.ui.view - -import android.graphics.Typeface -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Paint -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.graphics.drawscope.drawIntoCanvas -import androidx.compose.ui.graphics.nativeCanvas -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.dosei.music.scoreconverter.ui.theme.AppTheme - -@Composable -fun Tablature( - modifier: Modifier = Modifier, - strings: Int = 6, - positions: Map = mapOf() -) { - val lineColor = MaterialTheme.colors.onSurface - val spaceColor = MaterialTheme.colors.surface - val textSizeDp = LocalDensity.current.run { 18.sp.toPx() } - val paint = Paint().asFrameworkPaint().apply { - color = lineColor.hashCode() - isAntiAlias = true - textSize = textSizeDp - typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) - } - val paint2 = Paint().asFrameworkPaint().apply { - color = lineColor.hashCode() - isAntiAlias = true - textSize = textSizeDp - textAlign = android.graphics.Paint.Align.CENTER - typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) - } - Canvas( - modifier = modifier - .fillMaxWidth() - .height(32.dp * strings) - ) { - val positionSize = textSizeDp + 8.dp.toPx() - val itemPadding = 2.dp.toPx() - val lineSize = positionSize + itemPadding * 2f - val stroke = 1.dp.toPx() - var yCursor = 0f - - val startX = 20.dp.toPx() - val endX = size.width - 20.dp.toPx() - - val ids = listOf("E", "B", "G", "D", "A", "E") - - for (string in 1..strings) { - val rectY = yCursor + itemPadding - val lineY = rectY + (positionSize / 2) - val id = ids[string.dec()] - - drawLine( - color = lineColor, - strokeWidth = stroke, - start = Offset(x = startX, y = lineY), - end = Offset(x = endX, y = lineY) - ) - - drawRoundRect( - color = spaceColor, - cornerRadius = CornerRadius(x = 4.dp.toPx(), y = 4.dp.toPx()), - topLeft = Offset(x = (size.width / 2f) - 24.dp.toPx(), y = rectY), - size = Size(width = 48.dp.toPx(), height = positionSize) - ) - - drawRoundRect( - color = lineColor, - cornerRadius = CornerRadius(x = 4.dp.toPx(), y = 4.dp.toPx()), - topLeft = Offset(x = (size.width / 2f) - 24.dp.toPx(), y = rectY), - size = Size(width = 48.dp.toPx(), height = positionSize), - style = Stroke(width = 1.dp.toPx()) - ) - - drawIntoCanvas { - it.nativeCanvas.drawText( - id, - 0f, - lineY + 7.dp.toPx(), - paint - ) - - val text = positions[string]?.toString() ?: "-" - it.nativeCanvas.drawText( - text, - size.width / 2f, - lineY + 7.dp.toPx(), - paint2 - ) - } - yCursor += lineSize - } - - drawLine( - color = lineColor, - strokeWidth = stroke, - start = Offset(x = startX, y = (positionSize / 2f) + itemPadding), - end = Offset(x = startX, y = yCursor - itemPadding - (positionSize / 2f)) - ) - - drawLine( - color = lineColor, - strokeWidth = stroke, - start = Offset(x = endX, y = (positionSize / 2f) + itemPadding), - end = Offset(x = endX, y = yCursor - itemPadding - (positionSize / 2f)) - ) - } - -} - -@Preview(showBackground = true) -@Composable -private fun PreviewComposableTablature() { - AppTheme(darkTheme = true) { - Surface { - Tablature( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - strings = 6, - positions = mapOf( - 1 to 0, - 2 to 5, - 3 to 10 - ) - ) - } - } -} \ No newline at end of file diff --git a/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/ToggleRow.kt b/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/ToggleRow.kt deleted file mode 100644 index edcfdfa..0000000 --- a/ui/src/main/java/com/dosei/music/scoreconverter/ui/view/ToggleRow.kt +++ /dev/null @@ -1,202 +0,0 @@ -package com.dosei.music.scoreconverter.ui.view - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.Image -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.MaterialTheme -import androidx.compose.material.OutlinedButton -import androidx.compose.material.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex -import com.dosei.music.scoreconverter.ui.R -import com.dosei.music.scoreconverter.ui.theme.AppTheme - -data class Icon( - val painter: Painter, - val description: String -) - -@Composable -fun ToggleIconRow( - modifier: Modifier = Modifier, - icons: List, - selectedIndex: Int, - onSelectIndex: (Int) -> Unit, - iconSize: Dp = 48.dp, - cornerRadius: Dp = 8.dp -) { - Row( - modifier = modifier - ) { - icons.forEachIndexed { index, item -> - val position = when (index) { - 0 -> ButtonPosition.Start - icons.lastIndex -> ButtonPosition.End - else -> ButtonPosition.Middle - } - if (index == selectedIndex) { - SelectedButton( - cornerRadius = cornerRadius, - index = index, - icon = item, - iconSize = iconSize, - position = position, - onClick = { onSelectIndex(index) } - ) - } else { - CommonButton( - cornerRadius = cornerRadius, - index = index, - icon = item, - iconSize = iconSize, - position = position, - onClick = { onSelectIndex(index) } - ) - } - } - } -} - -private enum class ButtonPosition { - Start { - override fun createOutline(radius: Dp): RoundedCornerShape = RoundedCornerShape( - topStart = radius, - topEnd = 0.dp, - bottomStart = radius, - bottomEnd = 0.dp - ) - }, - Middle { - override fun createOutline(radius: Dp): RoundedCornerShape = RoundedCornerShape( - topStart = 0.dp, - topEnd = 0.dp, - bottomStart = 0.dp, - bottomEnd = 0.dp - ) - }, - End { - override fun createOutline(radius: Dp): RoundedCornerShape = RoundedCornerShape( - topStart = 0.dp, - topEnd = radius, - bottomStart = 0.dp, - bottomEnd = radius - ) - }; - - abstract fun createOutline(radius: Dp): RoundedCornerShape -} - - -@Composable -private fun SelectedButton( - cornerRadius: Dp, - index: Int, - icon: Icon, - iconSize: Dp, - position: ButtonPosition, - onClick: () -> Unit -) { - val strokeColor = - if (isSystemInDarkTheme()) MaterialTheme.colors.onSurface else MaterialTheme.colors.primary - - OutlinedButton( - modifier = Modifier - .offset((-1 * index).dp, 0.dp) - .zIndex(1f), - onClick = onClick, - shape = position.createOutline(cornerRadius), - border = BorderStroke( - width = 1.dp, - color = strokeColor - ), - colors = ButtonDefaults.outlinedButtonColors( - backgroundColor = strokeColor.copy( - alpha = 0.2f - ), - contentColor = strokeColor - ), - ) { - Image( - modifier = Modifier.size(iconSize), - painter = icon.painter, - contentDescription = icon.description, - colorFilter = ColorFilter.tint(strokeColor) - ) - } -} - -@Composable -private fun CommonButton( - cornerRadius: Dp, - index: Int, - icon: Icon, - iconSize: Dp, - position: ButtonPosition, - onClick: () -> Unit -) { - OutlinedButton( - modifier = Modifier - .offset((-1 * index).dp, 0.dp) - .zIndex(0f), - onClick = onClick, - shape = position.createOutline(cornerRadius), - border = BorderStroke( - width = 1.dp, - color = MaterialTheme.colors.onSurface.copy(alpha = 0.5f) - ), - colors = ButtonDefaults.outlinedButtonColors( - backgroundColor = MaterialTheme.colors.surface, - contentColor = MaterialTheme.colors.primary - ), - ) { - Image( - colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface), - modifier = Modifier.size(iconSize), - painter = icon.painter, - contentDescription = icon.description - ) - } -} - - -@Preview(showBackground = true) -@Composable -private fun PreviewToggleRow() { - AppTheme(darkTheme = true) { - Surface { - ToggleIconRow( - modifier = Modifier, - selectedIndex = 1, - onSelectIndex = {}, - icons = listOf( - Icon( - painter = painterResource(id = R.drawable.ic_flat_black), - description = "flat button" - ), - Icon( - painter = painterResource(id = R.drawable.ic_natural_note), - description = "natural button" - ), - Icon( - painter = painterResource(id = R.drawable.ic_sharp_black), - description = "sharp button" - ) - ) - ) - } - } -} \ No newline at end of file diff --git a/ui/src/main/res/drawable/bd_white_tile.xml b/ui/src/main/res/drawable/bd_white_tile.xml deleted file mode 100644 index d3a59e0..0000000 --- a/ui/src/main/res/drawable/bd_white_tile.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/ui/src/main/res/drawable/ic_flat_active.xml b/ui/src/main/res/drawable/ic_flat_active.xml deleted file mode 100644 index 30d0ba6..0000000 --- a/ui/src/main/res/drawable/ic_flat_active.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/ui/src/main/res/drawable/ic_flat_black.xml b/ui/src/main/res/drawable/ic_flat_black.xml deleted file mode 100644 index 2cd6396..0000000 --- a/ui/src/main/res/drawable/ic_flat_black.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/ui/src/main/res/drawable/ic_natural_note.xml b/ui/src/main/res/drawable/ic_natural_note.xml deleted file mode 100644 index c823d3d..0000000 --- a/ui/src/main/res/drawable/ic_natural_note.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - diff --git a/ui/src/main/res/drawable/ic_note.xml b/ui/src/main/res/drawable/ic_note.xml deleted file mode 100644 index f9c2ec1..0000000 --- a/ui/src/main/res/drawable/ic_note.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/ui/src/main/res/drawable/ic_sharp_active.xml b/ui/src/main/res/drawable/ic_sharp_active.xml deleted file mode 100644 index 647605f..0000000 --- a/ui/src/main/res/drawable/ic_sharp_active.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - diff --git a/ui/src/main/res/drawable/ic_sharp_black.xml b/ui/src/main/res/drawable/ic_sharp_black.xml deleted file mode 100644 index bddd10b..0000000 --- a/ui/src/main/res/drawable/ic_sharp_black.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - diff --git a/ui/src/main/res/drawable/ic_treble_clef.xml b/ui/src/main/res/drawable/ic_treble_clef.xml deleted file mode 100644 index 605e6c9..0000000 --- a/ui/src/main/res/drawable/ic_treble_clef.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/ui/src/main/res/drawable/shape_black_rectangle.xml b/ui/src/main/res/drawable/shape_black_rectangle.xml deleted file mode 100644 index e608c63..0000000 --- a/ui/src/main/res/drawable/shape_black_rectangle.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/ui/src/main/res/values-night/styles.xml b/ui/src/main/res/values-night/styles.xml index 889dfee..e69ff93 100644 --- a/ui/src/main/res/values-night/styles.xml +++ b/ui/src/main/res/values-night/styles.xml @@ -1,8 +1,17 @@ - \ No newline at end of file diff --git a/ui/src/main/res/values-pt-rBR/strings.xml b/ui/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000..d48d45b --- /dev/null +++ b/ui/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,4 @@ + + + Menu + \ No newline at end of file diff --git a/ui/src/main/res/values/colors.xml b/ui/src/main/res/values/colors.xml index 921c24c..f3a9f28 100644 --- a/ui/src/main/res/values/colors.xml +++ b/ui/src/main/res/values/colors.xml @@ -1,14 +1,64 @@ - #bf360c - #870000 - #e64a19 + #FF006874 + #FFFFFFFF + #FF97F0FF + #FF001F24 + #FF4A6267 + #FFFFFFFF + #FFCDE7EC + #FF051F23 + #FF525E7D + #FFFFFFFF + #FFDAE2FF + #FF0E1B37 + #FFBA1A1A + #FFFFDAD6 + #FFFFFFFF + #FF410002 + #FFFAFDFD + #FF191C1D + #FFFAFDFD + #FF191C1D + #FFDBE4E6 + #FF3F484A + #FF6F797A + #FFEFF1F1 + #FF2E3132 + #FF4FD8EB + #FF000000 + #FF006874 + #FFBFC8CA + #FF000000 - #fff - #00000000 - - #000 - #b1b1b1 - - @color/colorAccent + #FF4FD8EB + #FF00363D + #FF004F58 + #FF97F0FF + #FFB1CBD0 + #FF1C3438 + #FF334B4F + #FFCDE7EC + #FFBAC6EA + #FF24304D + #FF3B4664 + #FFDAE2FF + #FFFFB4AB + #FF93000A + #FF690005 + #FFFFDAD6 + #FF191C1D + #FFE1E3E3 + #FF191C1D + #FFE1E3E3 + #FF3F484A + #FFBFC8CA + #FF899294 + #FF191C1D + #FFE1E3E3 + #FF006874 + #FF000000 + #FF4FD8EB + #FF3F484A + #FF000000 \ No newline at end of file diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index aa51ee7..28f9e35 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -6,4 +6,5 @@ D A E + Menu \ No newline at end of file diff --git a/ui/src/main/res/values/styles.xml b/ui/src/main/res/values/styles.xml index c057612..504ba27 100644 --- a/ui/src/main/res/values/styles.xml +++ b/ui/src/main/res/values/styles.xml @@ -1,31 +1,16 @@ - - - - - - - - - - \ No newline at end of file diff --git a/uitest/.gitignore b/uitest/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/uitest/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/uitest/build.gradle b/uitest/build.gradle deleted file mode 100644 index a5e0c42..0000000 --- a/uitest/build.gradle +++ /dev/null @@ -1,40 +0,0 @@ -plugins { - id 'com.android.application' - id 'kotlin-android' -} - -android { - compileSdkVersion 31 - - defaultConfig { - applicationId "com.dosei.music.scoreconverter.uitest" - minSdkVersion 21 - targetSdkVersion 31 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - kotlinOptions { - jvmTarget = '1.8' - } -} - -dependencies { - implementation project(":ui") - implementation "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}" - implementation 'androidx.core:core-ktx:1.3.2' - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'com.google.android.material:material:1.3.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - testImplementation 'junit:junit:4.+' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' -} \ No newline at end of file diff --git a/uitest/proguard-rules.pro b/uitest/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/uitest/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/uitest/src/androidTest/java/com/dosei/music/scoreconverter/uitest/ExampleInstrumentedTest.kt b/uitest/src/androidTest/java/com/dosei/music/scoreconverter/uitest/ExampleInstrumentedTest.kt deleted file mode 100644 index 627b85b..0000000 --- a/uitest/src/androidTest/java/com/dosei/music/scoreconverter/uitest/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.dosei.music.scoreconverter.uitest - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.dosei.music.scoreconverter.uitest", appContext.packageName) - } -} \ No newline at end of file diff --git a/uitest/src/main/AndroidManifest.xml b/uitest/src/main/AndroidManifest.xml deleted file mode 100644 index c961d9c..0000000 --- a/uitest/src/main/AndroidManifest.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/uitest/src/main/java/com/dosei/music/scoreconverter/uitest/MainActivity.kt b/uitest/src/main/java/com/dosei/music/scoreconverter/uitest/MainActivity.kt deleted file mode 100644 index afd2ba8..0000000 --- a/uitest/src/main/java/com/dosei/music/scoreconverter/uitest/MainActivity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.dosei.music.scoreconverter.uitest - -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity - -class MainActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - } -} \ No newline at end of file diff --git a/uitest/src/main/res/drawable-v24/ic_launcher_foreground.xml b/uitest/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/uitest/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/uitest/src/main/res/drawable/ic_launcher_background.xml b/uitest/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/uitest/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/uitest/src/main/res/layout/activity_main.xml b/uitest/src/main/res/layout/activity_main.xml deleted file mode 100644 index 607b746..0000000 --- a/uitest/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/uitest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/uitest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cf..0000000 --- a/uitest/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/uitest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/uitest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index eca70cf..0000000 --- a/uitest/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/uitest/src/main/res/mipmap-hdpi/ic_launcher.png b/uitest/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a571e60..0000000 Binary files a/uitest/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/uitest/src/main/res/mipmap-hdpi/ic_launcher_round.png b/uitest/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 61da551..0000000 Binary files a/uitest/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/uitest/src/main/res/mipmap-mdpi/ic_launcher.png b/uitest/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index c41dd28..0000000 Binary files a/uitest/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/uitest/src/main/res/mipmap-mdpi/ic_launcher_round.png b/uitest/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index db5080a..0000000 Binary files a/uitest/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/uitest/src/main/res/mipmap-xhdpi/ic_launcher.png b/uitest/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 6dba46d..0000000 Binary files a/uitest/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/uitest/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/uitest/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index da31a87..0000000 Binary files a/uitest/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/uitest/src/main/res/mipmap-xxhdpi/ic_launcher.png b/uitest/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 15ac681..0000000 Binary files a/uitest/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/uitest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/uitest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index b216f2d..0000000 Binary files a/uitest/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/uitest/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/uitest/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index f25a419..0000000 Binary files a/uitest/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/uitest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/uitest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index e96783c..0000000 Binary files a/uitest/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/uitest/src/main/res/values-night/themes.xml b/uitest/src/main/res/values-night/themes.xml deleted file mode 100644 index b0b7cec..0000000 --- a/uitest/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/uitest/src/main/res/values/colors.xml b/uitest/src/main/res/values/colors.xml deleted file mode 100644 index f8c6127..0000000 --- a/uitest/src/main/res/values/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/uitest/src/main/res/values/strings.xml b/uitest/src/main/res/values/strings.xml deleted file mode 100644 index e88c7ff..0000000 --- a/uitest/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - UITest - \ No newline at end of file diff --git a/uitest/src/main/res/values/themes.xml b/uitest/src/main/res/values/themes.xml deleted file mode 100644 index b57ba76..0000000 --- a/uitest/src/main/res/values/themes.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/uitest/src/test/java/com/dosei/music/scoreconverter/uitest/ExampleUnitTest.kt b/uitest/src/test/java/com/dosei/music/scoreconverter/uitest/ExampleUnitTest.kt deleted file mode 100644 index 130e18b..0000000 --- a/uitest/src/test/java/com/dosei/music/scoreconverter/uitest/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.dosei.music.scoreconverter.uitest - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file