mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-06-06 10:33:05 +03:00
Merge branch 'RefactorContextActions' into 'develop'
Use Coroutines for triggering the download or playback of music through the context menus See merge request ultrasonic/ultrasonic!998
This commit is contained in:
commit
fd8afe0231
@ -1,6 +1,6 @@
|
|||||||
[versions]
|
[versions]
|
||||||
# You need to run ./gradlew wrapper after updating the version
|
# You need to run ./gradlew wrapper after updating the version
|
||||||
gradle = "7.6"
|
gradle = "8.1.1"
|
||||||
|
|
||||||
navigation = "2.5.3"
|
navigation = "2.5.3"
|
||||||
gradlePlugin = "8.0.1"
|
gradlePlugin = "8.0.1"
|
||||||
|
@ -21,6 +21,7 @@ import org.moire.ultrasonic.domain.GenericEntry
|
|||||||
import org.moire.ultrasonic.domain.Identifiable
|
import org.moire.ultrasonic.domain.Identifiable
|
||||||
import org.moire.ultrasonic.service.RxBus
|
import org.moire.ultrasonic.service.RxBus
|
||||||
import org.moire.ultrasonic.service.plusAssign
|
import org.moire.ultrasonic.service.plusAssign
|
||||||
|
import org.moire.ultrasonic.subsonic.DownloadAction
|
||||||
import org.moire.ultrasonic.subsonic.DownloadHandler
|
import org.moire.ultrasonic.subsonic.DownloadHandler
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
|
|
||||||
@ -129,81 +130,54 @@ abstract class EntryListFragment<T : GenericEntry> : MultiListFragment<T>() {
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
R.id.menu_play_now ->
|
R.id.menu_play_now ->
|
||||||
downloadHandler.downloadRecursively(
|
downloadHandler.fetchTracksAndAddToController(
|
||||||
fragment,
|
fragment,
|
||||||
item.id,
|
item.id,
|
||||||
save = false,
|
|
||||||
append = false,
|
append = false,
|
||||||
autoPlay = true,
|
autoPlay = true,
|
||||||
shuffle = false,
|
shuffle = false,
|
||||||
background = false,
|
|
||||||
playNext = false,
|
playNext = false,
|
||||||
unpin = false,
|
|
||||||
isArtist = isArtist
|
isArtist = isArtist
|
||||||
)
|
)
|
||||||
R.id.menu_play_next ->
|
R.id.menu_play_next ->
|
||||||
downloadHandler.downloadRecursively(
|
downloadHandler.fetchTracksAndAddToController(
|
||||||
fragment,
|
fragment,
|
||||||
item.id,
|
item.id,
|
||||||
save = false,
|
|
||||||
append = false,
|
append = false,
|
||||||
autoPlay = true,
|
autoPlay = true,
|
||||||
shuffle = true,
|
shuffle = true,
|
||||||
background = false,
|
|
||||||
playNext = true,
|
playNext = true,
|
||||||
unpin = false,
|
|
||||||
isArtist = isArtist
|
isArtist = isArtist
|
||||||
)
|
)
|
||||||
R.id.menu_play_last ->
|
R.id.menu_play_last ->
|
||||||
downloadHandler.downloadRecursively(
|
downloadHandler.fetchTracksAndAddToController(
|
||||||
fragment,
|
fragment,
|
||||||
item.id,
|
item.id,
|
||||||
save = false,
|
|
||||||
append = true,
|
append = true,
|
||||||
autoPlay = false,
|
autoPlay = false,
|
||||||
shuffle = false,
|
shuffle = false,
|
||||||
background = false,
|
|
||||||
playNext = false,
|
playNext = false,
|
||||||
unpin = false,
|
|
||||||
isArtist = isArtist
|
isArtist = isArtist
|
||||||
)
|
)
|
||||||
R.id.menu_pin ->
|
R.id.menu_pin ->
|
||||||
downloadHandler.downloadRecursively(
|
downloadHandler.justDownload(
|
||||||
|
action = DownloadAction.PIN,
|
||||||
fragment,
|
fragment,
|
||||||
item.id,
|
item.id,
|
||||||
save = true,
|
|
||||||
append = true,
|
|
||||||
autoPlay = false,
|
|
||||||
shuffle = false,
|
|
||||||
background = false,
|
|
||||||
playNext = false,
|
|
||||||
unpin = false,
|
|
||||||
isArtist = isArtist
|
isArtist = isArtist
|
||||||
)
|
)
|
||||||
R.id.menu_unpin ->
|
R.id.menu_unpin ->
|
||||||
downloadHandler.downloadRecursively(
|
downloadHandler.justDownload(
|
||||||
|
action = DownloadAction.UNPIN,
|
||||||
fragment,
|
fragment,
|
||||||
item.id,
|
item.id,
|
||||||
save = false,
|
|
||||||
append = false,
|
|
||||||
autoPlay = false,
|
|
||||||
shuffle = false,
|
|
||||||
background = false,
|
|
||||||
playNext = false,
|
|
||||||
unpin = true,
|
|
||||||
isArtist = isArtist
|
isArtist = isArtist
|
||||||
)
|
)
|
||||||
R.id.menu_download ->
|
R.id.menu_download ->
|
||||||
downloadHandler.downloadRecursively(
|
downloadHandler.justDownload(
|
||||||
|
action = DownloadAction.DOWNLOAD,
|
||||||
fragment,
|
fragment,
|
||||||
item.id,
|
item.id,
|
||||||
save = false,
|
|
||||||
append = false,
|
|
||||||
autoPlay = false,
|
|
||||||
shuffle = false,
|
|
||||||
background = true,
|
|
||||||
playNext = false,
|
|
||||||
unpin = false,
|
|
||||||
isArtist = isArtist
|
isArtist = isArtist
|
||||||
)
|
)
|
||||||
else -> return false
|
else -> return false
|
||||||
|
@ -309,7 +309,6 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
|||||||
}
|
}
|
||||||
mediaPlayerController.addToPlaylist(
|
mediaPlayerController.addToPlaylist(
|
||||||
listOf(song),
|
listOf(song),
|
||||||
cachePermanently = false,
|
|
||||||
autoPlay = false,
|
autoPlay = false,
|
||||||
shuffle = false,
|
shuffle = false,
|
||||||
insertionMode = MediaPlayerController.InsertionMode.APPEND
|
insertionMode = MediaPlayerController.InsertionMode.APPEND
|
||||||
@ -367,40 +366,37 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
|||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
R.id.song_menu_play_now -> {
|
R.id.song_menu_play_now -> {
|
||||||
songs.add(item)
|
songs.add(item)
|
||||||
downloadHandler.download(
|
downloadHandler.addTracksToMediaController(
|
||||||
fragment = this,
|
|
||||||
append = false,
|
|
||||||
save = false,
|
|
||||||
autoPlay = true,
|
|
||||||
playNext = false,
|
|
||||||
shuffle = false,
|
|
||||||
songs = songs,
|
songs = songs,
|
||||||
|
append = false,
|
||||||
|
playNext = false,
|
||||||
|
autoPlay = true,
|
||||||
|
shuffle = false,
|
||||||
|
fragment = this,
|
||||||
playlistName = null
|
playlistName = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.song_menu_play_next -> {
|
R.id.song_menu_play_next -> {
|
||||||
songs.add(item)
|
songs.add(item)
|
||||||
downloadHandler.download(
|
downloadHandler.addTracksToMediaController(
|
||||||
fragment = this,
|
|
||||||
append = true,
|
|
||||||
save = false,
|
|
||||||
autoPlay = false,
|
|
||||||
playNext = true,
|
|
||||||
shuffle = false,
|
|
||||||
songs = songs,
|
songs = songs,
|
||||||
|
append = true,
|
||||||
|
playNext = true,
|
||||||
|
autoPlay = false,
|
||||||
|
shuffle = false,
|
||||||
|
fragment = this,
|
||||||
playlistName = null
|
playlistName = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.song_menu_play_last -> {
|
R.id.song_menu_play_last -> {
|
||||||
songs.add(item)
|
songs.add(item)
|
||||||
downloadHandler.download(
|
downloadHandler.addTracksToMediaController(
|
||||||
fragment = this,
|
|
||||||
append = true,
|
|
||||||
save = false,
|
|
||||||
autoPlay = false,
|
|
||||||
playNext = false,
|
|
||||||
shuffle = false,
|
|
||||||
songs = songs,
|
songs = songs,
|
||||||
|
append = true,
|
||||||
|
playNext = false,
|
||||||
|
autoPlay = false,
|
||||||
|
shuffle = false,
|
||||||
|
fragment = this,
|
||||||
playlistName = null
|
playlistName = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -40,11 +40,10 @@ import org.moire.ultrasonic.domain.MusicDirectory
|
|||||||
import org.moire.ultrasonic.domain.Track
|
import org.moire.ultrasonic.domain.Track
|
||||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||||
import org.moire.ultrasonic.model.TrackCollectionModel
|
import org.moire.ultrasonic.model.TrackCollectionModel
|
||||||
import org.moire.ultrasonic.service.DownloadService
|
|
||||||
import org.moire.ultrasonic.service.MediaPlayerController
|
import org.moire.ultrasonic.service.MediaPlayerController
|
||||||
import org.moire.ultrasonic.service.RxBus
|
import org.moire.ultrasonic.service.RxBus
|
||||||
import org.moire.ultrasonic.service.plusAssign
|
import org.moire.ultrasonic.service.plusAssign
|
||||||
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
|
import org.moire.ultrasonic.subsonic.DownloadAction
|
||||||
import org.moire.ultrasonic.subsonic.ShareHandler
|
import org.moire.ultrasonic.subsonic.ShareHandler
|
||||||
import org.moire.ultrasonic.subsonic.VideoPlayer
|
import org.moire.ultrasonic.subsonic.VideoPlayer
|
||||||
import org.moire.ultrasonic.util.CancellationToken
|
import org.moire.ultrasonic.util.CancellationToken
|
||||||
@ -84,7 +83,6 @@ open class TrackCollectionFragment(
|
|||||||
private var shareButton: MenuItem? = null
|
private var shareButton: MenuItem? = null
|
||||||
|
|
||||||
internal val mediaPlayerController: MediaPlayerController by inject()
|
internal val mediaPlayerController: MediaPlayerController by inject()
|
||||||
private val networkAndStorageChecker: NetworkAndStorageChecker by inject()
|
|
||||||
private val shareHandler: ShareHandler by inject()
|
private val shareHandler: ShareHandler by inject()
|
||||||
internal var cancellationToken: CancellationToken? = null
|
internal var cancellationToken: CancellationToken? = null
|
||||||
|
|
||||||
@ -211,11 +209,14 @@ open class TrackCollectionFragment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
playNextButton?.setOnClickListener {
|
playNextButton?.setOnClickListener {
|
||||||
downloadHandler.download(
|
downloadHandler.addTracksToMediaController(
|
||||||
this@TrackCollectionFragment, append = true,
|
|
||||||
save = false, autoPlay = false, playNext = true, shuffle = false,
|
|
||||||
songs = getSelectedSongs(),
|
songs = getSelectedSongs(),
|
||||||
playlistName = navArgs.playlistName
|
append = true,
|
||||||
|
playNext = true,
|
||||||
|
autoPlay = false,
|
||||||
|
shuffle = false,
|
||||||
|
playlistName = navArgs.playlistName,
|
||||||
|
this@TrackCollectionFragment
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,9 +305,14 @@ open class TrackCollectionFragment(
|
|||||||
selectedSongs: List<Track> = getSelectedSongs()
|
selectedSongs: List<Track> = getSelectedSongs()
|
||||||
) {
|
) {
|
||||||
if (selectedSongs.isNotEmpty()) {
|
if (selectedSongs.isNotEmpty()) {
|
||||||
downloadHandler.download(
|
downloadHandler.addTracksToMediaController(
|
||||||
this, append, false, !append, playNext = false,
|
songs = selectedSongs,
|
||||||
shuffle = false, songs = selectedSongs, null
|
append = append,
|
||||||
|
playNext = false,
|
||||||
|
autoPlay = !append,
|
||||||
|
shuffle = false,
|
||||||
|
playlistName = null,
|
||||||
|
fragment = this
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
playAll(false, append)
|
playAll(false, append)
|
||||||
@ -337,31 +343,29 @@ open class TrackCollectionFragment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val isArtist = navArgs.isArtist
|
val isArtist = navArgs.isArtist
|
||||||
val id = navArgs.id
|
|
||||||
|
// Need a valid id to download stuff
|
||||||
|
val id = navArgs.id ?: return
|
||||||
|
|
||||||
if (hasSubFolders) {
|
if (hasSubFolders) {
|
||||||
downloadHandler.downloadRecursively(
|
downloadHandler.fetchTracksAndAddToController(
|
||||||
fragment = this,
|
fragment = this,
|
||||||
id = id,
|
id = id,
|
||||||
save = false,
|
|
||||||
append = append,
|
append = append,
|
||||||
autoPlay = !append,
|
autoPlay = !append,
|
||||||
shuffle = shuffle,
|
shuffle = shuffle,
|
||||||
background = false,
|
|
||||||
playNext = false,
|
playNext = false,
|
||||||
unpin = false,
|
|
||||||
isArtist = isArtist
|
isArtist = isArtist
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
downloadHandler.download(
|
downloadHandler.addTracksToMediaController(
|
||||||
fragment = this,
|
|
||||||
append = append,
|
|
||||||
save = false,
|
|
||||||
autoPlay = !append,
|
|
||||||
playNext = false,
|
|
||||||
shuffle = shuffle,
|
|
||||||
songs = getAllSongs(),
|
songs = getAllSongs(),
|
||||||
playlistName = navArgs.playlistName
|
append = append,
|
||||||
|
playNext = false,
|
||||||
|
autoPlay = !append,
|
||||||
|
shuffle = shuffle,
|
||||||
|
playlistName = navArgs.playlistName,
|
||||||
|
fragment = this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -416,62 +420,35 @@ open class TrackCollectionFragment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadBackground(save: Boolean) {
|
private fun downloadBackground(save: Boolean, tracks: List<Track> = getSelectedSongs()) {
|
||||||
var songs = getSelectedSongs()
|
var songs = tracks
|
||||||
|
|
||||||
if (songs.isEmpty()) {
|
if (songs.isEmpty()) {
|
||||||
songs = getAllSongs()
|
songs = getAllSongs()
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadBackground(save, songs)
|
val action = if (save) DownloadAction.PIN else DownloadAction.DOWNLOAD
|
||||||
}
|
downloadHandler.justDownload(
|
||||||
|
action = action,
|
||||||
private fun downloadBackground(
|
fragment = this,
|
||||||
save: Boolean,
|
tracks = songs
|
||||||
songs: List<Track?>
|
)
|
||||||
) {
|
|
||||||
val onValid = Runnable {
|
|
||||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
|
||||||
DownloadService.download(songs.filterNotNull(), save)
|
|
||||||
|
|
||||||
if (save) {
|
|
||||||
Util.toast(
|
|
||||||
context,
|
|
||||||
resources.getQuantityString(
|
|
||||||
R.plurals.select_album_n_songs_pinned, songs.size, songs.size
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Util.toast(
|
|
||||||
context,
|
|
||||||
resources.getQuantityString(
|
|
||||||
R.plurals.select_album_n_songs_downloaded, songs.size, songs.size
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onValid.run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun delete(songs: List<Track> = getSelectedSongs()) {
|
internal fun delete(songs: List<Track> = getSelectedSongs()) {
|
||||||
Util.toast(
|
downloadHandler.justDownload(
|
||||||
context,
|
action = DownloadAction.DELETE,
|
||||||
resources.getQuantityString(
|
fragment = this,
|
||||||
R.plurals.select_album_n_songs_deleted, songs.size, songs.size
|
tracks = songs
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
DownloadService.delete(songs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun unpin(songs: List<Track> = getSelectedSongs()) {
|
internal fun unpin(songs: List<Track> = getSelectedSongs()) {
|
||||||
Util.toast(
|
downloadHandler.justDownload(
|
||||||
context,
|
action = DownloadAction.UNPIN,
|
||||||
resources.getQuantityString(
|
fragment = this,
|
||||||
R.plurals.select_album_n_songs_unpinned, songs.size, songs.size
|
tracks = songs
|
||||||
)
|
|
||||||
)
|
)
|
||||||
DownloadService.unpin(songs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override val defaultObserver: (List<MusicDirectory.Child>) -> Unit = {
|
override val defaultObserver: (List<MusicDirectory.Child>) -> Unit = {
|
||||||
@ -637,15 +614,14 @@ open class TrackCollectionFragment(
|
|||||||
playNow(false, songs)
|
playNow(false, songs)
|
||||||
}
|
}
|
||||||
R.id.song_menu_play_next -> {
|
R.id.song_menu_play_next -> {
|
||||||
downloadHandler.download(
|
downloadHandler.addTracksToMediaController(
|
||||||
fragment = this@TrackCollectionFragment,
|
|
||||||
append = true,
|
|
||||||
save = false,
|
|
||||||
autoPlay = false,
|
|
||||||
playNext = true,
|
|
||||||
shuffle = false,
|
|
||||||
songs = songs,
|
songs = songs,
|
||||||
playlistName = navArgs.playlistName
|
append = true,
|
||||||
|
playNext = true,
|
||||||
|
autoPlay = false,
|
||||||
|
shuffle = false,
|
||||||
|
playlistName = navArgs.playlistName,
|
||||||
|
fragment = this@TrackCollectionFragment
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.song_menu_play_last -> {
|
R.id.song_menu_play_last -> {
|
||||||
|
@ -38,6 +38,7 @@ import org.moire.ultrasonic.domain.Playlist
|
|||||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||||
import org.moire.ultrasonic.service.OfflineException
|
import org.moire.ultrasonic.service.OfflineException
|
||||||
|
import org.moire.ultrasonic.subsonic.DownloadAction
|
||||||
import org.moire.ultrasonic.subsonic.DownloadHandler
|
import org.moire.ultrasonic.subsonic.DownloadHandler
|
||||||
import org.moire.ultrasonic.util.BackgroundTask
|
import org.moire.ultrasonic.util.BackgroundTask
|
||||||
import org.moire.ultrasonic.util.CacheCleaner
|
import org.moire.ultrasonic.util.CacheCleaner
|
||||||
@ -147,45 +148,33 @@ class PlaylistsFragment : Fragment() {
|
|||||||
val playlist = playlistsListView!!.getItemAtPosition(info.position) as Playlist
|
val playlist = playlistsListView!!.getItemAtPosition(info.position) as Playlist
|
||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
R.id.playlist_menu_pin -> {
|
R.id.playlist_menu_pin -> {
|
||||||
downloadHandler.value.downloadPlaylist(
|
downloadHandler.value.justDownload(
|
||||||
this,
|
DownloadAction.PIN,
|
||||||
|
fragment = this,
|
||||||
id = playlist.id,
|
id = playlist.id,
|
||||||
name = playlist.name,
|
name = playlist.name,
|
||||||
save = true,
|
isShare = false,
|
||||||
append = true,
|
isDirectory = false
|
||||||
autoplay = false,
|
|
||||||
shuffle = false,
|
|
||||||
background = true,
|
|
||||||
playNext = false,
|
|
||||||
unpin = false
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.playlist_menu_unpin -> {
|
R.id.playlist_menu_unpin -> {
|
||||||
downloadHandler.value.downloadPlaylist(
|
downloadHandler.value.justDownload(
|
||||||
this,
|
DownloadAction.UNPIN,
|
||||||
|
fragment = this,
|
||||||
id = playlist.id,
|
id = playlist.id,
|
||||||
name = playlist.name,
|
name = playlist.name,
|
||||||
save = false,
|
isShare = false,
|
||||||
append = false,
|
isDirectory = false
|
||||||
autoplay = false,
|
|
||||||
shuffle = false,
|
|
||||||
background = true,
|
|
||||||
playNext = false,
|
|
||||||
unpin = true
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.playlist_menu_download -> {
|
R.id.playlist_menu_download -> {
|
||||||
downloadHandler.value.downloadPlaylist(
|
downloadHandler.value.justDownload(
|
||||||
this,
|
DownloadAction.DOWNLOAD,
|
||||||
|
fragment = this,
|
||||||
id = playlist.id,
|
id = playlist.id,
|
||||||
name = playlist.name,
|
name = playlist.name,
|
||||||
save = false,
|
isShare = false,
|
||||||
append = false,
|
isDirectory = false
|
||||||
autoplay = false,
|
|
||||||
shuffle = false,
|
|
||||||
background = true,
|
|
||||||
playNext = false,
|
|
||||||
unpin = false
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.playlist_menu_play_now -> {
|
R.id.playlist_menu_play_now -> {
|
||||||
|
@ -28,7 +28,8 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import org.koin.java.KoinJavaComponent
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
import org.moire.ultrasonic.NavigationGraphDirections
|
import org.moire.ultrasonic.NavigationGraphDirections
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException
|
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException
|
||||||
@ -36,6 +37,7 @@ import org.moire.ultrasonic.domain.Share
|
|||||||
import org.moire.ultrasonic.fragment.FragmentTitle
|
import org.moire.ultrasonic.fragment.FragmentTitle
|
||||||
import org.moire.ultrasonic.service.MusicServiceFactory
|
import org.moire.ultrasonic.service.MusicServiceFactory
|
||||||
import org.moire.ultrasonic.service.OfflineException
|
import org.moire.ultrasonic.service.OfflineException
|
||||||
|
import org.moire.ultrasonic.subsonic.DownloadAction
|
||||||
import org.moire.ultrasonic.subsonic.DownloadHandler
|
import org.moire.ultrasonic.subsonic.DownloadHandler
|
||||||
import org.moire.ultrasonic.util.BackgroundTask
|
import org.moire.ultrasonic.util.BackgroundTask
|
||||||
import org.moire.ultrasonic.util.CancellationToken
|
import org.moire.ultrasonic.util.CancellationToken
|
||||||
@ -50,14 +52,12 @@ import org.moire.ultrasonic.view.ShareAdapter
|
|||||||
*
|
*
|
||||||
* TODO: This file has been converted from Java, but not modernized yet.
|
* TODO: This file has been converted from Java, but not modernized yet.
|
||||||
*/
|
*/
|
||||||
class SharesFragment : Fragment() {
|
class SharesFragment : Fragment(), KoinComponent {
|
||||||
private var refreshSharesListView: SwipeRefreshLayout? = null
|
private var refreshSharesListView: SwipeRefreshLayout? = null
|
||||||
private var sharesListView: ListView? = null
|
private var sharesListView: ListView? = null
|
||||||
private var emptyTextView: View? = null
|
private var emptyTextView: View? = null
|
||||||
private var shareAdapter: ShareAdapter? = null
|
private var shareAdapter: ShareAdapter? = null
|
||||||
private val downloadHandler = KoinJavaComponent.inject<DownloadHandler>(
|
private val downloadHandler = inject<DownloadHandler>()
|
||||||
DownloadHandler::class.java
|
|
||||||
)
|
|
||||||
private var cancellationToken: CancellationToken? = null
|
private var cancellationToken: CancellationToken? = null
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
Util.applyTheme(this.context)
|
Util.applyTheme(this.context)
|
||||||
@ -72,7 +72,6 @@ class SharesFragment : Fragment() {
|
|||||||
return inflater.inflate(R.layout.select_share, container, false)
|
return inflater.inflate(R.layout.select_share, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("NAME_SHADOWING")
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
cancellationToken = CancellationToken()
|
cancellationToken = CancellationToken()
|
||||||
refreshSharesListView = view.findViewById(R.id.select_share_refresh)
|
refreshSharesListView = view.findViewById(R.id.select_share_refresh)
|
||||||
@ -132,73 +131,55 @@ class SharesFragment : Fragment() {
|
|||||||
val share = sharesListView!!.getItemAtPosition(info.position) as Share
|
val share = sharesListView!!.getItemAtPosition(info.position) as Share
|
||||||
when (menuItem.itemId) {
|
when (menuItem.itemId) {
|
||||||
R.id.share_menu_pin -> {
|
R.id.share_menu_pin -> {
|
||||||
downloadHandler.value.downloadShare(
|
downloadHandler.value.justDownload(
|
||||||
this,
|
DownloadAction.PIN,
|
||||||
share.id,
|
fragment = this,
|
||||||
share.name,
|
id = share.id,
|
||||||
save = true,
|
name = share.name,
|
||||||
append = true,
|
isShare = true,
|
||||||
autoplay = false,
|
isDirectory = false
|
||||||
shuffle = false,
|
|
||||||
background = true,
|
|
||||||
playNext = false,
|
|
||||||
unpin = false
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.share_menu_unpin -> {
|
R.id.share_menu_unpin -> {
|
||||||
downloadHandler.value.downloadShare(
|
downloadHandler.value.justDownload(
|
||||||
this,
|
DownloadAction.UNPIN,
|
||||||
share.id,
|
fragment = this,
|
||||||
share.name,
|
id = share.id,
|
||||||
save = false,
|
name = share.name,
|
||||||
append = false,
|
isShare = true,
|
||||||
autoplay = false,
|
isDirectory = false
|
||||||
shuffle = false,
|
|
||||||
background = true,
|
|
||||||
playNext = false,
|
|
||||||
unpin = true
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.share_menu_download -> {
|
R.id.share_menu_download -> {
|
||||||
downloadHandler.value.downloadShare(
|
downloadHandler.value.justDownload(
|
||||||
this,
|
DownloadAction.DOWNLOAD,
|
||||||
share.id,
|
fragment = this,
|
||||||
share.name,
|
id = share.id,
|
||||||
save = false,
|
name = share.name,
|
||||||
append = false,
|
isShare = true,
|
||||||
autoplay = false,
|
isDirectory = false
|
||||||
shuffle = false,
|
|
||||||
background = true,
|
|
||||||
playNext = false,
|
|
||||||
unpin = false
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.share_menu_play_now -> {
|
R.id.share_menu_play_now -> {
|
||||||
downloadHandler.value.downloadShare(
|
downloadHandler.value.fetchTracksAndAddToController(
|
||||||
this,
|
this,
|
||||||
share.id,
|
share.id,
|
||||||
share.name,
|
share.name,
|
||||||
save = false,
|
|
||||||
append = false,
|
append = false,
|
||||||
autoplay = true,
|
autoPlay = true,
|
||||||
shuffle = false,
|
shuffle = false,
|
||||||
background = false,
|
|
||||||
playNext = false,
|
playNext = false,
|
||||||
unpin = false
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.share_menu_play_shuffled -> {
|
R.id.share_menu_play_shuffled -> {
|
||||||
downloadHandler.value.downloadShare(
|
downloadHandler.value.fetchTracksAndAddToController(
|
||||||
this,
|
this,
|
||||||
share.id,
|
share.id,
|
||||||
share.name,
|
share.name,
|
||||||
save = false,
|
|
||||||
append = false,
|
append = false,
|
||||||
autoplay = true,
|
autoPlay = true,
|
||||||
shuffle = true,
|
shuffle = true,
|
||||||
background = false,
|
|
||||||
playNext = false,
|
playNext = false,
|
||||||
unpin = false
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
R.id.share_menu_delete -> {
|
R.id.share_menu_delete -> {
|
||||||
|
@ -42,7 +42,6 @@ import org.moire.ultrasonic.playback.PlaybackService
|
|||||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
import org.moire.ultrasonic.util.setPin
|
|
||||||
import org.moire.ultrasonic.util.toMediaItem
|
import org.moire.ultrasonic.util.toMediaItem
|
||||||
import org.moire.ultrasonic.util.toTrack
|
import org.moire.ultrasonic.util.toTrack
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -314,7 +313,6 @@ class MediaPlayerController(
|
|||||||
|
|
||||||
addToPlaylist(
|
addToPlaylist(
|
||||||
state.songs,
|
state.songs,
|
||||||
cachePermanently = false,
|
|
||||||
autoPlay = false,
|
autoPlay = false,
|
||||||
shuffle = false,
|
shuffle = false,
|
||||||
insertionMode = insertionMode
|
insertionMode = insertionMode
|
||||||
@ -408,7 +406,6 @@ class MediaPlayerController(
|
|||||||
@Synchronized
|
@Synchronized
|
||||||
fun addToPlaylist(
|
fun addToPlaylist(
|
||||||
songs: List<Track>,
|
songs: List<Track>,
|
||||||
cachePermanently: Boolean,
|
|
||||||
autoPlay: Boolean,
|
autoPlay: Boolean,
|
||||||
shuffle: Boolean,
|
shuffle: Boolean,
|
||||||
insertionMode: InsertionMode
|
insertionMode: InsertionMode
|
||||||
@ -423,7 +420,6 @@ class MediaPlayerController(
|
|||||||
|
|
||||||
val mediaItems: List<MediaItem> = songs.map {
|
val mediaItems: List<MediaItem> = songs.map {
|
||||||
val result = it.toMediaItem()
|
val result = it.toMediaItem()
|
||||||
if (cachePermanently) result.setPin(true)
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,17 +7,11 @@
|
|||||||
|
|
||||||
package org.moire.ultrasonic.subsonic
|
package org.moire.ultrasonic.subsonic
|
||||||
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import java.util.Collections
|
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
import kotlinx.coroutines.CancellationException
|
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||||
@ -26,12 +20,8 @@ import org.moire.ultrasonic.domain.Track
|
|||||||
import org.moire.ultrasonic.service.DownloadService
|
import org.moire.ultrasonic.service.DownloadService
|
||||||
import org.moire.ultrasonic.service.MediaPlayerController
|
import org.moire.ultrasonic.service.MediaPlayerController
|
||||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||||
import org.moire.ultrasonic.util.CommunicationError
|
|
||||||
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
|
|
||||||
import org.moire.ultrasonic.util.InfoDialog
|
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.executeTaskWithToast
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a list of songs and adds them to the now playing list
|
* Retrieves a list of songs and adds them to the now playing list
|
||||||
@ -39,279 +29,145 @@ import timber.log.Timber
|
|||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
class DownloadHandler(
|
class DownloadHandler(
|
||||||
val mediaPlayerController: MediaPlayerController,
|
val mediaPlayerController: MediaPlayerController,
|
||||||
val networkAndStorageChecker: NetworkAndStorageChecker
|
private val networkAndStorageChecker: NetworkAndStorageChecker
|
||||||
) : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
) : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||||
private val maxSongs = 500
|
private val maxSongs = 500
|
||||||
|
|
||||||
/**
|
fun justDownload(
|
||||||
* Exception Handler for Coroutines
|
action: DownloadAction,
|
||||||
*/
|
|
||||||
val exceptionHandler = CoroutineExceptionHandler { _, exception ->
|
|
||||||
Handler(Looper.getMainLooper()).post {
|
|
||||||
Timber.w(exception)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Use coroutine here (with proper exception handler)
|
|
||||||
fun download(
|
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
append: Boolean,
|
id: String? = null,
|
||||||
save: Boolean,
|
name: String? = "",
|
||||||
autoPlay: Boolean,
|
isShare: Boolean = false,
|
||||||
playNext: Boolean,
|
isDirectory: Boolean = true,
|
||||||
shuffle: Boolean,
|
isArtist: Boolean = false,
|
||||||
songs: List<Track>,
|
tracks: List<Track>? = null
|
||||||
playlistName: String?,
|
|
||||||
) {
|
) {
|
||||||
val onValid = Runnable {
|
var successString: String? = null
|
||||||
// TODO: The logic here is different than in the controller...
|
|
||||||
val insertionMode = when {
|
|
||||||
playNext -> MediaPlayerController.InsertionMode.AFTER_CURRENT
|
|
||||||
append -> MediaPlayerController.InsertionMode.APPEND
|
|
||||||
else -> MediaPlayerController.InsertionMode.CLEAR
|
|
||||||
}
|
|
||||||
|
|
||||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
|
||||||
mediaPlayerController.addToPlaylist(
|
|
||||||
songs,
|
|
||||||
save,
|
|
||||||
autoPlay,
|
|
||||||
shuffle,
|
|
||||||
insertionMode
|
|
||||||
)
|
|
||||||
|
|
||||||
if (playlistName != null) {
|
|
||||||
mediaPlayerController.suggestedPlaylistName = playlistName
|
|
||||||
}
|
|
||||||
if (autoPlay) {
|
|
||||||
if (Settings.shouldTransitionOnPlayback) {
|
|
||||||
fragment.findNavController().popBackStack(R.id.playerFragment, true)
|
|
||||||
fragment.findNavController().navigate(R.id.playerFragment)
|
|
||||||
}
|
|
||||||
} else if (save) {
|
|
||||||
Util.toast(
|
|
||||||
fragment.context,
|
|
||||||
fragment.resources.getQuantityString(
|
|
||||||
R.plurals.select_album_n_songs_pinned,
|
|
||||||
songs.size,
|
|
||||||
songs.size
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else if (playNext) {
|
|
||||||
Util.toast(
|
|
||||||
fragment.context,
|
|
||||||
fragment.resources.getQuantityString(
|
|
||||||
R.plurals.select_album_n_songs_play_next,
|
|
||||||
songs.size,
|
|
||||||
songs.size
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else if (append) {
|
|
||||||
Util.toast(
|
|
||||||
fragment.context,
|
|
||||||
fragment.resources.getQuantityString(
|
|
||||||
R.plurals.select_album_n_songs_added,
|
|
||||||
songs.size,
|
|
||||||
songs.size
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onValid.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun downloadPlaylist(
|
|
||||||
fragment: Fragment,
|
|
||||||
id: String,
|
|
||||||
name: String?,
|
|
||||||
save: Boolean,
|
|
||||||
append: Boolean,
|
|
||||||
autoplay: Boolean,
|
|
||||||
shuffle: Boolean,
|
|
||||||
background: Boolean,
|
|
||||||
playNext: Boolean,
|
|
||||||
unpin: Boolean
|
|
||||||
) {
|
|
||||||
downloadRecursively(
|
|
||||||
fragment,
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
isShare = false,
|
|
||||||
isDirectory = false,
|
|
||||||
save = save,
|
|
||||||
append = append,
|
|
||||||
autoPlay = autoplay,
|
|
||||||
shuffle = shuffle,
|
|
||||||
background = background,
|
|
||||||
playNext = playNext,
|
|
||||||
unpin = unpin,
|
|
||||||
isArtist = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun downloadShare(
|
|
||||||
fragment: Fragment,
|
|
||||||
id: String,
|
|
||||||
name: String?,
|
|
||||||
save: Boolean,
|
|
||||||
append: Boolean,
|
|
||||||
autoplay: Boolean,
|
|
||||||
shuffle: Boolean,
|
|
||||||
background: Boolean,
|
|
||||||
playNext: Boolean,
|
|
||||||
unpin: Boolean
|
|
||||||
) {
|
|
||||||
downloadRecursively(
|
|
||||||
fragment,
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
isShare = true,
|
|
||||||
isDirectory = false,
|
|
||||||
save = save,
|
|
||||||
append = append,
|
|
||||||
autoPlay = autoplay,
|
|
||||||
shuffle = shuffle,
|
|
||||||
background = background,
|
|
||||||
playNext = playNext,
|
|
||||||
unpin = unpin,
|
|
||||||
isArtist = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun downloadRecursively(
|
|
||||||
fragment: Fragment,
|
|
||||||
id: String?,
|
|
||||||
save: Boolean,
|
|
||||||
append: Boolean,
|
|
||||||
autoPlay: Boolean,
|
|
||||||
shuffle: Boolean,
|
|
||||||
background: Boolean,
|
|
||||||
playNext: Boolean,
|
|
||||||
unpin: Boolean,
|
|
||||||
isArtist: Boolean
|
|
||||||
) {
|
|
||||||
if (id.isNullOrEmpty()) return
|
|
||||||
downloadRecursively(
|
|
||||||
fragment,
|
|
||||||
id,
|
|
||||||
"",
|
|
||||||
isShare = false,
|
|
||||||
isDirectory = true,
|
|
||||||
save = save,
|
|
||||||
append = append,
|
|
||||||
autoPlay = autoPlay,
|
|
||||||
shuffle = shuffle,
|
|
||||||
background = background,
|
|
||||||
playNext = playNext,
|
|
||||||
unpin = unpin,
|
|
||||||
isArtist = isArtist
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun downloadRecursively(
|
|
||||||
fragment: Fragment,
|
|
||||||
id: String,
|
|
||||||
name: String?,
|
|
||||||
isShare: Boolean,
|
|
||||||
isDirectory: Boolean,
|
|
||||||
save: Boolean,
|
|
||||||
append: Boolean,
|
|
||||||
autoPlay: Boolean,
|
|
||||||
shuffle: Boolean,
|
|
||||||
background: Boolean,
|
|
||||||
playNext: Boolean,
|
|
||||||
unpin: Boolean,
|
|
||||||
isArtist: Boolean
|
|
||||||
) {
|
|
||||||
// Launch the Job
|
// Launch the Job
|
||||||
val job = launch(exceptionHandler) {
|
executeTaskWithToast(fragment, {
|
||||||
|
val tracksToDownload: List<Track> = tracks
|
||||||
|
?: getTracksFromServer(isArtist, id!!, isDirectory, name, isShare)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
// If we are just downloading tracks we don't need to add them to the controller
|
||||||
|
when (action) {
|
||||||
|
DownloadAction.DOWNLOAD -> DownloadService.download(tracksToDownload, false)
|
||||||
|
DownloadAction.PIN -> DownloadService.download(tracksToDownload, true)
|
||||||
|
DownloadAction.UNPIN -> DownloadService.unpin(tracksToDownload)
|
||||||
|
DownloadAction.DELETE -> DownloadService.delete(tracksToDownload)
|
||||||
|
}
|
||||||
|
successString = when (action) {
|
||||||
|
DownloadAction.DOWNLOAD -> fragment.resources.getQuantityString(
|
||||||
|
R.plurals.select_album_n_songs_downloaded,
|
||||||
|
tracksToDownload.size,
|
||||||
|
tracksToDownload.size
|
||||||
|
)
|
||||||
|
DownloadAction.UNPIN -> {
|
||||||
|
fragment.resources.getQuantityString(
|
||||||
|
R.plurals.select_album_n_songs_unpinned,
|
||||||
|
tracksToDownload.size,
|
||||||
|
tracksToDownload.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DownloadAction.PIN -> {
|
||||||
|
fragment.resources.getQuantityString(
|
||||||
|
R.plurals.select_album_n_songs_pinned,
|
||||||
|
tracksToDownload.size,
|
||||||
|
tracksToDownload.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DownloadAction.DELETE -> {
|
||||||
|
fragment.resources.getQuantityString(
|
||||||
|
R.plurals.select_album_n_songs_deleted,
|
||||||
|
tracksToDownload.size,
|
||||||
|
tracksToDownload.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) { successString }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fetchTracksAndAddToController(
|
||||||
|
fragment: Fragment,
|
||||||
|
id: String,
|
||||||
|
name: String? = "",
|
||||||
|
isShare: Boolean = false,
|
||||||
|
isDirectory: Boolean = true,
|
||||||
|
append: Boolean,
|
||||||
|
autoPlay: Boolean,
|
||||||
|
shuffle: Boolean,
|
||||||
|
playNext: Boolean,
|
||||||
|
isArtist: Boolean = false
|
||||||
|
) {
|
||||||
|
var successString: String? = null
|
||||||
|
// Launch the Job
|
||||||
|
executeTaskWithToast(fragment, {
|
||||||
val songs: MutableList<Track> =
|
val songs: MutableList<Track> =
|
||||||
getTracksFromServer(isArtist, id, isDirectory, name, isShare)
|
getTracksFromServer(isArtist, id, isDirectory, name, isShare)
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
addTracksToMediaController(
|
addTracksToMediaController(
|
||||||
songs,
|
songs = songs,
|
||||||
background,
|
append = append,
|
||||||
unpin,
|
playNext = playNext,
|
||||||
append,
|
autoPlay = autoPlay,
|
||||||
playNext,
|
shuffle = shuffle,
|
||||||
save,
|
playlistName = null,
|
||||||
autoPlay,
|
fragment = fragment
|
||||||
shuffle,
|
|
||||||
fragment
|
|
||||||
)
|
)
|
||||||
|
// Play Now doesn't get a Toast :)
|
||||||
|
if (playNext) {
|
||||||
|
successString = fragment.resources.getQuantityString(
|
||||||
|
R.plurals.select_album_n_songs_play_next,
|
||||||
|
songs.size,
|
||||||
|
songs.size
|
||||||
|
)
|
||||||
|
} else if (append) {
|
||||||
|
successString = fragment.resources.getQuantityString(
|
||||||
|
R.plurals.select_album_n_songs_added,
|
||||||
|
songs.size,
|
||||||
|
songs.size
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}) { successString }
|
||||||
|
|
||||||
// Create the dialog
|
|
||||||
val builder = InfoDialog.Builder(fragment.requireContext())
|
|
||||||
builder.setTitle(R.string.background_task_wait)
|
|
||||||
builder.setMessage(R.string.background_task_loading)
|
|
||||||
builder.setOnCancelListener { job.cancel() }
|
|
||||||
builder.setPositiveButton(R.string.common_cancel) { _, i -> job.cancel() }
|
|
||||||
val dialog = builder.create()
|
|
||||||
dialog.show()
|
|
||||||
|
|
||||||
job.invokeOnCompletion {
|
|
||||||
dialog.dismiss()
|
|
||||||
if (it != null && it !is CancellationException) {
|
|
||||||
Util.toast(
|
|
||||||
fragment.requireContext(),
|
|
||||||
CommunicationError.getErrorMessage(it, fragment.requireContext())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addTracksToMediaController(
|
fun addTracksToMediaController(
|
||||||
songs: MutableList<Track>,
|
songs: List<Track>,
|
||||||
background: Boolean,
|
|
||||||
unpin: Boolean,
|
|
||||||
append: Boolean,
|
append: Boolean,
|
||||||
playNext: Boolean,
|
playNext: Boolean,
|
||||||
save: Boolean,
|
|
||||||
autoPlay: Boolean,
|
autoPlay: Boolean,
|
||||||
shuffle: Boolean,
|
shuffle: Boolean,
|
||||||
|
playlistName: String? = null,
|
||||||
fragment: Fragment
|
fragment: Fragment
|
||||||
) {
|
) {
|
||||||
if (songs.isEmpty()) return
|
if (songs.isEmpty()) return
|
||||||
if (Settings.shouldSortByDisc) {
|
|
||||||
Collections.sort(songs, EntryByDiscAndTrackComparator())
|
|
||||||
}
|
|
||||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
||||||
if (!background) {
|
|
||||||
if (unpin) {
|
val insertionMode = when {
|
||||||
DownloadService.unpin(songs)
|
append -> MediaPlayerController.InsertionMode.APPEND
|
||||||
} else {
|
playNext -> MediaPlayerController.InsertionMode.AFTER_CURRENT
|
||||||
val insertionMode = when {
|
else -> MediaPlayerController.InsertionMode.CLEAR
|
||||||
append -> MediaPlayerController.InsertionMode.APPEND
|
}
|
||||||
playNext -> MediaPlayerController.InsertionMode.AFTER_CURRENT
|
|
||||||
else -> MediaPlayerController.InsertionMode.CLEAR
|
if (playlistName != null) {
|
||||||
}
|
mediaPlayerController.suggestedPlaylistName = playlistName
|
||||||
mediaPlayerController.addToPlaylist(
|
}
|
||||||
songs,
|
|
||||||
save,
|
mediaPlayerController.addToPlaylist(
|
||||||
autoPlay,
|
songs,
|
||||||
shuffle,
|
autoPlay,
|
||||||
insertionMode
|
shuffle,
|
||||||
)
|
insertionMode
|
||||||
if (
|
)
|
||||||
!append &&
|
if (Settings.shouldTransitionOnPlayback && (!append || autoPlay)) {
|
||||||
Settings.shouldTransitionOnPlayback
|
fragment.findNavController().popBackStack(R.id.playerFragment, true)
|
||||||
) {
|
fragment.findNavController().navigate(R.id.playerFragment)
|
||||||
fragment.findNavController().popBackStack(
|
|
||||||
R.id.playerFragment,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
fragment.findNavController().navigate(R.id.playerFragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (unpin) {
|
|
||||||
DownloadService.unpin(songs)
|
|
||||||
} else {
|
|
||||||
DownloadService.download(songs, save)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,3 +252,7 @@ class DownloadHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class DownloadAction {
|
||||||
|
DOWNLOAD, PIN, UNPIN, DELETE
|
||||||
|
}
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* CoroutinePatterns.kt
|
||||||
|
* Copyright (C) 2009-2023 Ultrasonic developers
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GNU GPLv3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.moire.ultrasonic.util
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.moire.ultrasonic.R
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
object CoroutinePatterns {
|
||||||
|
val loggingExceptionHandler by lazy {
|
||||||
|
CoroutineExceptionHandler { _, exception ->
|
||||||
|
Handler(Looper.getMainLooper()).post {
|
||||||
|
Timber.w(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CoroutineScope.executeTaskWithToast(
|
||||||
|
fragment: Fragment,
|
||||||
|
task: suspend CoroutineScope.() -> Unit,
|
||||||
|
successString: () -> String?
|
||||||
|
): Job {
|
||||||
|
// Launch the Job
|
||||||
|
val job = launch(CoroutinePatterns.loggingExceptionHandler, block = task)
|
||||||
|
|
||||||
|
// Setup a handler when the job is done
|
||||||
|
job.invokeOnCompletion {
|
||||||
|
val toastString = if (it != null && it !is CancellationException) {
|
||||||
|
CommunicationError.getErrorMessage(it, fragment.context)
|
||||||
|
} else {
|
||||||
|
successString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return early if nothing to post
|
||||||
|
if (toastString == null) return@invokeOnCompletion
|
||||||
|
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
|
Util.toast(fragment.context, toastString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return job
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CoroutineScope.executeTaskWithModalDialog(
|
||||||
|
fragment: Fragment,
|
||||||
|
task: suspend CoroutineScope.() -> Unit,
|
||||||
|
successString: () -> String
|
||||||
|
) {
|
||||||
|
// Create the job
|
||||||
|
val job = executeTaskWithToast(fragment, task, successString)
|
||||||
|
|
||||||
|
// Create the dialog
|
||||||
|
val builder = InfoDialog.Builder(fragment.requireContext())
|
||||||
|
builder.setTitle(R.string.background_task_wait)
|
||||||
|
builder.setMessage(R.string.background_task_loading)
|
||||||
|
builder.setOnCancelListener { job.cancel() }
|
||||||
|
builder.setPositiveButton(R.string.common_cancel) { _, _ -> job.cancel() }
|
||||||
|
val dialog = builder.create()
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
// Add additional handler to close the dialog
|
||||||
|
job.invokeOnCompletion {
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -148,8 +148,8 @@ object Util {
|
|||||||
if (shortDuration) Toast.LENGTH_SHORT else Toast.LENGTH_LONG
|
if (shortDuration) Toast.LENGTH_SHORT else Toast.LENGTH_LONG
|
||||||
}
|
}
|
||||||
toast!!.show()
|
toast!!.show()
|
||||||
} catch (_: Exception) {
|
} catch (all: Exception) {
|
||||||
// Ignore
|
Timber.w(all)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user