From 521189060790bb99ef519056342840315991677b Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Sun, 6 Sep 2020 12:48:47 +0530 Subject: [PATCH 1/4] Fix Album & Artist crash when no network --- .../fragments/albums/AlbumDetailsFragment.kt | 32 ++++++---- .../fragments/albums/AlbumDetailsViewModel.kt | 58 +++++-------------- .../artists/ArtistDetailsFragment.kt | 15 +++-- .../artists/ArtistDetailsViewModel.kt | 42 +++++--------- .../monkey/retromusic/{ => network}/Result.kt | 2 +- .../retromusic/repository/Repository.kt | 25 ++++++-- 6 files changed, 83 insertions(+), 91 deletions(-) rename app/src/main/java/code/name/monkey/retromusic/{ => network}/Result.kt (94%) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt index c047f6c3..2dfc5101 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt @@ -38,6 +38,7 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.network.Result import code.name.monkey.retromusic.network.model.LastFmAlbum import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.util.MusicUtil @@ -83,15 +84,7 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det startPostponedEnterTransition() showAlbum(it) }) - detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer { - loadArtistImage(it) - }) - detailsViewModel.getMoreAlbums().observe(viewLifecycleOwner, Observer { - moreAlbums(it) - }) - detailsViewModel.getAlbumInfo().observe(viewLifecycleOwner, Observer { - aboutAlbum(it) - }) + setupRecyclerView() artistImage.setOnClickListener { requireActivity().findNavController(R.id.fragment_container) @@ -172,8 +165,25 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det } loadAlbumCover(album) simpleSongAdapter.swapDataSet(album.songs) - detailsViewModel.loadArtist(album.artistId) - detailsViewModel.loadAlbumInfo(album) + detailsViewModel.getArtist(album.artistId).observe(viewLifecycleOwner, Observer { + loadArtistImage(it) + }) + /*detailsViewModel.getMoreAlbums().observe(viewLifecycleOwner, Observer { + moreAlbums(it) + })*/ + detailsViewModel.getAlbumInfo(album).observe(viewLifecycleOwner, Observer { result -> + when (result) { + is Result.Loading -> { + println("Loading") + } + is Result.Error -> { + println("Error") + } + is Result.Success -> { + aboutAlbum(result.data) + } + } + }) } private fun moreAlbums(albums: List) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt index f698f882..3311046e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt @@ -1,70 +1,44 @@ package code.name.monkey.retromusic.fragments.albums -import androidx.lifecycle.* +import androidx.lifecycle.LiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.liveData import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.network.Result import code.name.monkey.retromusic.network.model.LastFmAlbum import code.name.monkey.retromusic.repository.RealRepository -import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.async -import kotlinx.coroutines.launch class AlbumDetailsViewModel( private val realRepository: RealRepository, private val albumId: Int ) : ViewModel(), MusicServiceEventListener { - private val _album = MutableLiveData() - private val _artist = MutableLiveData() - private val _lastFmAlbum = MutableLiveData() - private val _moreAlbums = MutableLiveData>() - fun getAlbum(): LiveData = liveData(IO) { val album = realRepository.albumByIdAsync(albumId) emit(album) } - fun getArtist(): LiveData = _artist - fun getAlbumInfo(): LiveData = _lastFmAlbum - fun getMoreAlbums(): LiveData> = _moreAlbums - - init { - loadAlbumDetails() - } - - fun getAlbum2() = liveData(context = viewModelScope.coroutineContext + IO) { - val album = realRepository.albumByIdAsync(albumId) - emit(album) - } - - private fun loadAlbumDetails() = viewModelScope.launch(IO) { - val album = loadAlbumAsync.await() ?: throw NullPointerException("Album couldn't found") - _album.postValue(album) - } - - fun loadAlbumInfo(album: Album) = viewModelScope.launch(IO) { - val lastFmAlbum = realRepository.albumInfo(album.artistName ?: "-", album.title ?: "-") - _lastFmAlbum.postValue(lastFmAlbum) - } - - fun loadArtist(artistId: Int) = viewModelScope.launch(IO) { + fun getArtist(artistId: Int): LiveData = liveData(IO) { val artist = realRepository.artistById(artistId) - _artist.postValue(artist) - - artist.albums?.filter { item -> item.id != albumId }?.let { albums -> - if (albums.isNotEmpty()) _moreAlbums.postValue(albums) - } + emit(artist) } - private val loadAlbumAsync: Deferred - get() = viewModelScope.async(IO) { - realRepository.albumByIdAsync(albumId) + fun getAlbumInfo(album: Album): LiveData> = liveData { + emit(Result.Loading) + emit( realRepository.albumInfo(album.artistName ?: "-", album.title ?: "-")) + } + + fun getMoreAlbums(artist: Artist): LiveData> = liveData(IO) { + artist.albums?.filter { item -> item.id != albumId }?.let { albums -> + if (albums.isNotEmpty()) emit(albums) } + } override fun onMediaStoreChanged() { - loadAlbumDetails() + } override fun onServiceConnected() {} diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt index f9db60bf..91743de4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt @@ -32,6 +32,7 @@ import code.name.monkey.retromusic.glide.ArtistGlideRequest import code.name.monkey.retromusic.glide.SingleColorTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.network.Result import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.util.CustomArtistImageUtil @@ -77,9 +78,7 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d showArtist(it) startPostponedEnterTransition() }) - detailsViewModel.getArtistInfo().observe(viewLifecycleOwner, Observer { - artistInfo(it) - }) + playAction.apply { setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } @@ -140,6 +139,7 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d albumTitle.text = albumText songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber }) artist.albums?.let { albumAdapter.swapDataSet(it) } + } private fun loadBiography( @@ -148,7 +148,14 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d ) { biography = null this.lang = lang - detailsViewModel.loadBiography(name, lang, null) + detailsViewModel.getArtistInfo(name, lang, null) + .observe(viewLifecycleOwner, Observer { result -> + when (result) { + is Result.Loading -> println("Loading") + is Result.Error -> println("Error") + is Result.Success -> artistInfo(result.data) + } + }) } private fun artistInfo(lastFmArtist: LastFmArtist?) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt index f2ee5fe6..89039735 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsViewModel.kt @@ -1,51 +1,37 @@ package code.name.monkey.retromusic.fragments.artists import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.liveData import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.model.Artist +import code.name.monkey.retromusic.network.Result import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.repository.RealRepository -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.launch +import kotlinx.coroutines.Dispatchers.IO class ArtistDetailsViewModel( private val realRepository: RealRepository, private val artistId: Int ) : ViewModel(), MusicServiceEventListener { - private val loadArtistDetailsAsync: Deferred - get() = viewModelScope.async(Dispatchers.IO) { - realRepository.artistById(artistId) - } - - private val _artist = MutableLiveData() - private val _lastFmArtist = MutableLiveData() - - fun getArtist(): LiveData = _artist - fun getArtistInfo(): LiveData = _lastFmArtist - - init { - loadArtistDetails() + fun getArtist(): LiveData = liveData(IO) { + val artist = realRepository.artistById(artistId) + emit(artist) } - private fun loadArtistDetails() = viewModelScope.launch { - val artist = - loadArtistDetailsAsync.await() ?: throw NullPointerException("Album couldn't found") - _artist.postValue(artist) - } - - fun loadBiography(name: String, lang: String?, cache: String?) = viewModelScope.launch { + fun getArtistInfo( + name: String, + lang: String?, + cache: String? + ): LiveData> = liveData(IO) { + emit(Result.Loading) val info = realRepository.artistInfo(name, lang, cache) - _lastFmArtist.postValue(info) + emit(info) } override fun onMediaStoreChanged() { - loadArtistDetails() + getArtist() } override fun onServiceConnected() {} diff --git a/app/src/main/java/code/name/monkey/retromusic/Result.kt b/app/src/main/java/code/name/monkey/retromusic/network/Result.kt similarity index 94% rename from app/src/main/java/code/name/monkey/retromusic/Result.kt rename to app/src/main/java/code/name/monkey/retromusic/network/Result.kt index 4c241a14..05211fd3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/Result.kt +++ b/app/src/main/java/code/name/monkey/retromusic/network/Result.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package code.name.monkey.retromusic +package code.name.monkey.retromusic.network /** * Generic class that holds the network state diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt index 06478f49..dcd94ff5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt @@ -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.Result import code.name.monkey.retromusic.network.model.LastFmAlbum import code.name.monkey.retromusic.network.model.LastFmArtist import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -48,8 +49,8 @@ interface Repository { suspend fun search(query: String?): MutableList suspend fun getPlaylistSongs(playlist: Playlist): List suspend fun getGenre(genreId: Int): List - suspend fun artistInfo(name: String, lang: String?, cache: String?): LastFmArtist - suspend fun albumInfo(artist: String, album: String): LastFmAlbum + suspend fun artistInfo(name: String, lang: String?, cache: String?): Result + suspend fun albumInfo(artist: String, album: String): Result suspend fun artistById(artistId: Int): Artist suspend fun recentArtists(): List suspend fun topArtists(): List @@ -110,7 +111,9 @@ class RealRepository( override suspend fun fetchAlbums(): List = albumRepository.albums() override suspend fun albumByIdAsync(albumId: Int): Album = albumRepository.album(albumId) + override fun albumById(albumId: Int): Album = albumRepository.album(albumId) + override suspend fun fetchArtists(): List = artistRepository.artists() override suspend fun albumArtists(): List = artistRepository.albumArtists() @@ -147,17 +150,29 @@ class RealRepository( name: String, lang: String?, cache: String? - ): LastFmArtist = lastFMService.artistInfo(name, lang, cache) + ): Result { + return try { + Result.Success(lastFMService.artistInfo(name, lang, cache)) + } catch (e: Exception) { + Result.Error + } + } override suspend fun albumInfo( artist: String, album: String - ): LastFmAlbum = lastFMService.albumInfo(artist, album) + ): Result { + return try { + val lastFmAlbum = lastFMService.albumInfo(artist, album) + Result.Success(lastFmAlbum) + } catch (e: Exception) { + Result.Error + } + } @ExperimentalCoroutinesApi override suspend fun homeSectionsFlow(): Flow>> { val homes = MutableStateFlow>>(value = Result.Loading) - println("homeSections:Loading") val homeSections = mutableListOf() val sections = listOf( topArtistsHome(), From 332c2dc69b77674e4185f9414082c54697bdf33d Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Sun, 6 Sep 2020 16:17:06 +0530 Subject: [PATCH 2/4] Code refactor --- .../code/name/monkey/retromusic/MainModule.kt | 3 +++ .../name/monkey/retromusic/db/HistoryDao.kt | 26 +++++++++++++++++++ .../name/monkey/retromusic/db/PlaylistDao.kt | 11 -------- .../monkey/retromusic/db/RetroDatabase.kt | 1 + .../fragments/albums/AlbumDetailsFragment.kt | 7 ++--- .../artists/ArtistDetailsFragment.kt | 3 ++- .../retromusic/repository/Repository.kt | 10 ++++--- .../retromusic/repository/RoomRepository.kt | 26 ++++++++----------- 8 files changed, 54 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/code/name/monkey/retromusic/db/HistoryDao.kt diff --git a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt index aae0ed48..16741bc6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt +++ b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt @@ -55,6 +55,9 @@ private val roomModule = module { factory { get().playCountDao() } + factory { + get().historyDao() + } single { RealRoomRepository(get(), get(), get()) diff --git a/app/src/main/java/code/name/monkey/retromusic/db/HistoryDao.kt b/app/src/main/java/code/name/monkey/retromusic/db/HistoryDao.kt new file mode 100644 index 00000000..5ea731a2 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/db/HistoryDao.kt @@ -0,0 +1,26 @@ +package code.name.monkey.retromusic.db + +import androidx.lifecycle.LiveData +import androidx.room.* + +@Dao +interface HistoryDao { + companion object { + private const val HISTORY_LIMIT = 100 + } + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertSongInHistory(historyEntity: HistoryEntity) + + @Query("SELECT * FROM HistoryEntity WHERE id = :songId LIMIT 1") + suspend fun isSongPresentInHistory(songId: Int): HistoryEntity? + + @Update + suspend fun updateHistorySong(historyEntity: HistoryEntity) + + @Query("SELECT * FROM HistoryEntity ORDER BY time_played DESC LIMIT $HISTORY_LIMIT") + fun historySongs(): List + + @Query("SELECT * FROM HistoryEntity ORDER BY time_played DESC LIMIT $HISTORY_LIMIT") + fun observableHistorySongs(): LiveData> +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDao.kt b/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDao.kt index c3b7f801..0cfe2b8a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDao.kt +++ b/app/src/main/java/code/name/monkey/retromusic/db/PlaylistDao.kt @@ -45,17 +45,6 @@ interface PlaylistDao { @Delete suspend fun deleteSongsInPlaylist(songs: List) - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertSongInHistory(historyEntity: HistoryEntity) - - @Query("SELECT * FROM HistoryEntity WHERE id = :songId LIMIT 1") - suspend fun isSongPresentInHistory(songId: Int): HistoryEntity? - - @Update - suspend fun updateHistorySong(historyEntity: HistoryEntity) - - @Query("SELECT * FROM HistoryEntity ORDER BY time_played DESC") - fun historySongs(): LiveData> @Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId") fun favoritesSongsLiveData(playlistId: Int): LiveData> diff --git a/app/src/main/java/code/name/monkey/retromusic/db/RetroDatabase.kt b/app/src/main/java/code/name/monkey/retromusic/db/RetroDatabase.kt index 02cb6586..e9b99cca 100644 --- a/app/src/main/java/code/name/monkey/retromusic/db/RetroDatabase.kt +++ b/app/src/main/java/code/name/monkey/retromusic/db/RetroDatabase.kt @@ -12,4 +12,5 @@ abstract class RetroDatabase : RoomDatabase() { abstract fun playlistDao(): PlaylistDao abstract fun blackListStore(): BlackListStoreDao abstract fun playCountDao(): PlayCountDao + abstract fun historyDao(): HistoryDao } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt index 2dfc5101..05572748 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt @@ -168,9 +168,7 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det detailsViewModel.getArtist(album.artistId).observe(viewLifecycleOwner, Observer { loadArtistImage(it) }) - /*detailsViewModel.getMoreAlbums().observe(viewLifecycleOwner, Observer { - moreAlbums(it) - })*/ + detailsViewModel.getAlbumInfo(album).observe(viewLifecycleOwner, Observer { result -> when (result) { is Result.Loading -> { @@ -224,6 +222,9 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det } private fun loadArtistImage(artist: Artist) { + detailsViewModel.getMoreAlbums(artist).observe(viewLifecycleOwner, Observer { + moreAlbums(it) + }) ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist) .forceDownload(PreferenceUtil.isAllowedToDownloadMetadata()) .generatePalette(requireContext()) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt index 91743de4..877def06 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistDetailsFragment.kt @@ -18,6 +18,7 @@ import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import code.name.monkey.appthemehelper.ThemeStore +import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter @@ -208,7 +209,7 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d override fun onAlbumClick(albumId: Int, view: View) { findNavController().navigate( R.id.albumDetailsFragment, - bundleOf("extra_album_id" to albumId), + bundleOf(EXTRA_ALBUM_ID to albumId), null, FragmentNavigatorExtras( view to getString(R.string.transition_album_art) diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt index dcd94ff5..f0af9ecc 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt @@ -36,11 +36,12 @@ interface Repository { fun artistsFlow(): Flow>> fun playlistsFlow(): Flow>> fun genresFlow(): Flow>> - fun historySong(): LiveData> + fun historySong(): List fun favorites(): LiveData> + fun observableHistorySongs(): LiveData> + fun albumById(albumId: Int): Album suspend fun fetchAlbums(): List suspend fun albumByIdAsync(albumId: Int): Album - fun albumById(albumId: Int): Album suspend fun allSongs(): List suspend fun fetchArtists(): List suspend fun albumArtists(): List @@ -294,7 +295,10 @@ class RealRepository( override suspend fun blackListPaths(): List = roomRepository.blackListPaths() - override fun historySong(): LiveData> = + override fun observableHistorySongs(): LiveData> = + roomRepository.observableHistorySongs() + + override fun historySong(): List = roomRepository.historySongs() override fun favorites(): LiveData> = diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/RoomRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/RoomRepository.kt index a523883d..8267b36a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/RoomRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/RoomRepository.kt @@ -7,9 +7,10 @@ import code.name.monkey.retromusic.model.Song interface RoomRepository { - fun historySongs(): LiveData> + fun historySongs(): List fun favoritePlaylistLiveData(favorite: String): LiveData> fun insertBlacklistPath(blackListStoreEntity: BlackListStoreEntity) + fun observableHistorySongs(): LiveData> suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long suspend fun checkPlaylistExists(playlistName: String): List suspend fun playlists(): List @@ -42,7 +43,8 @@ interface RoomRepository { class RealRoomRepository( private val playlistDao: PlaylistDao, private val blackListStoreDao: BlackListStoreDao, - private val playCountDao: PlayCountDao + private val playCountDao: PlayCountDao, + private val historyDao: HistoryDao ) : RoomRepository { @WorkerThread override suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long = @@ -59,12 +61,6 @@ class RealRoomRepository( override suspend fun playlistWithSongs(): List = playlistDao.playlistsWithSongs() - /* val tempList = ArrayList(songs) - val existingSongs = songs.map { - playlistDao.checkSongExistsWithPlaylistName(it.playlistCreatorName, it.songId) - }.first() - println("Existing ${existingSongs.size}") - tempList.removeAll(existingSongs)*/ @WorkerThread override suspend fun insertSongs(songs: List) = playlistDao.insertSongsToPlaylist(songs) @@ -96,7 +92,6 @@ class RealRoomRepository( } } - override suspend fun isFavoriteSong(songEntity: SongEntity): List = playlistDao.isSongExistsInPlaylist( songEntity.playlistCreatorId, @@ -107,17 +102,18 @@ class RealRoomRepository( playlistDao.removeSongFromPlaylist(songEntity.playlistCreatorId, songEntity.id) override suspend fun addSongToHistory(currentSong: Song) = - playlistDao.insertSongInHistory(currentSong.toHistoryEntity(System.currentTimeMillis())) + historyDao.insertSongInHistory(currentSong.toHistoryEntity(System.currentTimeMillis())) override suspend fun songPresentInHistory(song: Song): HistoryEntity? = - playlistDao.isSongPresentInHistory(song.id) + historyDao.isSongPresentInHistory(song.id) override suspend fun updateHistorySong(song: Song) = - playlistDao.updateHistorySong(song.toHistoryEntity(System.currentTimeMillis())) + historyDao.updateHistorySong(song.toHistoryEntity(System.currentTimeMillis())) - override fun historySongs(): LiveData> { - return playlistDao.historySongs() - } + override fun observableHistorySongs(): LiveData> = + historyDao.observableHistorySongs() + + override fun historySongs(): List = historyDao.historySongs() override fun favoritePlaylistLiveData(favorite: String): LiveData> = playlistDao.favoritesSongsLiveData( From 8eb7859f3098ce7d3cbd118c68c266f4228de328 Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Sun, 6 Sep 2020 16:33:24 +0530 Subject: [PATCH 3/4] Code refactor --- .../code/name/monkey/retromusic/MainModule.kt | 3 +- .../{DeezerApiService.kt => DeezerService.kt} | 4 +- .../fragments/DetailListFragment.kt | 2 +- .../glide/artistimage/ArtistImageLoader.kt | 18 +-- .../retromusic/network/RetrofitClient.kt | 104 +++++++++++------- .../retromusic/repository/Repository.kt | 47 ++++---- 6 files changed, 104 insertions(+), 74 deletions(-) rename app/src/main/java/code/name/monkey/retromusic/deezer/{DeezerApiService.kt => DeezerService.kt} (97%) diff --git a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt index 16741bc6..8d0fafa8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt +++ b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt @@ -55,12 +55,13 @@ private val roomModule = module { factory { get().playCountDao() } + factory { get().historyDao() } single { - RealRoomRepository(get(), get(), get()) + RealRoomRepository(get(), get(), get(), get()) } bind RoomRepository::class } private val mainModule = module { diff --git a/app/src/main/java/code/name/monkey/retromusic/deezer/DeezerApiService.kt b/app/src/main/java/code/name/monkey/retromusic/deezer/DeezerService.kt similarity index 97% rename from app/src/main/java/code/name/monkey/retromusic/deezer/DeezerApiService.kt rename to app/src/main/java/code/name/monkey/retromusic/deezer/DeezerService.kt index fd107234..327f6848 100644 --- a/app/src/main/java/code/name/monkey/retromusic/deezer/DeezerApiService.kt +++ b/app/src/main/java/code/name/monkey/retromusic/deezer/DeezerService.kt @@ -16,7 +16,7 @@ import java.util.* private const val BASE_QUERY_ARTIST = "search/artist" private const val BASE_URL = "https://api.deezer.com/" -interface DeezerApiService { +interface DeezerService { @GET("$BASE_QUERY_ARTIST&limit=1") fun getArtistImage( @@ -26,7 +26,7 @@ interface DeezerApiService { companion object { operator fun invoke( client: okhttp3.Call.Factory - ): DeezerApiService { + ): DeezerService { return Retrofit.Builder() .baseUrl(BASE_URL) .callFactory(client) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt index c71bcceb..6c7230a5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt @@ -104,7 +104,7 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de adapter = songAdapter layoutManager = linearLayoutManager() } - repository.historySong().observe(viewLifecycleOwner, Observer { + repository.observableHistorySongs().observe(viewLifecycleOwner, Observer { val songs = it.map { historyEntity -> historyEntity.toSong() } songAdapter.swapDataSet(songs) }) diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/artistimage/ArtistImageLoader.kt b/app/src/main/java/code/name/monkey/retromusic/glide/artistimage/ArtistImageLoader.kt index 398916ce..f9792c42 100644 --- a/app/src/main/java/code/name/monkey/retromusic/glide/artistimage/ArtistImageLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/glide/artistimage/ArtistImageLoader.kt @@ -16,7 +16,7 @@ package code.name.monkey.retromusic.glide.artistimage import android.content.Context import code.name.monkey.retromusic.deezer.Data -import code.name.monkey.retromusic.deezer.DeezerApiService +import code.name.monkey.retromusic.deezer.DeezerService import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil import com.bumptech.glide.Priority @@ -38,7 +38,7 @@ class ArtistImage(val artistName: String) class ArtistImageFetcher( private val context: Context, - private val deezerApiService: DeezerApiService, + private val deezerService: DeezerService, val model: ArtistImage, val urlLoader: ModelLoader, val width: Int, @@ -66,7 +66,7 @@ class ArtistImageFetcher( PreferenceUtil.isAllowedToDownloadMetadata() ) { val artists = model.artistName.split(",") - val response = deezerApiService.getArtistImage(artists[0]).execute() + val response = deezerService.getArtistImage(artists[0]).execute() if (!response.isSuccessful) { throw IOException("Request failed with code: " + response.code()) @@ -106,7 +106,7 @@ class ArtistImageFetcher( class ArtistImageLoader( val context: Context, - private val deezerApiService: DeezerApiService, + private val deezerService: DeezerService, private val urlLoader: ModelLoader ) : StreamModelLoader { @@ -115,7 +115,7 @@ class ArtistImageLoader( width: Int, height: Int ): DataFetcher { - return ArtistImageFetcher(context, deezerApiService, model, urlLoader, width, height) + return ArtistImageFetcher(context, deezerService, model, urlLoader, width, height) } } @@ -123,7 +123,7 @@ class Factory( val context: Context ) : ModelLoaderFactory { - private var deezerApiService: DeezerApiService + private var deezerService: DeezerService private var okHttpFactory: OkHttpUrlLoader.Factory init { @@ -134,8 +134,8 @@ class Factory( .writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .build() ) - deezerApiService = DeezerApiService.invoke( - DeezerApiService.createDefaultOkHttpClient(context) + deezerService = DeezerService.invoke( + DeezerService.createDefaultOkHttpClient(context) .connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .readTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS) @@ -156,7 +156,7 @@ class Factory( ): ModelLoader { return ArtistImageLoader( context!!, - deezerApiService, + deezerService, okHttpFactory.build(context, factories) ) } diff --git a/app/src/main/java/code/name/monkey/retromusic/network/RetrofitClient.kt b/app/src/main/java/code/name/monkey/retromusic/network/RetrofitClient.kt index affb3823..2121c812 100644 --- a/app/src/main/java/code/name/monkey/retromusic/network/RetrofitClient.kt +++ b/app/src/main/java/code/name/monkey/retromusic/network/RetrofitClient.kt @@ -1,12 +1,14 @@ package code.name.monkey.retromusic.network +import android.content.Context import code.name.monkey.retromusic.App -import code.name.monkey.retromusic.Constants.AUDIO_SCROBBLER_URL +import code.name.monkey.retromusic.BuildConfig +import code.name.monkey.retromusic.deezer.DeezerService import com.google.gson.Gson import okhttp3.Cache -import okhttp3.ConnectionPool import okhttp3.Interceptor import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor import org.koin.dsl.module import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory @@ -16,50 +18,22 @@ import java.util.concurrent.TimeUnit private const val TIMEOUT: Long = 700 val networkModule = module { - factory { - provideHttpClient(get(), get()) - } - factory { - provideCacheControlInterceptor() - } + factory { provideDefaultCache() } factory { - provideLastFmService(get()) + provideOkHttp(get(), get()) } single { - providerRetrofit(get()) + provideLastFmRetrofit(get()) + } + single { + provideDeezerRest(get()) + } + single { + provideLastFmRest(get()) } -} - -fun provideLastFmService(retrofit: Retrofit): LastFMService = - retrofit.create(LastFMService::class.java) - -fun providerRetrofit(okHttpClient: OkHttpClient.Builder): Retrofit = Retrofit.Builder() - .baseUrl(AUDIO_SCROBBLER_URL) - .callFactory(okHttpClient.build()) - .addConverterFactory(GsonConverterFactory.create(Gson())) - .build() - -fun provideHttpClient( - cache: Cache, - interceptor: Interceptor -): OkHttpClient.Builder = OkHttpClient.Builder() - .connectionPool(ConnectionPool(0, 1, TimeUnit.NANOSECONDS)) - .retryOnConnectionFailure(true) - .connectTimeout(TIMEOUT, TimeUnit.MINUTES) - .writeTimeout(TIMEOUT, TimeUnit.MINUTES) - .readTimeout(TIMEOUT, TimeUnit.MINUTES) - .cache(cache) - .addInterceptor(interceptor) - - -fun provideCacheControlInterceptor(): Interceptor = Interceptor { chain: Interceptor.Chain -> - val modifiedRequest = chain.request().newBuilder() - .addHeader("Cache-Control", "max-age=31536000, max-stale=31536000") - .build() - chain.proceed(modifiedRequest) } fun provideDefaultCache(): Cache? { @@ -69,3 +43,55 @@ fun provideDefaultCache(): Cache? { } return null } + +fun logInterceptor(): Interceptor { + val loggingInterceptor = HttpLoggingInterceptor() + if (BuildConfig.DEBUG) { + loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY + } else { + // disable retrofit log on release + loggingInterceptor.level = HttpLoggingInterceptor.Level.NONE + } + return loggingInterceptor +} + +fun headerInterceptor(context: Context): Interceptor { + return Interceptor { + val original = it.request() + val request = original.newBuilder() + .header("User-Agent", context.packageName) + .addHeader("Content-Type", "application/json; charset=utf-8") + .method(original.method(), original.body()) + .build() + it.proceed(request) + } +} + +fun provideOkHttp(context: Context, cache: Cache): OkHttpClient { + return OkHttpClient.Builder() + .addNetworkInterceptor(logInterceptor()) + .addInterceptor(headerInterceptor(context)) + .connectTimeout(1, TimeUnit.SECONDS) + .readTimeout(1, TimeUnit.SECONDS) + .cache(cache) + .build() +} + +fun provideLastFmRetrofit(client: OkHttpClient): Retrofit { + return Retrofit.Builder() + .baseUrl("https://ws.audioscrobbler.com/2.0/") + .addConverterFactory(GsonConverterFactory.create(Gson())) + .callFactory { request -> client.newCall(request) } + .build() +} + +fun provideLastFmRest(retrofit: Retrofit): LastFMService { + return retrofit.create(LastFMService::class.java) +} + +fun provideDeezerRest(retrofit: Retrofit): DeezerService { + val newBuilder = retrofit.newBuilder() + .baseUrl("https://api.deezer.com/") + .build() + return newBuilder.create(DeezerService::class.java) +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt index f0af9ecc..4dd683a1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt @@ -22,6 +22,7 @@ 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.Result +import code.name.monkey.retromusic.network.Result.* import code.name.monkey.retromusic.network.model.LastFmAlbum import code.name.monkey.retromusic.network.model.LastFmArtist import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -153,9 +154,10 @@ class RealRepository( cache: String? ): Result { return try { - Result.Success(lastFMService.artistInfo(name, lang, cache)) + Success(lastFMService.artistInfo(name, lang, cache)) } catch (e: Exception) { - Result.Error + println(e) + Error } } @@ -165,15 +167,16 @@ class RealRepository( ): Result { return try { val lastFmAlbum = lastFMService.albumInfo(artist, album) - Result.Success(lastFmAlbum) + Success(lastFmAlbum) } catch (e: Exception) { - Result.Error + println(e) + Error } } @ExperimentalCoroutinesApi override suspend fun homeSectionsFlow(): Flow>> { - val homes = MutableStateFlow>>(value = Result.Loading) + val homes = MutableStateFlow>>(value = Loading) val homeSections = mutableListOf() val sections = listOf( topArtistsHome(), @@ -191,9 +194,9 @@ class RealRepository( } } if (homeSections.isEmpty()) { - homes.value = Result.Error + homes.value = Error } else { - homes.value = Result.Success(homeSections) + homes.value = Success(homeSections) } return homes } @@ -350,52 +353,52 @@ class RealRepository( } override fun songsFlow(): Flow>> = flow { - emit(Result.Loading) + emit(Loading) val data = songRepository.songs() if (data.isEmpty()) { - emit(Result.Error) + emit(Error) } else { - emit(Result.Success(data)) + emit(Success(data)) } } override fun albumsFlow(): Flow>> = flow { - emit(Result.Loading) + emit(Loading) val data = albumRepository.albums() if (data.isEmpty()) { - emit(Result.Error) + emit(Error) } else { - emit(Result.Success(data)) + emit(Success(data)) } } override fun artistsFlow(): Flow>> = flow { - emit(Result.Loading) + emit(Loading) val data = artistRepository.artists() if (data.isEmpty()) { - emit(Result.Error) + emit(Error) } else { - emit(Result.Success(data)) + emit(Success(data)) } } override fun playlistsFlow(): Flow>> = flow { - emit(Result.Loading) + emit(Loading) val data = playlistRepository.playlists() if (data.isEmpty()) { - emit(Result.Error) + emit(Error) } else { - emit(Result.Success(data)) + emit(Success(data)) } } override fun genresFlow(): Flow>> = flow { - emit(Result.Loading) + emit(Loading) val data = genreRepository.genres() if (data.isEmpty()) { - emit(Result.Error) + emit(Error) } else { - emit(Result.Success(data)) + emit(Success(data)) } } } \ No newline at end of file From 26edcdf4daf7859287b46cf85a70d5b5681a83bd Mon Sep 17 00:00:00 2001 From: Hemanth S Date: Sun, 6 Sep 2020 23:26:39 +0530 Subject: [PATCH 4/4] WIP Lyrics --- app/build.gradle | 3 +- app/src/debug/res/values/styles.xml | 9 ++-- .../code/name/monkey/retromusic/MainModule.kt | 39 +++++++++++++- .../adapter/album/AlbumCoverPagerAdapter.kt | 6 ++- .../monkey/retromusic/dialogs/LyricsDialog.kt | 51 +++++++++++++++++++ .../retromusic/extensions/DialogExtension.kt | 2 +- .../fragments/search/SearchViewModel.kt | 4 +- .../glide/artistimage/ArtistImageLoader.kt | 4 +- .../{deezer => model}/DeezerResponse.kt | 2 +- .../{deezer => network}/DeezerService.kt | 3 +- .../retromusic/network/LyricsService.kt | 12 +++++ .../retromusic/network/RetrofitClient.kt | 32 ++++-------- .../conversion/LyricsConverterFactory.kt | 51 +++++++++++++++++++ .../retromusic/repository/Repository.kt | 11 +++- .../name/monkey/retromusic/util/MusicUtil.kt | 4 +- app/src/main/res/layout/item_contributor.xml | 1 + app/src/main/res/layout/lyrics_dialog.xml | 33 ++++++++++++ app/src/main/res/values/styles.xml | 6 ++- 18 files changed, 228 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/code/name/monkey/retromusic/dialogs/LyricsDialog.kt rename app/src/main/java/code/name/monkey/retromusic/{deezer => model}/DeezerResponse.kt (94%) rename app/src/main/java/code/name/monkey/retromusic/{deezer => network}/DeezerService.kt (95%) create mode 100644 app/src/main/java/code/name/monkey/retromusic/network/LyricsService.kt create mode 100644 app/src/main/java/code/name/monkey/retromusic/network/conversion/LyricsConverterFactory.kt create mode 100644 app/src/main/res/layout/lyrics_dialog.xml diff --git a/app/build.gradle b/app/build.gradle index d928d693..4725b1d8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/debug/res/values/styles.xml b/app/src/debug/res/values/styles.xml index 7f4fe30c..c45bc56a 100644 --- a/app/src/debug/res/values/styles.xml +++ b/app/src/debug/res/values/styles.xml @@ -60,20 +60,23 @@ \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt index 8d0fafa8..fe513d50 100644 --- a/app/src/main/java/code/name/monkey/retromusic/MainModule.kt +++ b/app/src/main/java/code/name/monkey/retromusic/MainModule.kt @@ -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 { diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt index 039aa9fe..8cd40e97 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt @@ -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(), diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/LyricsDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/LyricsDialog.kt new file mode 100644 index 00000000..d1f5f022 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/LyricsDialog.kt @@ -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() + + 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 = 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 + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/DialogExtension.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/DialogExtension.kt index d2c38310..c56a4045 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/DialogExtension.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/DialogExtension.kt @@ -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) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt index eecff933..50681023 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/search/SearchViewModel.kt @@ -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>() @@ -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 } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/artistimage/ArtistImageLoader.kt b/app/src/main/java/code/name/monkey/retromusic/glide/artistimage/ArtistImageLoader.kt index f9792c42..43d23120 100644 --- a/app/src/main/java/code/name/monkey/retromusic/glide/artistimage/ArtistImageLoader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/glide/artistimage/ArtistImageLoader.kt @@ -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 diff --git a/app/src/main/java/code/name/monkey/retromusic/deezer/DeezerResponse.kt b/app/src/main/java/code/name/monkey/retromusic/model/DeezerResponse.kt similarity index 94% rename from app/src/main/java/code/name/monkey/retromusic/deezer/DeezerResponse.kt rename to app/src/main/java/code/name/monkey/retromusic/model/DeezerResponse.kt index b029ab53..929a9d13 100644 --- a/app/src/main/java/code/name/monkey/retromusic/deezer/DeezerResponse.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/DeezerResponse.kt @@ -1,4 +1,4 @@ -package code.name.monkey.retromusic.deezer +package code.name.monkey.retromusic.model import com.google.gson.annotations.SerializedName diff --git a/app/src/main/java/code/name/monkey/retromusic/deezer/DeezerService.kt b/app/src/main/java/code/name/monkey/retromusic/network/DeezerService.kt similarity index 95% rename from app/src/main/java/code/name/monkey/retromusic/deezer/DeezerService.kt rename to app/src/main/java/code/name/monkey/retromusic/network/DeezerService.kt index 327f6848..1ea0c16c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/deezer/DeezerService.kt +++ b/app/src/main/java/code/name/monkey/retromusic/network/DeezerService.kt @@ -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 diff --git a/app/src/main/java/code/name/monkey/retromusic/network/LyricsService.kt b/app/src/main/java/code/name/monkey/retromusic/network/LyricsService.kt new file mode 100644 index 00000000..0a0e0490 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/network/LyricsService.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/network/RetrofitClient.kt b/app/src/main/java/code/name/monkey/retromusic/network/RetrofitClient.kt index 2121c812..df75072d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/network/RetrofitClient.kt +++ b/app/src/main/java/code/name/monkey/retromusic/network/RetrofitClient.kt @@ -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) } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/network/conversion/LyricsConverterFactory.kt b/app/src/main/java/code/name/monkey/retromusic/network/conversion/LyricsConverterFactory.kt new file mode 100644 index 00000000..474e81c9 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/network/conversion/LyricsConverterFactory.kt @@ -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?, + retrofit: Retrofit? + ): Converter? { + return if (String::class.java == type) { + Converter { value -> value.string() } + } else null + } + + override fun requestBodyConverter( + type: Type?, + parameterAnnotations: Array?, + methodAnnotations: Array?, + retrofit: Retrofit? + ): Converter<*, RequestBody>? { + + return if (String::class.java == type) { + Converter { value -> RequestBody.create(MEDIA_TYPE, value) } + } else null + } + + companion object { + private val MEDIA_TYPE = MediaType.parse("text/plain") + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt index 4dd683a1..32ca1ffe 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt @@ -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 suspend fun playCountSongs(): List suspend fun blackListPaths(): List + suspend fun lyrics(artist: String, title: String): Result } 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 = try { + Success(lyricsRestService.getLyrics(artist, title)) + } catch (e: Exception) { + Error + } + override suspend fun fetchAlbums(): List = albumRepository.albums() override suspend fun albumByIdAsync(albumId: Int): Album = albumRepository.album(albumId) diff --git a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt index a3bbf776..8659a0ba 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt @@ -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 = diff --git a/app/src/main/res/layout/item_contributor.xml b/app/src/main/res/layout/item_contributor.xml index ef73b468..d7ba8832 100644 --- a/app/src/main/res/layout/item_contributor.xml +++ b/app/src/main/res/layout/item_contributor.xml @@ -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" diff --git a/app/src/main/res/layout/lyrics_dialog.xml b/app/src/main/res/layout/lyrics_dialog.xml new file mode 100644 index 00000000..27fa8a95 --- /dev/null +++ b/app/src/main/res/layout/lyrics_dialog.xml @@ -0,0 +1,33 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index c0eb21c3..3c37bd74 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -215,14 +215,16 @@