Migrate DownloadsFragment to new system

This commit is contained in:
tzugen 2021-11-14 21:20:23 +01:00
parent e81b1ef8c2
commit d0e39efc50
No known key found for this signature in database
GPG Key ID: 61E9C34BC10EC930
24 changed files with 175 additions and 188 deletions

View File

@ -226,10 +226,10 @@ public class BookmarksFragment extends Fragment {
}
}
// Display toast: N tracks selected / N tracks unselected
// Display toast: N tracks selected
if (toast)
{
int toastResId = selected ? R.string.select_album_n_selected : R.string.select_album_n_unselected;
int toastResId = R.string.select_album_n_selected;
Util.toast(getContext(), getString(toastResId, selectedCount));
}

View File

@ -89,7 +89,7 @@ class AlbumHeader(
get() = "HEADER"
override val longId: Long
get() = id.hashCode().toLong()
get() = -1L
override fun compareTo(other: Identifiable): Int {
return this.longId.compareTo(other.longId)

View File

@ -1,24 +1,23 @@
package org.moire.ultrasonic.adapters
import android.annotation.SuppressLint
import android.view.MotionEvent
import androidx.recyclerview.selection.ItemDetailsLookup
import androidx.recyclerview.selection.ItemKeyProvider
import androidx.recyclerview.selection.SelectionTracker
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.AdapterListUpdateCallback
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.AsyncListDiffer.ListListener
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.drakeet.multitype.MultiTypeAdapter
import org.moire.ultrasonic.domain.Identifiable
import timber.log.Timber
import java.util.TreeSet
class MultiTypeDiffAdapter<T : Identifiable> : MultiTypeAdapter() {
val diffCallback = GenericDiffCallback<T>()
var tracker: SelectionTracker<Long>? = null
internal var selectedSet: TreeSet<Long> = TreeSet()
internal var selectionRevision: MutableLiveData<Int> = MutableLiveData(0)
private val diffCallback = GenericDiffCallback<T>()
init {
setHasStableIds(true)
@ -28,10 +27,14 @@ class MultiTypeDiffAdapter<T : Identifiable> : MultiTypeAdapter() {
return getItem(position).longId
}
private fun getItem(position: Int): T {
return mDiffer.currentList[position]
}
override var items: List<Any>
get() = getCurrentList()
set(value) {
throw Exception("You must use submitList() to add data to the MultiTypeDiffAdapter")
throw IllegalAccessException("You must use submitList() to add data to the MultiTypeDiffAdapter")
}
@ -86,9 +89,7 @@ class MultiTypeDiffAdapter<T : Identifiable> : MultiTypeAdapter() {
mDiffer.submitList(list, commitCallback)
}
protected fun getItem(position: Int): T {
return mDiffer.currentList[position]
}
override fun getItemCount(): Int {
return mDiffer.currentList.size
@ -130,8 +131,42 @@ class MultiTypeDiffAdapter<T : Identifiable> : MultiTypeAdapter() {
// Void
}
fun notifySelected(id: Long) {
selectedSet.add(id)
// Update revision counter
selectionRevision.postValue(selectionRevision.value!! + 1)
}
fun notifyUnselected(id: Long) {
selectedSet.remove(id)
// Update revision counter
selectionRevision.postValue(selectionRevision.value!! + 1)
}
fun setSelectionStatusOfAll(select: Boolean): Int {
// Clear current selection
selectedSet.clear()
// Update revision counter
selectionRevision.postValue(selectionRevision.value!! + 1)
// Nothing to reselect
if (!select) return 0
// Select them all
getCurrentList().mapNotNullTo(selectedSet, { entry ->
// Exclude any -1 ids, eg. headers and other UI elements
entry.longId.takeIf { it != -1L }
})
return selectedSet.count()
}
fun isSelected(longId: Long): Boolean {
return selectedSet.contains(longId)
}
companion object {
@ -150,8 +185,6 @@ class MultiTypeDiffAdapter<T : Identifiable> : MultiTypeAdapter() {
}
}
}

View File

@ -3,6 +3,7 @@ package org.moire.ultrasonic.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.lifecycle.LifecycleOwner
import com.drakeet.multitype.ItemViewBinder
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -11,12 +12,13 @@ import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.Downloader
import timber.log.Timber
class TrackViewBinder(
val selectedSet: MutableSet<Long>,
val checkable: Boolean,
val draggable: Boolean,
context: Context
context: Context,
val lifecycleOwner: LifecycleOwner
) : ItemViewBinder<Identifiable, TrackViewHolder>(), KoinComponent {
@ -35,12 +37,10 @@ class TrackViewBinder(
val contextMenuLayout = R.menu.artist_context_menu
private val downloader: Downloader by inject()
private val imageHelper: ImageHelper = ImageHelper(context)
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): TrackViewHolder {
return TrackViewHolder(inflater.inflate(layout, parent, false), selectedSet)
return TrackViewHolder(inflater.inflate(layout, parent, false), adapter as MultiTypeDiffAdapter<Identifiable>)
}
override fun onBindViewHolder(holder: TrackViewHolder, item: Identifiable) {
@ -64,23 +64,29 @@ class TrackViewBinder(
holder.setSong(
file = downloadFile,
checkable = checkable,
draggable = draggable
draggable = draggable,
holder.adapter.isSelected(item.longId)
)
// Listen to changes in selection status and update ourselves
holder.adapter.selectionRevision.observe(lifecycleOwner, {
val newStatus = holder.adapter.isSelected(item.longId)
if (newStatus != holder.check.isChecked) holder.check.isChecked = newStatus
})
// Observe download status
// item.status.observe(
// lifecycleOwner,
// {
// holder.updateDownloadStatus(item)
// }
// )
//
// item.progress.observe(
// lifecycleOwner,
// {
// holder.updateDownloadStatus(item)
// }
// )
downloadFile.status.observe(lifecycleOwner, {
Timber.w("CAUGHT STATUS CHANGE")
holder.updateDownloadStatus(downloadFile)
}
)
downloadFile.progress.observe(lifecycleOwner, {
Timber.w("CAUGHT PROGRESS CHANGE")
holder.updateDownloadStatus(downloadFile)
}
)
}

View File

@ -9,12 +9,14 @@ import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.view.isVisible
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
import org.koin.core.component.inject
import org.moire.ultrasonic.R
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.featureflags.Feature
import org.moire.ultrasonic.featureflags.FeatureStorage
@ -29,8 +31,9 @@ import timber.log.Timber
* Used to display songs and videos in a `ListView`.
* TODO: Video List item
*/
class TrackViewHolder(val view: View, val selectedSet: MutableSet<Long>) :
class TrackViewHolder(val view: View, var adapter: MultiTypeDiffAdapter<Identifiable>) :
RecyclerView.ViewHolder(view), Checkable, KoinComponent {
var check: CheckedTextView = view.findViewById(R.id.song_check)
var rating: LinearLayout = view.findViewById(R.id.song_rating)
private var fiveStar1: ImageView = view.findViewById(R.id.song_five_star_1)
@ -99,6 +102,7 @@ class TrackViewHolder(val view: View, val selectedSet: MutableSet<Long>) :
}
check.isVisible = (checkable && !song.isVideo)
check.isChecked = isSelected
drag.isVisible = draggable
if (ActiveServerProvider.isOffline()) {
@ -109,9 +113,6 @@ class TrackViewHolder(val view: View, val selectedSet: MutableSet<Long>) :
}
update()
isChecked = isSelected
}
private fun setupStarButtons(song: MusicDirectory.Entry) {
@ -219,7 +220,6 @@ class TrackViewHolder(val view: View, val selectedSet: MutableSet<Long>) :
}
fun updateDownloadStatus(downloadFile: DownloadFile) {
if (downloadFile.isWorkDone) {
val newLeftImageType =
if (downloadFile.isSaved) ImageType.Pin else ImageType.Downloaded
@ -274,10 +274,9 @@ class TrackViewHolder(val view: View, val selectedSet: MutableSet<Long>) :
override fun setChecked(newStatus: Boolean) {
if (newStatus) {
selectedSet.add(downloadFile!!.longId)
Timber.d("Selectedset %s", selectedSet.toString())
adapter.notifySelected(downloadFile!!.longId)
} else {
selectedSet.remove(downloadFile!!.longId)
adapter.notifyUnselected(downloadFile!!.longId)
}
check.isChecked = newStatus
}

View File

@ -3,6 +3,7 @@ package org.moire.ultrasonic.fragment
import android.app.Application
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import androidx.fragment.app.viewModels
import androidx.lifecycle.LiveData
import org.koin.core.component.inject
@ -13,9 +14,8 @@ import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.Downloader
import org.moire.ultrasonic.util.Util
import java.util.TreeSet
class DownloadsFragment : MultiListFragment<DownloadFile, MultiTypeDiffAdapter<Identifiable>>() {
class DownloadsFragment : MultiListFragment<DownloadFile>() {
/**
* The ViewModel to use to get the data
@ -36,22 +36,6 @@ class DownloadsFragment : MultiListFragment<DownloadFile, MultiTypeDiffAdapter<I
return listModel.getList()
}
/**
* Provide the Adapter for the RecyclerView with a lazy delegate
*/
override val viewAdapter: MultiTypeDiffAdapter<Identifiable> by lazy {
val adapter = MultiTypeDiffAdapter<Identifiable>()
adapter.register(
TrackViewBinder(
selectedSet = TreeSet(),
checkable = false,
draggable = false,
context = requireContext()
)
)
adapter
}
override fun onContextMenuItemSelected(menuItem: MenuItem, item: DownloadFile): Boolean {
// Do nothing
return true
@ -64,6 +48,21 @@ class DownloadsFragment : MultiListFragment<DownloadFile, MultiTypeDiffAdapter<I
override fun setTitle(title: String?) {
FragmentTitle.setTitle(this, Util.appContext().getString(R.string.menu_downloads))
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewAdapter.register(
TrackViewBinder(
checkable = true,
draggable = false,
context = requireContext(),
lifecycleOwner = viewLifecycleOwner
)
)
viewAdapter.submitList(listModel.getList().value)
}
}

View File

@ -16,6 +16,7 @@ import com.drakeet.multitype.MultiTypeAdapter
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.moire.ultrasonic.R
import org.moire.ultrasonic.adapters.MultiTypeDiffAdapter
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.GenericEntry
@ -33,7 +34,7 @@ import org.moire.ultrasonic.view.SelectMusicFolderView
* @param T: The type of data which will be used (must extend GenericEntry)
* @param TA: The Adapter to use (must extend GenericRowAdapter)
*/
abstract class MultiListFragment<T : Identifiable, TA : MultiTypeAdapter> : Fragment() {
abstract class MultiListFragment<T : Identifiable> : Fragment() {
internal val activeServerProvider: ActiveServerProvider by inject()
internal val serverSettingsModel: ServerSettingsModel by viewModel()
internal val imageLoaderProvider: ImageLoaderProvider by inject()
@ -47,7 +48,9 @@ abstract class MultiListFragment<T : Identifiable, TA : MultiTypeAdapter> : Frag
* The Adapter for the RecyclerView
* Recommendation: Implement this as a lazy delegate
*/
internal abstract val viewAdapter: TA
internal val viewAdapter: MultiTypeDiffAdapter<Identifiable> by lazy {
MultiTypeDiffAdapter()
}
/**
* The ViewModel to use to get the data
@ -144,9 +147,9 @@ abstract class MultiListFragment<T : Identifiable, TA : MultiTypeAdapter> : Frag
liveDataItems = getLiveData(arguments)
// Register an observer to update our UI when the data changes
// liveDataItems.observe(viewLifecycleOwner, {
// newItems -> viewAdapter.submitList(newItems)
// })
liveDataItems.observe(viewLifecycleOwner, {
newItems -> viewAdapter.submitList(newItems)
})
// Setup the Music folder handling
listModel.getMusicFolders().observe(viewLifecycleOwner, musicFolderObserver)

View File

@ -16,7 +16,6 @@ import kotlinx.coroutines.withContext
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.moire.ultrasonic.R
import org.moire.ultrasonic.adapters.ServerRowAdapter
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.fragment.EditServerFragment.Companion.EDIT_SERVER_INTENT_INDEX
import org.moire.ultrasonic.service.MediaPlayerController

View File

@ -34,7 +34,6 @@ import org.moire.ultrasonic.R
import org.moire.ultrasonic.adapters.HeaderViewBinder
import org.moire.ultrasonic.adapters.MultiTypeDiffAdapter
import org.moire.ultrasonic.adapters.TrackViewBinder
import org.moire.ultrasonic.adapters.TrackViewHolder
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.domain.MusicDirectory
@ -51,7 +50,6 @@ import org.moire.ultrasonic.util.Settings
import org.moire.ultrasonic.util.Util
import timber.log.Timber
import java.util.Collections
import java.util.TreeSet
/**
* Displays a group of tracks, eg. the songs of an album, of a playlist etc.
@ -61,7 +59,7 @@ import java.util.TreeSet
* TODO: Handle updates (playstatus, download status)
*/
class TrackCollectionFragment :
MultiListFragment<MusicDirectory.Entry, MultiTypeDiffAdapter<Identifiable>>() {
MultiListFragment<MusicDirectory.Entry>() {
private var albumButtons: View? = null
private var emptyView: TextView? = null
@ -86,8 +84,6 @@ class TrackCollectionFragment :
override val listModel: TrackCollectionModel by viewModels()
private var selectedSet: TreeSet<Long> = TreeSet()
/**
* The id of the main layout
*/
@ -111,19 +107,6 @@ class TrackCollectionFragment :
override val itemClickTarget: Int = R.id.trackCollectionFragment
override fun onCreate(savedInstanceState: Bundle?) {
Util.applyTheme(this.context)
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.track_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
cancellationToken = CancellationToken()
@ -136,7 +119,7 @@ class TrackCollectionFragment :
updateDisplay(true)
}
listModel.currentList.observe(viewLifecycleOwner, defaultObserver)
listModel.currentList.observe(viewLifecycleOwner, updateInterfaceWithEntries)
listModel.songsForGenre.observe(viewLifecycleOwner, songsForGenreObserver)
// listView!!.setOnItemClickListener { parent, theView, position, _ ->
@ -185,9 +168,11 @@ class TrackCollectionFragment :
selectButton!!.setOnClickListener {
selectAllOrNone()
}
playNowButton!!.setOnClickListener {
playNow(false)
}
playNextButton!!.setOnClickListener {
downloadHandler.download(
this@TrackCollectionFragment, append = true,
@ -195,18 +180,23 @@ class TrackCollectionFragment :
songs = getSelectedSongs()
)
}
playLastButton!!.setOnClickListener {
playNow(true)
}
pinButton!!.setOnClickListener {
downloadBackground(true)
}
unpinButton!!.setOnClickListener {
unpin()
}
downloadButton!!.setOnClickListener {
downloadBackground(false)
}
deleteButton!!.setOnClickListener {
delete()
}
@ -214,7 +204,6 @@ class TrackCollectionFragment :
registerForContextMenu(listView!!)
setHasOptionsMenu(true)
// Create a View Manager
viewManager = LinearLayoutManager(this.context)
@ -225,7 +214,6 @@ class TrackCollectionFragment :
adapter = viewAdapter
}
viewAdapter.register(
HeaderViewBinder(
context = requireContext()
@ -234,16 +222,20 @@ class TrackCollectionFragment :
viewAdapter.register(
TrackViewBinder(
selectedSet = selectedSet,
checkable = true,
draggable = false,
context = requireContext()
context = requireContext(),
lifecycleOwner = viewLifecycleOwner
)
)
enableButtons()
// Update the buttons when the selection has changed
viewAdapter.selectionRevision.observe(viewLifecycleOwner, {
enableButtons()
})
// Loads the data
updateDisplay(false)
}
@ -387,33 +379,24 @@ class TrackCollectionFragment :
}
}
private val viewHolders: List<TrackViewHolder>
get() {
val list: MutableList<TrackViewHolder> = mutableListOf()
for (i in 0 until listView!!.childCount) {
val vh = listView!!.findViewHolderForAdapterPosition(i)
if (vh is TrackViewHolder) {
list.add(vh)
}
}
return list
}
/**
* Get the size of the underlying list
*/
private val childCount: Int
get() {
val count = viewAdapter.getCurrentList().count()
if (listModel.showHeader) {
return listView!!.childCount - 1
return count - 1
} else {
return listView!!.childCount
return count
}
}
private fun playAll(shuffle: Boolean = false, append: Boolean = false) {
var hasSubFolders = false
for (vh in viewHolders) {
val entry = vh.entry
if (entry != null && entry.isDirectory) {
for (item in viewAdapter.getCurrentList()) {
if (item is MusicDirectory.Entry && item.isDirectory) {
hasSubFolders = true
break
}
@ -436,7 +419,6 @@ class TrackCollectionFragment :
isArtist = isArtist
)
} else {
selectAll(selected = true, toast = false)
downloadHandler.download(
fragment = this,
append = append,
@ -444,49 +426,38 @@ class TrackCollectionFragment :
autoPlay = !append,
playNext = false,
shuffle = shuffle,
songs = getSelectedSongs()
songs = getAllSongs()
)
selectAll(selected = false, toast = false)
}
}
@Suppress("UNCHECKED_CAST")
private fun getAllSongs(): List<MusicDirectory.Entry> {
return viewAdapter.getCurrentList().filter {
it is MusicDirectory.Entry && !it.isDirectory
} as List<MusicDirectory.Entry>
}
private fun selectAllOrNone() {
val someUnselected = selectedSet.size < childCount
val someUnselected = viewAdapter.selectedSet.size < childCount
selectAll(someUnselected, true)
}
private fun selectAll(selected: Boolean, toast: Boolean) {
var selectedCount = viewAdapter.selectedSet.size * -1
var selectedCount = 0
selectedCount += viewAdapter.setSelectionStatusOfAll(selected)
listView!!
for (vh in viewHolders) {
val entry = vh.entry
if (entry != null && !entry.isDirectory && !entry.isVideo) {
vh.isChecked = selected
selectedCount++
}
}
// Display toast: N tracks selected / N tracks unselected
// Display toast: N tracks selected
if (toast) {
val toastResId = if (selected)
R.string.select_album_n_selected
else
R.string.select_album_n_unselected
Util.toast(activity, getString(toastResId, selectedCount))
val toastResId = R.string.select_album_n_selected
Util.toast(activity, getString(toastResId, selectedCount.coerceAtLeast(0)))
}
enableButtons()
}
private fun enableButtons() {
val selection = getSelectedSongs()
private fun enableButtons(selection: List<MusicDirectory.Entry> = getSelectedSongs()) {
val enabled = selection.isNotEmpty()
var unpinEnabled = false
var deleteEnabled = false
@ -517,8 +488,7 @@ class TrackCollectionFragment :
var songs = getSelectedSongs()
if (songs.isEmpty()) {
selectAll(selected = true, toast = false)
songs = getSelectedSongs()
songs = getAllSongs()
}
downloadBackground(save, songs)
@ -596,15 +566,12 @@ class TrackCollectionFragment :
Navigation.findNavController(requireView())
.navigate(R.id.trackCollectionFragment, bundle)
}
//updateInterfaceWithEntries(musicDirectory)
}
private val defaultObserver = Observer(this::updateInterfaceWithEntries)
private fun updateInterfaceWithEntries(newList: List<MusicDirectory.Entry>) {
private val updateInterfaceWithEntries = Observer<List<MusicDirectory.Entry>> {
val entryList: MutableList<MusicDirectory.Entry> = newList.toMutableList()
val entryList: MutableList<MusicDirectory.Entry> = it.toMutableList()
if (listModel.currentListIsSortable && Settings.shouldSortByDisc) {
Collections.sort(entryList, EntryByDiscAndTrackComparator())
@ -683,6 +650,7 @@ class TrackCollectionFragment :
playAllButtonVisible = !(isAlbumList || entryList.isEmpty()) && !allVideos
shareButtonVisible = !isOffline() && songCount > 0
// TODO!!
// listView!!.removeHeaderView(emptyView!!)
// if (entries.isEmpty()) {
// emptyView!!.text = getString(R.string.select_album_empty)
@ -700,9 +668,9 @@ class TrackCollectionFragment :
if (songCount > 0 && listModel.showHeader) {
var name = listModel.currentDirectory.value?.name
val name = listModel.currentDirectory.value?.name
val intentAlbumName = requireArguments().getString(Constants.INTENT_EXTRA_NAME_NAME, "Name")!!
val albumHeader = AlbumHeader(newList, name?: intentAlbumName, songCount)
val albumHeader = AlbumHeader(it, name?: intentAlbumName, songCount)
val mixedList: MutableList<Identifiable> = mutableListOf(albumHeader)
mixedList.addAll(entryList)
viewAdapter.submitList(mixedList)
@ -724,26 +692,17 @@ class TrackCollectionFragment :
}
private fun getSelectedSongs(): MutableList<MusicDirectory.Entry> {
val songs: MutableList<MusicDirectory.Entry> = mutableListOf()
for (vh in viewHolders) {
if (vh.isChecked) {
songs.add(vh.entry!!)
}
private fun getSelectedSongs(): List<MusicDirectory.Entry> {
// Walk through selected set and get the Entries based on the saved ids.
return viewAdapter.getCurrentList().mapNotNull {
if (it is MusicDirectory.Entry && viewAdapter.isSelected(it.longId))
it
else
null
}
for (key in selectedSet) {
songs.add(viewAdapter.getCurrentList().findLast {
it.longId == key
} as MusicDirectory.Entry)
}
return songs
}
override val viewAdapter: MultiTypeDiffAdapter<Identifiable> by lazy {
MultiTypeDiffAdapter()
}
override fun setTitle(title: String?) {
setTitle(this@TrackCollectionFragment, title)

View File

@ -34,7 +34,6 @@ class TrackCollectionModel(application: Application) : GenericListModel(applicat
val currentDirectory: MutableLiveData<MusicDirectory> = MutableLiveData()
val currentList: MutableLiveData<List<MusicDirectory.Entry>> = MutableLiveData()
val songsForGenre: MutableLiveData<MusicDirectory> = MutableLiveData()
private val downloader: Downloader by inject()
suspend fun getMusicFolders(refresh: Boolean) {
withContext(Dispatchers.IO) {

View File

@ -139,6 +139,8 @@ class DownloadFile(
Util.delete(completeFile)
Util.delete(saveFile)
status.postValue(DownloadStatus.IDLE)
Util.scanMedia(saveFile)
}
@ -150,6 +152,7 @@ class DownloadFile(
saveFile.name, completeFile.name
)
}
status.postValue(DownloadStatus.DONE)
}
}

View File

@ -130,7 +130,6 @@
<string name="search.title">Hledat</string>
<string name="select_album.empty">Média nenalezena</string>
<string name="select_album.n_selected">%d skladeb označeno.</string>
<string name="select_album.n_unselected">%d skladeb odznačeno.</string>
<string name="select_album.no_network">Varování: Připojení nedostupné.</string>
<string name="select_album.no_sdcard">Chyba: SD karta nedostupná.</string>
<string name="select_album.play_all">Přehrát vše</string>

View File

@ -128,8 +128,7 @@
<string name="search.songs">Titel</string>
<string name="search.title">Suche</string>
<string name="select_album.empty">Keine Medien gefunden</string>
<string name="select_album.n_selected">%d Titel ausgewählt.</string>
<string name="select_album.n_unselected">%d Titel abgewählt.</string>
<string name="select_album.n_selected">%d Titel ausgewählt</string>
<string name="select_album.no_network">Warnung: kein Netz.</string>
<string name="select_album.no_sdcard">Fehler: Keine SD Karte verfügbar.</string>
<string name="select_album.play_all">Alles wiedergeben</string>

View File

@ -145,7 +145,6 @@
<string name="search.title">Buscar</string>
<string name="select_album.empty">No se han encontrado medios</string>
<string name="select_album.n_selected">%d pista(s) seleccionada(s).</string>
<string name="select_album.n_unselected">%d pista(s) deseleccionada(s).</string>
<string name="select_album.no_network">Atención: No hay red disponible.</string>
<string name="select_album.no_sdcard">Error: No hay tarjeta SD disponible.</string>
<string name="select_album.play_all">Reproducir todo</string>

View File

@ -142,7 +142,6 @@
<string name="search.title">Recherche</string>
<string name="select_album.empty">Aucun titre trouvé</string>
<string name="select_album.n_selected">%d pistes sélectionnées.</string>
<string name="select_album.n_unselected">%d pistes non sélectionnés.</string>
<string name="select_album.no_network">Avertissement : Aucun réseau disponible.</string>
<string name="select_album.no_sdcard">Erreur : Aucune carte SD disponible.</string>
<string name="select_album.play_all">Tout jouer</string>

View File

@ -140,7 +140,6 @@
<string name="search.title">Keresés</string>
<string name="select_album.empty">Nem található média!</string>
<string name="select_album.n_selected">%d dal kijelölve.</string>
<string name="select_album.n_unselected">%d dal visszavonva.</string>
<string name="select_album.no_network">Figyelem: Hálózat nem áll rendelkezésre!</string>
<string name="select_album.no_sdcard">Hiba: SD kártya nem áll rendelkezésre!</string>
<string name="select_album.play_all">Összes lejátszása</string>

View File

@ -126,7 +126,6 @@
<string name="search.title">Cerca</string>
<string name="select_album.empty">Nessun media trovato</string>
<string name="select_album.n_selected">%dtracce selezionate.</string>
<string name="select_album.n_unselected">%d tracce non selezionate.</string>
<string name="select_album.no_network">Attenzione: nessuna rete disponibile.</string>
<string name="select_album.no_sdcard">Errore: Nessuna memoria SD disponibile.</string>
<string name="select_album.play_all">Riproduci tutto</string>

View File

@ -145,7 +145,6 @@
<string name="search.title">Zoeken</string>
<string name="select_album.empty">Geen media gevonden</string>
<string name="select_album.n_selected">%d nummers geselecteerd.</string>
<string name="select_album.n_unselected">%d nummers gedeselecteerd.</string>
<string name="select_album.no_network">Waarschuwing: geen internetverbinding.</string>
<string name="select_album.no_sdcard">Fout: geen SD-kaart beschikbaar.</string>
<string name="select_album.play_all">Alles afspelen</string>

View File

@ -128,7 +128,6 @@
<string name="search.title">Wyszukiwanie</string>
<string name="select_album.empty">Brak mediów</string>
<string name="select_album.n_selected">Zaznaczono %d utworów.</string>
<string name="select_album.n_unselected">Odznaczono %d utworów.</string>
<string name="select_album.no_network">Uwaga: sieć niedostępna.</string>
<string name="select_album.no_sdcard">Błąd: Niedostępna karta SD.</string>
<string name="select_album.play_all">Odtwórz wszystkie</string>

View File

@ -142,7 +142,6 @@
<string name="search.title">Pesquisar</string>
<string name="select_album.empty">Nenhuma mídia encontrada</string>
<string name="select_album.n_selected">%d faixas selecionadas.</string>
<string name="select_album.n_unselected">%d faixas desselecionadas.</string>
<string name="select_album.no_network">Aviso: Nenhuma rede disponível.</string>
<string name="select_album.no_sdcard">Erro: Nenhum cartão SD disponível.</string>
<string name="select_album.play_all">Tocar Tudo</string>

View File

@ -128,7 +128,6 @@
<string name="search.title">Pesquisar</string>
<string name="select_album.empty">Nenhuma mídia encontrada</string>
<string name="select_album.n_selected">%d faixas selecionadas.</string>
<string name="select_album.n_unselected">%d faixas desselecionadas.</string>
<string name="select_album.no_network">Aviso: Nenhuma rede disponível.</string>
<string name="select_album.no_sdcard">Erro: Nenhum cartão SD disponível.</string>
<string name="select_album.play_all">Tocar Tudo</string>

View File

@ -142,7 +142,6 @@
<string name="search.title">Поиск</string>
<string name="select_album.empty">Медиа не найдена</string>
<string name="select_album.n_selected">%d треки выбраны.</string>
<string name="select_album.n_unselected">%d треки не выбраны.</string>
<string name="select_album.no_network">Предупреждение: сеть недоступна.</string>
<string name="select_album.no_sdcard">Ошибка: нет SD-карты</string>
<string name="select_album.play_all">Воспроизвести все</string>

View File

@ -141,7 +141,6 @@
<string name="search.title">搜索</string>
<string name="select_album.empty">找不到歌曲</string>
<string name="select_album.n_selected">已选择 %d 首曲目。</string>
<string name="select_album.n_unselected">未选择 %d 首曲目。</string>
<string name="select_album.no_network">警告:网络不可用</string>
<string name="select_album.no_sdcard">错误没有SD卡</string>
<string name="select_album.play_all">播放所有</string>

View File

@ -146,8 +146,7 @@
<string name="search.songs">Songs</string>
<string name="search.title">Search</string>
<string name="select_album.empty">No media found</string>
<string name="select_album.n_selected">%d tracks selected.</string>
<string name="select_album.n_unselected">%d tracks unselected.</string>
<string name="select_album.n_selected">%d tracks selected</string>
<string name="select_album.no_network">Warning: No network available.</string>
<string name="select_album.no_sdcard">Error: No SD card available.</string>
<string name="select_album.play_all">Play All</string>
@ -454,24 +453,24 @@
<item quantity="other">%d songs</item>
</plurals>
<plurals name="select_album_n_songs_pinned">
<item quantity="one">%d song selected to be pinned.</item>
<item quantity="other">%d songs selected to be pinned.</item>
<item quantity="one">%d song selected to be pinned</item>
<item quantity="other">%d songs selected to be pinned</item>
</plurals>
<plurals name="select_album_n_songs_downloaded">
<item quantity="one">%d song selected to be downloaded.</item>
<item quantity="other">%d songs selected to be downloaded.</item>
<item quantity="one">%d song selected to be downloaded</item>
<item quantity="other">%d songs selected to be downloaded</item>
</plurals>
<plurals name="select_album_n_songs_unpinned">
<item quantity="one">%d song selected to be unpinned.</item>
<item quantity="other">%d songs selected to be unpinned.</item>
<item quantity="one">%d song unpinned</item>
<item quantity="other">%d songs unpinned</item>
</plurals>
<plurals name="select_album_n_songs_added">
<item quantity="one">%d song added to the end of play queue.</item>
<item quantity="other">%d songs added to the end of play queue.</item>
<item quantity="one">%d song added to the end of play queue</item>
<item quantity="other">%d songs added to the end of play queue</item>
</plurals>
<plurals name="select_album_n_songs_play_next">
<item quantity="one">%d song inserted after current song.</item>
<item quantity="other">%d songs inserted after current song.</item>
<item quantity="one">%d song inserted after current song</item>
<item quantity="other">%d songs inserted after current song</item>
</plurals>
<plurals name="select_album_donate_dialog_n_trial_days_left">
<item quantity="one">%d day left of trial period</item>