Change music storage to shared object
Change MusicViewModel from a ViewModel to a shared object, so that it can be used everywhere in the app instead of just in fragments.
This commit is contained in:
parent
3376b57f8e
commit
96c30b3f93
15 changed files with 253 additions and 233 deletions
|
@ -14,7 +14,8 @@ import com.google.android.material.tabs.TabLayout
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||||
import org.oxycblt.auxio.library.LibraryFragment
|
import org.oxycblt.auxio.library.LibraryFragment
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.loading.LoadingViewModel
|
||||||
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.songs.SongsFragment
|
import org.oxycblt.auxio.songs.SongsFragment
|
||||||
import org.oxycblt.auxio.theme.accent
|
import org.oxycblt.auxio.theme.accent
|
||||||
import org.oxycblt.auxio.theme.getInactiveAlpha
|
import org.oxycblt.auxio.theme.getInactiveAlpha
|
||||||
|
@ -22,8 +23,8 @@ import org.oxycblt.auxio.theme.getTransparentAccent
|
||||||
import org.oxycblt.auxio.theme.toColor
|
import org.oxycblt.auxio.theme.toColor
|
||||||
|
|
||||||
class MainFragment : Fragment() {
|
class MainFragment : Fragment() {
|
||||||
private val musicModel: MusicViewModel by activityViewModels {
|
private val loadingModel: LoadingViewModel by activityViewModels {
|
||||||
MusicViewModel.Factory(requireActivity().application)
|
LoadingViewModel.Factory(requireActivity().application)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val shownFragments = listOf(0, 1)
|
private val shownFragments = listOf(0, 1)
|
||||||
|
@ -39,9 +40,9 @@ class MainFragment : Fragment() {
|
||||||
): View? {
|
): View? {
|
||||||
val binding = FragmentMainBinding.inflate(inflater)
|
val binding = FragmentMainBinding.inflate(inflater)
|
||||||
|
|
||||||
// If musicModel was cleared while the app was closed [Likely due to Auxio being suspended
|
// If the music was cleared while the app was closed [Likely due to Auxio being suspended
|
||||||
// in the background], then navigate back to LoadingFragment to reload the music.
|
// in the background], then navigate back to LoadingFragment to reload the music.
|
||||||
if (musicModel.response.value == null) {
|
if (MusicStore.getInstance().songs.isEmpty()) {
|
||||||
findNavController().navigate(MainFragmentDirections.actionReturnToLoading())
|
findNavController().navigate(MainFragmentDirections.actionReturnToLoading())
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -12,7 +12,7 @@ import androidx.navigation.fragment.navArgs
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding
|
import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding
|
||||||
import org.oxycblt.auxio.detail.adapters.DetailSongAdapter
|
import org.oxycblt.auxio.detail.adapters.DetailSongAdapter
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.theme.applyDivider
|
import org.oxycblt.auxio.theme.applyDivider
|
||||||
import org.oxycblt.auxio.theme.disable
|
import org.oxycblt.auxio.theme.disable
|
||||||
|
@ -22,7 +22,6 @@ class AlbumDetailFragment : Fragment() {
|
||||||
private val args: AlbumDetailFragmentArgs by navArgs()
|
private val args: AlbumDetailFragmentArgs by navArgs()
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
private val musicModel: MusicViewModel by activityViewModels()
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -31,22 +30,21 @@ class AlbumDetailFragment : Fragment() {
|
||||||
): View? {
|
): View? {
|
||||||
val binding = FragmentAlbumDetailBinding.inflate(inflater)
|
val binding = FragmentAlbumDetailBinding.inflate(inflater)
|
||||||
|
|
||||||
// If DetailViewModel isn't already storing the album, get it from MusicViewModel
|
// If DetailViewModel isn't already storing the album, get it from MusicStore
|
||||||
// using the ID given by the navigation arguments.
|
// using the ID given by the navigation arguments.
|
||||||
if (detailModel.currentAlbum.value == null ||
|
if (detailModel.currentAlbum.value == null ||
|
||||||
detailModel.currentAlbum.value?.id != args.albumId
|
detailModel.currentAlbum.value?.id != args.albumId
|
||||||
) {
|
) {
|
||||||
val musicModel: MusicViewModel by activityViewModels()
|
|
||||||
|
|
||||||
detailModel.updateAlbum(
|
detailModel.updateAlbum(
|
||||||
musicModel.albums.value!!.find {
|
MusicStore.getInstance().albums.find {
|
||||||
it.id == args.albumId
|
it.id == args.albumId
|
||||||
}!!
|
}!!
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val songAdapter = DetailSongAdapter {
|
val songAdapter = DetailSongAdapter {
|
||||||
playbackModel.update(it, musicModel.songs.value!!)
|
playbackModel.update(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- UI SETUP ---
|
// --- UI SETUP ---
|
||||||
|
|
|
@ -11,7 +11,7 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding
|
import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding
|
||||||
import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter
|
import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.theme.applyDivider
|
import org.oxycblt.auxio.theme.applyDivider
|
||||||
import org.oxycblt.auxio.theme.disable
|
import org.oxycblt.auxio.theme.disable
|
||||||
|
|
||||||
|
@ -30,15 +30,13 @@ class ArtistDetailFragment : Fragment() {
|
||||||
): View? {
|
): View? {
|
||||||
val binding = FragmentArtistDetailBinding.inflate(inflater)
|
val binding = FragmentArtistDetailBinding.inflate(inflater)
|
||||||
|
|
||||||
// If DetailViewModel isn't already storing the artist, get it from MusicViewModel
|
// If DetailViewModel isn't already storing the artist, get it from MusicStore
|
||||||
// using the ID given by the navigation arguments
|
// using the ID given by the navigation arguments
|
||||||
if (detailModel.currentArtist.value == null ||
|
if (detailModel.currentArtist.value == null ||
|
||||||
detailModel.currentArtist.value?.id != args.artistId
|
detailModel.currentArtist.value?.id != args.artistId
|
||||||
) {
|
) {
|
||||||
val musicModel: MusicViewModel by activityViewModels()
|
|
||||||
|
|
||||||
detailModel.updateArtist(
|
detailModel.updateArtist(
|
||||||
musicModel.artists.value!!.find {
|
MusicStore.getInstance().artists.find {
|
||||||
it.id == args.artistId
|
it.id == args.artistId
|
||||||
}!!
|
}!!
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding
|
import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding
|
||||||
import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter
|
import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.theme.applyDivider
|
import org.oxycblt.auxio.theme.applyDivider
|
||||||
import org.oxycblt.auxio.theme.disable
|
import org.oxycblt.auxio.theme.disable
|
||||||
|
|
||||||
|
@ -27,15 +27,13 @@ class GenreDetailFragment : Fragment() {
|
||||||
): View? {
|
): View? {
|
||||||
val binding = FragmentGenreDetailBinding.inflate(inflater)
|
val binding = FragmentGenreDetailBinding.inflate(inflater)
|
||||||
|
|
||||||
// If DetailViewModel isn't already storing the genre, get it from MusicViewModel
|
// If DetailViewModel isn't already storing the genre, get it from MusicStore
|
||||||
// using the ID given by the navigation arguments
|
// using the ID given by the navigation arguments
|
||||||
if (detailModel.currentGenre.value == null ||
|
if (detailModel.currentGenre.value == null ||
|
||||||
detailModel.currentGenre.value?.id != args.genreId
|
detailModel.currentGenre.value?.id != args.genreId
|
||||||
) {
|
) {
|
||||||
val musicModel: MusicViewModel by activityViewModels()
|
|
||||||
|
|
||||||
detailModel.updateGenre(
|
detailModel.updateGenre(
|
||||||
musicModel.genres.value!!.find {
|
MusicStore.getInstance().genres.find {
|
||||||
it.id == args.genreId
|
it.id == args.genreId
|
||||||
}!!
|
}!!
|
||||||
)
|
)
|
||||||
|
|
|
@ -23,10 +23,9 @@ import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.recycler.ShowMode
|
|
||||||
import org.oxycblt.auxio.theme.applyColor
|
import org.oxycblt.auxio.theme.applyColor
|
||||||
import org.oxycblt.auxio.theme.applyDivider
|
import org.oxycblt.auxio.theme.applyDivider
|
||||||
import org.oxycblt.auxio.theme.resolveAttr
|
import org.oxycblt.auxio.theme.resolveAttr
|
||||||
|
@ -34,10 +33,6 @@ import org.oxycblt.auxio.theme.resolveAttr
|
||||||
// A Fragment to show all the music in the Library.
|
// A Fragment to show all the music in the Library.
|
||||||
class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
|
|
||||||
private val musicModel: MusicViewModel by activityViewModels {
|
|
||||||
MusicViewModel.Factory(requireActivity().application)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val libraryModel: LibraryViewModel by activityViewModels()
|
private val libraryModel: LibraryViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
|
||||||
|
@ -48,6 +43,8 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
): View? {
|
): View? {
|
||||||
val binding = FragmentLibraryBinding.inflate(inflater)
|
val binding = FragmentLibraryBinding.inflate(inflater)
|
||||||
|
|
||||||
|
val musicStore = MusicStore.getInstance()
|
||||||
|
|
||||||
val libraryAdapter = LibraryAdapter(libraryModel.showMode.value!!) {
|
val libraryAdapter = LibraryAdapter(libraryModel.showMode.value!!) {
|
||||||
navToItem(it)
|
navToItem(it)
|
||||||
}
|
}
|
||||||
|
@ -137,13 +134,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
// Update the adapter with the new data
|
// Update the adapter with the new data
|
||||||
libraryAdapter.updateData(
|
libraryAdapter.updateData(
|
||||||
mode.getSortedBaseModelList(
|
mode.getSortedBaseModelList(
|
||||||
when (libraryModel.showMode.value) {
|
musicStore.getListForShowMode(libraryModel.showMode.value!!)
|
||||||
ShowMode.SHOW_GENRES -> musicModel.genres.value!!
|
|
||||||
ShowMode.SHOW_ARTISTS -> musicModel.artists.value!!
|
|
||||||
ShowMode.SHOW_ALBUMS -> musicModel.albums.value!!
|
|
||||||
|
|
||||||
else -> musicModel.artists.value!!
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -179,7 +170,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
override fun onQueryTextSubmit(query: String): Boolean = false
|
override fun onQueryTextSubmit(query: String): Boolean = false
|
||||||
|
|
||||||
override fun onQueryTextChange(query: String): Boolean {
|
override fun onQueryTextChange(query: String): Boolean {
|
||||||
libraryModel.updateSearchQuery(query, musicModel)
|
libraryModel.updateSearchQuery(query)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -190,7 +181,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||||
// If the item is a song [That was selected through search], then update the playback
|
// If the item is a song [That was selected through search], then update the playback
|
||||||
// to that song instead of doing any navigation
|
// to that song instead of doing any navigation
|
||||||
if (baseModel is Song) {
|
if (baseModel is Song) {
|
||||||
playbackModel.update(baseModel, musicModel.songs.value!!)
|
playbackModel.update(baseModel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import kotlinx.coroutines.launch
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
import org.oxycblt.auxio.music.Header
|
import org.oxycblt.auxio.music.Header
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.recycler.ShowMode
|
import org.oxycblt.auxio.recycler.ShowMode
|
||||||
import org.oxycblt.auxio.recycler.SortMode
|
import org.oxycblt.auxio.recycler.SortMode
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class LibraryViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSearchQuery(query: String, musicModel: MusicViewModel) {
|
fun updateSearchQuery(query: String) {
|
||||||
// Don't bother if the query is blank.
|
// Don't bother if the query is blank.
|
||||||
if (query == "") {
|
if (query == "") {
|
||||||
resetQuery()
|
resetQuery()
|
||||||
|
@ -52,16 +52,17 @@ class LibraryViewModel : ViewModel() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search MusicViewModel for all the items [Artists, Albums, Songs] that contain
|
// Search MusicStore for all the items [Artists, Albums, Songs] that contain
|
||||||
// the query, and update the LiveData with those items. This is done on a separate
|
// the query, and update the LiveData with those items. This is done on a separate
|
||||||
// thread as it can be a very long operation for large music libraries.
|
// thread as it can be a very long operation for large music libraries.
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
val musicStore = MusicStore.getInstance()
|
||||||
val combined = mutableListOf<BaseModel>()
|
val combined = mutableListOf<BaseModel>()
|
||||||
val children = showMode.value!!.getChildren()
|
val children = showMode.value!!.getChildren()
|
||||||
|
|
||||||
// If the Library ShowMode supports it, include artists / genres in the search.
|
// If the Library ShowMode supports it, include artists / genres in the search.
|
||||||
if (children.contains(ShowMode.SHOW_GENRES)) {
|
if (children.contains(ShowMode.SHOW_GENRES)) {
|
||||||
val genres = musicModel.genres.value!!.filter { it.name.contains(query, true) }
|
val genres = musicStore.genres.filter { it.name.contains(query, true) }
|
||||||
|
|
||||||
if (genres.isNotEmpty()) {
|
if (genres.isNotEmpty()) {
|
||||||
combined.add(Header(id = ShowMode.SHOW_GENRES.constant))
|
combined.add(Header(id = ShowMode.SHOW_GENRES.constant))
|
||||||
|
@ -70,7 +71,7 @@ class LibraryViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (children.contains(ShowMode.SHOW_ARTISTS)) {
|
if (children.contains(ShowMode.SHOW_ARTISTS)) {
|
||||||
val artists = musicModel.artists.value!!.filter { it.name.contains(query, true) }
|
val artists = musicStore.artists.filter { it.name.contains(query, true) }
|
||||||
|
|
||||||
if (artists.isNotEmpty()) {
|
if (artists.isNotEmpty()) {
|
||||||
combined.add(Header(id = ShowMode.SHOW_ARTISTS.constant))
|
combined.add(Header(id = ShowMode.SHOW_ARTISTS.constant))
|
||||||
|
@ -79,14 +80,14 @@ class LibraryViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Albums & Songs are always included.
|
// Albums & Songs are always included.
|
||||||
val albums = musicModel.albums.value!!.filter { it.name.contains(query, true) }
|
val albums = musicStore.albums.filter { it.name.contains(query, true) }
|
||||||
|
|
||||||
if (albums.isNotEmpty()) {
|
if (albums.isNotEmpty()) {
|
||||||
combined.add(Header(id = ShowMode.SHOW_ALBUMS.constant))
|
combined.add(Header(id = ShowMode.SHOW_ALBUMS.constant))
|
||||||
combined.addAll(albums)
|
combined.addAll(albums)
|
||||||
}
|
}
|
||||||
|
|
||||||
val songs = musicModel.songs.value!!.filter { it.name.contains(query, true) }
|
val songs = musicStore.songs.filter { it.name.contains(query, true) }
|
||||||
|
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
combined.add(Header(id = ShowMode.SHOW_SONGS.constant))
|
combined.add(Header(id = ShowMode.SHOW_SONGS.constant))
|
||||||
|
|
|
@ -14,13 +14,12 @@ import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentLoadingBinding
|
import org.oxycblt.auxio.databinding.FragmentLoadingBinding
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
|
||||||
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
||||||
|
|
||||||
class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
|
|
||||||
private val musicModel: MusicViewModel by activityViewModels {
|
private val loadingModel: LoadingViewModel by activityViewModels {
|
||||||
MusicViewModel.Factory(requireActivity().application)
|
LoadingViewModel.Factory(requireActivity().application)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
@ -39,18 +38,18 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
if (granted) {
|
if (granted) {
|
||||||
returnToLoading(binding)
|
returnToLoading(binding)
|
||||||
|
|
||||||
musicModel.reload()
|
loadingModel.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- UI SETUP ---
|
// --- UI SETUP ---
|
||||||
|
|
||||||
binding.lifecycleOwner = this
|
binding.lifecycleOwner = this
|
||||||
binding.musicModel = musicModel
|
binding.loadingModel = loadingModel
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
|
|
||||||
musicModel.response.observe(viewLifecycleOwner) {
|
loadingModel.response.observe(viewLifecycleOwner) {
|
||||||
if (it == MusicLoaderResponse.DONE) {
|
if (it == MusicLoaderResponse.DONE) {
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
LoadingFragmentDirections.actionToMain()
|
LoadingFragmentDirections.actionToMain()
|
||||||
|
@ -69,17 +68,17 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
musicModel.doReload.observe(viewLifecycleOwner) {
|
loadingModel.doReload.observe(viewLifecycleOwner) {
|
||||||
if (it) {
|
if (it) {
|
||||||
returnToLoading(binding)
|
returnToLoading(binding)
|
||||||
musicModel.doneWithReload()
|
loadingModel.doneWithReload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
musicModel.doGrant.observe(viewLifecycleOwner) {
|
loadingModel.doGrant.observe(viewLifecycleOwner) {
|
||||||
if (it) {
|
if (it) {
|
||||||
permLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
|
permLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
musicModel.doneWithGrant()
|
loadingModel.doneWithGrant()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +89,7 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
binding.loadingGrantButton.visibility = View.VISIBLE
|
binding.loadingGrantButton.visibility = View.VISIBLE
|
||||||
binding.loadingErrorText.text = getString(R.string.error_no_perms)
|
binding.loadingErrorText.text = getString(R.string.error_no_perms)
|
||||||
} else {
|
} else {
|
||||||
musicModel.go()
|
loadingModel.go()
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(this::class.simpleName, "Fragment created.")
|
Log.d(this::class.simpleName, "Fragment created.")
|
||||||
|
@ -109,7 +108,7 @@ class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
) == PackageManager.PERMISSION_DENIED
|
) == PackageManager.PERMISSION_DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the loading ui_indicator and show the error groups
|
// Remove the loading indicator and show the error groups
|
||||||
private fun showError(binding: FragmentLoadingBinding) {
|
private fun showError(binding: FragmentLoadingBinding) {
|
||||||
binding.loadingBar.visibility = View.GONE
|
binding.loadingBar.visibility = View.GONE
|
||||||
binding.loadingErrorIcon.visibility = View.VISIBLE
|
binding.loadingErrorIcon.visibility = View.VISIBLE
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package org.oxycblt.auxio.loading
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
|
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
||||||
|
|
||||||
|
class LoadingViewModel(private val app: Application) : ViewModel() {
|
||||||
|
// Coroutine
|
||||||
|
private val loadingJob = Job()
|
||||||
|
private val ioScope = CoroutineScope(
|
||||||
|
loadingJob + Dispatchers.IO
|
||||||
|
)
|
||||||
|
|
||||||
|
// UI control
|
||||||
|
private val mResponse = MutableLiveData<MusicLoaderResponse>()
|
||||||
|
val response: LiveData<MusicLoaderResponse> get() = mResponse
|
||||||
|
|
||||||
|
private val mRedo = MutableLiveData<Boolean>()
|
||||||
|
val doReload: LiveData<Boolean> get() = mRedo
|
||||||
|
|
||||||
|
private val mDoGrant = MutableLiveData<Boolean>()
|
||||||
|
val doGrant: LiveData<Boolean> get() = mDoGrant
|
||||||
|
|
||||||
|
private var started = false
|
||||||
|
|
||||||
|
// Start the music loading sequence.
|
||||||
|
// This should only be ran once, use reload() for all other loads.
|
||||||
|
fun go() {
|
||||||
|
if (!started) {
|
||||||
|
started = true
|
||||||
|
doLoad()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doLoad() {
|
||||||
|
ioScope.launch {
|
||||||
|
val musicStore = MusicStore.getInstance()
|
||||||
|
|
||||||
|
val response = musicStore.load(app)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
mResponse.value = response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI communication functions
|
||||||
|
// LoadingFragment uses these so that button presses can update the ViewModel.
|
||||||
|
// all doneWithX functions are to reset the value so that LoadingFragment doesn't
|
||||||
|
// repeat commands if the view is recreated.
|
||||||
|
fun reload() {
|
||||||
|
mRedo.value = true
|
||||||
|
|
||||||
|
doLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doneWithReload() {
|
||||||
|
mRedo.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun grant() {
|
||||||
|
mDoGrant.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doneWithGrant() {
|
||||||
|
mDoGrant.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
super.onCleared()
|
||||||
|
|
||||||
|
// Cancel the current loading job if the app has been stopped
|
||||||
|
loadingJob.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory(private val application: Application) : ViewModelProvider.Factory {
|
||||||
|
@Suppress("unchecked_cast")
|
||||||
|
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||||
|
if (modelClass.isAssignableFrom(LoadingViewModel::class.java)) {
|
||||||
|
return LoadingViewModel(application) as T
|
||||||
|
}
|
||||||
|
|
||||||
|
throw IllegalArgumentException("Unknown ViewModel class.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt
Normal file
100
app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package org.oxycblt.auxio.music
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.util.Log
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.music.processing.MusicLoader
|
||||||
|
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
||||||
|
import org.oxycblt.auxio.music.processing.MusicSorter
|
||||||
|
import org.oxycblt.auxio.recycler.ShowMode
|
||||||
|
|
||||||
|
// Storage for Music Data. Only use getInstance() to access this object.
|
||||||
|
class MusicStore private constructor() {
|
||||||
|
private var mGenres = listOf<Genre>()
|
||||||
|
val genres: List<Genre> get() = mGenres
|
||||||
|
|
||||||
|
private var mArtists = listOf<Artist>()
|
||||||
|
val artists: List<Artist> get() = mArtists
|
||||||
|
|
||||||
|
private var mAlbums = listOf<Album>()
|
||||||
|
val albums: List<Album> get() = mAlbums
|
||||||
|
|
||||||
|
private var mSongs = listOf<Song>()
|
||||||
|
val songs: List<Song> get() = mSongs
|
||||||
|
|
||||||
|
suspend fun load(app: Application): MusicLoaderResponse {
|
||||||
|
Log.i(this::class.simpleName, "Starting initial music load...")
|
||||||
|
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
|
|
||||||
|
// Get the placeholder strings, which are used by MusicLoader & MusicSorter for
|
||||||
|
// any music that doesn't have metadata.
|
||||||
|
val genrePlaceholder = app.getString(R.string.placeholder_genre)
|
||||||
|
val artistPlaceholder = app.getString(R.string.placeholder_artist)
|
||||||
|
val albumPlaceholder = app.getString(R.string.placeholder_album)
|
||||||
|
|
||||||
|
val loader = MusicLoader(
|
||||||
|
app.contentResolver,
|
||||||
|
|
||||||
|
genrePlaceholder,
|
||||||
|
artistPlaceholder,
|
||||||
|
albumPlaceholder
|
||||||
|
)
|
||||||
|
|
||||||
|
if (loader.response == MusicLoaderResponse.DONE) {
|
||||||
|
// If the loading succeeds, then sort the songs and update the value
|
||||||
|
val sorter = MusicSorter(
|
||||||
|
loader.genres,
|
||||||
|
loader.artists,
|
||||||
|
loader.albums,
|
||||||
|
loader.songs,
|
||||||
|
|
||||||
|
genrePlaceholder,
|
||||||
|
artistPlaceholder,
|
||||||
|
albumPlaceholder
|
||||||
|
)
|
||||||
|
|
||||||
|
mSongs = sorter.songs.toList()
|
||||||
|
mAlbums = sorter.albums.toList()
|
||||||
|
mArtists = sorter.artists.toList()
|
||||||
|
mGenres = sorter.genres.toList()
|
||||||
|
|
||||||
|
val elapsed = System.currentTimeMillis() - start
|
||||||
|
|
||||||
|
Log.i(
|
||||||
|
this::class.simpleName,
|
||||||
|
"Music load completed successfully in ${elapsed}ms."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return loader.response
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getListForShowMode(showMode: ShowMode): List<BaseModel> {
|
||||||
|
return when (showMode) {
|
||||||
|
ShowMode.SHOW_GENRES -> mGenres
|
||||||
|
ShowMode.SHOW_ARTISTS -> mArtists
|
||||||
|
ShowMode.SHOW_ALBUMS -> mAlbums
|
||||||
|
ShowMode.SHOW_SONGS -> mSongs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
private var INSTANCE: MusicStore? = null
|
||||||
|
|
||||||
|
fun getInstance(): MusicStore {
|
||||||
|
val currentInstance = INSTANCE
|
||||||
|
|
||||||
|
if (currentInstance != null) {
|
||||||
|
return currentInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
val newInstance = MusicStore()
|
||||||
|
INSTANCE = newInstance
|
||||||
|
return newInstance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,154 +0,0 @@
|
||||||
package org.oxycblt.auxio.music
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.oxycblt.auxio.R
|
|
||||||
import org.oxycblt.auxio.music.processing.MusicLoader
|
|
||||||
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
|
||||||
import org.oxycblt.auxio.music.processing.MusicSorter
|
|
||||||
|
|
||||||
// ViewModel for music storage.
|
|
||||||
// TODO: Move genre usage to songs [If there's a way to find songs without a genre]
|
|
||||||
class MusicViewModel(private val app: Application) : ViewModel() {
|
|
||||||
|
|
||||||
// Coroutine
|
|
||||||
private val loadingJob = Job()
|
|
||||||
private val ioScope = CoroutineScope(
|
|
||||||
loadingJob + Dispatchers.IO
|
|
||||||
)
|
|
||||||
|
|
||||||
// Values
|
|
||||||
private val mGenres = MutableLiveData<List<Genre>>()
|
|
||||||
val genres: LiveData<List<Genre>> get() = mGenres
|
|
||||||
|
|
||||||
private val mArtists = MutableLiveData<List<Artist>>()
|
|
||||||
val artists: LiveData<List<Artist>> get() = mArtists
|
|
||||||
|
|
||||||
private val mAlbums = MutableLiveData<List<Album>>()
|
|
||||||
val albums: LiveData<List<Album>> get() = mAlbums
|
|
||||||
|
|
||||||
private val mSongs = MutableLiveData<List<Song>>()
|
|
||||||
val songs: LiveData<List<Song>> get() = mSongs
|
|
||||||
|
|
||||||
private val mResponse = MutableLiveData<MusicLoaderResponse>()
|
|
||||||
val response: LiveData<MusicLoaderResponse> get() = mResponse
|
|
||||||
|
|
||||||
// UI control
|
|
||||||
private val mRedo = MutableLiveData<Boolean>()
|
|
||||||
val doReload: LiveData<Boolean> get() = mRedo
|
|
||||||
|
|
||||||
private val mDoGrant = MutableLiveData<Boolean>()
|
|
||||||
val doGrant: LiveData<Boolean> get() = mDoGrant
|
|
||||||
|
|
||||||
private var started = false
|
|
||||||
|
|
||||||
// Start the music loading sequence.
|
|
||||||
// This should only be ran once, use reload() for all other loads.
|
|
||||||
fun go() {
|
|
||||||
if (!started) {
|
|
||||||
started = true
|
|
||||||
doLoad()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun doLoad() {
|
|
||||||
Log.i(this::class.simpleName, "Starting initial music load...")
|
|
||||||
|
|
||||||
ioScope.launch {
|
|
||||||
val start = System.currentTimeMillis()
|
|
||||||
|
|
||||||
// Get the placeholder strings, which are used by MusicLoader & MusicSorter for
|
|
||||||
// any music that doesn't have metadata.
|
|
||||||
val genrePlaceholder = app.getString(R.string.placeholder_genre)
|
|
||||||
val artistPlaceholder = app.getString(R.string.placeholder_artist)
|
|
||||||
val albumPlaceholder = app.getString(R.string.placeholder_album)
|
|
||||||
|
|
||||||
val loader = MusicLoader(
|
|
||||||
app.contentResolver,
|
|
||||||
|
|
||||||
genrePlaceholder,
|
|
||||||
artistPlaceholder,
|
|
||||||
albumPlaceholder
|
|
||||||
)
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
if (loader.response == MusicLoaderResponse.DONE) {
|
|
||||||
// If the loading succeeds, then sort the songs and update the value
|
|
||||||
val sorter = MusicSorter(
|
|
||||||
loader.genres,
|
|
||||||
loader.artists,
|
|
||||||
loader.albums,
|
|
||||||
loader.songs,
|
|
||||||
|
|
||||||
genrePlaceholder,
|
|
||||||
artistPlaceholder,
|
|
||||||
albumPlaceholder
|
|
||||||
)
|
|
||||||
|
|
||||||
mSongs.value = sorter.songs.toList()
|
|
||||||
mAlbums.value = sorter.albums.toList()
|
|
||||||
mArtists.value = sorter.artists.toList()
|
|
||||||
mGenres.value = sorter.genres.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
mResponse.value = loader.response
|
|
||||||
|
|
||||||
val elapsed = System.currentTimeMillis() - start
|
|
||||||
|
|
||||||
Log.i(
|
|
||||||
this::class.simpleName,
|
|
||||||
"Music load completed successfully in ${elapsed}ms."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI communication functions
|
|
||||||
// LoadingFragment uses these so that button presses can update the ViewModel.
|
|
||||||
// all doneWithX functions are to reset the value so that LoadingFragment doesn't
|
|
||||||
// repeat commands if the view is recreated.
|
|
||||||
fun reload() {
|
|
||||||
mRedo.value = true
|
|
||||||
|
|
||||||
doLoad()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun doneWithReload() {
|
|
||||||
mRedo.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun grant() {
|
|
||||||
mDoGrant.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun doneWithGrant() {
|
|
||||||
mDoGrant.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCleared() {
|
|
||||||
super.onCleared()
|
|
||||||
|
|
||||||
// Cancel the current loading job if the app has been stopped
|
|
||||||
loadingJob.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Factory(private val application: Application) : ViewModelProvider.Factory {
|
|
||||||
@Suppress("unchecked_cast")
|
|
||||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
|
||||||
if (modelClass.isAssignableFrom(MusicViewModel::class.java)) {
|
|
||||||
return MusicViewModel(application) as T
|
|
||||||
}
|
|
||||||
|
|
||||||
throw IllegalArgumentException("Unknown ViewModel class.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,14 +13,9 @@ import androidx.navigation.fragment.findNavController
|
||||||
import org.oxycblt.auxio.MainFragmentDirections
|
import org.oxycblt.auxio.MainFragmentDirections
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
|
import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import kotlin.time.seconds
|
|
||||||
|
|
||||||
class CompactPlaybackFragment : Fragment() {
|
class CompactPlaybackFragment : Fragment() {
|
||||||
private val musicModel: MusicViewModel by activityViewModels {
|
|
||||||
MusicViewModel.Factory(requireActivity().application)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
@ -44,7 +39,7 @@ class CompactPlaybackFragment : Fragment() {
|
||||||
|
|
||||||
// Put a placeholder song in the binding & hide the playback fragment initially,
|
// Put a placeholder song in the binding & hide the playback fragment initially,
|
||||||
// as for some reason the attach event doesn't register anymore w/LiveData
|
// as for some reason the attach event doesn't register anymore w/LiveData
|
||||||
binding.song = musicModel.songs.value!![0]
|
binding.song = MusicStore.getInstance().songs[0]
|
||||||
binding.playbackModel = playbackModel
|
binding.playbackModel = playbackModel
|
||||||
binding.root.visibility = View.GONE
|
binding.root.visibility = View.GONE
|
||||||
|
|
||||||
|
|
|
@ -78,12 +78,9 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
binding.playbackSkipPrev.disable(requireContext())
|
binding.playbackSkipPrev.disable(requireContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(this::class.simpleName, it.toString())
|
|
||||||
|
|
||||||
if (it < playbackModel.queue.value!!.lastIndex) {
|
if (it < playbackModel.queue.value!!.lastIndex) {
|
||||||
binding.playbackSkipNext.enable(requireContext())
|
binding.playbackSkipNext.enable(requireContext())
|
||||||
} else {
|
} else {
|
||||||
Log.d(this::class.simpleName, "Fucking stupid retard.")
|
|
||||||
binding.playbackSkipNext.disable(requireContext())
|
binding.playbackSkipNext.disable(requireContext())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.toDuration
|
import org.oxycblt.auxio.music.toDuration
|
||||||
|
|
||||||
|
@ -40,11 +41,13 @@ class PlaybackViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the current song while changing the queue to All Songs.
|
// Update the current song while changing the queue to All Songs.
|
||||||
fun update(song: Song, allSongs: List<Song>) {
|
fun update(song: Song) {
|
||||||
|
val musicStore = MusicStore.getInstance()
|
||||||
|
|
||||||
updatePlayback(song)
|
updatePlayback(song)
|
||||||
|
|
||||||
mQueue.value = allSongs.toMutableList()
|
mQueue.value = musicStore.songs.toMutableList()
|
||||||
mCurrentIndex.value = allSongs.indexOf(song)
|
mCurrentIndex.value = musicStore.songs.indexOf(song)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePlayback(song: Song) {
|
private fun updatePlayback(song: Song) {
|
||||||
|
|
|
@ -8,15 +8,11 @@ import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import org.oxycblt.auxio.databinding.FragmentSongsBinding
|
import org.oxycblt.auxio.databinding.FragmentSongsBinding
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.theme.applyDivider
|
import org.oxycblt.auxio.theme.applyDivider
|
||||||
|
|
||||||
class SongsFragment : Fragment() {
|
class SongsFragment : Fragment() {
|
||||||
private val musicModel: MusicViewModel by activityViewModels {
|
|
||||||
MusicViewModel.Factory(requireActivity().application)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
@ -26,14 +22,16 @@ class SongsFragment : Fragment() {
|
||||||
): View? {
|
): View? {
|
||||||
val binding = FragmentSongsBinding.inflate(inflater)
|
val binding = FragmentSongsBinding.inflate(inflater)
|
||||||
|
|
||||||
|
val musicStore = MusicStore.getInstance()
|
||||||
|
|
||||||
// TODO: Add option to search songs if LibraryFragment isn't enabled
|
// TODO: Add option to search songs if LibraryFragment isn't enabled
|
||||||
// TODO: Maybe add fast scrolling or sorting
|
// TODO: Maybe add fast scrolling or sorting
|
||||||
|
|
||||||
// --- UI SETUP ---
|
// --- UI SETUP ---
|
||||||
|
|
||||||
binding.songRecycler.apply {
|
binding.songRecycler.apply {
|
||||||
adapter = SongAdapter(musicModel.songs.value!!) {
|
adapter = SongAdapter(musicStore.songs) {
|
||||||
playbackModel.update(it, musicModel.songs.value!!)
|
playbackModel.update(it)
|
||||||
}
|
}
|
||||||
applyDivider()
|
applyDivider()
|
||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="musicModel"
|
name="loadingModel"
|
||||||
type="org.oxycblt.auxio.music.MusicViewModel" />
|
type="org.oxycblt.auxio.loading.LoadingViewModel" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fontFamily="@font/inter_semibold"
|
android:fontFamily="@font/inter_semibold"
|
||||||
android:onClick="@{() -> musicModel.reload()}"
|
android:onClick="@{() -> loadingModel.reload()}"
|
||||||
android:text="@string/label_retry"
|
android:text="@string/label_retry"
|
||||||
android:textColor="?attr/colorPrimary"
|
android:textColor="?attr/colorPrimary"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fontFamily="@font/inter_semibold"
|
android:fontFamily="@font/inter_semibold"
|
||||||
android:onClick="@{() -> musicModel.grant()}"
|
android:onClick="@{() -> loadingModel.grant()}"
|
||||||
android:text="@string/label_grant"
|
android:text="@string/label_grant"
|
||||||
android:textColor="?attr/colorPrimary"
|
android:textColor="?attr/colorPrimary"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
|
Loading…
Reference in a new issue