diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 5da218cd..ec516055 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -25,7 +25,7 @@ viewModelKtx = "2.4.1"
retrofit = "2.9.0"
jackson = "2.10.1"
-okhttp = "4.9.1"
+okhttp = "4.10.0"
koin = "3.0.2"
picasso = "2.71828"
diff --git a/ultrasonic/lint-baseline.xml b/ultrasonic/lint-baseline.xml
index 032bf5a6..cb0bd135 100644
--- a/ultrasonic/lint-baseline.xml
+++ b/ultrasonic/lint-baseline.xml
@@ -19,7 +19,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -59,6 +59,17 @@
column="10"/>
+
+
+
+
-
-
-
-
+
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Scrobbler.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Scrobbler.java
index 59a428e7..27ea8172 100644
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/Scrobbler.java
+++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/Scrobbler.java
@@ -2,6 +2,7 @@ package org.moire.ultrasonic.service;
import timber.log.Timber;
import org.moire.ultrasonic.data.ActiveServerProvider;
+import org.moire.ultrasonic.domain.Track;
/**
* Scrobbles played songs to Last.fm.
@@ -14,12 +15,11 @@ public class Scrobbler
private String lastSubmission;
private String lastNowPlaying;
- public void scrobble(final DownloadFile song, final boolean submission)
+ public void scrobble(final Track song, final boolean submission)
{
if (song == null || !ActiveServerProvider.Companion.isScrobblingEnabled()) return;
- final String id = song.getTrack().getId();
- if (id == null) return;
+ final String id = song.getId();
// Avoid duplicate registrations.
if (submission && id.equals(lastSubmission)) return;
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt
index 4efe0338..f6b96d10 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewBinder.kt
@@ -9,16 +9,13 @@ import android.view.ViewGroup
import androidx.lifecycle.LifecycleOwner
import com.drakeet.multitype.ItemViewBinder
import org.koin.core.component.KoinComponent
-import org.koin.core.component.inject
import org.moire.ultrasonic.R
import org.moire.ultrasonic.domain.Identifiable
import org.moire.ultrasonic.domain.Track
-import org.moire.ultrasonic.service.DownloadFile
-import org.moire.ultrasonic.service.Downloader
class TrackViewBinder(
- val onItemClick: (DownloadFile, Int) -> Unit,
- val onContextMenuClick: ((MenuItem, DownloadFile) -> Boolean)? = null,
+ val onItemClick: (Track, Int) -> Unit,
+ val onContextMenuClick: ((MenuItem, Track) -> Boolean)? = null,
val checkable: Boolean,
val draggable: Boolean,
context: Context,
@@ -31,7 +28,6 @@ class TrackViewBinder(
val layout = R.layout.list_item_track
private val contextMenuLayout = R.menu.context_menu_track
- private val downloader: Downloader by inject()
private val imageHelper: Utils.ImageHelper = Utils.ImageHelper(context)
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): TrackViewHolder {
@@ -43,11 +39,8 @@ class TrackViewBinder(
override fun onBindViewHolder(holder: TrackViewHolder, item: Identifiable) {
val diffAdapter = adapter as BaseAdapter<*>
- val downloadFile: DownloadFile = when (item) {
+ val track: Track = when (item) {
is Track -> {
- downloader.getDownloadFileForSong(item)
- }
- is DownloadFile -> {
item
}
else -> {
@@ -61,7 +54,7 @@ class TrackViewBinder(
holder.observableChecked.removeObservers(lifecycleOwner)
holder.setSong(
- file = downloadFile,
+ song = track,
checkable = checkable,
draggable = draggable,
diffAdapter.isSelected(item.longId)
@@ -72,11 +65,11 @@ class TrackViewBinder(
val popup = Utils.createPopupMenu(holder.itemView, contextMenuLayout)
popup.setOnMenuItemClickListener { menuItem ->
- onContextMenuClick.invoke(menuItem, downloadFile)
+ onContextMenuClick.invoke(menuItem, track)
}
} else {
// Minimize or maximize the Text view (if song title is very long)
- if (!downloadFile.track.isDirectory) {
+ if (!track.isDirectory) {
holder.maximizeOrMinimize()
}
}
@@ -85,11 +78,11 @@ class TrackViewBinder(
}
holder.itemView.setOnClickListener {
- if (checkable && !downloadFile.track.isVideo) {
+ if (checkable && !track.isVideo) {
val nowChecked = !holder.check.isChecked
holder.isChecked = nowChecked
} else {
- onItemClick(downloadFile, holder.bindingAdapterPosition)
+ onItemClick(track, holder.bindingAdapterPosition)
}
}
@@ -119,20 +112,6 @@ class TrackViewBinder(
if (newStatus != holder.check.isChecked) holder.check.isChecked = newStatus
}
-
- // Observe download status
- downloadFile.status.observe(
- lifecycleOwner
- ) {
- holder.updateStatus(it)
- diffAdapter.notifyChanged()
- }
-
- downloadFile.progress.observe(
- lifecycleOwner
- ) {
- holder.updateProgress(it)
- }
}
override fun onViewRecycled(holder: TrackViewHolder) {
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt
index 95f47112..75aa02a7 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt
@@ -11,15 +11,17 @@ import android.widget.TextView
import androidx.core.view.isVisible
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.RecyclerView
-import io.reactivex.rxjava3.disposables.Disposable
+import io.reactivex.rxjava3.disposables.CompositeDisposable
import org.koin.core.component.KoinComponent
+import org.koin.core.component.inject
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.Downloader
import org.moire.ultrasonic.service.MusicServiceFactory
import org.moire.ultrasonic.service.RxBus
+import org.moire.ultrasonic.service.plusAssign
import org.moire.ultrasonic.util.Settings
import org.moire.ultrasonic.util.Util
import timber.log.Timber
@@ -29,6 +31,8 @@ import timber.log.Timber
*/
class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable, KoinComponent {
+ private val downloader: Downloader by inject()
+
var check: CheckedTextView = view.findViewById(R.id.song_check)
private var rating: LinearLayout = view.findViewById(R.id.song_five_star)
private var fiveStar1: ImageView = view.findViewById(R.id.song_five_star_1)
@@ -46,29 +50,25 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
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
+ private var rxBusSubscription: CompositeDisposable? = null
var observableChecked = MutableLiveData(false)
lateinit var imageHelper: Utils.ImageHelper
fun setSong(
- file: DownloadFile,
+ song: Track,
checkable: Boolean,
draggable: Boolean,
isSelected: Boolean = false
) {
val useFiveStarRating = Settings.useFiveStarRating
- val song = file.track
- downloadFile = file
entry = song
val entryDescription = Util.readableEntryDescription(song)
@@ -94,8 +94,8 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
setupStarButtons(song, useFiveStarRating)
}
- updateProgress(downloadFile!!.progress.value!!)
- updateStatus(downloadFile!!.status.value!!)
+ updateStatus(downloader.getDownloadState(song))
+ updateProgress(0)
if (useFiveStarRating) {
setFiveStars(entry?.userRating ?: 0)
@@ -108,13 +108,22 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
progress.isVisible = false
}
- rxSubscription = RxBus.playerStateObservable.subscribe {
- setPlayIcon(it.index == bindingAdapterPosition && it.track == downloadFile)
+ // Create new Disposable for the new Subscriptions
+ rxBusSubscription = CompositeDisposable()
+ rxBusSubscription!! += RxBus.playerStateObservable.subscribe {
+ setPlayIcon(it.index == bindingAdapterPosition && it.track?.id == song.id)
+ }
+
+ rxBusSubscription!! += RxBus.trackDownloadStateObservable.subscribe {
+ if (it.track.id != song.id) return@subscribe
+ updateStatus(it.state)
+ updateProgress(it.progress)
}
}
+ // This is called when the Holder is recycled and receives a new Song
fun dispose() {
- rxSubscription?.dispose()
+ rxBusSubscription?.dispose()
}
private fun setPlayIcon(isPlaying: Boolean) {
@@ -198,7 +207,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
}
}
- fun updateStatus(status: DownloadStatus) {
+ private fun updateStatus(status: DownloadStatus) {
if (status == cachedStatus) return
cachedStatus = status
@@ -227,7 +236,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable
updateImages()
}
- fun updateProgress(p: Int) {
+ private fun updateProgress(p: Int) {
if (cachedStatus == DownloadStatus.DOWNLOADING) {
progress.text = Util.formatPercentage(p)
} else {
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt
index 658893ea..70f6968b 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MediaPlayerModule.kt
@@ -1,7 +1,6 @@
package org.moire.ultrasonic.di
import org.koin.dsl.module
-import org.moire.ultrasonic.playback.LegacyPlaylistManager
import org.moire.ultrasonic.service.Downloader
import org.moire.ultrasonic.service.ExternalStorageMonitor
import org.moire.ultrasonic.service.JukeboxMediaPlayer
@@ -13,13 +12,12 @@ import org.moire.ultrasonic.service.PlaybackStateSerializer
* This Koin module contains the registration of classes related to the media player
*/
val mediaPlayerModule = module {
- single { JukeboxMediaPlayer(get()) }
+ single { JukeboxMediaPlayer() }
single { MediaPlayerLifecycleSupport() }
single { PlaybackStateSerializer() }
single { ExternalStorageMonitor() }
- single { LegacyPlaylistManager() }
- single { Downloader(get(), get()) }
+ single { Downloader(get()) }
// TODO Ideally this can be cleaned up when all circular references are removed.
- single { MediaPlayerController(get(), get(), get(), get(), get()) }
+ single { MediaPlayerController(get(), get(), get(), get()) }
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt
index 913154b4..3b6b302b 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/DownloadsFragment.kt
@@ -17,8 +17,8 @@ import androidx.lifecycle.LiveData
import org.koin.core.component.inject
import org.moire.ultrasonic.R
import org.moire.ultrasonic.adapters.TrackViewBinder
+import org.moire.ultrasonic.domain.Track
import org.moire.ultrasonic.model.GenericListModel
-import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.Downloader
import org.moire.ultrasonic.util.Util
@@ -31,7 +31,7 @@ import org.moire.ultrasonic.util.Util
*
* TODO: Add code to enable manipulation of the download list
*/
-class DownloadsFragment : MultiListFragment() {
+class DownloadsFragment : MultiListFragment