Fixed updating button states in TrackCollectionFragment according to track states

This commit is contained in:
Nite 2022-08-08 14:01:43 +00:00 committed by birdbird
parent 4be7769cb5
commit f6f524f5a9
13 changed files with 96 additions and 99 deletions

View File

@ -1,6 +1,5 @@
package org.moire.ultrasonic.adapters package org.moire.ultrasonic.adapters
import android.graphics.drawable.AnimationDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.view.View import android.view.View
import android.widget.Checkable import android.widget.Checkable
@ -94,8 +93,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
setupStarButtons(song, useFiveStarRating) setupStarButtons(song, useFiveStarRating)
} }
updateStatus(downloader.getDownloadState(song)) updateStatus(downloader.getDownloadState(song), null)
updateProgress(0)
if (useFiveStarRating) { if (useFiveStarRating) {
setFiveStars(entry?.userRating ?: 0) setFiveStars(entry?.userRating ?: 0)
@ -116,8 +114,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
rxBusSubscription!! += RxBus.trackDownloadStateObservable.subscribe { rxBusSubscription!! += RxBus.trackDownloadStateObservable.subscribe {
if (it.id != song.id) return@subscribe if (it.id != song.id) return@subscribe
updateStatus(it.state) updateStatus(it.state, it.progress)
updateProgress(it.progress)
} }
} }
@ -207,8 +204,8 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
} }
} }
private fun updateStatus(status: DownloadStatus) { private fun updateStatus(status: DownloadStatus, p: Int?) {
if (status == cachedStatus) return if (status != cachedStatus) {
cachedStatus = status cachedStatus = status
when (status) { when (status) {
@ -226,33 +223,30 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
progress.text = null progress.text = null
} }
DownloadStatus.DOWNLOADING -> { DownloadStatus.DOWNLOADING -> {
statusImage = imageHelper.downloadingImage statusImage = imageHelper.downloadingImage[0]
} }
else -> { else -> {
statusImage = null statusImage = null
} }
} }
}
if (cachedStatus == DownloadStatus.DOWNLOADING && p != null) {
progress.text = Util.formatPercentage(p)
statusImage =
imageHelper.downloadingImage[(imageHelper.downloadingImage.size / 100F * p).toInt()]
} else {
progress.text = null
}
updateImages() updateImages()
} }
private fun updateProgress(p: Int) {
if (cachedStatus == DownloadStatus.DOWNLOADING) {
progress.text = Util.formatPercentage(p)
} else {
progress.text = null
}
}
private fun updateImages() { private fun updateImages() {
progress.post {
progress.setCompoundDrawablesWithIntrinsicBounds( progress.setCompoundDrawablesWithIntrinsicBounds(
null, null, statusImage, null null, null, statusImage, null
) )
if (statusImage === imageHelper.downloadingImage) {
val frameAnimation = statusImage as AnimationDrawable?
frameAnimation?.setVisible(true, true)
frameAnimation?.start()
} }
} }

View File

@ -39,7 +39,7 @@ object Utils {
lateinit var errorImage: Drawable lateinit var errorImage: Drawable
lateinit var pinImage: Drawable lateinit var pinImage: Drawable
lateinit var downloadedImage: Drawable lateinit var downloadedImage: Drawable
lateinit var downloadingImage: Drawable lateinit var downloadingImage: List<Drawable>
lateinit var playingImage: Drawable lateinit var playingImage: Drawable
var theme: String var theme: String
@ -63,7 +63,15 @@ object Utils {
downloadedImage = downloadedImage =
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_0)!! ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_0)!!
errorImage = ContextCompat.getDrawable(context, R.drawable.ic_baseline_error)!! errorImage = ContextCompat.getDrawable(context, R.drawable.ic_baseline_error)!!
downloadingImage = ContextCompat.getDrawable(context, R.drawable.stat_sys_download)!! downloadingImage = listOf(
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_1)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_2)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_3)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_4)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_5)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_6)!!,
ContextCompat.getDrawable(context, R.drawable.stat_sys_download_anim_7)!!,
)
playingImage = ContextCompat.getDrawable(context, R.drawable.ic_stat_play)!! playingImage = ContextCompat.getDrawable(context, R.drawable.ic_stat_play)!!
} }
} }

View File

@ -21,6 +21,7 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.reactivex.rxjava3.disposables.CompositeDisposable
import java.util.Collections import java.util.Collections
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -39,6 +40,8 @@ import org.moire.ultrasonic.model.TrackCollectionModel
import org.moire.ultrasonic.service.DownloadStatus import org.moire.ultrasonic.service.DownloadStatus
import org.moire.ultrasonic.service.Downloader import org.moire.ultrasonic.service.Downloader
import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.service.MediaPlayerController
import org.moire.ultrasonic.service.RxBus
import org.moire.ultrasonic.service.plusAssign
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
import org.moire.ultrasonic.subsonic.ShareHandler import org.moire.ultrasonic.subsonic.ShareHandler
import org.moire.ultrasonic.subsonic.VideoPlayer import org.moire.ultrasonic.subsonic.VideoPlayer
@ -83,6 +86,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
internal var cancellationToken: CancellationToken? = null internal var cancellationToken: CancellationToken? = null
override val listModel: TrackCollectionModel by viewModels() override val listModel: TrackCollectionModel by viewModels()
private val rxBusSubscription: CompositeDisposable = CompositeDisposable()
/** /**
* The id of the main layout * The id of the main layout
@ -143,6 +147,14 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
) )
) )
// Change the buttons if the status of any selected track changes
rxBusSubscription += RxBus.trackDownloadStateObservable.subscribe {
if (it.progress != null) return@subscribe
val selectedSongs = getSelectedSongs()
if (!selectedSongs.any { song -> song.id == it.id }) return@subscribe
enableButtons(selectedSongs)
}
enableButtons() enableButtons()
// Update the buttons when the selection has changed // Update the buttons when the selection has changed
@ -261,6 +273,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
override fun onDestroyView() { override fun onDestroyView() {
cancellationToken!!.cancel() cancellationToken!!.cancel()
rxBusSubscription.dispose()
super.onDestroyView() super.onDestroyView()
} }
@ -356,10 +369,12 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
} }
} }
@Suppress("ComplexMethod")
internal open fun enableButtons(selection: List<Track> = getSelectedSongs()) { internal open fun enableButtons(selection: List<Track> = getSelectedSongs()) {
val enabled = selection.isNotEmpty() val enabled = selection.isNotEmpty()
var unpinEnabled = false var unpinEnabled = false
var deleteEnabled = false var deleteEnabled = false
var downloadEnabled = false
val multipleSelection = viewAdapter.hasMultipleSelection() val multipleSelection = viewAdapter.hasMultipleSelection()
var pinnedCount = 0 var pinnedCount = 0
@ -373,6 +388,9 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
pinnedCount++ pinnedCount++
unpinEnabled = true unpinEnabled = true
} }
if (state == DownloadStatus.IDLE || state == DownloadStatus.FAILED) {
downloadEnabled = true
}
} }
playNowButton?.isVisible = enabled playNowButton?.isVisible = enabled
@ -380,7 +398,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
playLastButton?.isVisible = enabled && multipleSelection playLastButton?.isVisible = enabled && multipleSelection
pinButton?.isVisible = (enabled && !isOffline() && selection.size > pinnedCount) pinButton?.isVisible = (enabled && !isOffline() && selection.size > pinnedCount)
unpinButton?.isVisible = (enabled && unpinEnabled) unpinButton?.isVisible = (enabled && unpinEnabled)
downloadButton?.isVisible = (enabled && !deleteEnabled && !isOffline()) downloadButton?.isVisible = (enabled && downloadEnabled && !isOffline())
deleteButton?.isVisible = (enabled && deleteEnabled) deleteButton?.isVisible = (enabled && deleteEnabled)
} }

View File

@ -185,7 +185,7 @@ class Downloader(
val existingItem = downloadQueue.firstOrNull { it.track.id == track.id } val existingItem = downloadQueue.firstOrNull { it.track.id == track.id }
if (existingItem != null) { if (existingItem != null) {
existingItem.priority = priority + 1 existingItem.priority = priority + 1
return continue
} }
// Set correct priority (the lower the number, the higher the priority) // Set correct priority (the lower the number, the higher the priority)
@ -267,7 +267,9 @@ class Downloader(
fun downloadBackground(tracks: List<Track>, save: Boolean) { fun downloadBackground(tracks: List<Track>, save: Boolean) {
// By using the counter we ensure that the songs are added in the correct order // By using the counter we ensure that the songs are added in the correct order
for (track in tracks) { for (track in tracks) {
if (downloadQueue.any { t -> t.track.id == track.id }) continue if (downloadQueue.any { t -> t.track.id == track.id } ||
activelyDownloading.any { t -> t.key.track.id == track.id }
) continue
val file = DownloadableTrack(track, save, 0, backgroundPriorityCounter++) val file = DownloadableTrack(track, save, 0, backgroundPriorityCounter++)
downloadQueue.add(file) downloadQueue.add(file)
} }
@ -281,7 +283,7 @@ class Downloader(
Storage.delete(track.getPartialFile()) Storage.delete(track.getPartialFile())
Storage.delete(track.getCompleteFile()) Storage.delete(track.getCompleteFile())
Storage.delete(track.getPinnedFile()) Storage.delete(track.getPinnedFile())
postState(track, DownloadStatus.IDLE, 0) postState(track, DownloadStatus.IDLE)
Util.scanMedia(track.getPinnedFile()) Util.scanMedia(track.getPinnedFile())
} }
@ -295,7 +297,7 @@ class Downloader(
if (!Storage.isPathExists(pinnedFile)) return if (!Storage.isPathExists(pinnedFile)) return
val file = Storage.getFromPath(track.getPinnedFile()) ?: return val file = Storage.getFromPath(track.getPinnedFile()) ?: return
Storage.rename(file, track.getCompleteFile()) Storage.rename(file, track.getCompleteFile())
postState(track, DownloadStatus.DONE, 100) postState(track, DownloadStatus.DONE)
} }
@Suppress("ReturnCount") @Suppress("ReturnCount")
@ -318,7 +320,7 @@ class Downloader(
const val REFRESH_INTERVAL = 100 const val REFRESH_INTERVAL = 100
} }
private fun postState(track: Track, state: DownloadStatus, progress: Int) { private fun postState(track: Track, state: DownloadStatus, progress: Int? = null) {
RxBus.trackDownloadStatePublisher.onNext( RxBus.trackDownloadStatePublisher.onNext(
RxBus.TrackDownloadState( RxBus.TrackDownloadState(
track.id, track.id,
@ -340,7 +342,7 @@ class Downloader(
try { try {
if (Storage.isPathExists(item.pinnedFile)) { if (Storage.isPathExists(item.pinnedFile)) {
Timber.i("%s already exists. Skipping.", item.pinnedFile) Timber.i("%s already exists. Skipping.", item.pinnedFile)
postState(item.track, DownloadStatus.PINNED, 100) postState(item.track, DownloadStatus.PINNED)
return return
} }
@ -365,11 +367,11 @@ class Downloader(
} catch (ignore: Exception) { } catch (ignore: Exception) {
Timber.w(ignore) Timber.w(ignore)
} }
postState(item.track, newStatus, 100) postState(item.track, newStatus)
return return
} }
postState(item.track, DownloadStatus.DOWNLOADING, 0) postState(item.track, DownloadStatus.DOWNLOADING)
// Some devices seem to throw error on partial file which doesn't exist // Some devices seem to throw error on partial file which doesn't exist
val needsDownloading: Boolean val needsDownloading: Boolean
@ -415,7 +417,7 @@ class Downloader(
outputStream.close() outputStream.close()
if (isCancelled) { if (isCancelled) {
postState(item.track, DownloadStatus.CANCELLED, 0) postState(item.track, DownloadStatus.CANCELLED)
throw RuntimeException( throw RuntimeException(
String.format( String.format(
Locale.ROOT, "Download of '%s' was cancelled", Locale.ROOT, "Download of '%s' was cancelled",
@ -436,14 +438,14 @@ class Downloader(
item.partialFile, item.partialFile,
item.pinnedFile item.pinnedFile
) )
postState(item.track, DownloadStatus.PINNED, 100) postState(item.track, DownloadStatus.PINNED)
Util.scanMedia(item.pinnedFile) Util.scanMedia(item.pinnedFile)
} else { } else {
Storage.rename( Storage.rename(
item.partialFile, item.partialFile,
item.completeFile item.completeFile
) )
postState(item.track, DownloadStatus.DONE, 100) postState(item.track, DownloadStatus.DONE)
} }
} catch (all: Exception) { } catch (all: Exception) {
outputStream.safeClose() outputStream.safeClose()
@ -451,12 +453,12 @@ class Downloader(
Storage.delete(item.pinnedFile) Storage.delete(item.pinnedFile)
if (!isCancelled) { if (!isCancelled) {
if (item.tryCount < MAX_RETRIES) { if (item.tryCount < MAX_RETRIES) {
postState(item.track, DownloadStatus.RETRYING, 0) postState(item.track, DownloadStatus.RETRYING)
item.tryCount++ item.tryCount++
activelyDownloading.remove(item) activelyDownloading.remove(item)
downloadQueue.add(item) downloadQueue.add(item)
} else { } else {
postState(item.track, DownloadStatus.FAILED, 0) postState(item.track, DownloadStatus.FAILED)
activelyDownloading.remove(item) activelyDownloading.remove(item)
downloadQueue.remove(item) downloadQueue.remove(item)
failedList.add(item) failedList.add(item)

View File

@ -91,7 +91,7 @@ class RxBus {
data class TrackDownloadState( data class TrackDownloadState(
val id: String, val id: String,
val state: DownloadStatus, val state: DownloadStatus,
val progress: Int val progress: Int?
) )
} }

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/res/drawable/status_icon_background.xml
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/stat_sys_download_anim_0" android:duration="200" />
<item android:drawable="@drawable/stat_sys_download_anim_1" android:duration="200" />
<item android:drawable="@drawable/stat_sys_download_anim_2" android:duration="200" />
<item android:drawable="@drawable/stat_sys_download_anim_3" android:duration="200" />
<item android:drawable="@drawable/stat_sys_download_anim_4" android:duration="200" />
<item android:drawable="@drawable/stat_sys_download_anim_5" android:duration="200" />
<item android:drawable="@drawable/stat_sys_download_anim_6" android:duration="200" />
<item android:drawable="@drawable/stat_sys_download_anim_7" android:duration="200" />
</animation-list>

View File

@ -8,6 +8,7 @@
android:fillColor="#FFF" android:fillColor="#FFF"
android:pathData="M15,5V3H9v2"/> android:pathData="M15,5V3H9v2"/>
<path <path
android:fillColor="#888" android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="M9,5V9H5l7,7 7,-7H15V5M5,18v2h14v-2z"/> android:pathData="M9,5V9H5l7,7 7,-7H15V5M5,18v2h14v-2z"/>
</vector> </vector>

View File

@ -8,6 +8,7 @@
android:fillColor="#FFF" android:fillColor="#FFF"
android:pathData="M15,7V3H9v4"/> android:pathData="M15,7V3H9v4"/>
<path <path
android:fillColor="#888" android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="M9,7V9H5l7,7 7,-7H15V7M5,18v2h14v-2z"/> android:pathData="M9,7V9H5l7,7 7,-7H15V7M5,18v2h14v-2z"/>
</vector> </vector>

View File

@ -8,6 +8,7 @@
android:fillColor="#FFF" android:fillColor="#FFF"
android:pathData="M15,9V3H9v6"/> android:pathData="M15,9V3H9v6"/>
<path <path
android:fillColor="#888" android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="M9,9H5l7,7 7,-7H15M5,18v2h14v-2z"/> android:pathData="M9,9H5l7,7 7,-7H15M5,18v2h14v-2z"/>
</vector> </vector>

View File

@ -8,6 +8,7 @@
android:fillColor="#FFF" android:fillColor="#FFF"
android:pathData="M17,11 L19,9H15V3H9V9H5l2,2"/> android:pathData="M17,11 L19,9H15V3H9V9H5l2,2"/>
<path <path
android:fillColor="#888" android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="m7,11 l5,5 5,-5M5,18v2h14v-2z"/> android:pathData="m7,11 l5,5 5,-5M5,18v2h14v-2z"/>
</vector> </vector>

View File

@ -8,6 +8,7 @@
android:fillColor="#FFF" android:fillColor="#FFF"
android:pathData="M15,13 L19,9H15V3H9V9H5l4,4"/> android:pathData="M15,13 L19,9H15V3H9V9H5l4,4"/>
<path <path
android:fillColor="#888" android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="m9,13 l3,3 3,-3M5,18v2h14v-2z"/> android:pathData="m9,13 l3,3 3,-3M5,18v2h14v-2z"/>
</vector> </vector>

View File

@ -8,6 +8,7 @@
android:fillColor="#FFF" android:fillColor="#FFF"
android:pathData="M13,15 L19,9H15V3H9V9H5l6,6"/> android:pathData="M13,15 L19,9H15V3H9V9H5l6,6"/>
<path <path
android:fillColor="#888" android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="m11,15 l1,1 1,-1m-8,3v2h14v-2z"/> android:pathData="m11,15 l1,1 1,-1m-8,3v2h14v-2z"/>
</vector> </vector>

View File

@ -8,6 +8,7 @@
android:fillColor="#FFF" android:fillColor="#FFF"
android:pathData="M19,9H15V3H9V9H5l7,7z"/> android:pathData="M19,9H15V3H9V9H5l7,7z"/>
<path <path
android:fillColor="#888" android:fillColor="#FFF"
android:fillAlpha="150"
android:pathData="m5,18v2h14v-2z"/> android:pathData="m5,18v2h14v-2z"/>
</vector> </vector>