Move loading functions into ViewModel

This commit is contained in:
tzugen 2021-04-21 22:31:56 +02:00
parent 1f57fb334b
commit fa4f4d6c9c
No known key found for this signature in database
GPG Key ID: 61E9C34BC10EC930
2 changed files with 404 additions and 380 deletions

View File

@ -1,8 +1,13 @@
/*
* SelectAlbumFragment.kt
* Copyright (C) 2009-2021 Ultrasonic developers
*
* Distributed under terms of the GNU GPLv3 license.
*/
package org.moire.ultrasonic.fragment package org.moire.ultrasonic.fragment
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.ContextMenu import android.view.ContextMenu
import android.view.ContextMenu.ContextMenuInfo import android.view.ContextMenu.ContextMenuInfo
import android.view.LayoutInflater import android.view.LayoutInflater
@ -18,30 +23,23 @@ import android.widget.ImageView
import android.widget.ListView import android.widget.ListView
import android.widget.TextView import android.widget.TextView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.LifecycleOwner import androidx.fragment.app.viewModels
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.navigation.Navigation import androidx.navigation.Navigation
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.android.viewmodel.ext.android.viewModel import org.koin.android.viewmodel.ext.android.viewModel
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.MusicFolder import org.moire.ultrasonic.domain.MusicFolder
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.getTitle import org.moire.ultrasonic.fragment.FragmentTitle.Companion.getTitle
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
import org.moire.ultrasonic.service.CommunicationErrorHandler
import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.service.MediaPlayerController
import org.moire.ultrasonic.service.MusicService
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
import org.moire.ultrasonic.subsonic.DownloadHandler import org.moire.ultrasonic.subsonic.DownloadHandler
import org.moire.ultrasonic.subsonic.ImageLoaderProvider import org.moire.ultrasonic.subsonic.ImageLoaderProvider
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
@ -51,7 +49,6 @@ import org.moire.ultrasonic.util.AlbumHeader
import org.moire.ultrasonic.util.CancellationToken import org.moire.ultrasonic.util.CancellationToken
import org.moire.ultrasonic.util.Constants import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
import org.moire.ultrasonic.util.FragmentBackgroundTask
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
import org.moire.ultrasonic.view.AlbumView import org.moire.ultrasonic.view.AlbumView
import org.moire.ultrasonic.view.EntryAdapter import org.moire.ultrasonic.view.EntryAdapter
@ -60,7 +57,6 @@ import org.moire.ultrasonic.view.SongView
import timber.log.Timber import timber.log.Timber
import java.security.SecureRandom import java.security.SecureRandom
import java.util.Collections import java.util.Collections
import java.util.LinkedList
import java.util.Random import java.util.Random
/** /**
@ -68,7 +64,6 @@ import java.util.Random
*/ */
class SelectAlbumFragment : Fragment() { class SelectAlbumFragment : Fragment() {
private val allSongsId = "-1"
private var refreshAlbumListView: SwipeRefreshLayout? = null private var refreshAlbumListView: SwipeRefreshLayout? = null
private var albumListView: ListView? = null private var albumListView: ListView? = null
private var header: View? = null private var header: View? = null
@ -88,18 +83,6 @@ class SelectAlbumFragment : Fragment() {
private var shareButtonVisible = false private var shareButtonVisible = false
private var playAllButton: MenuItem? = null private var playAllButton: MenuItem? = null
private var shareButton: MenuItem? = null private var shareButton: MenuItem? = null
private var showHeader = true
private var showSelectFolderHeader = false
private val random: Random = SecureRandom()
private val musicFolders: MutableLiveData<List<MusicFolder>> = MutableLiveData()
private val artists: MutableLiveData<MusicDirectory> = MutableLiveData()
private val albumList: MutableLiveData<MusicDirectory> = MutableLiveData()
private val currentDirectory: MutableLiveData<MusicDirectory> = MutableLiveData()
private val songsForGenre: MutableLiveData<MusicDirectory> = MutableLiveData()
private var currentDirectoryIsSortable = true
private val mediaPlayerController: MediaPlayerController by inject() private val mediaPlayerController: MediaPlayerController by inject()
private val videoPlayer: VideoPlayer by inject() private val videoPlayer: VideoPlayer by inject()
@ -111,6 +94,11 @@ class SelectAlbumFragment : Fragment() {
private val activeServerProvider: ActiveServerProvider by inject() private val activeServerProvider: ActiveServerProvider by inject()
private val serverSettingsModel: ServerSettingsModel by viewModel() private val serverSettingsModel: ServerSettingsModel by viewModel()
private val model: SelectAlbumModel by viewModels()
private val random: Random = SecureRandom()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Util.applyTheme(this.context) Util.applyTheme(this.context)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -157,10 +145,10 @@ class SelectAlbumFragment : Fragment() {
} }
) )
musicFolders.observe(viewLifecycleOwner, musicFolderObserver) model.musicFolders.observe(viewLifecycleOwner, musicFolderObserver)
currentDirectory.observe(viewLifecycleOwner, defaultObserver) model.currentDirectory.observe(viewLifecycleOwner, defaultObserver)
songsForGenre.observe(viewLifecycleOwner, songsForGenreObserver) model.songsForGenre.observe(viewLifecycleOwner, songsForGenreObserver)
albumList.observe(viewLifecycleOwner, albumListObserver) model.albumList.observe(viewLifecycleOwner, albumListObserver)
albumListView!!.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE) albumListView!!.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE)
albumListView!!.setOnItemClickListener( albumListView!!.setOnItemClickListener(
@ -314,10 +302,8 @@ class SelectAlbumFragment : Fragment() {
Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0 Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0
) )
triggerLoad(refresh, id, name, playlistId, playlistName, podcastChannelId, shareId, shareName, albumListType, albumListTitle, albumListSize, albumListOffset, genreName, getStarredTracks, getVideos, getRandomTracks, isAlbum, parentId) triggerLoad(refresh, id, name, playlistId, playlistName, podcastChannelId, shareId, shareName, albumListType, albumListTitle, albumListSize, albumListOffset, genreName, getStarredTracks, getVideos, getRandomTracks, isAlbum, parentId)
} }
private fun triggerLoad( private fun triggerLoad(
@ -343,33 +329,42 @@ class SelectAlbumFragment : Fragment() {
serverSettingsModel.viewModelScope.launch { serverSettingsModel.viewModelScope.launch {
refreshAlbumListView!!.isRefreshing = true refreshAlbumListView!!.isRefreshing = true
this@SelectAlbumFragment.getMusicFolders(refresh) model.getMusicFolders(refresh)
if (playlistId != null) { if (playlistId != null) {
this@SelectAlbumFragment.getPlaylist(playlistId, playlistName) setTitle(playlistName)
model.getPlaylist(playlistId, playlistName)
} else if (podcastChannelId != null) { } else if (podcastChannelId != null) {
this@SelectAlbumFragment.getPodcastEpisodes(podcastChannelId) setTitle(getString(R.string.podcasts_label))
model.getPodcastEpisodes(podcastChannelId)
} else if (shareId != null) { } else if (shareId != null) {
this@SelectAlbumFragment.getShare(shareId, shareName) setTitle(shareName)
model.getShare(shareId, shareName)
} else if (albumListType != null) { } else if (albumListType != null) {
this@SelectAlbumFragment.getAlbumList(albumListType, albumListTitle, albumListSize, albumListOffset) setTitle(this@SelectAlbumFragment, albumListTitle)
model.getAlbumList(albumListType, albumListSize, albumListOffset)
} else if (genreName != null) { } else if (genreName != null) {
this@SelectAlbumFragment.getSongsForGenre(genreName, albumListSize, albumListOffset) setTitle(genreName)
model.getSongsForGenre(genreName, albumListSize, albumListOffset)
} else if (getStarredTracks != 0) { } else if (getStarredTracks != 0) {
this@SelectAlbumFragment.getStarred() setTitle(getString(R.string.main_songs_starred))
model.getStarred()
} else if (getVideos != 0) { } else if (getVideos != 0) {
this@SelectAlbumFragment.getVideos(refresh) setTitle(this@SelectAlbumFragment, R.string.main_videos)
model.getVideos(refresh)
} else if (getRandomTracks != 0) { } else if (getRandomTracks != 0) {
this@SelectAlbumFragment.getRandom(albumListSize) setTitle(this@SelectAlbumFragment, R.string.main_songs_random)
model.getRandom(albumListSize)
} else { } else {
setTitle(name)
if (!isOffline(activity) && Util.getShouldUseId3Tags(activity)) { if (!isOffline(activity) && Util.getShouldUseId3Tags(activity)) {
if (isAlbum) { if (isAlbum) {
this@SelectAlbumFragment.getAlbum(refresh, id, name, parentId) model.getAlbum(refresh, id, name, parentId)
} else { } else {
this@SelectAlbumFragment.getArtist(refresh, id, name) model.getArtist(refresh, id, name)
} }
} else { } else {
this@SelectAlbumFragment.getMusicDirectory(refresh, id, name, parentId) model.getMusicDirectory(refresh, id, name, parentId)
} }
} }
@ -377,6 +372,10 @@ class SelectAlbumFragment : Fragment() {
} }
} }
private fun setTitle(name: String?) {
setTitle(this@SelectAlbumFragment, name)
}
override fun onCreateContextMenu(menu: ContextMenu, view: View, menuInfo: ContextMenuInfo?) { override fun onCreateContextMenu(menu: ContextMenu, view: View, menuInfo: ContextMenuInfo?) {
super.onCreateContextMenu(menu, view, menuInfo) super.onCreateContextMenu(menu, view, menuInfo)
@ -536,334 +535,6 @@ class SelectAlbumFragment : Fragment() {
} }
private suspend fun getMusicFolders(refresh: Boolean) {
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val musicService = getMusicService(requireContext())
try {
musicFolders.postValue(musicService.getMusicFolders(refresh, context))
} catch (exception: Exception) {
Handler(Looper.getMainLooper()).post {
CommunicationErrorHandler.handleError(exception, requireContext())
}
}
}
}
}
private suspend fun getMusicDirectory(refresh: Boolean, id: String?, name: String?, parentId: String?) {
setTitle(this, name)
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
fun getSongsRecursively(
parent: MusicDirectory,
songs: MutableList<MusicDirectory.Entry>
) {
for (song in parent.getChildren(false, true)) {
if (!song.isVideo && !song.isDirectory) {
songs.add(song)
}
}
for ((id1, _, _, title) in parent.getChildren(true, false)) {
var root: MusicDirectory
if (allSongsId != id1) {
root = service.getMusicDirectory(id1, title, false, context)
getSongsRecursively(root, songs)
}
}
}
var root = MusicDirectory()
if (allSongsId == id) {
val musicDirectory = service.getMusicDirectory(parentId, name, refresh, context)
val songs: MutableList<MusicDirectory.Entry> = LinkedList()
getSongsRecursively(musicDirectory, songs)
for (song in songs) {
if (!song.isDirectory) {
root.addChild(song)
}
}
} else {
val musicDirectory = service.getMusicDirectory(id, name, refresh, context)
if (Util.getShouldShowAllSongsByArtist(context) &&
musicDirectory.findChild(allSongsId) == null &&
musicDirectory.getChildren(true, false).size ==
musicDirectory.getChildren(true, true).size
) {
val allSongs = MusicDirectory.Entry()
allSongs.isDirectory = true
allSongs.artist = name
allSongs.parent = id
allSongs.id = allSongsId
allSongs.title = String.format(
resources.getString(R.string.select_album_all_songs), name
)
root.addChild(allSongs)
root.addAll(musicDirectory.getChildren())
} else {
root = musicDirectory
}
}
currentDirectory.postValue(root)
}
}
}
private suspend fun getArtist(refresh: Boolean, id: String?, name: String?) {
setTitle(this, name)
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
var root = MusicDirectory()
val musicDirectory = service.getArtist(id, name, refresh, context)
if (Util.getShouldShowAllSongsByArtist(context) &&
musicDirectory.findChild(allSongsId) == null &&
musicDirectory.getChildren(true, false).size ==
musicDirectory.getChildren(true, true).size
) {
val allSongs = MusicDirectory.Entry()
allSongs.isDirectory = true
allSongs.artist = name
allSongs.parent = id
allSongs.id = allSongsId
allSongs.title = String.format(
resources.getString(R.string.select_album_all_songs), name
)
root.addFirst(allSongs)
root.addAll(musicDirectory.getChildren())
} else {
root = musicDirectory
}
currentDirectory.postValue(root)
}
}
}
private suspend fun getAlbum(refresh: Boolean, id: String?, name: String?, parentId: String?) {
setTitle(this, name)
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
val musicDirectory: MusicDirectory
musicDirectory = if (allSongsId == id) {
val root = MusicDirectory()
val songs: MutableCollection<MusicDirectory.Entry> = LinkedList()
val artist = service.getArtist(parentId, "", false, context)
for ((id1) in artist.getChildren()) {
if (allSongsId != id1) {
val albumDirectory = service.getAlbum(id1, "", false, context)
for (song in albumDirectory.getChildren()) {
if (!song.isVideo) {
songs.add(song)
}
}
}
}
for (song in songs) {
if (!song.isDirectory) {
root.addChild(song)
}
}
root
} else {
service.getAlbum(id, name, refresh, context)
}
currentDirectory.postValue(musicDirectory);
}
}
}
private suspend fun getSongsForGenre(genre: String, count: Int, offset: Int) {
setTitle(this, genre)
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
val musicDirectory: MusicDirectory
musicDirectory = service.getSongsByGenre(genre, count, offset, context)
songsForGenre.postValue(musicDirectory)
}
}
}
private suspend fun getStarred() {
setTitle(this, R.string.main_songs_starred)
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
val musicDirectory: MusicDirectory
val context = requireContext()
if (Util.getShouldUseId3Tags(context)) {
musicDirectory = Util.getSongsFromSearchResult(service.getStarred2(context))
} else {
musicDirectory = Util.getSongsFromSearchResult(service.getStarred(context))
}
currentDirectory.postValue(musicDirectory)
}
}
}
private suspend fun getVideos(refresh: Boolean) {
showHeader = false
setTitle(this, R.string.main_videos)
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
currentDirectory.postValue(service.getVideos(refresh, context))
}
}
}
private suspend fun getRandom(size: Int) {
setTitle(this, R.string.main_songs_random)
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
val musicDirectory = service.getRandomSongs(size, context)
currentDirectoryIsSortable = false
currentDirectory.postValue(musicDirectory)
}
}
}
private suspend fun getPlaylist(playlistId: String, playlistName: String?) {
setTitle(this, playlistName)
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
val musicDirectory: MusicDirectory
musicDirectory = service.getPlaylist(playlistId, playlistName, context)
currentDirectory.postValue(musicDirectory)
}
}
}
private suspend fun getPodcastEpisodes(podcastChannelId: String) {
setTitle(this, R.string.podcasts_label)
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
val musicDirectory: MusicDirectory
musicDirectory = service.getPodcastEpisodes(podcastChannelId, context)
currentDirectory.postValue(musicDirectory)
}
}
}
private suspend fun getShare(shareId: String, shareName: CharSequence?) {
setTitle(this, shareName)
// setActionBarSubtitle(shareName);
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
val musicDirectory = MusicDirectory()
val shares = service.getShares(true, context)
for (share in shares) {
if (share.id == shareId) {
for (entry in share.getEntries()) {
musicDirectory.addChild(entry)
}
break
}
}
currentDirectory.postValue(musicDirectory)
}
}
}
private suspend fun getAlbumList(albumListType: String, albumListTitle: Int, size: Int, offset: Int) {
showHeader = false
showSelectFolderHeader = !isOffline(context) && !Util.getShouldUseId3Tags(context) &&
(
(albumListType == AlbumListType.SORTED_BY_NAME.toString()) ||
(albumListType == AlbumListType.SORTED_BY_ARTIST.toString())
)
setTitle(this, albumListTitle)
// setActionBarSubtitle(albumListTitle);
fun sortableCollection(): Boolean {
return albumListType != "newest" && albumListType != "random" &&
albumListType != "highest" && albumListType != "recent" &&
albumListType != "frequent"
}
withContext(Dispatchers.IO) {
if (!isOffline(context)) {
val service = getMusicService(requireContext())
val musicDirectory: MusicDirectory
val musicFolderId = if (showSelectFolderHeader) {
this@SelectAlbumFragment.activeServerProvider.getActiveServer().musicFolderId
} else {
null
}
if (Util.getShouldUseId3Tags(context)) {
musicDirectory = service.getAlbumList2(albumListType, size, offset, musicFolderId, context)
} else {
musicDirectory = service.getAlbumList(albumListType, size, offset, musicFolderId, context)
}
currentDirectoryIsSortable = sortableCollection()
albumList.postValue(musicDirectory)
}
}
}
private fun selectAllOrNone() { private fun selectAllOrNone() {
var someUnselected = false var someUnselected = false
@ -1085,8 +756,7 @@ class SelectAlbumFragment : Fragment() {
private fun updateInterfaceWithEntries(musicDirectory: MusicDirectory) { private fun updateInterfaceWithEntries(musicDirectory: MusicDirectory) {
val entries = musicDirectory.getChildren() val entries = musicDirectory.getChildren()
// FIXME if (model.currentDirectoryIsSortable && Util.getShouldSortByDisc(context)) {
if (sortableCollection() && Util.getShouldSortByDisc(context)) {
Collections.sort(entries, EntryByDiscAndTrackComparator()) Collections.sort(entries, EntryByDiscAndTrackComparator())
} }
@ -1105,7 +775,7 @@ class SelectAlbumFragment : Fragment() {
val listSize = requireArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0) val listSize = requireArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0)
if (songCount > 0) { if (songCount > 0) {
if (showHeader) { if (model.showHeader) {
val intentAlbumName = requireArguments().getString(Constants.INTENT_EXTRA_NAME_NAME) val intentAlbumName = requireArguments().getString(Constants.INTENT_EXTRA_NAME_NAME)
val directoryName = musicDirectory.name val directoryName = musicDirectory.name
val header = createHeader( val header = createHeader(
@ -1146,7 +816,7 @@ class SelectAlbumFragment : Fragment() {
} }
} }
} else { } else {
if (showSelectFolderHeader) { if (model.showSelectFolderHeader) {
if (albumListView!!.headerViewsCount == 0) { if (albumListView!!.headerViewsCount == 0) {
albumListView!!.addHeaderView(selectFolderHeader!!.itemView, null, false) albumListView!!.addHeaderView(selectFolderHeader!!.itemView, null, false)
} }
@ -1202,7 +872,7 @@ class SelectAlbumFragment : Fragment() {
) )
} }
currentDirectoryIsSortable = true model.currentDirectoryIsSortable = true
} }
protected fun createHeader( protected fun createHeader(
@ -1274,10 +944,6 @@ class SelectAlbumFragment : Fragment() {
return header return header
} }
private fun sortableCollection(): Boolean {
return currentDirectoryIsSortable
}
private fun getSelectedSongs(albumListView: ListView?): List<MusicDirectory.Entry?> { private fun getSelectedSongs(albumListView: ListView?): List<MusicDirectory.Entry?> {
val songs: MutableList<MusicDirectory.Entry?> = ArrayList(10) val songs: MutableList<MusicDirectory.Entry?> = ArrayList(10)

View File

@ -0,0 +1,358 @@
package org.moire.ultrasonic.fragment
import android.app.Application
import android.os.Handler
import android.os.Looper
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import java.util.LinkedList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinApiExtension
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.moire.ultrasonic.R
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.domain.MusicFolder
import org.moire.ultrasonic.service.CommunicationErrorHandler
import org.moire.ultrasonic.service.MusicServiceFactory
import org.moire.ultrasonic.util.Util
// TODO: Break up this class into smaller more specific classes, extending a base class if necessary
@KoinApiExtension
class SelectAlbumModel(application: Application) : AndroidViewModel(application), KoinComponent {
private val context = getApplication<Application>().applicationContext
private val activeServerProvider: ActiveServerProvider by inject()
private val allSongsId = "-1"
val musicFolders: MutableLiveData<List<MusicFolder>> = MutableLiveData()
val albumList: MutableLiveData<MusicDirectory> = MutableLiveData()
val currentDirectory: MutableLiveData<MusicDirectory> = MutableLiveData()
val songsForGenre: MutableLiveData<MusicDirectory> = MutableLiveData()
var currentDirectoryIsSortable = true
var showHeader = true
var showSelectFolderHeader = false
suspend fun getMusicFolders(refresh: Boolean) {
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val musicService = MusicServiceFactory.getMusicService(context)
try {
musicFolders.postValue(musicService.getMusicFolders(refresh, context))
} catch (exception: Exception) {
Handler(Looper.getMainLooper()).post {
CommunicationErrorHandler.handleError(exception, context)
}
}
}
}
}
suspend fun getMusicDirectory(
refresh: Boolean,
id: String?,
name: String?,
parentId: String?
) {
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
var root = MusicDirectory()
if (allSongsId == id) {
val musicDirectory = service.getMusicDirectory(
parentId, name, refresh, context
)
val songs: MutableList<MusicDirectory.Entry> = LinkedList()
getSongsRecursively(musicDirectory, songs)
for (song in songs) {
if (!song.isDirectory) {
root.addChild(song)
}
}
} else {
val musicDirectory = service.getMusicDirectory(id, name, refresh, context)
if (Util.getShouldShowAllSongsByArtist(context) &&
musicDirectory.findChild(allSongsId) == null &&
hasOnlyFolders(musicDirectory)
) {
val allSongs = MusicDirectory.Entry()
allSongs.isDirectory = true
allSongs.artist = name
allSongs.parent = id
allSongs.id = allSongsId
allSongs.title = String.format(
context.resources.getString(R.string.select_album_all_songs), name
)
root.addChild(allSongs)
root.addAll(musicDirectory.getChildren())
} else {
root = musicDirectory
}
}
currentDirectory.postValue(root)
}
}
}
// Given a Music directory "songs" it recursively adds all children to "songs"
private fun getSongsRecursively(
parent: MusicDirectory,
songs: MutableList<MusicDirectory.Entry>
) {
val service = MusicServiceFactory.getMusicService(context)
for (song in parent.getChildren(includeDirs = false, includeFiles = true)) {
if (!song.isVideo && !song.isDirectory) {
songs.add(song)
}
}
for ((id1, _, _, title) in parent.getChildren(true, includeFiles = false)) {
var root: MusicDirectory
if (allSongsId != id1) {
root = service.getMusicDirectory(id1, title, false, context)
getSongsRecursively(root, songs)
}
}
}
suspend fun getArtist(refresh: Boolean, id: String?, name: String?) {
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
var root = MusicDirectory()
val musicDirectory = service.getArtist(id, name, refresh, context)
if (Util.getShouldShowAllSongsByArtist(context) &&
musicDirectory.findChild(allSongsId) == null &&
hasOnlyFolders(musicDirectory)
) {
val allSongs = MusicDirectory.Entry()
allSongs.isDirectory = true
allSongs.artist = name
allSongs.parent = id
allSongs.id = allSongsId
allSongs.title = String.format(
context.resources.getString(R.string.select_album_all_songs), name
)
root.addFirst(allSongs)
root.addAll(musicDirectory.getChildren())
} else {
root = musicDirectory
}
currentDirectory.postValue(root)
}
}
}
suspend fun getAlbum(refresh: Boolean, id: String?, name: String?, parentId: String?) {
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
val musicDirectory: MusicDirectory
musicDirectory = if (allSongsId == id) {
val root = MusicDirectory()
val songs: MutableCollection<MusicDirectory.Entry> = LinkedList()
val artist = service.getArtist(parentId, "", false, context)
for ((id1) in artist.getChildren()) {
if (allSongsId != id1) {
val albumDirectory = service.getAlbum(
id1, "", false, context
)
for (song in albumDirectory.getChildren()) {
if (!song.isVideo) {
songs.add(song)
}
}
}
}
for (song in songs) {
if (!song.isDirectory) {
root.addChild(song)
}
}
root
} else {
service.getAlbum(id, name, refresh, context)
}
currentDirectory.postValue(musicDirectory)
}
}
}
suspend fun getSongsForGenre(genre: String, count: Int, offset: Int) {
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
val musicDirectory = service.getSongsByGenre(genre, count, offset, context)
songsForGenre.postValue(musicDirectory)
}
}
}
suspend fun getStarred() {
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
val musicDirectory: MusicDirectory
val context = context
if (Util.getShouldUseId3Tags(context)) {
musicDirectory = Util.getSongsFromSearchResult(service.getStarred2(context))
} else {
musicDirectory = Util.getSongsFromSearchResult(service.getStarred(context))
}
currentDirectory.postValue(musicDirectory)
}
}
}
suspend fun getVideos(refresh: Boolean) {
showHeader = false
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
currentDirectory.postValue(service.getVideos(refresh, context))
}
}
}
suspend fun getRandom(size: Int) {
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
val musicDirectory = service.getRandomSongs(size, context)
currentDirectoryIsSortable = false
currentDirectory.postValue(musicDirectory)
}
}
}
suspend fun getPlaylist(playlistId: String, playlistName: String?) {
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
val musicDirectory = service.getPlaylist(playlistId, playlistName, context)
currentDirectory.postValue(musicDirectory)
}
}
}
suspend fun getPodcastEpisodes(podcastChannelId: String) {
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
val musicDirectory = service.getPodcastEpisodes(podcastChannelId, context)
currentDirectory.postValue(musicDirectory)
}
}
}
suspend fun getShare(shareId: String, shareName: CharSequence?) {
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
val musicDirectory = MusicDirectory()
val shares = service.getShares(true, context)
for (share in shares) {
if (share.id == shareId) {
for (entry in share.getEntries()) {
musicDirectory.addChild(entry)
}
break
}
}
currentDirectory.postValue(musicDirectory)
}
}
}
suspend fun getAlbumList(albumListType: String, size: Int, offset: Int) {
showHeader = false
showSelectFolderHeader = !ActiveServerProvider.isOffline(context) &&
!Util.getShouldUseId3Tags(context) && (
(albumListType == AlbumListType.SORTED_BY_NAME.toString()) ||
(albumListType == AlbumListType.SORTED_BY_ARTIST.toString())
)
withContext(Dispatchers.IO) {
if (!ActiveServerProvider.isOffline(context)) {
val service = MusicServiceFactory.getMusicService(context)
val musicDirectory: MusicDirectory
val musicFolderId = if (showSelectFolderHeader) {
activeServerProvider.getActiveServer().musicFolderId
} else {
null
}
if (Util.getShouldUseId3Tags(context)) {
musicDirectory = service.getAlbumList2(
albumListType, size,
offset, musicFolderId, context
)
} else {
musicDirectory = service.getAlbumList(
albumListType, size,
offset, musicFolderId, context
)
}
currentDirectoryIsSortable = sortableCollection(albumListType)
albumList.postValue(musicDirectory)
}
}
}
private fun sortableCollection(albumListType: String): Boolean {
return albumListType != "newest" && albumListType != "random" &&
albumListType != "highest" && albumListType != "recent" &&
albumListType != "frequent"
}
// Returns true if the directory contains only folders
private fun hasOnlyFolders(musicDirectory: MusicDirectory) =
musicDirectory.getChildren(includeDirs = true, includeFiles = false).size ==
musicDirectory.getChildren(includeDirs = true, includeFiles = true).size
}