Working towards single activity design
This commit is contained in:
parent
31534769a8
commit
3ebf777d7b
87 changed files with 1908 additions and 2423 deletions
|
@ -0,0 +1,11 @@
|
|||
package code.name.monkey.retromusic.fragments
|
||||
|
||||
import androidx.annotation.LayoutRes
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||
|
||||
open class MainActivityFragment(@LayoutRes layoutRes: Int) : AbsMusicServiceFragment(layoutRes) {
|
||||
val mainActivity by lazy {
|
||||
requireActivity() as MainActivity
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
package code.name.monkey.retromusic.fragments.about
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.Constants.APP_INSTAGRAM_LINK
|
||||
import code.name.monkey.retromusic.Constants.APP_TELEGRAM_LINK
|
||||
import code.name.monkey.retromusic.Constants.APP_TWITTER_LINK
|
||||
import code.name.monkey.retromusic.Constants.FAQ_LINK
|
||||
import code.name.monkey.retromusic.Constants.GITHUB_PROJECT
|
||||
import code.name.monkey.retromusic.Constants.PINTEREST
|
||||
import code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY
|
||||
import code.name.monkey.retromusic.Constants.TELEGRAM_CHANGE_LOG
|
||||
import code.name.monkey.retromusic.Constants.TRANSLATE
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.adapter.ContributorAdapter
|
||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||
import code.name.monkey.retromusic.model.Contributor
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import kotlinx.android.synthetic.main.activity_about.*
|
||||
import kotlinx.android.synthetic.main.card_credit.*
|
||||
import kotlinx.android.synthetic.main.card_other.*
|
||||
import kotlinx.android.synthetic.main.card_retro_info.*
|
||||
import kotlinx.android.synthetic.main.card_social.*
|
||||
import java.io.IOException
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
class AboutActivity : AbsBaseActivity(), View.OnClickListener {
|
||||
|
||||
private val contributorsJson: String?
|
||||
get() {
|
||||
val json: String
|
||||
try {
|
||||
val inputStream = assets.open("contributors.json")
|
||||
val size = inputStream.available()
|
||||
val buffer = ByteArray(size)
|
||||
inputStream.read(buffer)
|
||||
inputStream.close()
|
||||
json = String(buffer, StandardCharsets.UTF_8)
|
||||
} catch (ex: IOException) {
|
||||
ex.printStackTrace()
|
||||
return null
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_about)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
applyToolbar(toolbar)
|
||||
version.setSummary(getAppVersion())
|
||||
setUpView()
|
||||
loadContributors()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun openUrl(url: String) {
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
i.data = Uri.parse(url)
|
||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(i)
|
||||
}
|
||||
|
||||
private fun setUpView() {
|
||||
appGithub.setOnClickListener(this)
|
||||
faqLink.setOnClickListener(this)
|
||||
telegramLink.setOnClickListener(this)
|
||||
appRate.setOnClickListener(this)
|
||||
appTranslation.setOnClickListener(this)
|
||||
appShare.setOnClickListener(this)
|
||||
donateLink.setOnClickListener(this)
|
||||
instagramLink.setOnClickListener(this)
|
||||
twitterLink.setOnClickListener(this)
|
||||
changelog.setOnClickListener(this)
|
||||
openSource.setOnClickListener(this)
|
||||
pinterestLink.setOnClickListener(this)
|
||||
bugReportLink.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.pinterestLink -> openUrl(PINTEREST)
|
||||
R.id.faqLink -> openUrl(FAQ_LINK)
|
||||
R.id.telegramLink -> openUrl(APP_TELEGRAM_LINK)
|
||||
R.id.appGithub -> openUrl(GITHUB_PROJECT)
|
||||
R.id.appTranslation -> openUrl(TRANSLATE)
|
||||
R.id.appRate -> openUrl(RATE_ON_GOOGLE_PLAY)
|
||||
R.id.appShare -> shareApp()
|
||||
R.id.donateLink -> NavigationUtil.goToSupportDevelopment(this)
|
||||
R.id.instagramLink -> openUrl(APP_INSTAGRAM_LINK)
|
||||
R.id.twitterLink -> openUrl(APP_TWITTER_LINK)
|
||||
R.id.changelog -> openUrl(TELEGRAM_CHANGE_LOG)
|
||||
R.id.openSource -> NavigationUtil.goToOpenSource(this)
|
||||
R.id.bugReportLink -> NavigationUtil.bugReport(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAppVersion(): String {
|
||||
return try {
|
||||
val isPro = if (App.isProVersion()) "Pro" else "Free"
|
||||
val packageInfo = packageManager.getPackageInfo(packageName, 0)
|
||||
"${packageInfo.versionName} $isPro"
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
e.printStackTrace()
|
||||
"0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareApp() {
|
||||
ShareCompat.IntentBuilder.from(this).setType("text/plain")
|
||||
.setChooserTitle(R.string.share_app)
|
||||
.setText(String.format(getString(R.string.app_share), packageName)).startChooser()
|
||||
}
|
||||
|
||||
private fun loadContributors() {
|
||||
val type = object : TypeToken<List<Contributor>>() {
|
||||
|
||||
}.type
|
||||
val contributors = Gson().fromJson<List<Contributor>>(contributorsJson, type)
|
||||
|
||||
val contributorAdapter = ContributorAdapter(contributors)
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
recyclerView.itemAnimator = DefaultItemAnimator()
|
||||
recyclerView.adapter = contributorAdapter
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package code.name.monkey.retromusic.fragments.about
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.Constants
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.ContributorAdapter
|
||||
import code.name.monkey.retromusic.fragments.MainActivityFragment
|
||||
import code.name.monkey.retromusic.model.Contributor
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import kotlinx.android.synthetic.main.activity_about.*
|
||||
import kotlinx.android.synthetic.main.card_credit.*
|
||||
import kotlinx.android.synthetic.main.card_other.*
|
||||
import kotlinx.android.synthetic.main.card_retro_info.*
|
||||
import kotlinx.android.synthetic.main.card_social.*
|
||||
import java.io.IOException
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
class AboutFragment : MainActivityFragment(R.layout.fragment_about), View.OnClickListener {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.setSupportActionBar(toolbar)
|
||||
version.setSummary(getAppVersion())
|
||||
setUpView()
|
||||
loadContributors()
|
||||
}
|
||||
|
||||
private val contributorsJson: String?
|
||||
get() {
|
||||
val json: String
|
||||
try {
|
||||
val inputStream = requireActivity().assets.open("contributors.json")
|
||||
val size = inputStream.available()
|
||||
val buffer = ByteArray(size)
|
||||
inputStream.read(buffer)
|
||||
inputStream.close()
|
||||
json = String(buffer, StandardCharsets.UTF_8)
|
||||
} catch (ex: IOException) {
|
||||
ex.printStackTrace()
|
||||
return null
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
|
||||
private fun openUrl(url: String) {
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
i.data = Uri.parse(url)
|
||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(i)
|
||||
}
|
||||
|
||||
private fun setUpView() {
|
||||
appGithub.setOnClickListener(this)
|
||||
faqLink.setOnClickListener(this)
|
||||
telegramLink.setOnClickListener(this)
|
||||
appRate.setOnClickListener(this)
|
||||
appTranslation.setOnClickListener(this)
|
||||
appShare.setOnClickListener(this)
|
||||
donateLink.setOnClickListener(this)
|
||||
instagramLink.setOnClickListener(this)
|
||||
twitterLink.setOnClickListener(this)
|
||||
changelog.setOnClickListener(this)
|
||||
openSource.setOnClickListener(this)
|
||||
pinterestLink.setOnClickListener(this)
|
||||
bugReportLink.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.pinterestLink -> openUrl(Constants.PINTEREST)
|
||||
R.id.faqLink -> openUrl(Constants.FAQ_LINK)
|
||||
R.id.telegramLink -> openUrl(Constants.APP_TELEGRAM_LINK)
|
||||
R.id.appGithub -> openUrl(Constants.GITHUB_PROJECT)
|
||||
R.id.appTranslation -> openUrl(Constants.TRANSLATE)
|
||||
R.id.appRate -> openUrl(Constants.RATE_ON_GOOGLE_PLAY)
|
||||
R.id.appShare -> shareApp()
|
||||
R.id.donateLink -> NavigationUtil.goToSupportDevelopment(requireActivity())
|
||||
R.id.instagramLink -> openUrl(Constants.APP_INSTAGRAM_LINK)
|
||||
R.id.twitterLink -> openUrl(Constants.APP_TWITTER_LINK)
|
||||
R.id.changelog -> openUrl(Constants.TELEGRAM_CHANGE_LOG)
|
||||
R.id.openSource -> NavigationUtil.goToOpenSource(requireActivity())
|
||||
R.id.bugReportLink -> NavigationUtil.bugReport(requireActivity())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAppVersion(): String {
|
||||
return try {
|
||||
val isPro = if (App.isProVersion()) "Pro" else "Free"
|
||||
val packageInfo =
|
||||
requireActivity().packageManager.getPackageInfo(requireActivity().packageName, 0)
|
||||
"${packageInfo.versionName} $isPro"
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
e.printStackTrace()
|
||||
"0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareApp() {
|
||||
ShareCompat.IntentBuilder.from(requireActivity()).setType("text/plain")
|
||||
.setChooserTitle(R.string.share_app)
|
||||
.setText(String.format(getString(R.string.app_share), requireActivity().packageName))
|
||||
.startChooser()
|
||||
}
|
||||
|
||||
private fun loadContributors() {
|
||||
val type = object : TypeToken<List<Contributor>>() {
|
||||
|
||||
}.type
|
||||
val contributors = Gson().fromJson<List<Contributor>>(contributorsJson, type)
|
||||
|
||||
val contributorAdapter = ContributorAdapter(contributors)
|
||||
recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
adapter = contributorAdapter
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
package code.name.monkey.retromusic.fragments.albums
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
||||
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.fragments.MainActivityFragment
|
||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
||||
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.model.Album
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.network.model.LastFmAlbum
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.activity_album_content.*
|
||||
import kotlinx.android.synthetic.main.activity_album_details.*
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import java.util.*
|
||||
|
||||
class AlbumDetailsFragment : MainActivityFragment(R.layout.fragment_album_details),
|
||||
AlbumClickListener {
|
||||
private lateinit var simpleSongAdapter: SimpleSongAdapter
|
||||
private lateinit var album: Album
|
||||
|
||||
private val savedSortOrder: String
|
||||
get() = PreferenceUtil.albumDetailSongSortOrder
|
||||
|
||||
private val detailsViewModel by viewModel<AlbumDetailsViewModel> {
|
||||
parametersOf(extraNotNull<Int>(EXTRA_ALBUM_ID).value)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
mainActivity.setSupportActionBar(toolbar)
|
||||
mainActivity.setBottomBarVisibility(View.GONE)
|
||||
toolbar.title = null
|
||||
|
||||
postponeEnterTransition()
|
||||
playerActivity?.addMusicServiceEventListener(detailsViewModel)
|
||||
detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer {
|
||||
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)
|
||||
.navigate(
|
||||
R.id.artistDetailsFragment,
|
||||
bundleOf(EXTRA_ARTIST_ID to album.artistId)
|
||||
)
|
||||
}
|
||||
playAction.setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) }
|
||||
|
||||
shuffleAction.setOnClickListener {
|
||||
MusicPlayerRemote.openAndShuffleQueue(
|
||||
album.songs!!,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
aboutAlbumText.setOnClickListener {
|
||||
if (aboutAlbumText.maxLines == 4) {
|
||||
aboutAlbumText.maxLines = Integer.MAX_VALUE
|
||||
} else {
|
||||
aboutAlbumText.maxLines = 4
|
||||
}
|
||||
}
|
||||
image.apply {
|
||||
transitionName = getString(R.string.transition_album_art)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val activity = activity as AppCompatActivity
|
||||
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
playerActivity?.removeMusicServiceEventListener(detailsViewModel)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> findNavController().navigateUp()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
simpleSongAdapter = SimpleSongAdapter(
|
||||
requireActivity() as AppCompatActivity,
|
||||
ArrayList(),
|
||||
R.layout.item_song,
|
||||
null
|
||||
)
|
||||
recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
isNestedScrollingEnabled = false
|
||||
adapter = simpleSongAdapter
|
||||
}
|
||||
}
|
||||
|
||||
private fun showAlbum(album: Album) {
|
||||
if (album.songs!!.isEmpty()) {
|
||||
return
|
||||
}
|
||||
this.album = album
|
||||
|
||||
albumTitle.text = album.title
|
||||
val songText =
|
||||
resources.getQuantityString(
|
||||
R.plurals.albumSongs,
|
||||
album.songCount,
|
||||
album.songCount
|
||||
)
|
||||
songTitle.text = songText
|
||||
|
||||
if (MusicUtil.getYearString(album.year) == "-") {
|
||||
albumText.text = String.format(
|
||||
"%s • %s",
|
||||
album.artistName,
|
||||
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs))
|
||||
)
|
||||
} else {
|
||||
albumText.text = String.format(
|
||||
"%s • %s • %s",
|
||||
album.artistName,
|
||||
MusicUtil.getYearString(album.year),
|
||||
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs))
|
||||
)
|
||||
}
|
||||
loadAlbumCover()
|
||||
simpleSongAdapter.swapDataSet(album.songs)
|
||||
detailsViewModel.loadArtist(album.artistId)
|
||||
detailsViewModel.loadAlbumInfo(album)
|
||||
}
|
||||
|
||||
private fun moreAlbums(albums: List<Album>) {
|
||||
moreTitle.show()
|
||||
moreRecyclerView.show()
|
||||
moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName)
|
||||
|
||||
val albumAdapter =
|
||||
HorizontalAlbumAdapter(requireActivity() as AppCompatActivity, albums, null, this)
|
||||
moreRecyclerView.layoutManager = GridLayoutManager(
|
||||
requireContext(),
|
||||
1,
|
||||
GridLayoutManager.HORIZONTAL,
|
||||
false
|
||||
)
|
||||
moreRecyclerView.adapter = albumAdapter
|
||||
}
|
||||
|
||||
private fun aboutAlbum(lastFmAlbum: LastFmAlbum) {
|
||||
if (lastFmAlbum.album != null) {
|
||||
if (lastFmAlbum.album.wiki != null) {
|
||||
aboutAlbumText.show()
|
||||
aboutAlbumTitle.show()
|
||||
aboutAlbumTitle.text =
|
||||
String.format(getString(R.string.about_album_label), lastFmAlbum.album.name)
|
||||
aboutAlbumText.text = lastFmAlbum.album.wiki.content
|
||||
}
|
||||
if (lastFmAlbum.album.listeners.isNotEmpty()) {
|
||||
listeners.show()
|
||||
listenersLabel.show()
|
||||
scrobbles.show()
|
||||
scrobblesLabel.show()
|
||||
|
||||
listeners.text = RetroUtil.formatValue(lastFmAlbum.album.listeners.toFloat())
|
||||
scrobbles.text = RetroUtil.formatValue(lastFmAlbum.album.playcount.toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadArtistImage(artist: Artist) {
|
||||
ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
|
||||
.generatePalette(requireContext())
|
||||
.build()
|
||||
.dontAnimate()
|
||||
.dontTransform()
|
||||
.into(object : RetroMusicColoredTarget(artistImage) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadAlbumCover() {
|
||||
AlbumGlideRequest.Builder.from(Glide.with(requireContext()), album.safeGetFirstSong())
|
||||
.checkIgnoreMediaStore(requireContext())
|
||||
.ignoreMediaStore(PreferenceUtil.isIgnoreMediaStoreArtwork)
|
||||
.generatePalette(requireContext())
|
||||
.build()
|
||||
.dontAnimate()
|
||||
.dontTransform()
|
||||
.into(object : RetroMusicColoredTarget(image) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setColors(color: MediaNotificationProcessor) {
|
||||
MaterialUtil.tintColor(
|
||||
button = shuffleAction,
|
||||
textColor = color.primaryTextColor,
|
||||
backgroundColor = color.backgroundColor
|
||||
)
|
||||
MaterialUtil.tintColor(
|
||||
button = playAction,
|
||||
textColor = color.primaryTextColor,
|
||||
backgroundColor = color.backgroundColor
|
||||
)
|
||||
}
|
||||
|
||||
override fun onAlbumClick(albumId: Int) {
|
||||
findNavController().navigate(
|
||||
R.id.albumDetailsFragment,
|
||||
bundleOf("extra_album_id" to albumId)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package code.name.monkey.retromusic.fragments.albums
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
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.model.LastFmAlbum
|
||||
import code.name.monkey.retromusic.providers.RepositoryImpl
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AlbumDetailsViewModel(
|
||||
private val repository: RepositoryImpl,
|
||||
private val albumId: Int
|
||||
) : 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> = _album
|
||||
fun getArtist(): LiveData<Artist> = _artist
|
||||
fun getAlbumInfo(): LiveData<LastFmAlbum> = _lastFmAlbum
|
||||
fun getMoreAlbums(): LiveData<List<Album>> = _moreAlbums;
|
||||
|
||||
init {
|
||||
loadAlbumDetails()
|
||||
}
|
||||
|
||||
private fun loadAlbumDetails() = viewModelScope.launch {
|
||||
val album = loadAlbumAsync.await() ?: throw NullPointerException("Album couldn't found")
|
||||
_album.postValue(album)
|
||||
}
|
||||
|
||||
fun loadAlbumInfo(album: Album) = viewModelScope.launch(Dispatchers.IO) {
|
||||
val lastFmAlbum = repository.albumInfo(album.artistName ?: "-", album.title ?: "-")
|
||||
_lastFmAlbum.postValue(lastFmAlbum)
|
||||
}
|
||||
|
||||
fun loadArtist(artistId: Int) = viewModelScope.launch(Dispatchers.IO) {
|
||||
val artist = repository.artistById(artistId)
|
||||
_artist.postValue(artist)
|
||||
|
||||
artist.albums?.filter { item -> item.id != albumId }?.let { albums ->
|
||||
if (albums.isNotEmpty()) _moreAlbums.postValue(albums)
|
||||
}
|
||||
}
|
||||
|
||||
private val loadAlbumAsync: Deferred<Album?>
|
||||
get() = viewModelScope.async(Dispatchers.IO) {
|
||||
repository.albumById(albumId)
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
loadAlbumDetails()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {}
|
||||
override fun onServiceDisconnected() {}
|
||||
override fun onQueueChanged() {}
|
||||
override fun onPlayingMetaChanged() {}
|
||||
override fun onPlayStateChanged() {}
|
||||
override fun onRepeatModeChanged() {}
|
||||
override fun onShuffleModeChanged() {}
|
||||
}
|
|
@ -2,22 +2,20 @@ package code.name.monkey.retromusic.fragments.albums
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.album.AlbumAdapter
|
||||
import code.name.monkey.retromusic.fragments.ReloadType
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
|
||||
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
|
||||
class AlbumsFragment :
|
||||
AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
|
||||
MainActivityFragmentCallbacks {
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
return false
|
||||
}
|
||||
AlbumClickListener {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
@ -40,10 +38,11 @@ class AlbumsFragment :
|
|||
override fun createAdapter(): AlbumAdapter {
|
||||
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
|
||||
return AlbumAdapter(
|
||||
mainActivity,
|
||||
requireActivity(),
|
||||
dataSet,
|
||||
itemLayoutRes(),
|
||||
mainActivity
|
||||
R.layout.item_grid,
|
||||
null,
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -90,12 +89,17 @@ class AlbumsFragment :
|
|||
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
var TAG: String = AlbumsFragment::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): AlbumsFragment {
|
||||
return AlbumsFragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAlbumClick(albumId: Int) {
|
||||
val controller = requireActivity().findNavController(R.id.fragment_container)
|
||||
controller.navigate(R.id.albumDetailsFragment, bundleOf(EXTRA_ALBUM_ID to albumId))
|
||||
}
|
||||
}
|
||||
|
||||
interface AlbumClickListener {
|
||||
fun onAlbumClick(albumId: Int)
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
package code.name.monkey.retromusic.fragments.artists
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Spanned
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.fragments.MainActivityFragment
|
||||
import code.name.monkey.retromusic.fragments.albums.AlbumClickListener
|
||||
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.model.Artist
|
||||
import code.name.monkey.retromusic.network.model.LastFmArtist
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.activity_artist_content.*
|
||||
import kotlinx.android.synthetic.main.activity_artist_details.*
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class ArtistDetailsFragment : MainActivityFragment(R.layout.fragment_artist_details),
|
||||
AlbumClickListener {
|
||||
|
||||
private var biography: Spanned? = null
|
||||
private lateinit var artist: Artist
|
||||
private lateinit var songAdapter: SimpleSongAdapter
|
||||
private lateinit var albumAdapter: HorizontalAlbumAdapter
|
||||
private var forceDownload: Boolean = false
|
||||
private var lang: String? = null
|
||||
|
||||
private val detailsViewModel: ArtistDetailsViewModel by viewModel {
|
||||
parametersOf(extraNotNull<Int>(EXTRA_ARTIST_ID).value)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.setSupportActionBar(toolbar)
|
||||
mainActivity.setBottomBarVisibility(View.GONE)
|
||||
toolbar.title = null
|
||||
|
||||
postponeEnterTransition()
|
||||
detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer {
|
||||
startPostponedEnterTransition()
|
||||
showArtist(it)
|
||||
})
|
||||
detailsViewModel.getArtistInfo().observe(viewLifecycleOwner, Observer {
|
||||
artistInfo(it)
|
||||
})
|
||||
setupRecyclerView()
|
||||
playAction.apply {
|
||||
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) }
|
||||
}
|
||||
shuffleAction.apply {
|
||||
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) }
|
||||
}
|
||||
|
||||
biographyText.setOnClickListener {
|
||||
if (biographyText.maxLines == 4) {
|
||||
biographyText.maxLines = Integer.MAX_VALUE
|
||||
} else {
|
||||
biographyText.maxLines = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
albumAdapter = HorizontalAlbumAdapter(requireActivity(), ArrayList(), null, this)
|
||||
albumRecyclerView.apply {
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false)
|
||||
adapter = albumAdapter
|
||||
}
|
||||
songAdapter = SimpleSongAdapter(requireActivity(), ArrayList(), R.layout.item_song, null)
|
||||
recyclerView.apply {
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
layoutManager = LinearLayoutManager(this.context)
|
||||
adapter = songAdapter
|
||||
}
|
||||
}
|
||||
|
||||
fun showArtist(artist: Artist) {
|
||||
this.artist = artist
|
||||
loadArtistImage(artist)
|
||||
if (RetroUtil.isAllowedToDownloadMetadata(requireContext())) {
|
||||
loadBiography(artist.name)
|
||||
}
|
||||
artistTitle.text = artist.name
|
||||
text.text = String.format(
|
||||
"%s • %s",
|
||||
MusicUtil.getArtistInfoString(requireContext(), artist),
|
||||
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs))
|
||||
)
|
||||
val songText =
|
||||
resources.getQuantityString(
|
||||
R.plurals.albumSongs,
|
||||
artist.songCount,
|
||||
artist.songCount
|
||||
)
|
||||
val albumText =
|
||||
resources.getQuantityString(
|
||||
R.plurals.albums,
|
||||
artist.songCount,
|
||||
artist.songCount
|
||||
)
|
||||
songTitle.text = songText
|
||||
albumTitle.text = albumText
|
||||
songAdapter.swapDataSet(artist.songs)
|
||||
artist.albums?.let { albumAdapter.swapDataSet(it) }
|
||||
}
|
||||
|
||||
private fun loadBiography(
|
||||
name: String,
|
||||
lang: String? = Locale.getDefault().language
|
||||
) {
|
||||
biography = null
|
||||
this.lang = lang
|
||||
detailsViewModel.loadBiography(name, lang, null)
|
||||
}
|
||||
|
||||
private fun artistInfo(lastFmArtist: LastFmArtist?) {
|
||||
if (lastFmArtist != null && lastFmArtist.artist != null) {
|
||||
val bioContent = lastFmArtist.artist.bio.content
|
||||
if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) {
|
||||
biographyText.visibility = View.VISIBLE
|
||||
biographyTitle.visibility = View.VISIBLE
|
||||
biography = HtmlCompat.fromHtml(bioContent, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
biographyText.text = biography
|
||||
if (lastFmArtist.artist.stats.listeners.isNotEmpty()) {
|
||||
listeners.show()
|
||||
listenersLabel.show()
|
||||
scrobbles.show()
|
||||
scrobblesLabel.show()
|
||||
listeners.text =
|
||||
RetroUtil.formatValue(lastFmArtist.artist.stats.listeners.toFloat())
|
||||
scrobbles.text =
|
||||
RetroUtil.formatValue(lastFmArtist.artist.stats.playcount.toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the "lang" parameter is set and no biography is given, retry with default language
|
||||
if (biography == null && lang != null) {
|
||||
loadBiography(artist.name, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun loadArtistImage(artist: Artist) {
|
||||
ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
|
||||
.generatePalette(requireContext()).build()
|
||||
.dontAnimate().into(object : RetroMusicColoredTarget(image) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setColors(color: MediaNotificationProcessor) {
|
||||
MaterialUtil.tintColor(
|
||||
button = shuffleAction,
|
||||
textColor = color.primaryTextColor,
|
||||
backgroundColor = color.backgroundColor
|
||||
)
|
||||
MaterialUtil.tintColor(
|
||||
button = playAction,
|
||||
textColor = color.primaryTextColor,
|
||||
backgroundColor = color.backgroundColor
|
||||
)
|
||||
}
|
||||
|
||||
override fun onAlbumClick(albumId: Int) {
|
||||
findNavController().navigate(
|
||||
R.id.albumDetailsFragment,
|
||||
bundleOf("extra_album_id" to albumId)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package code.name.monkey.retromusic.fragments.artists
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.providers.RepositoryImpl
|
||||
import code.name.monkey.retromusic.network.model.LastFmArtist
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ArtistDetailsViewModel(
|
||||
private val repository: RepositoryImpl,
|
||||
private val artistId: Int
|
||||
) : ViewModel(), MusicServiceEventListener {
|
||||
|
||||
private val loadArtistDetailsAsync: Deferred<Artist?>
|
||||
get() = viewModelScope.async(Dispatchers.IO) {
|
||||
repository.artistById(artistId)
|
||||
}
|
||||
|
||||
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 {
|
||||
val artist =
|
||||
loadArtistDetailsAsync.await() ?: throw NullPointerException("Album couldn't found")
|
||||
_artist.postValue(artist)
|
||||
}
|
||||
|
||||
fun loadBiography(name: String, lang: String?, cache: String?) = viewModelScope.launch {
|
||||
val info = repository.artistInfo(name, lang, cache)
|
||||
_lastFmArtist.postValue(info)
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
loadArtistDetails()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {}
|
||||
override fun onServiceDisconnected() {}
|
||||
override fun onQueueChanged() {}
|
||||
override fun onPlayingMetaChanged() {}
|
||||
override fun onPlayStateChanged() {}
|
||||
override fun onRepeatModeChanged() {}
|
||||
override fun onShuffleModeChanged() {}
|
||||
}
|
|
@ -2,10 +2,13 @@ package code.name.monkey.retromusic.fragments.artists
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
||||
import code.name.monkey.retromusic.extensions.findActivityNavController
|
||||
import code.name.monkey.retromusic.fragments.ReloadType
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
|
||||
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
|
||||
|
@ -13,7 +16,7 @@ import code.name.monkey.retromusic.util.PreferenceUtil
|
|||
|
||||
class ArtistsFragment :
|
||||
AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
|
||||
MainActivityFragmentCallbacks {
|
||||
MainActivityFragmentCallbacks, ArtistClickListener {
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
return false
|
||||
|
@ -46,10 +49,11 @@ class ArtistsFragment :
|
|||
override fun createAdapter(): ArtistAdapter {
|
||||
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
|
||||
return ArtistAdapter(
|
||||
mainActivity,
|
||||
requireActivity(),
|
||||
dataSet,
|
||||
itemLayoutRes(),
|
||||
mainActivity
|
||||
R.layout.item_grid_circle,
|
||||
null,
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -91,12 +95,18 @@ class ArtistsFragment :
|
|||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val TAG: String = ArtistsFragment::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): ArtistsFragment {
|
||||
return ArtistsFragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onArtist(artistId: Int) {
|
||||
val controller = findActivityNavController(R.id.fragment_container)
|
||||
controller.navigate(R.id.artistDetailsFragment, bundleOf(EXTRA_ARTIST_ID to artistId))
|
||||
}
|
||||
}
|
||||
|
||||
interface ArtistClickListener {
|
||||
fun onArtist(artistId: Int)
|
||||
}
|
|
@ -14,6 +14,10 @@ import android.view.View
|
|||
import android.widget.Toast
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.navigation.findNavController
|
||||
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
||||
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
||||
import code.name.monkey.retromusic.activities.tageditor.SongTagEditorActivity
|
||||
|
@ -86,11 +90,17 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragme
|
|||
return true
|
||||
}
|
||||
R.id.action_go_to_album -> {
|
||||
NavigationUtil.goToAlbum(requireActivity(), song.albumId)
|
||||
requireActivity().findNavController(R.id.fragment_container).navigate(
|
||||
R.id.albumDetailsFragment,
|
||||
bundleOf(EXTRA_ALBUM_ID to song.albumId)
|
||||
)
|
||||
return true
|
||||
}
|
||||
R.id.action_go_to_artist -> {
|
||||
NavigationUtil.goToArtist(requireActivity(), song.artistId)
|
||||
requireActivity().findNavController(R.id.fragment_container).navigate(
|
||||
R.id.artistDetailsFragment,
|
||||
bundleOf(EXTRA_ARTIST_ID to song.artistId)
|
||||
)
|
||||
return true
|
||||
}
|
||||
R.id.now_playing -> {
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.annotation.NonNull
|
|||
import androidx.annotation.StringRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.util.DensityUtil
|
||||
|
@ -24,8 +23,7 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
|||
AppBarLayout.OnOffsetChangedListener {
|
||||
|
||||
val libraryViewModel: LibraryViewModel by sharedViewModel()
|
||||
val mainActivity: MainActivity
|
||||
get() = requireActivity() as MainActivity
|
||||
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
@ -38,7 +36,6 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.addOnAppBarOffsetChangedListener(this)
|
||||
initLayoutManager()
|
||||
initAdapter()
|
||||
setUpRecyclerView()
|
||||
|
@ -112,7 +109,7 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
|||
container.paddingLeft,
|
||||
container.paddingTop,
|
||||
container.paddingRight,
|
||||
mainActivity.getTotalAppBarScrollingRange() + i
|
||||
i
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -137,11 +134,6 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
|||
recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
mainActivity.removeOnAppBarOffsetChangedListener(this)
|
||||
}
|
||||
|
||||
fun recyclerView(): RecyclerView {
|
||||
return recyclerView
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package code.name.monkey.retromusic.fragments.genres
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.EXTRA_GENRE
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||
import code.name.monkey.retromusic.extensions.dipToPix
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.fragments.MainActivityFragment
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import kotlinx.android.synthetic.main.fragment_playlist_detail.*
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import java.util.*
|
||||
|
||||
class GenreDetailsFragment : MainActivityFragment(R.layout.fragment_playlist_detail) {
|
||||
private val detailsViewModel: GenreDetailsViewModel by viewModel {
|
||||
parametersOf(extraNotNull<Genre>(EXTRA_GENRE).value)
|
||||
}
|
||||
|
||||
private lateinit var genre: Genre
|
||||
private lateinit var songAdapter: SongAdapter
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.addMusicServiceEventListener(detailsViewModel)
|
||||
mainActivity.setSupportActionBar(toolbar)
|
||||
|
||||
setupRecyclerView()
|
||||
detailsViewModel.getSongs().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
|
||||
songs(it)
|
||||
})
|
||||
detailsViewModel.getGenre().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
|
||||
genre = it
|
||||
toolbar?.title = it.name
|
||||
})
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
songAdapter = SongAdapter(requireActivity(), ArrayList(), R.layout.item_list, null)
|
||||
recyclerView.apply {
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = songAdapter
|
||||
}
|
||||
songAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
super.onChanged()
|
||||
checkIsEmpty()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun songs(songs: List<Song>) {
|
||||
songAdapter.swapDataSet(songs)
|
||||
}
|
||||
|
||||
private fun getEmojiByUnicode(unicode: Int): String {
|
||||
return String(Character.toChars(unicode))
|
||||
}
|
||||
|
||||
private fun checkIsEmpty() {
|
||||
checkForPadding()
|
||||
emptyEmoji.text = getEmojiByUnicode(0x1F631)
|
||||
empty?.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun checkForPadding() {
|
||||
val height = dipToPix(52f).toInt()
|
||||
recyclerView.setPadding(0, 0, 0, height)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package code.name.monkey.retromusic.fragments.genres
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.providers.RepositoryImpl
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class GenreDetailsViewModel(
|
||||
private val repository: RepositoryImpl,
|
||||
private val genre: Genre
|
||||
) : ViewModel(), MusicServiceEventListener {
|
||||
|
||||
private val _playListSongs = MutableLiveData<List<Song>>()
|
||||
private val _genre = MutableLiveData<Genre>().apply {
|
||||
postValue(genre)
|
||||
}
|
||||
|
||||
fun getSongs(): LiveData<List<Song>> = _playListSongs
|
||||
|
||||
fun getGenre(): LiveData<Genre> = _genre
|
||||
|
||||
init {
|
||||
loadGenreSongs(genre)
|
||||
}
|
||||
|
||||
private fun loadGenreSongs(genre: Genre) = viewModelScope.launch {
|
||||
val songs = repository.getGenre(genre.id)
|
||||
withContext(Main) { _playListSongs.postValue(songs) }
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
loadGenreSongs(genre)
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {}
|
||||
override fun onServiceDisconnected() {}
|
||||
override fun onQueueChanged() {}
|
||||
override fun onPlayingMetaChanged() {}
|
||||
override fun onPlayStateChanged() {}
|
||||
override fun onRepeatModeChanged() {}
|
||||
override fun onShuffleModeChanged() {}
|
||||
}
|
|
@ -49,7 +49,7 @@ class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager
|
|||
|
||||
override fun createAdapter(): GenreAdapter {
|
||||
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
|
||||
return GenreAdapter(mainActivity, dataSet, R.layout.item_list_no_image)
|
||||
return GenreAdapter(requireActivity(), dataSet, R.layout.item_list_no_image)
|
||||
}
|
||||
|
||||
override val emptyMessage: Int
|
||||
|
|
|
@ -18,8 +18,11 @@ import android.app.ActivityOptions
|
|||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.retromusic.EXTRA_PLAYLIST
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.HomeAdapter
|
||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||
|
@ -40,7 +43,7 @@ import kotlinx.android.synthetic.main.fragment_banner_home.*
|
|||
import kotlinx.android.synthetic.main.home_content.*
|
||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||
|
||||
class BannerHomeFragment :
|
||||
class HomeFragment :
|
||||
AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home),
|
||||
MainActivityFragmentCallbacks {
|
||||
|
||||
|
@ -73,13 +76,16 @@ class BannerHomeFragment :
|
|||
}
|
||||
|
||||
lastAdded.setOnClickListener {
|
||||
NavigationUtil.goToPlaylistNew(requireActivity(), LastAddedPlaylist(requireActivity()))
|
||||
requireActivity().findNavController(R.id.fragment_container).navigate(
|
||||
R.id.playlistDetailsFragment,
|
||||
bundleOf(EXTRA_PLAYLIST to LastAddedPlaylist(requireActivity()))
|
||||
)
|
||||
}
|
||||
|
||||
topPlayed.setOnClickListener {
|
||||
NavigationUtil.goToPlaylistNew(
|
||||
requireActivity(),
|
||||
MyTopTracksPlaylist(requireActivity())
|
||||
requireActivity().findNavController(R.id.fragment_container).navigate(
|
||||
R.id.playlistDetailsFragment,
|
||||
bundleOf(EXTRA_PLAYLIST to MyTopTracksPlaylist(requireActivity()))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -88,7 +94,10 @@ class BannerHomeFragment :
|
|||
}
|
||||
|
||||
history.setOnClickListener {
|
||||
NavigationUtil.goToPlaylistNew(requireActivity(), HistoryPlaylist(requireActivity()))
|
||||
requireActivity().findNavController(R.id.fragment_container).navigate(
|
||||
R.id.playlistDetailsFragment,
|
||||
bundleOf(EXTRA_PLAYLIST to HistoryPlaylist(requireActivity()))
|
||||
)
|
||||
}
|
||||
|
||||
userImage.setOnClickListener {
|
||||
|
@ -101,7 +110,7 @@ class BannerHomeFragment :
|
|||
}
|
||||
titleWelcome?.text = String.format("%s", PreferenceUtil.userName)
|
||||
|
||||
val homeAdapter = HomeAdapter(mainActivity, displayMetrics)
|
||||
val homeAdapter = HomeAdapter(mainActivity)
|
||||
recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(mainActivity)
|
||||
adapter = homeAdapter
|
||||
|
@ -133,8 +142,8 @@ class BannerHomeFragment :
|
|||
const val TAG: String = "BannerHomeFragment"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): BannerHomeFragment {
|
||||
return BannerHomeFragment()
|
||||
fun newInstance(): HomeFragment {
|
||||
return HomeFragment()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
package code.name.monkey.retromusic.fragments.library
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.navOptions
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.findNavController
|
||||
import code.name.monkey.retromusic.fragments.MainActivityFragment
|
||||
import code.name.monkey.retromusic.fragments.folder.FoldersFragment
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import kotlinx.android.synthetic.main.fragment_library.*
|
||||
|
||||
class LibraryFragment : MainActivityFragment(R.layout.fragment_library) {
|
||||
private val navOptions by lazy {
|
||||
navOptions {
|
||||
launchSingleTop = true
|
||||
anim {
|
||||
enter = R.anim.retro_fragment_open_enter
|
||||
exit = R.anim.retro_fragment_open_exit
|
||||
popEnter = R.anim.retro_fragment_close_enter
|
||||
popExit = R.anim.retro_fragment_close_exit
|
||||
}
|
||||
}
|
||||
}
|
||||
private val appBarConfiguration by lazy {
|
||||
AppBarConfiguration(
|
||||
setOf(
|
||||
R.id.libraryFragment,
|
||||
R.id.settingsFragment,
|
||||
R.id.searchFragment
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
mainActivity.setBottomBarVisibility(View.VISIBLE)
|
||||
mainActivity.setSupportActionBar(toolbar)
|
||||
setupNavigationController()
|
||||
}
|
||||
|
||||
private fun setupNavigationController() {
|
||||
val navController = findNavController(R.id.fragment_container)
|
||||
val navOptions = navOptions {
|
||||
launchSingleTop = true
|
||||
anim {
|
||||
enter = R.anim.retro_fragment_open_enter
|
||||
exit = R.anim.retro_fragment_open_exit
|
||||
popEnter = R.anim.retro_fragment_close_enter
|
||||
popExit = R.anim.retro_fragment_close_exit
|
||||
}
|
||||
popUpTo(navController.graph.startDestination) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
mainActivity.getBottomNavigationView().setOnNavigationItemSelectedListener {
|
||||
var handled = false
|
||||
if (navController.graph.findNode(it.itemId) != null) {
|
||||
navController.navigate(it.itemId, null, navOptions)
|
||||
handled = true
|
||||
}
|
||||
when (it.itemId) {
|
||||
R.id.action_folder -> navController.navigate(
|
||||
R.id.action_folder,
|
||||
Bundle().apply {
|
||||
putSerializable(
|
||||
FoldersFragment.PATH,
|
||||
PreferenceUtil.startDirectory
|
||||
)
|
||||
},
|
||||
navOptions
|
||||
)
|
||||
}
|
||||
return@setOnNavigationItemSelectedListener handled
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), toolbar)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.menu_main, menu)
|
||||
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
|
||||
requireContext(),
|
||||
toolbar,
|
||||
menu,
|
||||
getToolbarBackgroundColor(toolbar)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_search -> findNavController().navigate(
|
||||
R.id.searchFragment,
|
||||
null,
|
||||
navOptions
|
||||
)
|
||||
R.id.action_settings -> findNavController().navigate(
|
||||
R.id.settingsFragment,
|
||||
null,
|
||||
navOptions
|
||||
)
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
fun addOnAppBarOffsetChangedListener(changedListener: AppBarLayout.OnOffsetChangedListener) {
|
||||
appBarLayout.addOnOffsetChangedListener(changedListener)
|
||||
}
|
||||
|
||||
fun removeOnAppBarOffsetChangedListener(changedListener: AppBarLayout.OnOffsetChangedListener) {
|
||||
appBarLayout.removeOnOffsetChangedListener(changedListener)
|
||||
}
|
||||
|
||||
fun getTotalAppBarScrollingRange(): Int {
|
||||
return 0
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import android.os.Bundle
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.NavController
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.navController
|
||||
import code.name.monkey.retromusic.extensions.findNavController
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
|
||||
|
@ -15,7 +15,7 @@ class NowPlayingPlayerFragment : Fragment(R.layout.fragment_now_playing_player)
|
|||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
val navController = navController(R.id.playerFragmentContainer)
|
||||
val navController = findNavController(R.id.playerFragmentContainer)
|
||||
updateNowPlaying(navController)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package code.name.monkey.retromusic.fragments.player.full
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
|
@ -8,7 +7,10 @@ import android.view.View
|
|||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.navigation.findNavController
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
|
@ -22,7 +24,6 @@ import code.name.monkey.retromusic.loaders.ArtistLoader
|
|||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
|
||||
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.fragment_full.*
|
||||
|
@ -150,19 +151,11 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full),
|
|||
|
||||
private fun setupArtist() {
|
||||
artistImage.setOnClickListener {
|
||||
val transitionName =
|
||||
"${getString(R.string.transition_artist_image)}_${MusicPlayerRemote.currentSong.artistId}"
|
||||
val activityOptions =
|
||||
ActivityOptions.makeSceneTransitionAnimation(
|
||||
requireActivity(),
|
||||
artistImage,
|
||||
transitionName
|
||||
requireActivity().findNavController(R.id.fragment_container)
|
||||
.navigate(
|
||||
R.id.artistDetailsFragment,
|
||||
bundleOf(EXTRA_ARTIST_ID to MusicPlayerRemote.currentSong.artistId)
|
||||
)
|
||||
NavigationUtil.goToArtistOptions(
|
||||
requireActivity(),
|
||||
MusicPlayerRemote.currentSong.artistId,
|
||||
activityOptions
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
package code.name.monkey.retromusic.fragments.playlists
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.EXTRA_PLAYLIST
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.song.OrderablePlaylistSongAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||
import code.name.monkey.retromusic.extensions.dipToPix
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.fragments.MainActivityFragment
|
||||
import code.name.monkey.retromusic.model.AbsCustomPlaylist
|
||||
import code.name.monkey.retromusic.model.Playlist
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||
import kotlinx.android.synthetic.main.activity_playlist_detail.*
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
|
||||
class PlaylistDetailsFragment : MainActivityFragment(R.layout.fragment_playlist_detail) {
|
||||
private val viewModel: PlaylistDetailsViewModel by viewModel {
|
||||
parametersOf(extraNotNull<Playlist>(EXTRA_PLAYLIST).value)
|
||||
}
|
||||
private lateinit var playlist: Playlist
|
||||
private lateinit var adapter: SongAdapter
|
||||
|
||||
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.addMusicServiceEventListener(viewModel)
|
||||
mainActivity.setSupportActionBar(toolbar)
|
||||
|
||||
playlist = extraNotNull<Playlist>(EXTRA_PLAYLIST).value
|
||||
|
||||
setUpRecyclerView()
|
||||
|
||||
viewModel.getSongs().observe(viewLifecycleOwner, Observer {
|
||||
songs(it)
|
||||
})
|
||||
|
||||
viewModel.getPlaylist().observe(viewLifecycleOwner, Observer {
|
||||
playlist = it
|
||||
toolbar.title = it.name
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
if (playlist is AbsCustomPlaylist) {
|
||||
adapter = SongAdapter(requireActivity(), ArrayList(), R.layout.item_list, null)
|
||||
recyclerView.adapter = adapter
|
||||
} else {
|
||||
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||
val animator = RefactoredDefaultItemAnimator()
|
||||
adapter = OrderablePlaylistSongAdapter(requireActivity(),
|
||||
ArrayList(),
|
||||
R.layout.item_list,
|
||||
null,
|
||||
object : OrderablePlaylistSongAdapter.OnMoveItemListener {
|
||||
override fun onMoveItem(fromPosition: Int, toPosition: Int) {
|
||||
if (PlaylistsUtil.moveItem(
|
||||
requireContext(),
|
||||
playlist.id,
|
||||
fromPosition,
|
||||
toPosition
|
||||
)
|
||||
) {
|
||||
val song = adapter.dataSet.removeAt(fromPosition)
|
||||
adapter.dataSet.add(toPosition, song)
|
||||
adapter.notifyItemMoved(fromPosition, toPosition)
|
||||
}
|
||||
}
|
||||
})
|
||||
wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(adapter)
|
||||
|
||||
recyclerView.adapter = wrappedAdapter
|
||||
recyclerView.itemAnimator = animator
|
||||
|
||||
recyclerViewDragDropManager?.attachRecyclerView(recyclerView)
|
||||
}
|
||||
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
super.onChanged()
|
||||
checkIsEmpty()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(
|
||||
if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail
|
||||
else R.menu.menu_playlist_detail, menu
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private fun checkForPadding() {
|
||||
val height = dipToPix(52f)
|
||||
recyclerView.setPadding(0, 0, 0, height.toInt())
|
||||
}
|
||||
|
||||
private fun checkIsEmpty() {
|
||||
checkForPadding()
|
||||
emptyEmoji.text = getEmojiByUnicode(0x1F631)
|
||||
empty.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||
emptyText.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun getEmojiByUnicode(unicode: Int): String {
|
||||
return String(Character.toChars(unicode))
|
||||
}
|
||||
|
||||
public override fun onPause() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.cancelDrag()
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.release()
|
||||
recyclerViewDragDropManager = null
|
||||
}
|
||||
|
||||
if (recyclerView != null) {
|
||||
recyclerView!!.itemAnimator = null
|
||||
recyclerView!!.adapter = null
|
||||
}
|
||||
|
||||
if (wrappedAdapter != null) {
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
wrappedAdapter = null
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
fun showEmptyView() {
|
||||
empty.visibility = View.VISIBLE
|
||||
emptyText.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
fun songs(songs: List<Song>) {
|
||||
if (songs.isNotEmpty()) {
|
||||
adapter.swapDataSet(songs)
|
||||
} else {
|
||||
showEmptyView()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package code.name.monkey.retromusic.fragments.playlists
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||
import code.name.monkey.retromusic.loaders.PlaylistLoader
|
||||
import code.name.monkey.retromusic.model.AbsCustomPlaylist
|
||||
import code.name.monkey.retromusic.model.Playlist
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.providers.RepositoryImpl
|
||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class PlaylistDetailsViewModel(
|
||||
private val repository: RepositoryImpl,
|
||||
private var playlist: Playlist
|
||||
) : ViewModel(), MusicServiceEventListener {
|
||||
private val _playListSongs = MutableLiveData<List<Song>>()
|
||||
|
||||
private val _playlist = MutableLiveData<Playlist>().apply {
|
||||
postValue(playlist)
|
||||
}
|
||||
|
||||
fun getPlaylist(): LiveData<Playlist> = _playlist
|
||||
|
||||
fun getSongs(): LiveData<List<Song>> = _playListSongs
|
||||
|
||||
init {
|
||||
loadPlaylistSongs(playlist)
|
||||
}
|
||||
|
||||
private fun loadPlaylistSongs(playlist: Playlist) = viewModelScope.launch {
|
||||
val songs = repository.getPlaylistSongs(playlist)
|
||||
withContext(Main) { _playListSongs.postValue(songs) }
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
if (playlist !is AbsCustomPlaylist) {
|
||||
// Playlist deleted
|
||||
if (!PlaylistsUtil.doesPlaylistExist(App.getContext(), playlist.id)) {
|
||||
//TODO Finish the page
|
||||
return
|
||||
}
|
||||
// Playlist renamed
|
||||
val playlistName =
|
||||
PlaylistsUtil.getNameForPlaylist(App.getContext(), playlist.id.toLong())
|
||||
if (playlistName != playlist.name) {
|
||||
playlist = PlaylistLoader.getPlaylist(App.getContext(), playlist.id)
|
||||
_playlist.postValue(playlist)
|
||||
}
|
||||
}
|
||||
loadPlaylistSongs(playlist)
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {}
|
||||
override fun onServiceDisconnected() {}
|
||||
override fun onQueueChanged() {}
|
||||
override fun onPlayingMetaChanged() {}
|
||||
override fun onPlayStateChanged() {}
|
||||
override fun onRepeatModeChanged() {}
|
||||
override fun onShuffleModeChanged() {}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
package code.name.monkey.retromusic.fragments.playlists
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
|
@ -12,7 +10,7 @@ import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
|
|||
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
|
||||
|
||||
class PlaylistsFragment :
|
||||
AbsRecyclerViewFragment<PlaylistAdapter, GridLayoutManager>() ,
|
||||
AbsRecyclerViewFragment<PlaylistAdapter, GridLayoutManager>(),
|
||||
MainActivityFragmentCallbacks {
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
|
@ -39,26 +37,14 @@ class PlaylistsFragment :
|
|||
|
||||
override fun createAdapter(): PlaylistAdapter {
|
||||
return PlaylistAdapter(
|
||||
mainActivity,
|
||||
requireActivity(),
|
||||
ArrayList(),
|
||||
R.layout.item_list,
|
||||
mainActivity
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
menu.apply {
|
||||
removeItem(R.id.action_sort_order)
|
||||
removeItem(R.id.action_grid_size)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val TAG: String = PlaylistsFragment::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): PlaylistsFragment {
|
||||
return PlaylistsFragment()
|
||||
}
|
||||
|
|
|
@ -19,13 +19,11 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.SettingsActivity
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
|
@ -78,12 +76,4 @@ class MainSettingsFragment : Fragment(), View.OnClickListener {
|
|||
diamondIcon.imageTintList = ColorStateList.valueOf(it)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
}
|
||||
|
||||
private fun inflateFragment(fragment: Fragment, @StringRes title: Int) {
|
||||
(requireActivity() as SettingsActivity).setupFragment(fragment, title)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package code.name.monkey.retromusic.fragments.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.navigation.NavController
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.findNavController
|
||||
import code.name.monkey.retromusic.fragments.MainActivityFragment
|
||||
import kotlinx.android.synthetic.main.fragment_settings.*
|
||||
|
||||
class SettingsFragment : MainActivityFragment(R.layout.fragment_settings) {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mainActivity.setSupportActionBar(toolbar)
|
||||
mainActivity.hideBottomNavigation()
|
||||
mainActivity.setBottomBarVisibility(View.GONE)
|
||||
val navController: NavController = findNavController(R.id.contentFrame)
|
||||
navController.addOnDestinationChangedListener { _, _, _ ->
|
||||
toolbar.title = navController.currentDestination?.label
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,11 @@
|
|||
package code.name.monkey.retromusic.fragments.songs
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.view.View
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||
import code.name.monkey.retromusic.fragments.ReloadType
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
|
||||
|
@ -36,26 +35,16 @@ class SongsFragment :
|
|||
get() = R.string.no_songs
|
||||
|
||||
override fun createLayoutManager(): GridLayoutManager {
|
||||
return GridLayoutManager(requireActivity(), getGridSize()).apply {
|
||||
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if (position == 0) {
|
||||
getGridSize()
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return GridLayoutManager(requireActivity(), getGridSize())
|
||||
}
|
||||
|
||||
override fun createAdapter(): SongAdapter {
|
||||
val dataSet = if (adapter == null) mutableListOf() else adapter!!.dataSet
|
||||
return ShuffleButtonSongAdapter(
|
||||
mainActivity,
|
||||
return SongAdapter(
|
||||
requireActivity(),
|
||||
dataSet,
|
||||
itemLayoutRes(),
|
||||
mainActivity
|
||||
R.layout.item_list,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -109,72 +98,4 @@ class SongsFragment :
|
|||
return SongsFragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
setUpGridSizeMenu(menu.findItem(R.id.action_grid_size).subMenu)
|
||||
}
|
||||
|
||||
private fun setUpGridSizeMenu(
|
||||
|
||||
gridSizeMenu: SubMenu
|
||||
) {
|
||||
println(getGridSize())
|
||||
when (getGridSize()) {
|
||||
1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = true
|
||||
2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true
|
||||
3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true
|
||||
4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true
|
||||
5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true
|
||||
6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true
|
||||
7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true
|
||||
8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true
|
||||
}
|
||||
val maxGridSize = maxGridSize
|
||||
if (maxGridSize < 8) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false
|
||||
}
|
||||
if (maxGridSize < 7) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false
|
||||
}
|
||||
if (maxGridSize < 6) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false
|
||||
}
|
||||
if (maxGridSize < 5) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false
|
||||
}
|
||||
if (maxGridSize < 4) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false
|
||||
}
|
||||
if (maxGridSize < 3) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (handleGridSizeMenuItem(item)) return true
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
fun handleGridSizeMenuItem(
|
||||
item: MenuItem
|
||||
): Boolean {
|
||||
var gridSize = 0
|
||||
when (item.itemId) {
|
||||
R.id.action_grid_size_1 -> gridSize = 1
|
||||
R.id.action_grid_size_2 -> gridSize = 2
|
||||
R.id.action_grid_size_3 -> gridSize = 3
|
||||
R.id.action_grid_size_4 -> gridSize = 4
|
||||
R.id.action_grid_size_5 -> gridSize = 5
|
||||
R.id.action_grid_size_6 -> gridSize = 6
|
||||
R.id.action_grid_size_7 -> gridSize = 7
|
||||
R.id.action_grid_size_8 -> gridSize = 8
|
||||
}
|
||||
if (gridSize > 0) {
|
||||
item.isChecked = true
|
||||
setAndSaveGridSize(gridSize)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue