mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-25 05:10:55 +03:00
Updated track state display to use spinner
This commit is contained in:
parent
1a354765f9
commit
2118837210
@ -147,7 +147,10 @@ class BaseAdapter<T : Identifiable> : MultiTypeAdapter(), FastScrollRecyclerView
|
||||
* @see .getCurrentList
|
||||
*/
|
||||
fun onCurrentListChanged(previousList: List<T>, currentList: List<T>) {
|
||||
// Void
|
||||
previousList.minus(currentList.toSet()).map {
|
||||
selectedSet.remove(it.longId)
|
||||
}
|
||||
selectionRevision.postValue(selectionRevision.value!! + 1)
|
||||
}
|
||||
|
||||
fun notifySelected(id: Long) {
|
||||
|
@ -10,6 +10,7 @@ import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
@ -25,6 +26,9 @@ import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import timber.log.Timber
|
||||
|
||||
const val INDICATOR_THICKNESS_INDEFINITE = 5
|
||||
const val INDICATOR_THICKNESS_DEFINITE = 10
|
||||
|
||||
/**
|
||||
* Used to display songs and videos in a `ListView`.
|
||||
*/
|
||||
@ -32,35 +36,36 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
|
||||
|
||||
private val downloader: Downloader by inject()
|
||||
|
||||
var entry: Track? = null
|
||||
private set
|
||||
var check: CheckedTextView = view.findViewById(R.id.song_check)
|
||||
var drag: ImageView = view.findViewById(R.id.song_drag)
|
||||
var observableChecked = MutableLiveData(false)
|
||||
lateinit var imageHelper: Utils.ImageHelper
|
||||
|
||||
private var rating: LinearLayout = view.findViewById(R.id.song_five_star)
|
||||
private var fiveStar1: ImageView = view.findViewById(R.id.song_five_star_1)
|
||||
private var fiveStar2: ImageView = view.findViewById(R.id.song_five_star_2)
|
||||
private var fiveStar3: ImageView = view.findViewById(R.id.song_five_star_3)
|
||||
private var fiveStar4: ImageView = view.findViewById(R.id.song_five_star_4)
|
||||
private var fiveStar5: ImageView = view.findViewById(R.id.song_five_star_5)
|
||||
var star: ImageView = view.findViewById(R.id.song_star)
|
||||
var drag: ImageView = view.findViewById(R.id.song_drag)
|
||||
var track: TextView = view.findViewById(R.id.song_track)
|
||||
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 progress: TextView = view.findViewById(R.id.song_status)
|
||||
|
||||
var entry: Track? = null
|
||||
private set
|
||||
private var star: ImageView = view.findViewById(R.id.song_star)
|
||||
private var track: TextView = view.findViewById(R.id.song_track)
|
||||
private var title: TextView = view.findViewById(R.id.song_title)
|
||||
private var artist: TextView = view.findViewById(R.id.song_artist)
|
||||
private var duration: TextView = view.findViewById(R.id.song_duration)
|
||||
private var statusImage: ImageView = view.findViewById(R.id.song_status_image)
|
||||
private var progressIndicator: CircularProgressIndicator =
|
||||
view.findViewById<CircularProgressIndicator?>(R.id.song_status_progress).apply {
|
||||
this.max = 100
|
||||
}
|
||||
|
||||
private var isMaximized = false
|
||||
private var cachedStatus = DownloadStatus.UNKNOWN
|
||||
private var statusImage: Drawable? = null
|
||||
private var isPlayingCached = false
|
||||
|
||||
private var rxBusSubscription: CompositeDisposable? = null
|
||||
|
||||
var observableChecked = MutableLiveData(false)
|
||||
|
||||
lateinit var imageHelper: Utils.ImageHelper
|
||||
|
||||
fun setSong(
|
||||
song: Track,
|
||||
checkable: Boolean,
|
||||
@ -103,7 +108,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
|
||||
|
||||
if (song.isVideo) {
|
||||
artist.isVisible = false
|
||||
progress.isVisible = false
|
||||
progressIndicator.isVisible = false
|
||||
}
|
||||
|
||||
// Create new Disposable for the new Subscriptions
|
||||
@ -204,50 +209,58 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateStatus(status: DownloadStatus, p: Int?) {
|
||||
if (status != cachedStatus) {
|
||||
cachedStatus = status
|
||||
private fun updateStatus(status: DownloadStatus, progress: Int?) {
|
||||
progressIndicator.progress = progress ?: 0
|
||||
|
||||
when (status) {
|
||||
DownloadStatus.DONE -> {
|
||||
statusImage = imageHelper.downloadedImage
|
||||
progress.text = null
|
||||
}
|
||||
DownloadStatus.PINNED -> {
|
||||
statusImage = imageHelper.pinImage
|
||||
progress.text = null
|
||||
}
|
||||
DownloadStatus.FAILED,
|
||||
DownloadStatus.CANCELLED -> {
|
||||
statusImage = imageHelper.errorImage
|
||||
progress.text = null
|
||||
}
|
||||
DownloadStatus.DOWNLOADING -> {
|
||||
statusImage = imageHelper.downloadingImage[0]
|
||||
}
|
||||
else -> {
|
||||
statusImage = null
|
||||
}
|
||||
if (status == cachedStatus) return
|
||||
cachedStatus = status
|
||||
|
||||
when (status) {
|
||||
DownloadStatus.DONE -> {
|
||||
showStatusImage(imageHelper.downloadedImage)
|
||||
}
|
||||
DownloadStatus.PINNED -> {
|
||||
showStatusImage(imageHelper.pinImage)
|
||||
}
|
||||
DownloadStatus.FAILED,
|
||||
DownloadStatus.CANCELLED -> {
|
||||
showStatusImage(imageHelper.errorImage)
|
||||
}
|
||||
DownloadStatus.DOWNLOADING -> {
|
||||
showProgress()
|
||||
}
|
||||
DownloadStatus.RETRYING,
|
||||
DownloadStatus.QUEUED -> {
|
||||
showIndefiniteProgress()
|
||||
}
|
||||
else -> {
|
||||
showStatusImage(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()
|
||||
}
|
||||
|
||||
private fun updateImages() {
|
||||
progress.post {
|
||||
progress.setCompoundDrawablesWithIntrinsicBounds(
|
||||
null, null, statusImage, null
|
||||
)
|
||||
}
|
||||
private fun showStatusImage(image: Drawable?) {
|
||||
progressIndicator.isVisible = false
|
||||
statusImage.isVisible = true
|
||||
statusImage.setImageDrawable(image)
|
||||
}
|
||||
|
||||
private fun showIndefiniteProgress() {
|
||||
statusImage.isVisible = false
|
||||
progressIndicator.isVisible = true
|
||||
progressIndicator.isIndeterminate = true
|
||||
progressIndicator.indicatorDirection =
|
||||
CircularProgressIndicator.INDICATOR_DIRECTION_COUNTERCLOCKWISE
|
||||
progressIndicator.trackThickness = INDICATOR_THICKNESS_INDEFINITE
|
||||
}
|
||||
|
||||
private fun showProgress() {
|
||||
statusImage.isVisible = false
|
||||
progressIndicator.isVisible = true
|
||||
progressIndicator.isIndeterminate = false
|
||||
progressIndicator.indicatorDirection =
|
||||
CircularProgressIndicator.INDICATOR_DIRECTION_CLOCKWISE
|
||||
progressIndicator.trackThickness = INDICATOR_THICKNESS_DEFINITE
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -375,31 +375,42 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
var unpinEnabled = false
|
||||
var deleteEnabled = false
|
||||
var downloadEnabled = false
|
||||
var isNotInProgress = true
|
||||
val multipleSelection = viewAdapter.hasMultipleSelection()
|
||||
|
||||
var pinnedCount = 0
|
||||
|
||||
for (song in selection) {
|
||||
val state = downloader.getDownloadState(song)
|
||||
if (state == DownloadStatus.DONE || state == DownloadStatus.PINNED) {
|
||||
deleteEnabled = true
|
||||
}
|
||||
if (state == DownloadStatus.PINNED) {
|
||||
pinnedCount++
|
||||
unpinEnabled = true
|
||||
}
|
||||
if (state == DownloadStatus.IDLE || state == DownloadStatus.FAILED) {
|
||||
downloadEnabled = true
|
||||
when (state) {
|
||||
DownloadStatus.DONE -> {
|
||||
deleteEnabled = true
|
||||
}
|
||||
DownloadStatus.PINNED -> {
|
||||
deleteEnabled = true
|
||||
pinnedCount++
|
||||
unpinEnabled = true
|
||||
}
|
||||
DownloadStatus.IDLE, DownloadStatus.FAILED -> {
|
||||
downloadEnabled = true
|
||||
}
|
||||
DownloadStatus.DOWNLOADING,
|
||||
DownloadStatus.QUEUED,
|
||||
DownloadStatus.RETRYING -> {
|
||||
isNotInProgress = false
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
playNowButton?.isVisible = enabled
|
||||
playNextButton?.isVisible = enabled && multipleSelection
|
||||
playLastButton?.isVisible = enabled && multipleSelection
|
||||
pinButton?.isVisible = (enabled && !isOffline() && selection.size > pinnedCount)
|
||||
unpinButton?.isVisible = (enabled && unpinEnabled)
|
||||
downloadButton?.isVisible = (enabled && downloadEnabled && !isOffline())
|
||||
deleteButton?.isVisible = (enabled && deleteEnabled)
|
||||
pinButton?.isVisible =
|
||||
isNotInProgress && enabled && !isOffline() && selection.size > pinnedCount
|
||||
unpinButton?.isVisible = isNotInProgress && enabled && unpinEnabled
|
||||
downloadButton?.isVisible = isNotInProgress && enabled && downloadEnabled && !isOffline()
|
||||
deleteButton?.isVisible = isNotInProgress && enabled && deleteEnabled
|
||||
}
|
||||
|
||||
private fun downloadBackground(save: Boolean) {
|
||||
|
@ -190,6 +190,7 @@ class Downloader(
|
||||
|
||||
// Set correct priority (the lower the number, the higher the priority)
|
||||
downloadQueue.add(DownloadableTrack(track, item.shouldBePinned(), 0, priority++))
|
||||
postState(track, DownloadStatus.QUEUED)
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +241,9 @@ class Downloader(
|
||||
@Synchronized
|
||||
fun clearBackground() {
|
||||
// Clear the pending queue
|
||||
downloadQueue.clear()
|
||||
while (!downloadQueue.isEmpty()) {
|
||||
postState(downloadQueue.remove().track, DownloadStatus.IDLE)
|
||||
}
|
||||
|
||||
// Cancel all active downloads with a low priority
|
||||
for (key in activelyDownloading.keys) {
|
||||
@ -272,6 +275,7 @@ class Downloader(
|
||||
) continue
|
||||
val file = DownloadableTrack(track, save, 0, backgroundPriorityCounter++)
|
||||
downloadQueue.add(file)
|
||||
postState(track, DownloadStatus.QUEUED)
|
||||
}
|
||||
|
||||
Timber.v("downloadBackground Checking Downloads")
|
||||
@ -288,7 +292,7 @@ class Downloader(
|
||||
}
|
||||
|
||||
private fun cancelDownload(track: Track) {
|
||||
val key = activelyDownloading.keys.singleOrNull { t -> t.track.id == track.id } ?: return
|
||||
val key = activelyDownloading.keys.singleOrNull { it.track.id == track.id } ?: return
|
||||
activelyDownloading[key]?.cancel()
|
||||
}
|
||||
|
||||
@ -304,20 +308,21 @@ class Downloader(
|
||||
fun getDownloadState(track: Track): DownloadStatus {
|
||||
if (Storage.isPathExists(track.getCompleteFile())) return DownloadStatus.DONE
|
||||
if (Storage.isPathExists(track.getPinnedFile())) return DownloadStatus.PINNED
|
||||
if (downloads.any { it.id == track.id }) return DownloadStatus.QUEUED
|
||||
|
||||
val key = activelyDownloading.keys.firstOrNull { k -> k.track.id == track.id }
|
||||
val key = activelyDownloading.keys.firstOrNull { it.track.id == track.id }
|
||||
if (key != null) {
|
||||
if (key.tryCount > 0) return DownloadStatus.RETRYING
|
||||
return DownloadStatus.DOWNLOADING
|
||||
}
|
||||
if (failedList.any { t -> t.track.id == track.id }) return DownloadStatus.FAILED
|
||||
if (failedList.any { it.track.id == track.id }) return DownloadStatus.FAILED
|
||||
return DownloadStatus.IDLE
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CHECK_INTERVAL = 5000L
|
||||
const val MAX_RETRIES = 5
|
||||
const val REFRESH_INTERVAL = 100
|
||||
const val REFRESH_INTERVAL = 50
|
||||
}
|
||||
|
||||
private fun postState(track: Track, state: DownloadStatus, progress: Int? = null) {
|
||||
@ -579,5 +584,5 @@ class Downloader(
|
||||
}
|
||||
|
||||
enum class DownloadStatus {
|
||||
IDLE, DOWNLOADING, RETRYING, FAILED, CANCELLED, DONE, PINNED, UNKNOWN
|
||||
IDLE, QUEUED, DOWNLOADING, RETRYING, FAILED, CANCELLED, DONE, PINNED, UNKNOWN
|
||||
}
|
||||
|
@ -140,21 +140,6 @@ object Util {
|
||||
toast!!.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an Int to a percentage string
|
||||
* For instance:
|
||||
*
|
||||
* * `format(99)` returns *"99 %"*.
|
||||
*
|
||||
*
|
||||
* @param percent The percent as a range from 0 - 100
|
||||
* @return The formatted string.
|
||||
*/
|
||||
@Synchronized
|
||||
fun formatPercentage(percent: Int): String {
|
||||
return min(max(percent, 0), 100).toString() + " %"
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte-count to a formatted string suitable for display to the user.
|
||||
* For instance:
|
||||
|
@ -97,19 +97,28 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Artist" />
|
||||
|
||||
<TextView
|
||||
a:id="@+id/song_status"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:drawablePadding="6dip"
|
||||
a:paddingEnd="12dip"
|
||||
a:textAlignment="textEnd"
|
||||
app:layout_constraintBottom_toTopOf="@+id/song_duration"
|
||||
<ImageView
|
||||
a:id="@+id/song_status_image"
|
||||
a:layout_width="25dp"
|
||||
a:layout_height="0dp"
|
||||
a:layout_marginEnd="10dip"
|
||||
app:layout_constraintTop_toTopOf="@id/song_details"
|
||||
app:layout_constraintBottom_toTopOf="@id/song_duration"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/song_duration"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="100%" />
|
||||
a:importantForAccessibility="no" />
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
a:id="@+id/song_status_progress"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="0dp"
|
||||
a:layout_marginEnd="10dip"
|
||||
app:indicatorSize="20dp"
|
||||
app:indicatorColor="?attr/color_menu_selected"
|
||||
app:trackColor="?attr/color_selected"
|
||||
app:layout_constraintTop_toTopOf="@id/song_details"
|
||||
app:layout_constraintBottom_toTopOf="@id/song_duration"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
a:indeterminate="true" />
|
||||
|
||||
<TextView
|
||||
a:id="@+id/song_duration"
|
||||
@ -128,7 +137,7 @@
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
app:barrierDirection="left"
|
||||
app:constraint_referenced_ids="song_status,song_duration" />
|
||||
app:constraint_referenced_ids="song_duration" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
Loading…
x
Reference in New Issue
Block a user