Simplify and fix download status display

This commit is contained in:
tzugen 2021-11-15 00:19:47 +01:00
parent 7a2dbf65d9
commit 6277ee73c0
No known key found for this signature in database
GPG Key ID: 61E9C34BC10EC930
10 changed files with 118 additions and 68 deletions

View File

@ -11,6 +11,7 @@ import org.moire.ultrasonic.util.Util
*/
class ImageHelper(context: Context) {
lateinit var errorImage: Drawable
lateinit var starHollowDrawable: Drawable
lateinit var starDrawable: Drawable
lateinit var pinImage: Drawable
@ -39,6 +40,7 @@ class ImageHelper(context: Context) {
starDrawable = Util.getDrawableFromAttribute(context, R.attr.star_full)
pinImage = Util.getDrawableFromAttribute(context, R.attr.pin)
downloadedImage = Util.getDrawableFromAttribute(context, R.attr.downloaded)
errorImage = Util.getDrawableFromAttribute(context, R.attr.error)
downloadingImage = Util.getDrawableFromAttribute(context, R.attr.downloading)
playingImage = Util.getDrawableFromAttribute(context, R.attr.media_play_small)
}

View File

@ -1,7 +1,6 @@
package org.moire.ultrasonic.adapters
import android.annotation.SuppressLint
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.AdapterListUpdateCallback
import androidx.recyclerview.widget.AsyncDifferConfig
@ -145,6 +144,14 @@ class MultiTypeDiffAdapter<T : Identifiable> : MultiTypeAdapter() {
selectionRevision.postValue(selectionRevision.value!! + 1)
}
fun notifyChanged() {
// When the download state of an entry was changed by an external process,
// increase the revision counter in order to update the UI
selectionRevision.postValue(selectionRevision.value!! + 1)
}
fun setSelectionStatusOfAll(select: Boolean): Int {
// Clear current selection
selectedSet.clear()

View File

@ -78,13 +78,14 @@ class TrackViewBinder(
// Observe download status
downloadFile.status.observe(lifecycleOwner, {
Timber.w("CAUGHT STATUS CHANGE")
holder.updateDownloadStatus(downloadFile)
holder.updateStatus(it)
holder.adapter.notifyChanged()
}
)
downloadFile.progress.observe(lifecycleOwner, {
Timber.w("CAUGHT PROGRESS CHANGE")
holder.updateDownloadStatus(downloadFile)
holder.updateProgress(it)
}
)
}

View File

@ -9,7 +9,6 @@ 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
@ -21,6 +20,7 @@ import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.featureflags.Feature
import org.moire.ultrasonic.featureflags.FeatureStorage
import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.DownloadStatus
import org.moire.ultrasonic.service.MediaPlayerController
import org.moire.ultrasonic.service.MusicServiceFactory
import org.moire.ultrasonic.util.Settings
@ -47,7 +47,7 @@ class TrackViewHolder(val view: View, var adapter: MultiTypeDiffAdapter<Identifi
var title: TextView = view.findViewById(R.id.song_title)
var artist: TextView = view.findViewById(R.id.song_artist)
var duration: TextView = view.findViewById(R.id.song_duration)
var status: TextView = view.findViewById(R.id.song_status)
var progress: TextView = view.findViewById(R.id.song_status)
var entry: MusicDirectory.Entry? = null
private set
@ -55,10 +55,8 @@ class TrackViewHolder(val view: View, var adapter: MultiTypeDiffAdapter<Identifi
private set
private var isMaximized = false
private var leftImage: Drawable? = null
private var previousLeftImageType: ImageType? = null
private var previousRightImageType: ImageType? = null
private var leftImageType: ImageType? = null
private var cachedStatus = DownloadStatus.UNKNOWN
private var statusImage: Drawable? = null
private var playing = false
private val useFiveStarRating: Boolean by lazy {
@ -160,7 +158,8 @@ class TrackViewHolder(val view: View, var adapter: MultiTypeDiffAdapter<Identifi
// TODO: Should be removed
fun update() {
updateDownloadStatus(downloadFile!!)
updateProgress(downloadFile!!.progress.value!!)
updateStatus(downloadFile!!.status.value!!)
if (useFiveStarRating) {
val rating = entry?.userRating ?: 0
@ -220,52 +219,55 @@ class TrackViewHolder(val view: View, var adapter: MultiTypeDiffAdapter<Identifi
}
fun updateDownloadStatus(downloadFile: DownloadFile) {
if (downloadFile.isWorkDone) {
val saved = downloadFile.isSaved
val newLeftImageType =
if (saved) ImageType.Pin else ImageType.Downloaded
fun updateStatus(status: DownloadStatus) {
if (status == cachedStatus) return
cachedStatus = status
if (leftImageType != newLeftImageType) {
leftImage = if (saved) imageHelper.pinImage else imageHelper.downloadedImage
leftImageType = newLeftImageType
Timber.w("STATUS: %s", status)
when (status) {
DownloadStatus.DONE -> {
statusImage = imageHelper.downloadedImage
progress.text = null
}
DownloadStatus.PINNED -> {
statusImage = imageHelper.pinImage
progress.text = null
}
DownloadStatus.FAILED,
DownloadStatus.ABORTED -> {
statusImage = imageHelper.errorImage
progress.text = null
}
DownloadStatus.DOWNLOADING -> {
statusImage = imageHelper.downloadingImage
}
else -> {
statusImage = null
}
} else {
leftImageType = ImageType.None
leftImage = null
}
val rightImageType: ImageType
val rightImage: Drawable?
updateImages()
}
if (downloadFile.isDownloading && !downloadFile.isDownloadCancelled) {
status.text = Util.formatPercentage(downloadFile.progress.value!!)
rightImageType = ImageType.Downloading
rightImage = imageHelper.downloadingImage
} else {
rightImageType = ImageType.None
rightImage = null
val statusText = status.text
if (!statusText.isNullOrEmpty()) status.text = null
fun updateProgress(p: Int) {
if (cachedStatus == DownloadStatus.DOWNLOADING) {
progress.text = Util.formatPercentage(p)
} else {
progress.text = null
}
}
if (previousLeftImageType != leftImageType || previousRightImageType != rightImageType) {
previousLeftImageType = leftImageType
previousRightImageType = rightImageType
private fun updateImages() {
progress.setCompoundDrawablesWithIntrinsicBounds(
null, null, statusImage, null
)
status.setCompoundDrawablesWithIntrinsicBounds(
leftImage, null, rightImage, null
)
if (rightImage === imageHelper.downloadingImage) {
// FIXME
val frameAnimation = rightImage as AnimationDrawable?
frameAnimation?.setVisible(true, true)
frameAnimation?.start()
}
if (statusImage === imageHelper.downloadingImage) {
val frameAnimation = statusImage as AnimationDrawable?
frameAnimation?.setVisible(true, true)
frameAnimation?.start()
}
}
@ -293,11 +295,4 @@ class TrackViewHolder(val view: View, var adapter: MultiTypeDiffAdapter<Identifi
title.isSingleLine = !isMaximized
artist.isSingleLine = !isMaximized
}
enum class ImageType {
None, Pin, Downloaded, Downloading
}
}
}

View File

@ -32,6 +32,11 @@ import timber.log.Timber
/**
* This class represents a single Song or Video that can be downloaded.
*
* Terminology:
* PinnedFile: A "pinned" song. Will stay in cache permanently
* CompleteFile: A "downloaded" song. Will be quicker to be deleted if the cache is full
*
*/
class DownloadFile(
val song: MusicDirectory.Entry,
@ -63,11 +68,28 @@ class DownloadFile(
private val activeServerProvider: ActiveServerProvider by inject()
val progress: MutableLiveData<Int> = MutableLiveData(0)
val status: MutableLiveData<DownloadStatus> = MutableLiveData(DownloadStatus.IDLE)
val status: MutableLiveData<DownloadStatus>
init {
val state: DownloadStatus
partialFile = File(saveFile.parent, FileUtil.getPartialFile(saveFile.name))
completeFile = File(saveFile.parent, FileUtil.getCompleteFile(saveFile.name))
when {
saveFile.exists() -> {
state = DownloadStatus.PINNED
}
completeFile.exists() -> {
state = DownloadStatus.DONE
}
else -> {
state = DownloadStatus.IDLE
}
}
status = MutableLiveData(state)
}
/**
@ -143,13 +165,14 @@ class DownloadFile(
fun unpin() {
if (saveFile.exists()) {
if (!saveFile.renameTo(completeFile)) {
if (saveFile.renameTo(completeFile)) {
status.postValue(DownloadStatus.DONE)
} else {
Timber.w(
"Renaming file failed. Original file: %s; Rename to: %s",
saveFile.name, completeFile.name
)
}
status.postValue(DownloadStatus.DONE)
}
}
@ -212,23 +235,23 @@ class DownloadFile(
try {
if (saveFile.exists()) {
Timber.i("%s already exists. Skipping.", saveFile)
status.postValue(DownloadStatus.DONE)
Timber.i("UPDATING STATUS")
status.postValue(DownloadStatus.PINNED)
return
}
if (completeFile.exists()) {
var newStatus: DownloadStatus = DownloadStatus.DONE
if (shouldSave) {
if (isPlaying) {
saveWhenDone = true
} else {
Util.renameFile(completeFile, saveFile)
newStatus = DownloadStatus.PINNED
}
} else {
Timber.i("%s already exists. Skipping.", completeFile)
}
status.postValue(DownloadStatus.DONE)
Timber.i("UPDATING STATUS")
status.postValue(newStatus)
return
}
@ -285,8 +308,6 @@ class DownloadFile(
}
downloadAndSaveCoverArt()
status.postValue(DownloadStatus.DONE)
}
if (isPlaying) {
@ -294,9 +315,11 @@ class DownloadFile(
} else {
if (shouldSave) {
Util.renameFile(partialFile, saveFile)
status.postValue(DownloadStatus.PINNED)
Util.scanMedia(saveFile)
} else {
Util.renameFile(partialFile, completeFile)
status.postValue(DownloadStatus.DONE)
}
}
} catch (all: Exception) {
@ -378,7 +401,6 @@ class DownloadFile(
private fun setProgress(totalBytesCopied: Long) {
if (song.size != null) {
progress.postValue((totalBytesCopied * 100 / song.size!!).toInt())
Timber.i("UPDATING PROGESS")
}
}
@ -414,6 +436,7 @@ class DownloadFile(
override val id: String
get() = song.id
override val longId: Long by lazy {
id.hashCode().toLong()
}
@ -424,5 +447,5 @@ class DownloadFile(
}
enum class DownloadStatus {
IDLE, DOWNLOADING, RETRYING, FAILED, ABORTED, DONE
IDLE, DOWNLOADING, RETRYING, FAILED, ABORTED, DONE, PINNED, UNKNOWN
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
</vector>

View File

@ -39,7 +39,7 @@
a:layout_height="wrap_content"
a:layout_gravity="right|center_vertical"
a:drawablePadding="6dip"
a:paddingEnd="6dip"/>
a:paddingEnd="12dip"/>
</LinearLayout>
<LinearLayout

View File

@ -61,6 +61,7 @@
<attr name="share" format="reference"/>
<attr name="download" format="reference"/>
<attr name="downloaded" format="reference"/>
<attr name="error" format="reference"/>
<attr name="downloading" format="reference"/>
<attr name="media_previous" format="reference"/>
<attr name="media_next" format="reference"/>

View File

@ -34,6 +34,7 @@
<item name="share">@drawable/ic_menu_share_dark</item>
<item name="download">@drawable/ic_menu_download_dark</item>
<item name="downloaded">@drawable/stat_sys_download_anim_0_dark</item>
<item name="error">@drawable/ic_baseline_error_dark</item>
<item name="downloading">@drawable/stat_sys_download_dark</item>
<item name="media_previous">@drawable/media_backward_normal_dark</item>
<item name="media_next">@drawable/media_forward_normal_dark</item>
@ -99,6 +100,7 @@
<item name="share">@drawable/ic_menu_share_dark</item>
<item name="download">@drawable/ic_menu_download_dark</item>
<item name="downloaded">@drawable/stat_sys_download_anim_0_dark</item>
<item name="error">@drawable/ic_baseline_error_dark</item>
<item name="downloading">@drawable/stat_sys_download_dark</item>
<item name="media_previous">@drawable/media_backward_normal_dark</item>
<item name="media_next">@drawable/media_forward_normal_dark</item>
@ -163,6 +165,7 @@
<item name="share">@drawable/ic_menu_share_light</item>
<item name="download">@drawable/ic_menu_download_light</item>
<item name="downloaded">@drawable/stat_sys_download_anim_0_light</item>
<item name="error">@drawable/ic_baseline_error_light</item>
<item name="downloading">@drawable/stat_sys_download_light</item>
<item name="media_previous">@drawable/media_backward_normal_light</item>
<item name="media_next">@drawable/media_forward_normal_light</item>