mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-25 13:12:16 +03:00
Simplify and fix download status display
This commit is contained in:
parent
7a2dbf65d9
commit
6277ee73c0
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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>
|
@ -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>
|
@ -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
|
||||
|
@ -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"/>
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user