WIP Lyrics
This commit is contained in:
parent
8eb7859f30
commit
26edcdf4da
18 changed files with 228 additions and 45 deletions
|
@ -161,7 +161,6 @@ dependencies {
|
|||
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
|
||||
|
||||
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:3.4.0.201406110918-r'
|
||||
|
||||
implementation 'com.github.ksoichiro:android-observablescrollview:1.6.0'
|
||||
implementation 'com.github.kabouzeid:recyclerview-fastscroll:1.9-kmod'
|
||||
implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3'
|
||||
|
@ -169,7 +168,7 @@ dependencies {
|
|||
implementation 'com.r0adkll:slidableactivity:2.1.0'
|
||||
implementation 'com.heinrichreimersoftware:material-intro:1.6'
|
||||
implementation 'com.github.dhaval2404:imagepicker:1.7.1'
|
||||
|
||||
implementation 'org.jsoup:jsoup:1.11.1'
|
||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.0'
|
||||
implementation 'me.jorgecastillo:androidcolorx:0.2.0'
|
||||
|
||||
|
|
|
@ -60,20 +60,23 @@
|
|||
</style>
|
||||
|
||||
<style name="AppTextAppearance.MaterialAlertDialog.Button" parent="Widget.MaterialComponents.Button.TextButton">
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:textAppearance">@style/TextViewButton</item>
|
||||
<item name="fontFamily">@font/sans</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:padding">0dp</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTextAppearance.MaterialAlertDialog.Body" parent="MaterialAlertDialog.MaterialComponents.Body.Text">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:textAppearance">@style/TextViewBody1</item>
|
||||
<item name="fontFamily">@font/sans</item>
|
||||
<item name="android:paddingTop">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTextAppearance.MaterialAlertDialog.Title" parent="MaterialAlertDialog.MaterialComponents.Title.Text">
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="android:textAppearance">@style/TextViewHeadline6</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="fontFamily">@font/sans</item>
|
||||
<item name="android:padding">16dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -14,7 +14,7 @@ import code.name.monkey.retromusic.fragments.genres.GenreDetailsViewModel
|
|||
import code.name.monkey.retromusic.fragments.playlists.PlaylistDetailsViewModel
|
||||
import code.name.monkey.retromusic.fragments.search.SearchViewModel
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.network.networkModule
|
||||
import code.name.monkey.retromusic.network.*
|
||||
import code.name.monkey.retromusic.repository.*
|
||||
import code.name.monkey.retromusic.util.FilePathUtil
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
|
@ -25,6 +25,28 @@ import org.koin.androidx.viewmodel.dsl.viewModel
|
|||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
|
||||
val networkModule = module {
|
||||
|
||||
factory {
|
||||
provideDefaultCache()
|
||||
}
|
||||
factory {
|
||||
provideOkHttp(get(), get())
|
||||
}
|
||||
single {
|
||||
provideLastFmRetrofit(get())
|
||||
}
|
||||
single {
|
||||
provideDeezerRest(get())
|
||||
}
|
||||
single {
|
||||
provideLastFmRest(get())
|
||||
}
|
||||
single {
|
||||
provideLyrics(get())
|
||||
}
|
||||
}
|
||||
|
||||
private val roomModule = module {
|
||||
|
||||
single {
|
||||
|
@ -72,7 +94,20 @@ private val mainModule = module {
|
|||
}
|
||||
private val dataModule = module {
|
||||
single {
|
||||
RealRepository(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
|
||||
RealRepository(
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get(),
|
||||
get()
|
||||
)
|
||||
} bind Repository::class
|
||||
|
||||
single {
|
||||
|
|
|
@ -9,6 +9,7 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.dialogs.LyricsDialog
|
||||
import code.name.monkey.retromusic.fragments.AlbumCoverStyle
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
|
@ -90,14 +91,15 @@ class AlbumCoverPagerAdapter(
|
|||
val view = inflater.inflate(getLayoutWithPlayerTheme(), container, false)
|
||||
albumCover = view.findViewById(R.id.player_image)
|
||||
albumCover.setOnClickListener {
|
||||
showLyricsDialog()
|
||||
LyricsDialog().show(childFragmentManager, "LyricsDialog")
|
||||
//showLyricsDialog()
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
private fun showLyricsDialog() {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val data = MusicUtil.getLyrics(song)
|
||||
val data: String = MusicUtil.getLyrics(song) ?: "No lyrics found"
|
||||
withContext(Dispatchers.Main) {
|
||||
MaterialAlertDialogBuilder(
|
||||
requireContext(),
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package code.name.monkey.retromusic.dialogs
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.network.Result
|
||||
import code.name.monkey.retromusic.repository.Repository
|
||||
import kotlinx.android.synthetic.main.lyrics_dialog.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class LyricsDialog : DialogFragment() {
|
||||
override fun getTheme(): Int {
|
||||
return R.style.MaterialAlertDialogTheme
|
||||
}
|
||||
|
||||
private val repository by inject<Repository>()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.lyrics_dialog, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
lifecycleScope.launch(IO) {
|
||||
val result: Result<String> = repository.lyrics(
|
||||
MusicPlayerRemote.currentSong.artistName,
|
||||
MusicPlayerRemote.currentSong.title
|
||||
)
|
||||
withContext(Main) {
|
||||
when (result) {
|
||||
is Result.Error -> println("Error")
|
||||
is Result.Loading -> println("Loading")
|
||||
is Result.Success -> lyricsText.text = result.data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||
fun DialogFragment.materialDialog(title: Int): MaterialAlertDialogBuilder {
|
||||
return MaterialAlertDialogBuilder(
|
||||
requireContext(),
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
||||
R.style.MaterialAlertDialogTheme
|
||||
).setTitle(title)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import code.name.monkey.retromusic.repository.RealRepository
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class SearchViewModel(private val realRepository: RealRepository) : ViewModel() {
|
||||
private val results = MutableLiveData<MutableList<Any>>()
|
||||
|
@ -17,6 +15,6 @@ class SearchViewModel(private val realRepository: RealRepository) : ViewModel()
|
|||
|
||||
fun search(query: String?) = viewModelScope.launch(IO) {
|
||||
val result = realRepository.search(query)
|
||||
withContext(Main) { results.postValue(result) }
|
||||
results.value = result
|
||||
}
|
||||
}
|
|
@ -15,8 +15,8 @@
|
|||
package code.name.monkey.retromusic.glide.artistimage
|
||||
|
||||
import android.content.Context
|
||||
import code.name.monkey.retromusic.deezer.Data
|
||||
import code.name.monkey.retromusic.deezer.DeezerService
|
||||
import code.name.monkey.retromusic.model.Data
|
||||
import code.name.monkey.retromusic.network.DeezerService
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.bumptech.glide.Priority
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package code.name.monkey.retromusic.deezer
|
||||
package code.name.monkey.retromusic.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package code.name.monkey.retromusic.deezer
|
||||
package code.name.monkey.retromusic.network
|
||||
|
||||
import android.content.Context
|
||||
import code.name.monkey.retromusic.model.DeezerResponse
|
||||
import okhttp3.Cache
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
|
@ -0,0 +1,12 @@
|
|||
package code.name.monkey.retromusic.network
|
||||
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Headers
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface LyricsRestService {
|
||||
|
||||
@Headers("Cache-Control: public")
|
||||
@GET("/lyrics")
|
||||
suspend fun getLyrics(@Query("artist") artist: String, @Query("title") title: String): String
|
||||
}
|
|
@ -3,39 +3,17 @@ package code.name.monkey.retromusic.network
|
|||
import android.content.Context
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
import code.name.monkey.retromusic.deezer.DeezerService
|
||||
import code.name.monkey.retromusic.network.conversion.LyricsConverterFactory
|
||||
import com.google.gson.Gson
|
||||
import okhttp3.Cache
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import org.koin.dsl.module
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private const val TIMEOUT: Long = 700
|
||||
|
||||
val networkModule = module {
|
||||
|
||||
factory {
|
||||
provideDefaultCache()
|
||||
}
|
||||
factory {
|
||||
provideOkHttp(get(), get())
|
||||
}
|
||||
single {
|
||||
provideLastFmRetrofit(get())
|
||||
}
|
||||
single {
|
||||
provideDeezerRest(get())
|
||||
}
|
||||
single {
|
||||
provideLastFmRest(get())
|
||||
}
|
||||
}
|
||||
|
||||
fun provideDefaultCache(): Cache? {
|
||||
val cacheDir = File(App.getContext().cacheDir.absolutePath, "/okhttp-lastfm/")
|
||||
if (cacheDir.mkdirs() || cacheDir.isDirectory) {
|
||||
|
@ -94,4 +72,12 @@ fun provideDeezerRest(retrofit: Retrofit): DeezerService {
|
|||
.baseUrl("https://api.deezer.com/")
|
||||
.build()
|
||||
return newBuilder.create(DeezerService::class.java)
|
||||
}
|
||||
|
||||
fun provideLyrics(retrofit: Retrofit): LyricsRestService {
|
||||
val newBuilder = retrofit.newBuilder()
|
||||
.baseUrl("https://makeitpersonal.co")
|
||||
.addConverterFactory(LyricsConverterFactory())
|
||||
.build()
|
||||
return newBuilder.create(LyricsRestService::class.java)
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Naman Dwivedi.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
package code.name.monkey.retromusic.network.conversion
|
||||
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Converter
|
||||
import retrofit2.Retrofit
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class LyricsConverterFactory : Converter.Factory() {
|
||||
|
||||
override fun responseBodyConverter(
|
||||
type: Type?,
|
||||
annotations: Array<Annotation>?,
|
||||
retrofit: Retrofit?
|
||||
): Converter<ResponseBody, *>? {
|
||||
return if (String::class.java == type) {
|
||||
Converter<ResponseBody, String> { value -> value.string() }
|
||||
} else null
|
||||
}
|
||||
|
||||
override fun requestBodyConverter(
|
||||
type: Type?,
|
||||
parameterAnnotations: Array<Annotation>?,
|
||||
methodAnnotations: Array<Annotation>?,
|
||||
retrofit: Retrofit?
|
||||
): Converter<*, RequestBody>? {
|
||||
|
||||
return if (String::class.java == type) {
|
||||
Converter<String, RequestBody> { value -> RequestBody.create(MEDIA_TYPE, value) }
|
||||
} else null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val MEDIA_TYPE = MediaType.parse("text/plain")
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import code.name.monkey.retromusic.db.*
|
|||
import code.name.monkey.retromusic.model.*
|
||||
import code.name.monkey.retromusic.model.smartplaylist.NotPlayedPlaylist
|
||||
import code.name.monkey.retromusic.network.LastFMService
|
||||
import code.name.monkey.retromusic.network.LyricsRestService
|
||||
import code.name.monkey.retromusic.network.Result
|
||||
import code.name.monkey.retromusic.network.Result.*
|
||||
import code.name.monkey.retromusic.network.model.LastFmAlbum
|
||||
|
@ -94,6 +95,7 @@ interface Repository {
|
|||
suspend fun checkSongExistInPlayCount(songId: Int): List<PlayCountEntity>
|
||||
suspend fun playCountSongs(): List<PlayCountEntity>
|
||||
suspend fun blackListPaths(): List<BlackListStoreEntity>
|
||||
suspend fun lyrics(artist: String, title: String): Result<String>
|
||||
}
|
||||
|
||||
class RealRepository(
|
||||
|
@ -107,9 +109,16 @@ class RealRepository(
|
|||
private val playlistRepository: PlaylistRepository,
|
||||
private val searchRepository: RealSearchRepository,
|
||||
private val topPlayedRepository: TopPlayedRepository,
|
||||
private val roomRepository: RoomRepository
|
||||
private val roomRepository: RoomRepository,
|
||||
private val lyricsRestService: LyricsRestService
|
||||
) : Repository {
|
||||
|
||||
override suspend fun lyrics(artist: String, title: String): Result<String> = try {
|
||||
Success(lyricsRestService.getLyrics(artist, title))
|
||||
} catch (e: Exception) {
|
||||
Error
|
||||
}
|
||||
|
||||
override suspend fun fetchAlbums(): List<Album> = albumRepository.albums()
|
||||
|
||||
override suspend fun albumByIdAsync(albumId: Int): Album = albumRepository.album(albumId)
|
||||
|
|
|
@ -111,7 +111,7 @@ object MusicUtil : KoinComponent {
|
|||
}
|
||||
|
||||
fun getLyrics(song: Song): String? {
|
||||
var lyrics: String? = null
|
||||
var lyrics: String? = "No lyrics found"
|
||||
val file = File(song.data)
|
||||
try {
|
||||
lyrics = AudioFileIO.read(file).tagOrCreateDefault.getFirst(FieldKey.LYRICS)
|
||||
|
@ -151,7 +151,7 @@ object MusicUtil : KoinComponent {
|
|||
}
|
||||
false
|
||||
}
|
||||
if (files != null && files.size > 0) {
|
||||
if (files != null && files.isNotEmpty()) {
|
||||
for (f in files) {
|
||||
try {
|
||||
val newLyrics =
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
android:scaleType="centerCrop"
|
||||
app:civ_border="false"
|
||||
app:civ_shadow="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:retroCornerSize="21dp"
|
||||
|
|
33
app/src/main/res/layout/lyrics_dialog.xml
Normal file
33
app/src/main/res/layout/lyrics_dialog.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/lyricsText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextViewBody1"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
</ScrollView>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -215,14 +215,16 @@
|
|||
<style name="AppTextAppearance.MaterialAlertDialog.Button" parent="Widget.MaterialComponents.Button.TextButton">
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:padding">0dp</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTextAppearance.MaterialAlertDialog.Body" parent="MaterialAlertDialog.MaterialComponents.Body.Text">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:textAppearance">@style/TextViewBody1</item>
|
||||
<item name="android:paddingTop">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTextAppearance.MaterialAlertDialog.Title" parent="MaterialAlertDialog.MaterialComponents.Title.Text">
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="android:textAppearance">@style/TextViewHeadline6</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
|
|
Loading…
Reference in a new issue