Fix Album & Artist crash when no network

This commit is contained in:
Hemanth S 2020-09-06 12:48:47 +05:30
parent e8549513bd
commit 5211890607
6 changed files with 83 additions and 91 deletions

View file

@ -38,6 +38,7 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.helper.SortOrder
import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Artist 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.network.model.LastFmAlbum
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
@ -83,15 +84,7 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
startPostponedEnterTransition() startPostponedEnterTransition()
showAlbum(it) showAlbum(it)
}) })
detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer {
loadArtistImage(it)
})
detailsViewModel.getMoreAlbums().observe(viewLifecycleOwner, Observer {
moreAlbums(it)
})
detailsViewModel.getAlbumInfo().observe(viewLifecycleOwner, Observer {
aboutAlbum(it)
})
setupRecyclerView() setupRecyclerView()
artistImage.setOnClickListener { artistImage.setOnClickListener {
requireActivity().findNavController(R.id.fragment_container) requireActivity().findNavController(R.id.fragment_container)
@ -172,8 +165,25 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
} }
loadAlbumCover(album) loadAlbumCover(album)
simpleSongAdapter.swapDataSet(album.songs) simpleSongAdapter.swapDataSet(album.songs)
detailsViewModel.loadArtist(album.artistId) detailsViewModel.getArtist(album.artistId).observe(viewLifecycleOwner, Observer {
detailsViewModel.loadAlbumInfo(album) 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<Album>) { private fun moreAlbums(albums: List<Album>) {

View file

@ -1,70 +1,44 @@
package code.name.monkey.retromusic.fragments.albums 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.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Artist 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.network.model.LastFmAlbum
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
class AlbumDetailsViewModel( class AlbumDetailsViewModel(
private val realRepository: RealRepository, private val realRepository: RealRepository,
private val albumId: Int private val albumId: Int
) : ViewModel(), MusicServiceEventListener { ) : ViewModel(), MusicServiceEventListener {
private val _album = MutableLiveData<Album>()
private val _artist = MutableLiveData<Artist>()
private val _lastFmAlbum = MutableLiveData<LastFmAlbum>()
private val _moreAlbums = MutableLiveData<List<Album>>()
fun getAlbum(): LiveData<Album> = liveData(IO) { fun getAlbum(): LiveData<Album> = liveData(IO) {
val album = realRepository.albumByIdAsync(albumId) val album = realRepository.albumByIdAsync(albumId)
emit(album) emit(album)
} }
fun getArtist(): LiveData<Artist> = _artist fun getArtist(artistId: Int): LiveData<Artist> = liveData(IO) {
fun getAlbumInfo(): LiveData<LastFmAlbum> = _lastFmAlbum
fun getMoreAlbums(): LiveData<List<Album>> = _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) {
val artist = realRepository.artistById(artistId) val artist = realRepository.artistById(artistId)
_artist.postValue(artist) emit(artist)
artist.albums?.filter { item -> item.id != albumId }?.let { albums ->
if (albums.isNotEmpty()) _moreAlbums.postValue(albums)
}
} }
private val loadAlbumAsync: Deferred<Album?> fun getAlbumInfo(album: Album): LiveData<Result<LastFmAlbum>> = liveData {
get() = viewModelScope.async(IO) { emit(Result.Loading)
realRepository.albumByIdAsync(albumId) emit( realRepository.albumInfo(album.artistName ?: "-", album.title ?: "-"))
}
fun getMoreAlbums(artist: Artist): LiveData<List<Album>> = liveData(IO) {
artist.albums?.filter { item -> item.id != albumId }?.let { albums ->
if (albums.isNotEmpty()) emit(albums)
} }
}
override fun onMediaStoreChanged() { override fun onMediaStoreChanged() {
loadAlbumDetails()
} }
override fun onServiceConnected() {} override fun onServiceConnected() {}

View file

@ -32,6 +32,7 @@ import code.name.monkey.retromusic.glide.ArtistGlideRequest
import code.name.monkey.retromusic.glide.SingleColorTarget import code.name.monkey.retromusic.glide.SingleColorTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Artist 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.network.model.LastFmArtist
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.util.CustomArtistImageUtil import code.name.monkey.retromusic.util.CustomArtistImageUtil
@ -77,9 +78,7 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d
showArtist(it) showArtist(it)
startPostponedEnterTransition() startPostponedEnterTransition()
}) })
detailsViewModel.getArtistInfo().observe(viewLifecycleOwner, Observer {
artistInfo(it)
})
playAction.apply { playAction.apply {
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) }
@ -140,6 +139,7 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d
albumTitle.text = albumText albumTitle.text = albumText
songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber }) songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber })
artist.albums?.let { albumAdapter.swapDataSet(it) } artist.albums?.let { albumAdapter.swapDataSet(it) }
} }
private fun loadBiography( private fun loadBiography(
@ -148,7 +148,14 @@ class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_d
) { ) {
biography = null biography = null
this.lang = lang 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?) { private fun artistInfo(lastFmArtist: LastFmArtist?) {

View file

@ -1,51 +1,37 @@
package code.name.monkey.retromusic.fragments.artists package code.name.monkey.retromusic.fragments.artists
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.liveData
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.model.Artist 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.network.model.LastFmArtist
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
class ArtistDetailsViewModel( class ArtistDetailsViewModel(
private val realRepository: RealRepository, private val realRepository: RealRepository,
private val artistId: Int private val artistId: Int
) : ViewModel(), MusicServiceEventListener { ) : ViewModel(), MusicServiceEventListener {
private val loadArtistDetailsAsync: Deferred<Artist?> fun getArtist(): LiveData<Artist> = liveData(IO) {
get() = viewModelScope.async(Dispatchers.IO) { val artist = realRepository.artistById(artistId)
realRepository.artistById(artistId) emit(artist)
}
private val _artist = MutableLiveData<Artist>()
private val _lastFmArtist = MutableLiveData<LastFmArtist>()
fun getArtist(): LiveData<Artist> = _artist
fun getArtistInfo(): LiveData<LastFmArtist> = _lastFmArtist
init {
loadArtistDetails()
} }
private fun loadArtistDetails() = viewModelScope.launch { fun getArtistInfo(
val artist = name: String,
loadArtistDetailsAsync.await() ?: throw NullPointerException("Album couldn't found") lang: String?,
_artist.postValue(artist) cache: String?
} ): LiveData<Result<LastFmArtist>> = liveData(IO) {
emit(Result.Loading)
fun loadBiography(name: String, lang: String?, cache: String?) = viewModelScope.launch {
val info = realRepository.artistInfo(name, lang, cache) val info = realRepository.artistInfo(name, lang, cache)
_lastFmArtist.postValue(info) emit(info)
} }
override fun onMediaStoreChanged() { override fun onMediaStoreChanged() {
loadArtistDetails() getArtist()
} }
override fun onServiceConnected() {} override fun onServiceConnected() {}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package code.name.monkey.retromusic package code.name.monkey.retromusic.network
/** /**
* Generic class that holds the network state * Generic class that holds the network state

View file

@ -21,6 +21,7 @@ import code.name.monkey.retromusic.db.*
import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.model.smartplaylist.NotPlayedPlaylist import code.name.monkey.retromusic.model.smartplaylist.NotPlayedPlaylist
import code.name.monkey.retromusic.network.LastFMService 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.LastFmAlbum
import code.name.monkey.retromusic.network.model.LastFmArtist import code.name.monkey.retromusic.network.model.LastFmArtist
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -48,8 +49,8 @@ interface Repository {
suspend fun search(query: String?): MutableList<Any> suspend fun search(query: String?): MutableList<Any>
suspend fun getPlaylistSongs(playlist: Playlist): List<Song> suspend fun getPlaylistSongs(playlist: Playlist): List<Song>
suspend fun getGenre(genreId: Int): List<Song> suspend fun getGenre(genreId: Int): List<Song>
suspend fun artistInfo(name: String, lang: String?, cache: String?): LastFmArtist suspend fun artistInfo(name: String, lang: String?, cache: String?): Result<LastFmArtist>
suspend fun albumInfo(artist: String, album: String): LastFmAlbum suspend fun albumInfo(artist: String, album: String): Result<LastFmAlbum>
suspend fun artistById(artistId: Int): Artist suspend fun artistById(artistId: Int): Artist
suspend fun recentArtists(): List<Artist> suspend fun recentArtists(): List<Artist>
suspend fun topArtists(): List<Artist> suspend fun topArtists(): List<Artist>
@ -110,7 +111,9 @@ class RealRepository(
override suspend fun fetchAlbums(): List<Album> = albumRepository.albums() override suspend fun fetchAlbums(): List<Album> = albumRepository.albums()
override suspend fun albumByIdAsync(albumId: Int): Album = albumRepository.album(albumId) override suspend fun albumByIdAsync(albumId: Int): Album = albumRepository.album(albumId)
override fun albumById(albumId: Int): Album = albumRepository.album(albumId) override fun albumById(albumId: Int): Album = albumRepository.album(albumId)
override suspend fun fetchArtists(): List<Artist> = artistRepository.artists() override suspend fun fetchArtists(): List<Artist> = artistRepository.artists()
override suspend fun albumArtists(): List<Artist> = artistRepository.albumArtists() override suspend fun albumArtists(): List<Artist> = artistRepository.albumArtists()
@ -147,17 +150,29 @@ class RealRepository(
name: String, name: String,
lang: String?, lang: String?,
cache: String? cache: String?
): LastFmArtist = lastFMService.artistInfo(name, lang, cache) ): Result<LastFmArtist> {
return try {
Result.Success(lastFMService.artistInfo(name, lang, cache))
} catch (e: Exception) {
Result.Error
}
}
override suspend fun albumInfo( override suspend fun albumInfo(
artist: String, artist: String,
album: String album: String
): LastFmAlbum = lastFMService.albumInfo(artist, album) ): Result<LastFmAlbum> {
return try {
val lastFmAlbum = lastFMService.albumInfo(artist, album)
Result.Success(lastFmAlbum)
} catch (e: Exception) {
Result.Error
}
}
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
override suspend fun homeSectionsFlow(): Flow<Result<List<Home>>> { override suspend fun homeSectionsFlow(): Flow<Result<List<Home>>> {
val homes = MutableStateFlow<Result<List<Home>>>(value = Result.Loading) val homes = MutableStateFlow<Result<List<Home>>>(value = Result.Loading)
println("homeSections:Loading")
val homeSections = mutableListOf<Home>() val homeSections = mutableListOf<Home>()
val sections = listOf( val sections = listOf(
topArtistsHome(), topArtistsHome(),