ultrasonic-app-subsonic-and.../ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt

288 lines
9.5 KiB
Kotlin
Raw Normal View History

package org.moire.ultrasonic.adapters
import android.graphics.drawable.AnimationDrawable
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.Checkable
import android.widget.CheckedTextView
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.view.isVisible
2021-11-15 20:01:04 +01:00
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.rxjava3.disposables.Disposable
import org.koin.core.component.KoinComponent
import org.moire.ultrasonic.R
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.DownloadStatus
import org.moire.ultrasonic.service.MusicServiceFactory
import org.moire.ultrasonic.service.RxBus
import org.moire.ultrasonic.util.Settings
import org.moire.ultrasonic.util.Util
import timber.log.Timber
/**
* Used to display songs and videos in a `ListView`.
*/
2021-11-15 20:01:04 +01:00
class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable, KoinComponent {
var check: CheckedTextView = view.findViewById(R.id.song_check)
2021-11-30 21:50:53 +01:00
private var rating: LinearLayout = view.findViewById(R.id.song_rating)
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
var downloadFile: DownloadFile? = null
private set
private var isMaximized = false
private var cachedStatus = DownloadStatus.UNKNOWN
private var statusImage: Drawable? = null
private var isPlayingCached = false
private var rxSubscription: Disposable? = null
2021-11-15 20:01:04 +01:00
var observableChecked = MutableLiveData(false)
lateinit var imageHelper: Utils.ImageHelper
fun setSong(
file: DownloadFile,
checkable: Boolean,
draggable: Boolean,
isSelected: Boolean = false
) {
2021-12-07 13:10:15 +01:00
val useFiveStarRating = Settings.useFiveStarRating
val song = file.track
downloadFile = file
entry = song
val entryDescription = Util.readableEntryDescription(song)
artist.text = entryDescription.artist
title.text = entryDescription.title
duration.text = entryDescription.duration
if (Settings.shouldShowTrackNumber && song.track != null && song.track!! > 0) {
track.text = entryDescription.trackNumber
} else {
track.isVisible = false
}
check.isVisible = (checkable && !song.isVideo)
2021-11-30 21:50:53 +01:00
initChecked(isSelected)
drag.isVisible = draggable
if (ActiveServerProvider.isOffline()) {
star.isVisible = false
rating.isVisible = false
} else {
2021-12-07 13:10:15 +01:00
setupStarButtons(song, useFiveStarRating)
}
2021-11-15 20:01:04 +01:00
updateProgress(downloadFile!!.progress.value!!)
updateStatus(downloadFile!!.status.value!!)
if (useFiveStarRating) {
setFiveStars(entry?.userRating ?: 0)
} else {
setSingleStar(entry!!.starred)
}
2021-11-30 21:50:53 +01:00
if (song.isVideo) {
artist.isVisible = false
progress.isVisible = false
}
rxSubscription = RxBus.playerStateObservable.subscribe {
2022-04-03 23:57:50 +02:00
setPlayIcon(it.index == bindingAdapterPosition && it.track == downloadFile)
}
}
fun dispose() {
rxSubscription?.dispose()
}
private fun setPlayIcon(isPlaying: Boolean) {
if (isPlaying && !isPlayingCached) {
isPlayingCached = true
title.setCompoundDrawablesWithIntrinsicBounds(
imageHelper.playingImage, null, null, null
)
} else if (!isPlaying && isPlayingCached) {
isPlayingCached = false
title.setCompoundDrawablesWithIntrinsicBounds(
0, 0, 0, 0
)
}
}
private fun setupStarButtons(song: Track, useFiveStarRating: Boolean) {
if (useFiveStarRating) {
// Hide single star
star.isVisible = false
val rating = if (song.userRating == null) 0 else song.userRating!!
setFiveStars(rating)
} else {
// Hide five stars
rating.isVisible = false
setSingleStar(song.starred)
star.setOnClickListener {
val isStarred = song.starred
val id = song.id
if (!isStarred) {
star.setImageDrawable(imageHelper.starDrawable)
song.starred = true
} else {
star.setImageDrawable(imageHelper.starHollowDrawable)
song.starred = false
}
2022-07-02 01:27:12 +02:00
// Should this be done here ?
Thread {
val musicService = MusicServiceFactory.getMusicService()
try {
if (!isStarred) {
musicService.star(id, null, null)
} else {
musicService.unstar(id, null, null)
}
} catch (all: Exception) {
Timber.e(all)
}
}.start()
}
}
}
@Suppress("MagicNumber")
private fun setFiveStars(rating: Int) {
fiveStar1.setImageDrawable(
if (rating > 0) imageHelper.starDrawable else imageHelper.starHollowDrawable
)
fiveStar2.setImageDrawable(
if (rating > 1) imageHelper.starDrawable else imageHelper.starHollowDrawable
)
fiveStar3.setImageDrawable(
if (rating > 2) imageHelper.starDrawable else imageHelper.starHollowDrawable
)
fiveStar4.setImageDrawable(
if (rating > 3) imageHelper.starDrawable else imageHelper.starHollowDrawable
)
fiveStar5.setImageDrawable(
if (rating > 4) imageHelper.starDrawable else imageHelper.starHollowDrawable
)
}
private fun setSingleStar(starred: Boolean) {
if (starred) {
if (star.drawable !== imageHelper.starDrawable) {
star.setImageDrawable(imageHelper.starDrawable)
}
} else {
if (star.drawable !== imageHelper.starHollowDrawable) {
star.setImageDrawable(imageHelper.starHollowDrawable)
}
}
}
fun updateStatus(status: DownloadStatus) {
if (status == cachedStatus) return
cachedStatus = status
when (status) {
DownloadStatus.DONE -> {
statusImage = imageHelper.downloadedImage
progress.text = null
}
DownloadStatus.PINNED -> {
statusImage = imageHelper.pinImage
progress.text = null
}
DownloadStatus.FAILED,
2021-12-18 14:33:04 +01:00
DownloadStatus.CANCELLED -> {
statusImage = imageHelper.errorImage
progress.text = null
}
DownloadStatus.DOWNLOADING -> {
statusImage = imageHelper.downloadingImage
}
else -> {
statusImage = null
}
}
updateImages()
}
fun updateProgress(p: Int) {
if (cachedStatus == DownloadStatus.DOWNLOADING) {
progress.text = Util.formatPercentage(p)
2021-11-15 20:01:04 +01:00
} else {
progress.text = null
}
}
private fun updateImages() {
progress.setCompoundDrawablesWithIntrinsicBounds(
null, null, statusImage, null
)
if (statusImage === imageHelper.downloadingImage) {
val frameAnimation = statusImage as AnimationDrawable?
frameAnimation?.setVisible(true, true)
frameAnimation?.start()
}
}
2021-11-30 21:50:53 +01:00
/*
* Set the checked value and re-init the MutableLiveData.
* If we would post a new value, there might be a short glitch where the track is shown with its
* old selection status before the posted value has been processed.
*/
private fun initChecked(newStatus: Boolean) {
observableChecked = MutableLiveData(newStatus)
2021-11-15 20:01:04 +01:00
check.isChecked = newStatus
}
2021-11-30 21:50:53 +01:00
/*
* To be correct, this method doesn't directly set the checked status.
* It only notifies the observable. If the selection tracker accepts the selection
* (might be false for Singular SelectionTrackers) then it will cause the actual modification.
*/
override fun setChecked(newStatus: Boolean) {
2021-11-15 20:01:04 +01:00
observableChecked.postValue(newStatus)
}
override fun isChecked(): Boolean {
return check.isChecked
}
override fun toggle() {
isChecked = isChecked
}
fun maximizeOrMinimize() {
isMaximized = !isMaximized
title.isSingleLine = !isMaximized
artist.isSingleLine = !isMaximized
}
}