Added Material Cab for Album and Artist details

This commit is contained in:
h4h13 2019-11-26 20:57:28 +05:30
parent 72964be920
commit 0c5edf8ef6
21 changed files with 750 additions and 610 deletions

View file

@ -126,6 +126,7 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.6.2' implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-gson:2.6.2' implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.2' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.2'
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
implementation 'com.afollestad.material-dialogs:core:3.1.1' implementation 'com.afollestad.material-dialogs:core:3.1.1'
implementation 'com.afollestad.material-dialogs:input:3.1.1' implementation 'com.afollestad.material-dialogs:input:3.1.1'

View file

@ -4,25 +4,44 @@ import android.app.ActivityOptions
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.Menu
import android.view.MenuItem
import android.view.SubMenu
import android.view.View
import android.widget.ImageView import android.widget.ImageView
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.* 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.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.* import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.retromusic.* import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
import code.name.monkey.retromusic.activities.tageditor.* import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
import code.name.monkey.retromusic.activities.tageditor.AlbumTagEditorActivity
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
import code.name.monkey.retromusic.dialogs.* import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.glide.* import code.name.monkey.retromusic.glide.ArtistGlideRequest
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.glide.SongGlideRequest
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder
import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.mvp.presenter.* import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.util.* import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsPresenter
import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsView
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroColorUtil
import com.afollestad.materialcab.MaterialCab
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.activity_album.* import kotlinx.android.synthetic.main.activity_album.*
import kotlinx.android.synthetic.main.activity_album_content.* import kotlinx.android.synthetic.main.activity_album_content.*
@ -30,263 +49,251 @@ import java.util.*
import javax.inject.Inject import javax.inject.Inject
import android.util.Pair as UtilPair import android.util.Pair as UtilPair
class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView {
private lateinit var simpleSongAdapter: SimpleSongAdapter class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, CabHolder {
private lateinit var album: Album override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
private lateinit var artistImage: ImageView cab?.let {
private val savedSortOrder: String if (it.isActive) it.finish()
get() = PreferenceUtil.getInstance(this).albumDetailSongSortOrder }
cab = MaterialCab(this, R.id.cab_stub)
.setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
.start(callback)
return cab as MaterialCab
}
override fun createContentView(): View { private lateinit var simpleSongAdapter: SimpleSongAdapter
return wrapSlidingMusicPanel(R.layout.activity_album) private lateinit var album: Album
} private lateinit var artistImage: ImageView
private var cab: MaterialCab? = null
private val savedSortOrder: String
get() = PreferenceUtil.getInstance(this).albumDetailSongSortOrder
@Inject override fun createContentView(): View {
lateinit var albumDetailsPresenter: AlbumDetailsPresenter return wrapSlidingMusicPanel(R.layout.activity_album)
}
override fun onCreate(savedInstanceState: Bundle?) { @Inject
setDrawUnderStatusBar() lateinit var albumDetailsPresenter: AlbumDetailsPresenter
super.onCreate(savedInstanceState)
toggleBottomNavigationView(true)
setStatusbarColor(Color.TRANSPARENT)
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
setLightNavigationBar(true)
setLightStatusbar(ColorUtil.isColorLight(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
ActivityCompat.postponeEnterTransition(this) override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar()
super.onCreate(savedInstanceState)
toggleBottomNavigationView(true)
setStatusbarColor(Color.TRANSPARENT)
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
setLightNavigationBar(true)
setLightStatusbar(ColorUtil.isColorLight(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
App.musicComponent.inject(this) ActivityCompat.postponeEnterTransition(this)
artistImage = findViewById(R.id.artistImage)
setupRecyclerView() App.musicComponent.inject(this)
artistImage = findViewById(R.id.artistImage)
artistImage.setOnClickListener { setupRecyclerView()
val artistPairs = ActivityOptions.makeSceneTransitionAnimation(
this, artistImage.setOnClickListener {
UtilPair.create( val artistPairs = ActivityOptions.makeSceneTransitionAnimation(this, UtilPair.create(artistImage, getString(R.string.transition_artist_image)))
artistImage, NavigationUtil.goToArtistOptions(this, album.artistId, artistPairs)
getString(R.string.transition_artist_image) }
) playAction.apply {
) setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) }
NavigationUtil.goToArtistOptions(this, album.artistId, artistPairs) }
} shuffleAction.apply {
playAction.apply { setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(album.songs!!, true) }
setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) } }
}
shuffleAction.apply {
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(album.songs!!, true) }
}
albumDetailsPresenter.attachView(this) albumDetailsPresenter.attachView(this)
if (intent.extras!!.containsKey(EXTRA_ALBUM_ID)) { if (intent.extras!!.containsKey(EXTRA_ALBUM_ID)) {
intent.extras?.getInt(EXTRA_ALBUM_ID)?.let { albumDetailsPresenter.loadAlbum(it) } intent.extras?.getInt(EXTRA_ALBUM_ID)?.let { albumDetailsPresenter.loadAlbum(it) }
} else { } else {
finish() finish()
} }
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
simpleSongAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song) simpleSongAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this)
recyclerView.apply { recyclerView.apply {
layoutManager = LinearLayoutManager(this@AlbumDetailsActivity) layoutManager = LinearLayoutManager(this@AlbumDetailsActivity)
itemAnimator = DefaultItemAnimator() itemAnimator = DefaultItemAnimator()
isNestedScrollingEnabled = false isNestedScrollingEnabled = false
adapter = simpleSongAdapter adapter = simpleSongAdapter
} }
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
albumDetailsPresenter.detachView() albumDetailsPresenter.detachView()
} }
override fun complete() { override fun complete() {
ActivityCompat.startPostponedEnterTransition(this) ActivityCompat.startPostponedEnterTransition(this)
} }
override fun album(album: Album) { override fun album(album: Album) {
if (album.songs!!.isEmpty()) { if (album.songs!!.isEmpty()) {
finish() finish()
return return
} }
this.album = album this.album = album
albumTitle.text = album.title albumTitle.text = album.title
if (MusicUtil.getYearString(album.year) == "-") { if (MusicUtil.getYearString(album.year) == "-") {
albumText.text = String.format( albumText.text = String.format("%s • %s", album.artistName, MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs)))
"%s • %s", } else {
album.artistName, albumText.text = String.format("%s • %s • %s", album.artistName, MusicUtil.getYearString(album.year), MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs)))
MusicUtil.getReadableDurationString( }
MusicUtil.getTotalDuration( loadAlbumCover()
this, simpleSongAdapter.swapDataSet(album.songs)
album.songs albumDetailsPresenter.loadMore(album.artistId)
) }
)
)
} else {
albumText.text = String.format(
"%s • %s • %s",
album.artistName,
MusicUtil.getYearString(album.year),
MusicUtil.getReadableDurationString(
MusicUtil.getTotalDuration(
this,
album.songs
)
)
)
}
loadAlbumCover()
simpleSongAdapter.swapDataSet(album.songs)
albumDetailsPresenter.loadMore(album.artistId)
}
override fun moreAlbums(albums: ArrayList<Album>) { override fun moreAlbums(albums: ArrayList<Album>) {
moreTitle.show() moreTitle.show()
moreRecyclerView.show() moreRecyclerView.show()
moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName) moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName)
val albumAdapter = HorizontalAlbumAdapter(this, albums, false, null) val albumAdapter = HorizontalAlbumAdapter(this, albums, false, null)
moreRecyclerView.layoutManager = GridLayoutManager( moreRecyclerView.layoutManager = GridLayoutManager(
this, this,
1, 1,
GridLayoutManager.HORIZONTAL, GridLayoutManager.HORIZONTAL,
false false
) )
moreRecyclerView.adapter = albumAdapter moreRecyclerView.adapter = albumAdapter
} }
override fun loadArtistImage(artist: Artist) { override fun loadArtistImage(artist: Artist) {
ArtistGlideRequest.Builder.from(Glide.with(this), artist).generatePalette(this).build() ArtistGlideRequest.Builder.from(Glide.with(this), artist).generatePalette(this).build()
.dontAnimate().dontTransform().into(object : RetroMusicColoredTarget(artistImage) { .dontAnimate().dontTransform().into(object : RetroMusicColoredTarget(artistImage) {
override fun onColorReady(color: Int) { override fun onColorReady(color: Int) {
} }
}) })
} }
private fun loadAlbumCover() { private fun loadAlbumCover() {
SongGlideRequest.Builder.from(Glide.with(this), album.safeGetFirstSong()) SongGlideRequest.Builder.from(Glide.with(this), album.safeGetFirstSong())
.checkIgnoreMediaStore(this).generatePalette(this).build().dontAnimate().dontTransform() .checkIgnoreMediaStore(this).generatePalette(this).build().dontAnimate().dontTransform()
.into(object : RetroMusicColoredTarget(image) { .into(object : RetroMusicColoredTarget(image) {
override fun onColorReady(color: Int) { override fun onColorReady(color: Int) {
setColors(color) setColors(color)
} }
}) })
} }
private fun setColors(color: Int) { private fun setColors(color: Int) {
val themeColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color val themeColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color
else ThemeStore.accentColor(this) else ThemeStore.accentColor(this)
songTitle.setTextColor(themeColor) songTitle.setTextColor(themeColor)
moreTitle.setTextColor(themeColor) moreTitle.setTextColor(themeColor)
val buttonColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color val buttonColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color
else ATHUtil.resolveColor(this, R.attr.cardBackgroundColor) else ATHUtil.resolveColor(this, R.attr.cardBackgroundColor)
MaterialUtil.setTint(button = shuffleAction, color = buttonColor) MaterialUtil.setTint(button = shuffleAction, color = buttonColor)
MaterialUtil.setTint(button = playAction, color = buttonColor) MaterialUtil.setTint(button = playAction, color = buttonColor)
toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorPrimary)) toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorPrimary))
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
supportActionBar?.title = null supportActionBar?.title = null
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_album_detail, menu) menuInflater.inflate(R.menu.menu_album_detail, menu)
val sortOrder = menu.findItem(R.id.action_sort_order) val sortOrder = menu.findItem(R.id.action_sort_order)
setUpSortOrderMenu(sortOrder.subMenu) setUpSortOrderMenu(sortOrder.subMenu)
return super.onCreateOptionsMenu(menu) return super.onCreateOptionsMenu(menu)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return handleSortOrderMenuItem(item) return handleSortOrderMenuItem(item)
} }
private fun handleSortOrderMenuItem(item: MenuItem): Boolean { private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
var sortOrder: String? = null var sortOrder: String? = null
val songs = simpleSongAdapter.dataSet val songs = simpleSongAdapter.dataSet
when (item.itemId) { when (item.itemId) {
R.id.action_play_next -> { R.id.action_play_next -> {
MusicPlayerRemote.playNext(songs) MusicPlayerRemote.playNext(songs)
return true return true
} }
R.id.action_add_to_current_playing -> { R.id.action_add_to_current_playing -> {
MusicPlayerRemote.enqueue(songs) MusicPlayerRemote.enqueue(songs)
return true return true
} }
R.id.action_add_to_playlist -> { R.id.action_add_to_playlist -> {
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST") AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
return true return true
} }
R.id.action_delete_from_device -> { R.id.action_delete_from_device -> {
DeleteSongsDialog.create(songs).show(supportFragmentManager, "DELETE_SONGS") DeleteSongsDialog.create(songs).show(supportFragmentManager, "DELETE_SONGS")
return true return true
} }
android.R.id.home -> { android.R.id.home -> {
super.onBackPressed() super.onBackPressed()
return true return true
} }
R.id.action_tag_editor -> { R.id.action_tag_editor -> {
val intent = Intent(this, AlbumTagEditorActivity::class.java) val intent = Intent(this, AlbumTagEditorActivity::class.java)
intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id) intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id)
startActivityForResult(intent, TAG_EDITOR_REQUEST) startActivityForResult(intent, TAG_EDITOR_REQUEST)
return true return true
} }
/*Sort*/ /*Sort*/
R.id.action_sort_order_title -> sortOrder = AlbumSongSortOrder.SONG_A_Z R.id.action_sort_order_title -> sortOrder = AlbumSongSortOrder.SONG_A_Z
R.id.action_sort_order_title_desc -> sortOrder = AlbumSongSortOrder.SONG_Z_A R.id.action_sort_order_title_desc -> sortOrder = AlbumSongSortOrder.SONG_Z_A
R.id.action_sort_order_track_list -> sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST R.id.action_sort_order_track_list -> sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST
R.id.action_sort_order_artist_song_duration -> sortOrder = AlbumSongSortOrder.SONG_DURATION R.id.action_sort_order_artist_song_duration -> sortOrder = AlbumSongSortOrder.SONG_DURATION
} }
if (sortOrder != null) { if (sortOrder != null) {
item.isChecked = true item.isChecked = true
setSaveSortOrder(sortOrder) setSaveSortOrder(sortOrder)
} }
return true return true
} }
private fun setUpSortOrderMenu(sortOrder: SubMenu) { private fun setUpSortOrderMenu(sortOrder: SubMenu) {
when (savedSortOrder) { when (savedSortOrder) {
AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title) AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title)
.isChecked = true .isChecked = true
AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc) AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc)
.isChecked = true .isChecked = true
AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list) AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list)
.isChecked = true .isChecked = true
AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration) AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration)
.isChecked = true .isChecked = true
} }
} }
private fun setSaveSortOrder(sortOrder: String?) { private fun setSaveSortOrder(sortOrder: String?) {
PreferenceUtil.getInstance(this).albumDetailSongSortOrder = sortOrder PreferenceUtil.getInstance(this).albumDetailSongSortOrder = sortOrder
reload() reload()
} }
override fun onMediaStoreChanged() { override fun onMediaStoreChanged() {
super.onMediaStoreChanged() super.onMediaStoreChanged()
reload() reload()
} }
private fun reload() { private fun reload() {
if (intent.extras!!.containsKey(EXTRA_ALBUM_ID)) { if (intent.extras!!.containsKey(EXTRA_ALBUM_ID)) {
intent.extras?.getInt(EXTRA_ALBUM_ID)?.let { albumDetailsPresenter.loadAlbum(it) } intent.extras?.getInt(EXTRA_ALBUM_ID)?.let { albumDetailsPresenter.loadAlbum(it) }
} else { } else {
finish() finish()
} }
} }
companion object { companion object {
const val EXTRA_ALBUM_ID = "extra_album_id" const val EXTRA_ALBUM_ID = "extra_album_id"
private const val TAG_EDITOR_REQUEST = 2001 private const val TAG_EDITOR_REQUEST = 2001
} }
} }

View file

@ -3,26 +3,39 @@ package code.name.monkey.retromusic.activities
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.* import android.os.Build
import android.text.* import android.os.Bundle
import android.view.* import android.text.Html
import android.text.Spanned
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.* 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.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.* import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.retromusic.* import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
import code.name.monkey.retromusic.adapter.album.* import code.name.monkey.retromusic.adapter.album.AlbumAdapter
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
import code.name.monkey.retromusic.glide.* import code.name.monkey.retromusic.glide.ArtistGlideRequest
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.mvp.presenter.* import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter
import code.name.monkey.retromusic.rest.LastFMRestClient import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsView
import code.name.monkey.retromusic.rest.model.LastFmArtist import code.name.monkey.retromusic.rest.model.LastFmArtist
import code.name.monkey.retromusic.util.* import code.name.monkey.retromusic.util.*
import com.afollestad.materialcab.MaterialCab
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.activity_artist_content.* import kotlinx.android.synthetic.main.activity_artist_content.*
import kotlinx.android.synthetic.main.activity_artist_details.* import kotlinx.android.synthetic.main.activity_artist_details.*
@ -30,269 +43,277 @@ import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView { class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView, CabHolder {
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
cab?.let {
if (it.isActive) it.finish()
}
cab = MaterialCab(this, R.id.cab_stub)
.setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
.start(callback)
return cab as MaterialCab
}
private var biography: Spanned? = null private var cab: MaterialCab? = null
private lateinit var artist: Artist private var biography: Spanned? = null
private var lastFMRestClient: LastFMRestClient? = null private lateinit var artist: Artist
private lateinit var songAdapter: SimpleSongAdapter private lateinit var songAdapter: SimpleSongAdapter
private lateinit var albumAdapter: AlbumAdapter private lateinit var albumAdapter: AlbumAdapter
private var forceDownload: Boolean = false private var forceDownload: Boolean = false
override fun createContentView(): View { override fun createContentView(): View {
return wrapSlidingMusicPanel(R.layout.activity_artist_details) return wrapSlidingMusicPanel(R.layout.activity_artist_details)
} }
@Inject @Inject
lateinit var artistDetailsPresenter: ArtistDetailsPresenter lateinit var artistDetailsPresenter: ArtistDetailsPresenter
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar() setDrawUnderStatusBar()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
toggleBottomNavigationView(true) toggleBottomNavigationView(true)
setStatusbarColor(Color.TRANSPARENT) setStatusbarColor(Color.TRANSPARENT)
setNavigationbarColorAuto() setNavigationbarColorAuto()
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
setLightNavigationBar(true) setLightNavigationBar(true)
setLightStatusbar(ColorUtil.isColorLight(ATHUtil.resolveColor(this, R.attr.colorPrimary))) setLightStatusbar(ColorUtil.isColorLight(ATHUtil.resolveColor(this, R.attr.colorPrimary)))
ActivityCompat.postponeEnterTransition(this) ActivityCompat.postponeEnterTransition(this)
lastFMRestClient = LastFMRestClient(this) setUpViews()
setUpViews() playAction.apply {
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) }
}
shuffleAction.apply {
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) }
}
playAction.apply { biographyText.setOnClickListener {
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } if (biographyText.maxLines == 4) {
} biographyText.maxLines = Integer.MAX_VALUE
shuffleAction.apply { } else {
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) } biographyText.maxLines = 4
} }
}
biographyText.setOnClickListener { App.musicComponent.inject(this)
if (biographyText.maxLines == 4) { artistDetailsPresenter.attachView(this)
biographyText.maxLines = Integer.MAX_VALUE
} else {
biographyText.maxLines = 4
}
}
App.musicComponent.inject(this) if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) {
artistDetailsPresenter.attachView(this) intent.extras?.getInt(EXTRA_ARTIST_ID)?.let { artistDetailsPresenter.loadArtist(it) }
} else {
finish()
}
}
if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) { override fun onDestroy() {
intent.extras?.getInt(EXTRA_ARTIST_ID)?.let { artistDetailsPresenter.loadArtist(it) } super.onDestroy()
} else { artistDetailsPresenter.detachView()
finish() }
}
}
override fun onDestroy() { private fun setUpViews() {
super.onDestroy() setupRecyclerView()
artistDetailsPresenter.detachView() setupContainerHeight()
} }
private fun setUpViews() { private fun setupContainerHeight() {
setupRecyclerView() imageContainer?.let {
setupContainerHeight() val params = it.layoutParams
} params.width = DensityUtil.getScreenHeight(this) / 2
it.layoutParams = params
}
}
private fun setupContainerHeight() { private fun setupRecyclerView() {
imageContainer?.let { albumAdapter = HorizontalAlbumAdapter(this, ArrayList(), false, null)
val params = it.layoutParams albumRecyclerView.apply {
params.width = DensityUtil.getScreenHeight(this) / 2 itemAnimator = DefaultItemAnimator()
it.layoutParams = params layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false)
} adapter = albumAdapter
} }
songAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this)
recyclerView.apply {
itemAnimator = DefaultItemAnimator()
layoutManager = LinearLayoutManager(this.context)
adapter = songAdapter
}
}
private fun setupRecyclerView() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
albumAdapter = HorizontalAlbumAdapter(this, ArrayList(), false, null) super.onActivityResult(requestCode, resultCode, data)
albumRecyclerView.apply { when (requestCode) {
itemAnimator = DefaultItemAnimator() REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false) data?.data?.let {
adapter = albumAdapter CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist, it)
} }
songAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song) }
recyclerView.apply { else -> if (resultCode == Activity.RESULT_OK) {
itemAnimator = DefaultItemAnimator() reload()
layoutManager = LinearLayoutManager(this.context) }
adapter = songAdapter }
} }
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun showEmptyView() {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
data?.data?.let {
CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist, it)
}
}
else -> if (resultCode == Activity.RESULT_OK) {
reload()
}
}
}
override fun showEmptyView() { }
} override fun complete() {
ActivityCompat.startPostponedEnterTransition(this)
}
override fun complete() { override fun artist(artist: Artist) {
ActivityCompat.startPostponedEnterTransition(this) if (artist.songCount <= 0) {
} finish()
}
this.artist = artist
loadArtistImage()
override fun artist(artist: Artist) { if (RetroUtil.isAllowedToDownloadMetadata(this)) {
if (artist.songCount <= 0) { loadBiography(artist.name)
finish() }
} artistTitle.text = artist.name
this.artist = artist text.text = String.format(
loadArtistImage() "%s • %s",
MusicUtil.getArtistInfoString(this, artist),
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, artist.songs))
)
if (RetroUtil.isAllowedToDownloadMetadata(this)) { songAdapter.swapDataSet(artist.songs)
loadBiography(artist.name) albumAdapter.swapDataSet(artist.albums!!)
} }
artistTitle.text = artist.name
text.text = String.format(
"%s • %s",
MusicUtil.getArtistInfoString(this, artist),
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, artist.songs))
)
songAdapter.swapDataSet(artist.songs) private fun loadBiography(
albumAdapter.swapDataSet(artist.albums!!) name: String, lang: String? = Locale.getDefault().language
} ) {
biography = null
this.lang = lang
artistDetailsPresenter.loadBiography(name, lang, null)
}
private fun loadBiography( override fun artistInfo(lastFmArtist: LastFmArtist?) {
name: String, lang: String? = Locale.getDefault().language if (lastFmArtist != null && lastFmArtist.artist != null) {
) { val bioContent = lastFmArtist.artist.bio.content
biography = null if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) {
this.lang = lang biographyText.visibility = View.VISIBLE
artistDetailsPresenter.loadBiography(name, lang, null) biographyTitle.visibility = View.VISIBLE
} biography = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(bioContent, Html.FROM_HTML_MODE_LEGACY)
} else {
Html.fromHtml(bioContent)
}
biographyText.text = biography
}
}
override fun artistInfo(lastFmArtist: LastFmArtist?) { // If the "lang" parameter is set and no biography is given, retry with default language
if (lastFmArtist != null && lastFmArtist.artist != null) { if (biography == null && lang != null) {
val bioContent = lastFmArtist.artist.bio.content loadBiography(artist.name, null)
if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) { }
biographyText.visibility = View.VISIBLE }
biographyTitle.visibility = View.VISIBLE
biography = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(bioContent, Html.FROM_HTML_MODE_LEGACY)
} else {
Html.fromHtml(bioContent)
}
biographyText.text = biography
}
}
// If the "lang" parameter is set and no biography is given, retry with default language private var lang: String? = null
if (biography == null && lang != null) {
loadBiography(artist.name, null)
}
}
private var lang: String? = null private fun loadArtistImage() {
ArtistGlideRequest.Builder.from(Glide.with(this), artist).generatePalette(this).build()
.dontAnimate().into(object : RetroMusicColoredTarget(artistImage) {
override fun onColorReady(color: Int) {
setColors(color)
}
})
}
private fun loadArtistImage() { private fun setColors(color: Int) {
ArtistGlideRequest.Builder.from(Glide.with(this), artist).generatePalette(this).build()
.dontAnimate().into(object : RetroMusicColoredTarget(artistImage) {
override fun onColorReady(color: Int) {
setColors(color)
}
})
}
private fun setColors(color: Int) { val textColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color
else ThemeStore.accentColor(this)
val textColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color albumTitle.setTextColor(textColor)
else ThemeStore.accentColor(this) songTitle.setTextColor(textColor)
biographyTitle.setTextColor(textColor)
albumTitle.setTextColor(textColor) val buttonColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color
songTitle.setTextColor(textColor) else ATHUtil.resolveColor(this, R.attr.cardBackgroundColor)
biographyTitle.setTextColor(textColor)
val buttonColor = if (PreferenceUtil.getInstance(this).adaptiveColor) color MaterialUtil.setTint(button = shuffleAction, color = buttonColor)
else ATHUtil.resolveColor(this, R.attr.cardBackgroundColor) MaterialUtil.setTint(button = playAction, color = buttonColor)
MaterialUtil.setTint(button = shuffleAction, color = buttonColor) toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorPrimary))
MaterialUtil.setTint(button = playAction, color = buttonColor) setSupportActionBar(toolbar)
supportActionBar?.title = null
}
toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorPrimary)) override fun onOptionsItemSelected(item: MenuItem): Boolean {
setSupportActionBar(toolbar) return handleSortOrderMenuItem(item)
supportActionBar?.title = null }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean { private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
return handleSortOrderMenuItem(item) val songs = artist.songs
} when (item.itemId) {
android.R.id.home -> {
super.onBackPressed()
return true
}
R.id.action_play_next -> {
MusicPlayerRemote.playNext(songs)
return true
}
R.id.action_add_to_current_playing -> {
MusicPlayerRemote.enqueue(songs)
return true
}
R.id.action_add_to_playlist -> {
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
return true
}
R.id.action_set_artist_image -> {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
startActivityForResult(
Intent.createChooser(
intent, getString(R.string.pick_from_local_storage)
), REQUEST_CODE_SELECT_IMAGE
)
return true
}
R.id.action_reset_artist_image -> {
Toast.makeText(
this@ArtistDetailActivity,
resources.getString(R.string.updating),
Toast.LENGTH_SHORT
).show()
CustomArtistImageUtil.getInstance(this@ArtistDetailActivity)
.resetCustomArtistImage(artist)
forceDownload = true
return true
}
}
return true
}
private fun handleSortOrderMenuItem(item: MenuItem): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
val songs = artist.songs menuInflater.inflate(R.menu.menu_artist_detail, menu)
when (item.itemId) { return super.onCreateOptionsMenu(menu)
android.R.id.home -> { }
super.onBackPressed()
return true
}
R.id.action_play_next -> {
MusicPlayerRemote.playNext(songs)
return true
}
R.id.action_add_to_current_playing -> {
MusicPlayerRemote.enqueue(songs)
return true
}
R.id.action_add_to_playlist -> {
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
return true
}
R.id.action_set_artist_image -> {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
startActivityForResult(
Intent.createChooser(
intent,
getString(R.string.pick_from_local_storage)
), REQUEST_CODE_SELECT_IMAGE
)
return true
}
R.id.action_reset_artist_image -> {
Toast.makeText(
this@ArtistDetailActivity,
resources.getString(R.string.updating),
Toast.LENGTH_SHORT
).show()
CustomArtistImageUtil.getInstance(this@ArtistDetailActivity)
.resetCustomArtistImage(artist)
forceDownload = true
return true
}
}
return true
}
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onMediaStoreChanged() {
menuInflater.inflate(R.menu.menu_artist_detail, menu) super.onMediaStoreChanged()
return super.onCreateOptionsMenu(menu) reload()
} }
override fun onMediaStoreChanged() { private fun reload() {
super.onMediaStoreChanged() if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) {
reload() intent.extras?.getInt(EXTRA_ARTIST_ID)?.let { artistDetailsPresenter.loadArtist(it) }
} } else {
finish()
}
}
private fun reload() { companion object {
if (intent.extras!!.containsKey(EXTRA_ARTIST_ID)) {
intent.extras?.getInt(EXTRA_ARTIST_ID)?.let { artistDetailsPresenter.loadArtist(it) }
} else {
finish()
}
}
companion object { const val EXTRA_ARTIST_ID = "extra_artist_id"
const val REQUEST_CODE_SELECT_IMAGE = 9003
const val EXTRA_ARTIST_ID = "extra_artist_id" }
const val REQUEST_CODE_SELECT_IMAGE = 9003
}
} }

View file

@ -1,34 +1,39 @@
package code.name.monkey.retromusic.adapter.song package code.name.monkey.retromusic.adapter.song
import android.view.* import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import java.util.* import java.util.*
class SimpleSongAdapter( class SimpleSongAdapter(
context: AppCompatActivity, songs: ArrayList<Song>, i: Int context: AppCompatActivity,
) : SongAdapter(context, songs, i, false, null) { songs: ArrayList<Song>,
i: Int,
cabHolder: CabHolder?
) : SongAdapter(context, songs, i, false, cabHolder) {
override fun swapDataSet(dataSet: ArrayList<Song>) { override fun swapDataSet(dataSet: ArrayList<Song>) {
this.dataSet.clear() this.dataSet.clear()
this.dataSet = dataSet this.dataSet = dataSet
notifyDataSetChanged() notifyDataSetChanged()
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)) return ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false))
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
super.onBindViewHolder(holder, position) super.onBindViewHolder(holder, position)
val fixedTrackNumber = MusicUtil.getFixedTrackNumber(dataSet[position].trackNumber) val fixedTrackNumber = MusicUtil.getFixedTrackNumber(dataSet[position].trackNumber)
holder.imageText?.text = if (fixedTrackNumber > 0) fixedTrackNumber.toString() else "-" holder.imageText?.text = if (fixedTrackNumber > 0) fixedTrackNumber.toString() else "-"
holder.time?.text = MusicUtil.getReadableDurationString(dataSet[position].duration) holder.time?.text = MusicUtil.getReadableDurationString(dataSet[position].duration)
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return dataSet.size return dataSet.size
} }
} }

View file

@ -148,7 +148,7 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
}); });
getMainActivity().setSupportActionBar(toolbar); getMainActivity().setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(v -> showMainMenu(OptionsSheetDialogFragment.LIBRARY)); toolbar.setNavigationOnClickListener(v -> showMainMenu(OptionsSheetDialogFragment.LIBRARY));
ToolbarContentTintHelper.colorBackButton(toolbar ); ToolbarContentTintHelper.colorBackButton(toolbar);
toolbar.setTitleTextColor(ATHUtil.INSTANCE.resolveColor(requireContext(), R.attr.colorOnSecondary)); toolbar.setTitleTextColor(ATHUtil.INSTANCE.resolveColor(requireContext(), R.attr.colorOnSecondary));
} }

View file

@ -46,7 +46,9 @@ interface AlbumDetailsPresenter : Presenter<AlbumDetailsView> {
) : PresenterImpl<AlbumDetailsView>(), AlbumDetailsPresenter { ) : PresenterImpl<AlbumDetailsView>(), AlbumDetailsPresenter {
private lateinit var album: Album private lateinit var album: Album
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
override fun loadMore(artistId: Int) { override fun loadMore(artistId: Int) {
disposable += repository.getArtistByIdFlowable(artistId) disposable += repository.getArtistByIdFlowable(artistId)
.map { .map {

View file

@ -14,16 +14,17 @@
package code.name.monkey.retromusic.mvp.presenter package code.name.monkey.retromusic.mvp.presenter
import code.name.monkey.retromusic.Result
import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.mvp.BaseView import code.name.monkey.retromusic.mvp.BaseView
import code.name.monkey.retromusic.mvp.Presenter import code.name.monkey.retromusic.mvp.Presenter
import code.name.monkey.retromusic.mvp.PresenterImpl import code.name.monkey.retromusic.mvp.PresenterImpl
import code.name.monkey.retromusic.providers.interfaces.Repository import code.name.monkey.retromusic.providers.interfaces.Repository
import code.name.monkey.retromusic.rest.model.LastFmArtist import code.name.monkey.retromusic.rest.model.LastFmArtist
import io.reactivex.disposables.CompositeDisposable import kotlinx.coroutines.*
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
/** /**
* Created by hemanths on 20/08/17. * Created by hemanths on 20/08/17.
@ -38,38 +39,48 @@ interface ArtistDetailsPresenter : Presenter<ArtistDetailsView> {
fun loadArtist(artistId: Int) fun loadArtist(artistId: Int)
fun loadBiography(name: String, fun loadBiography(
lang: String? = Locale.getDefault().language, name: String, lang: String? = Locale.getDefault().language, cache: String?
cache: String?) )
class ArtistDetailsPresenterImpl @Inject constructor( class ArtistDetailsPresenterImpl @Inject constructor(
private val repository: Repository private val repository: Repository
) : PresenterImpl<ArtistDetailsView>(), ArtistDetailsPresenter { ) : PresenterImpl<ArtistDetailsView>(), ArtistDetailsPresenter, CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + job
override fun loadBiography(name: String, private val job = Job()
lang: String?,
cache: String?) { override fun loadBiography(name: String, lang: String?, cache: String?) {
disposable += repository.artistInfoFloable(name, lang, cache) launch {
.subscribe { when (val result = repository.artistInfo(name, lang, cache)) {
view?.artistInfo(it) is Result.Success -> withContext(Dispatchers.Main) {
view?.artistInfo(result.data)
} }
is Result.Error -> withContext(Dispatchers.Main) {
}
}
}
} }
private var disposable = CompositeDisposable()
override fun loadArtist(artistId: Int) { override fun loadArtist(artistId: Int) {
disposable += repository.getArtistByIdFlowable(artistId) launch {
.doOnComplete { when (val result = repository.artistById(artistId)) {
is Result.Success -> withContext(Dispatchers.Main) {
view?.artist(result.data)
view?.complete() view?.complete()
} }
.subscribe({ is Result.Error -> withContext(Dispatchers.Main) {
view?.artist(it) view?.showEmptyView()
}, { t -> println(t) }) }
}
}
} }
override fun detachView() { override fun detachView() {
super.detachView() super.detachView()
disposable.dispose() job.cancel()
} }
} }
} }

View file

@ -28,6 +28,7 @@ import code.name.monkey.retromusic.rest.model.LastFmArtist
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import java.io.IOException
class RepositoryImpl(private val context: Context) : Repository { class RepositoryImpl(private val context: Context) : Repository {
@ -98,14 +99,14 @@ class RepositoryImpl(private val context: Context) : Repository {
override suspend fun allSongs(): Result<ArrayList<Song>> { override suspend fun allSongs(): Result<ArrayList<Song>> {
return try { return try {
val songs = SongLoader.getAllSongs(context); val songs = SongLoader.getAllSongs(context)
if (songs.isEmpty()) { if (songs.isEmpty()) {
Error(Throwable("No items found")) Error(Throwable("No items found"))
} else { } else {
Success(songs); Success(songs)
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e); Error(e)
} }
} }
@ -116,7 +117,7 @@ class RepositoryImpl(private val context: Context) : Repository {
} else { } else {
PlaylistSongsLoader.getPlaylistSongList(context, playlist.id) PlaylistSongsLoader.getPlaylistSongList(context, playlist.id)
} }
Success(songs); Success(songs)
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
} }
@ -128,7 +129,7 @@ class RepositoryImpl(private val context: Context) : Repository {
if (songs.isEmpty()) { if (songs.isEmpty()) {
Error(Throwable("No items found")) Error(Throwable("No items found"))
} else { } else {
Success(songs); Success(songs)
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
@ -165,7 +166,7 @@ class RepositoryImpl(private val context: Context) : Repository {
albums, albums,
HomeAdapter.RECENT_ALBUMS, HomeAdapter.RECENT_ALBUMS,
R.drawable.ic_album_white_24dp R.drawable.ic_album_white_24dp
)); ))
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
@ -184,7 +185,7 @@ class RepositoryImpl(private val context: Context) : Repository {
albums, albums,
HomeAdapter.TOP_ALBUMS, HomeAdapter.TOP_ALBUMS,
R.drawable.ic_album_white_24dp R.drawable.ic_album_white_24dp
)); ))
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
@ -204,7 +205,7 @@ class RepositoryImpl(private val context: Context) : Repository {
artists, artists,
HomeAdapter.TOP_ARTISTS, HomeAdapter.TOP_ARTISTS,
R.drawable.ic_artist_white_24dp R.drawable.ic_artist_white_24dp
)); ))
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
@ -223,23 +224,33 @@ class RepositoryImpl(private val context: Context) : Repository {
playlists, playlists,
HomeAdapter.PLAYLISTS, HomeAdapter.PLAYLISTS,
R.drawable.ic_favorite_white_24dp R.drawable.ic_favorite_white_24dp
)); ))
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
} }
} }
override fun artistInfoFloable( override suspend fun artistInfo(
name: String, name: String,
lang: String?, lang: String?,
cache: String? cache: String?
): Observable<LastFmArtist> { ): Result<LastFmArtist> = safeApiCall(
return LastFMRestClient(context).apiService.getArtistInfoFloable(name, lang, cache) call = {
.subscribeOn(Schedulers.io()) Success(LastFMRestClient(context).apiService.artistInfo(name, lang, cache))
.observeOn(AndroidSchedulers.mainThread()) },
} errorMessage = "Error"
)
override suspend fun artistById(artistId: Int): Result<Artist> {
return try {
val artist = ArtistLoader.getArtist(context, artistId)
return Success(artist)
} catch (e: Exception) {
Error(Throwable("Error loading artist"))
}
}
override fun getSongFlowable(id: Int): Observable<Song> { override fun getSongFlowable(id: Int): Observable<Song> {
return SongLoader.getSongFlowable(context, id) return SongLoader.getSongFlowable(context, id)
@ -343,3 +354,9 @@ class RepositoryImpl(private val context: Context) : Repository {
} }
suspend fun <T : Any> safeApiCall(call: suspend () -> Result<T>, errorMessage: String): Result<T> = try {
call.invoke()
} catch (e: Exception) {
Result.Error(IOException(errorMessage, e))
}

View file

@ -51,6 +51,10 @@ interface Repository {
suspend fun favoritePlaylist(): Result<Home> suspend fun favoritePlaylist(): Result<Home>
suspend fun artistInfo(name: String, lang: String?, cache: String?): Result<LastFmArtist>
suspend fun artistById(artistId: Int): Result<Artist>
val allSongsFlowable: Observable<ArrayList<Song>> val allSongsFlowable: Observable<ArrayList<Song>>
val suggestionSongsFlowable: Observable<ArrayList<Song>> val suggestionSongsFlowable: Observable<ArrayList<Song>>
@ -91,7 +95,4 @@ interface Repository {
val favoritePlaylistFlowable: Observable<ArrayList<Playlist>> val favoritePlaylistFlowable: Observable<ArrayList<Playlist>>
fun artistInfoFloable(name: String,
lang: String?,
cache: String?): Observable<LastFmArtist>
} }

View file

@ -1,47 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* 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.rest.service;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import code.name.monkey.retromusic.rest.model.LastFmAlbum;
import code.name.monkey.retromusic.rest.model.LastFmArtist;
import io.reactivex.Observable;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Query;
public interface LastFMService {
String API_KEY_BAK = "bd9c6ea4d55ec9ed3af7d276e5ece304";
String API_KEY = "c679c8d3efa84613dc7dcb2e8d42da4c";
String BASE_QUERY_PARAMETERS = "?format=json&autocorrect=1&api_key=" + API_KEY;
String METHOD_TRACK = "track.getInfo";
@NonNull
@GET(BASE_QUERY_PARAMETERS + "&method=album.getinfo")
Observable<LastFmAlbum> getAlbumInfo(@Query("album") @NonNull String albumName, @Query("artist") @NonNull String artistName, @Nullable @Query("lang") String language);
@NonNull
@GET(BASE_QUERY_PARAMETERS + "&method=artist.getinfo")
Call<LastFmArtist> getArtistInfo(@Query("artist") @NonNull String artistName, @Nullable @Query("lang") String language, @Nullable @Header("Cache-Control") String cacheControl);
@NonNull
@GET(BASE_QUERY_PARAMETERS + "&method=artist.getinfo")
Observable<LastFmArtist> getArtistInfoFloable(@Query("artist") @NonNull String artistName, @Nullable @Query("lang") String language, @Nullable @Header("Cache-Control") String cacheControl);
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* 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.rest.service
import code.name.monkey.retromusic.rest.model.LastFmArtist
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query
/**
* Created by hemanths on 2019-11-26.
*/
interface LastFMService {
companion object {
const val API_KEY = "c679c8d3efa84613dc7dcb2e8d42da4c"
const val BASE_QUERY_PARAMETERS = "?format=json&autocorrect=1&api_key=$API_KEY"
}
@GET("$BASE_QUERY_PARAMETERS&method=artist.getinfo")
suspend fun artistInfo(@Query("artist") artistName: String,
@Query("lang") language: String?,
@Header("Cache-Control") cacheControl: String?
): LastFmArtist
}

View file

@ -25,10 +25,21 @@
android:layout_weight="1" android:layout_weight="1"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.appbar.MaterialToolbar <FrameLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
style="@style/Toolbar" android:layout_height="wrap_content">
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="48dp" />
</FrameLayout>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -25,11 +25,20 @@
android:layout_weight="1" android:layout_weight="1"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.appbar.MaterialToolbar <FrameLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
style="@style/Toolbar" android:layout_height="wrap_content">
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="48dp" />
</FrameLayout>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View file

@ -36,9 +36,21 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" /> android:layout_gravity="center_horizontal" />
<com.google.android.material.appbar.MaterialToolbar <FrameLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
style="@style/Toolbar" /> android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="48dp" />
</FrameLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View file

@ -34,10 +34,21 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_collapseMode="parallax" /> app:layout_collapseMode="parallax" />
<com.google.android.material.appbar.MaterialToolbar <FrameLayout
android:id="@+id/toolbar" app:layout_collapseMode="pin"
style="@style/Toolbar" android:layout_width="match_parent"
app:layout_collapseMode="pin" /> android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="48dp" />
</FrameLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
@ -55,8 +66,8 @@
android:id="@+id/contentContainer" android:id="@+id/contentContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cardElevation="8dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
app:cardElevation="8dp"
app:shapeAppearanceOverlay="@style/TopCornerCardView"> app:shapeAppearanceOverlay="@style/TopCornerCardView">
<LinearLayout <LinearLayout

View file

@ -36,11 +36,21 @@
android:layout_width="320dp" android:layout_width="320dp"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<com.google.android.material.appbar.MaterialToolbar <FrameLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
style="@style/Toolbar" android:layout_height="wrap_content">
app:layout_collapseMode="pin"
app:title="" /> <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="48dp" />
</FrameLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View file

@ -35,10 +35,21 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_collapseMode="parallax" /> app:layout_collapseMode="parallax" />
<com.google.android.material.appbar.MaterialToolbar <FrameLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
style="@style/Toolbar" android:layout_height="wrap_content"
app:layout_collapseMode="pin" /> app:layout_collapseMode="pin">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="48dp" />
</FrameLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
@ -56,8 +67,8 @@
android:id="@+id/contentContainer" android:id="@+id/contentContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cardElevation="8dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
app:cardElevation="8dp"
app:shapeAppearanceOverlay="@style/TopCornerCardView"> app:shapeAppearanceOverlay="@style/TopCornerCardView">
<LinearLayout <LinearLayout
@ -86,8 +97,8 @@
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/text" android:id="@+id/text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:paddingTop="4dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="4dp"
android:textAppearance="@style/TextViewHeadline6" android:textAppearance="@style/TextViewHeadline6"
tools:ignore="MissingPrefix" tools:ignore="MissingPrefix"
tools:text="Title" /> tools:text="Title" />

View file

@ -24,10 +24,20 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:liftOnScroll="true"> app:liftOnScroll="true">
<com.google.android.material.appbar.MaterialToolbar <FrameLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
style="@style/Toolbar" android:layout_height="wrap_content">
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="48dp" />
</FrameLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View file

@ -24,11 +24,20 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:liftOnScroll="true"> app:liftOnScroll="true">
<com.google.android.material.appbar.MaterialToolbar <FrameLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
style="@style/Toolbar" android:layout_height="wrap_content">
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:navigationIcon="@drawable/ic_keyboard_backspace_black_24dp" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="48dp" />
</FrameLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView

View file

@ -22,11 +22,13 @@
<item name="android:breakStrategy">simple</item> <item name="android:breakStrategy">simple</item>
<item name="android:hyphenationFrequency">none</item> <item name="android:hyphenationFrequency">none</item>
<item name="android:fontFamily">@font/circular</item> <item name="android:fontFamily">@font/circular</item>
<item name="android:textSize">14sp</item>
</style> </style>
<style name="BottomSheetItemTextAppearanceActive" parent="Widget.MaterialComponents.BottomNavigationView.Colored"> <style name="BottomSheetItemTextAppearanceActive" parent="Widget.MaterialComponents.BottomNavigationView.Colored">
<item name="android:breakStrategy">simple</item> <item name="android:breakStrategy">simple</item>
<item name="android:hyphenationFrequency">none</item> <item name="android:hyphenationFrequency">none</item>
<item name="android:fontFamily">@font/circular</item> <item name="android:fontFamily">@font/circular</item>
<item name="android:textSize">14sp</item>
</style> </style>
</resources> </resources>

View file

@ -169,7 +169,7 @@
</style> </style>
<style name="MaterialButtonTheme" parent="Widget.MaterialComponents.Button"> <style name="MaterialButtonTheme" parent="Widget.MaterialComponents.Button">
<item name="cornerRadius">24dp</item> <item name="cornerRadius">8dp</item>
<item name="android:textAppearance">@style/TextViewNormal</item> <item name="android:textAppearance">@style/TextViewNormal</item>
<item name="android:textAllCaps">false</item> <item name="android:textAllCaps">false</item>
<item name="android:paddingTop">@dimen/button_padding_vertical</item> <item name="android:paddingTop">@dimen/button_padding_vertical</item>