diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index ae388c2..0897082 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -4,16 +4,15 @@
diff --git a/app/src/main/java/io/github/teccheck/fastlyrics/api/LyricStorage.kt b/app/src/main/java/io/github/teccheck/fastlyrics/api/LyricStorage.kt
index 84338a4..b117873 100644
--- a/app/src/main/java/io/github/teccheck/fastlyrics/api/LyricStorage.kt
+++ b/app/src/main/java/io/github/teccheck/fastlyrics/api/LyricStorage.kt
@@ -11,6 +11,7 @@ import io.github.teccheck.fastlyrics.model.SongWithLyrics
import io.github.teccheck.fastlyrics.utils.Utils
import java.util.concurrent.Executors
import dev.forkhandles.result4k.Result
+import io.github.teccheck.fastlyrics.model.LyricsType
object LyricStorage {
private const val TAG = "LyricsStorage"
@@ -73,4 +74,7 @@ object LyricStorage {
fun findSong(title: String, artist: String): SongWithLyrics? =
database.songsDao().findSong(title, artist)
+
+ fun findSong(title: String, artist: String, type: LyricsType): SongWithLyrics? =
+ database.songsDao().findSong(title, artist, type)
}
\ No newline at end of file
diff --git a/app/src/main/java/io/github/teccheck/fastlyrics/api/LyricsApi.kt b/app/src/main/java/io/github/teccheck/fastlyrics/api/LyricsApi.kt
index 4934d2a..e20ba87 100644
--- a/app/src/main/java/io/github/teccheck/fastlyrics/api/LyricsApi.kt
+++ b/app/src/main/java/io/github/teccheck/fastlyrics/api/LyricsApi.kt
@@ -5,9 +5,12 @@ import androidx.lifecycle.MutableLiveData
import dev.forkhandles.result4k.Failure
import dev.forkhandles.result4k.Result
import dev.forkhandles.result4k.Success
+import io.github.teccheck.fastlyrics.api.provider.Deezer
+import io.github.teccheck.fastlyrics.api.provider.Genius
import io.github.teccheck.fastlyrics.api.provider.LyricsProvider
import io.github.teccheck.fastlyrics.exceptions.LyricsApiException
import io.github.teccheck.fastlyrics.exceptions.LyricsNotFoundException
+import io.github.teccheck.fastlyrics.model.LyricsType
import io.github.teccheck.fastlyrics.model.SearchResult
import io.github.teccheck.fastlyrics.model.SongMeta
import io.github.teccheck.fastlyrics.model.SongWithLyrics
@@ -17,12 +20,17 @@ object LyricsApi {
private const val TAG = "LyricsApi"
- private val executor = Executors.newSingleThreadExecutor()
+ private val executor = Executors.newFixedThreadPool(2)
+
+ private var providers: Array = arrayOf(Genius)
+ private var providers_synced: Array = arrayOf(Deezer)
- private var providers: Array = LyricsProvider.getAllProviders()
private val provider: LyricsProvider
get() = providers.first()
+ private val provider_synced: LyricsProvider
+ get() = providers_synced.first()
+
fun setProviderOrder(order: Array) {
val all = LyricsProvider.getAllProviders()
providers = order.mapNotNull { name -> all.find { provider -> provider.getName() == name } }
@@ -31,16 +39,20 @@ object LyricsApi {
fun getLyricsAsync(
songMeta: SongMeta,
- liveDataTarget: MutableLiveData>
+ liveDataTarget: MutableLiveData>,
+ synced: Boolean = false
) {
executor.submit {
- val song = songMeta.artist?.let { LyricStorage.findSong(songMeta.title, it) }
+ Log.d(TAG, "getLyricsAsync($synced)")
+
+ val type = if (synced) LyricsType.LRC else LyricsType.RAW_TEXT
+ val song = songMeta.artist?.let { LyricStorage.findSong(songMeta.title, it, type) }
if (song != null) {
liveDataTarget.postValue(Success(song))
return@submit
}
- val result = fetchLyrics(songMeta)
+ val result = fetchLyrics(songMeta, synced)
liveDataTarget.postValue(result)
if (result is Success) {
@@ -82,7 +94,8 @@ object LyricsApi {
}
private fun fetchLyrics(
- songMeta: SongMeta
+ songMeta: SongMeta,
+ synced: Boolean = false
): Result {
var searchQuery = songMeta.title
if (songMeta.artist != null) {
@@ -92,6 +105,12 @@ object LyricsApi {
var bestResult: SearchResult? = null
var bestResultScore = 0.0
+ val providers = if (synced) {
+ providers_synced
+ } else {
+ providers
+ }
+
for (provider in providers) {
val search = provider.search(searchQuery)
if (search !is Success)
diff --git a/app/src/main/java/io/github/teccheck/fastlyrics/api/storage/SongsDao.kt b/app/src/main/java/io/github/teccheck/fastlyrics/api/storage/SongsDao.kt
index 744d91a..ca37035 100644
--- a/app/src/main/java/io/github/teccheck/fastlyrics/api/storage/SongsDao.kt
+++ b/app/src/main/java/io/github/teccheck/fastlyrics/api/storage/SongsDao.kt
@@ -3,6 +3,7 @@ package io.github.teccheck.fastlyrics.api.storage
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
+import io.github.teccheck.fastlyrics.model.LyricsType
import io.github.teccheck.fastlyrics.model.SongWithLyrics
@Dao
@@ -14,6 +15,10 @@ interface SongsDao {
@Query("SELECT * FROM songs WHERE title = :title AND artist = :artist")
fun findSong(title: String, artist: String): SongWithLyrics?
+
+ @Query("SELECT * FROM songs WHERE title = :title AND artist = :artist AND type = :type")
+ fun findSong(title: String, artist: String, type: LyricsType): SongWithLyrics?
+
@Query("SELECT * FROM songs WHERE id = :id")
fun getSong(id: Long): SongWithLyrics?
diff --git a/app/src/main/java/io/github/teccheck/fastlyrics/ui/fastlyrics/FastLyricsFragment.kt b/app/src/main/java/io/github/teccheck/fastlyrics/ui/fastlyrics/FastLyricsFragment.kt
index 8f1fb8d..8157237 100644
--- a/app/src/main/java/io/github/teccheck/fastlyrics/ui/fastlyrics/FastLyricsFragment.kt
+++ b/app/src/main/java/io/github/teccheck/fastlyrics/ui/fastlyrics/FastLyricsFragment.kt
@@ -12,6 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.squareup.picasso.Picasso
import dev.forkhandles.result4k.Failure
import dev.forkhandles.result4k.Success
+import dev.forkhandles.result4k.Result
import io.github.teccheck.fastlyrics.R
import io.github.teccheck.fastlyrics.Settings
import io.github.teccheck.fastlyrics.api.provider.LyricsProvider
@@ -58,10 +59,23 @@ class FastLyricsFragment : Fragment() {
}
lyricsViewModel.songWithLyrics.observe(viewLifecycleOwner) { result ->
+ binding.header.syncedLyricsSwitch.isChecked = false
binding.refreshLayout.isRefreshing = false
+ displayResult(result)
+ }
+
+ lyricsViewModel.songWithLyricsSynced.observe(viewLifecycleOwner) { result ->
+ binding.refreshLayout.isRefreshing = false
+
when (result) {
- is Success -> displaySongWithLyrics(result.value)
- is Failure -> displayError(result.reason)
+ is Success -> {
+ lyricsViewModel.syncedLyricsAvailable = true
+ binding.header.syncedLyricsAvailable.visibility = View.VISIBLE
+ }
+ is Failure -> {
+ binding.header.syncedLyricsAvailable.visibility = View.GONE
+ binding.header.syncedLyricsSwitch.isChecked = false
+ }
}
}
@@ -72,6 +86,16 @@ class FastLyricsFragment : Fragment() {
)
binding.refreshLayout.setOnRefreshListener { loadLyricsForCurrentSong() }
+ binding.header.syncedLyricsSwitch.setOnCheckedChangeListener { _, checked ->
+ val song = if (checked && lyricsViewModel.syncedLyricsAvailable) {
+ lyricsViewModel.songWithLyricsSynced.value
+ } else {
+ lyricsViewModel.songWithLyrics.value
+ }
+
+ song?.let { displayResult(it) }
+ }
+
val notificationAccess =
context?.let { DummyNotificationListenerService.canAccessNotifications(it) } ?: false
@@ -104,6 +128,13 @@ class FastLyricsFragment : Fragment() {
}
}
+ private fun displayResult(result: Result) {
+ when (result) {
+ is Success -> displaySongWithLyrics(result.value)
+ is Failure -> displayError(result.reason)
+ }
+ }
+
private fun displaySongMeta(songMeta: SongMeta) {
binding.header.container.visibility = View.VISIBLE
binding.errorView.container.visibility = View.GONE
@@ -128,7 +159,12 @@ class FastLyricsFragment : Fragment() {
val providerIconRes = Utils.getProviderIconRes(it)!!
val icon = AppCompatResources.getDrawable(requireContext(), providerIconRes)
binding.lyricsView.source.setIconResource(providerIconRes)
- binding.lyricsView.textLyricsProvider.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null)
+ binding.lyricsView.textLyricsProvider.setCompoundDrawablesRelativeWithIntrinsicBounds(
+ icon,
+ null,
+ null,
+ null
+ )
val nameRes = Utils.getProviderNameRes(it)!!
val name = getString(nameRes)
diff --git a/app/src/main/java/io/github/teccheck/fastlyrics/ui/fastlyrics/FastLyricsViewModel.kt b/app/src/main/java/io/github/teccheck/fastlyrics/ui/fastlyrics/FastLyricsViewModel.kt
index 9521ba0..2efe49c 100644
--- a/app/src/main/java/io/github/teccheck/fastlyrics/ui/fastlyrics/FastLyricsViewModel.kt
+++ b/app/src/main/java/io/github/teccheck/fastlyrics/ui/fastlyrics/FastLyricsViewModel.kt
@@ -20,21 +20,25 @@ class FastLyricsViewModel : ViewModel() {
private val _songMeta = MutableLiveData>()
private val _songWithLyrics = MutableLiveData>()
+ private val _songWithLyricsSynced = MutableLiveData>()
private val _songPosition = MutableLiveData()
private var songPositionTimer: Timer? = null
val songMeta: LiveData> = _songMeta
val songWithLyrics: LiveData> = _songWithLyrics
+ val songWithLyricsSynced: LiveData> = _songWithLyricsSynced
val songPosition: LiveData = _songPosition
+ var syncedLyricsAvailable = false
var autoRefresh = false
private val songMetaCallback = MediaSession.SongMetaCallback {
if (!autoRefresh) return@SongMetaCallback
_songMeta.postValue(Success(it))
- LyricsApi.getLyricsAsync(it, _songWithLyrics)
+ loadLyrics(it)
+
}
fun loadLyricsForCurrentSong(context: Context): Boolean {
@@ -47,12 +51,18 @@ class FastLyricsViewModel : ViewModel() {
_songMeta.value = songMetaResult
if (songMetaResult is Success) {
- LyricsApi.getLyricsAsync(songMetaResult.value, _songWithLyrics)
+ loadLyrics(songMetaResult.value)
}
return songMetaResult is Success
}
+ private fun loadLyrics(songMeta: SongMeta) {
+ syncedLyricsAvailable = false
+ LyricsApi.getLyricsAsync(songMeta, _songWithLyrics)
+ LyricsApi.getLyricsAsync(songMeta, _songWithLyricsSynced, true)
+ }
+
fun setupSongMetaListener() {
MediaSession.registerSongMetaCallback(songMetaCallback)
}
diff --git a/app/src/main/java/io/github/teccheck/fastlyrics/ui/saved/RecyclerAdapter.kt b/app/src/main/java/io/github/teccheck/fastlyrics/ui/saved/RecyclerAdapter.kt
index 3a0e008..6cf88eb 100644
--- a/app/src/main/java/io/github/teccheck/fastlyrics/ui/saved/RecyclerAdapter.kt
+++ b/app/src/main/java/io/github/teccheck/fastlyrics/ui/saved/RecyclerAdapter.kt
@@ -10,7 +10,9 @@ import androidx.recyclerview.selection.SelectionTracker
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import io.github.teccheck.fastlyrics.R
+import io.github.teccheck.fastlyrics.api.provider.LyricsProvider
import io.github.teccheck.fastlyrics.model.SongWithLyrics
+import io.github.teccheck.fastlyrics.utils.Utils
class RecyclerAdapter :
RecyclerView.Adapter() {
@@ -22,6 +24,7 @@ class RecyclerAdapter :
private val imageArt: ImageView
private val textTitle: TextView
private val textArtist: TextView
+ private val iconProvider: ImageView
private val selectionIcon: ImageView
private var song: SongWithLyrics? = null
@@ -30,6 +33,7 @@ class RecyclerAdapter :
imageArt = view.findViewById(R.id.image_song_art)
textTitle = view.findViewById(R.id.text_song_title)
textArtist = view.findViewById(R.id.text_song_artist)
+ iconProvider = view.findViewById(R.id.provider_icon)
selectionIcon = view.findViewById(R.id.selection_icon)
}
@@ -37,6 +41,11 @@ class RecyclerAdapter :
this.song = song
textTitle.text = song.title
textArtist.text = song.artist
+
+ LyricsProvider.getProviderByName(song.provider)?.let { provider ->
+ Utils.getProviderIconRes(provider)?.let { iconProvider.setImageResource(it) }
+ }
+
Picasso.get().load(song.artUrl).into(imageArt)
selectionIcon.visibility = if (selected) View.VISIBLE else View.GONE
}
diff --git a/app/src/main/java/io/github/teccheck/fastlyrics/utils/Utils.kt b/app/src/main/java/io/github/teccheck/fastlyrics/utils/Utils.kt
index 4548138..d3bfa3b 100644
--- a/app/src/main/java/io/github/teccheck/fastlyrics/utils/Utils.kt
+++ b/app/src/main/java/io/github/teccheck/fastlyrics/utils/Utils.kt
@@ -44,7 +44,7 @@ object Utils {
return when(provider) {
Genius -> R.drawable.genius
Deezer -> R.drawable.deezer
- else -> null
+ else -> R.drawable.fastlyrics
}
}
@@ -52,7 +52,7 @@ object Utils {
return when(provider) {
Genius -> R.string.source_genius
Deezer -> R.string.source_deezer
- else -> null
+ else -> R.string.app_name
}
}
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/fastlyrics.xml b/app/src/main/res/drawable/fastlyrics.xml
new file mode 100644
index 0000000..13aa3c4
--- /dev/null
+++ b/app/src/main/res/drawable/fastlyrics.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/layout_header.xml b/app/src/main/res/layout/layout_header.xml
index b43281d..67be178 100644
--- a/app/src/main/res/layout/layout_header.xml
+++ b/app/src/main/res/layout/layout_header.xml
@@ -1,40 +1,70 @@
+
-
-
+ android:animateLayoutChanges="true">
+
+
-
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="16dp"
+ android:orientation="vertical">
-
+
+
+
+
+
+
+
+
+
+ android:layout_weight="1"
+ android:drawableStart="@drawable/fastlyrics"
+ android:drawablePadding="8dp"
+ android:drawableTint="?android:attr/colorControlNormal"
+ android:text="@string/synced_lyrics_available"
+ app:useMaterialThemeColors="true" />
+
diff --git a/app/src/main/res/layout/list_item_song.xml b/app/src/main/res/layout/list_item_song.xml
index c2ca48f..0c0a67a 100644
--- a/app/src/main/res/layout/list_item_song.xml
+++ b/app/src/main/res/layout/list_item_song.xml
@@ -40,9 +40,10 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 4bd068b..8b25659 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -20,6 +20,8 @@
Es gab ein Netzwerkproblem beim Laden des Textes
Unbekannter Fehler
+ Synchronisierter Songtext verfügbar
+
Songtext von
Kopieren
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bce1252..b1d3651 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -20,6 +20,8 @@
There was a network problem while loading lyrics
An unknown error occurred
+ Synced lyrics available
+
Lyrics from
Copy