Manage cached and downloaded tracks separately. Downloaded track are not automatically evicted and do not count towards cache storage limit. Contributes to #37. Fixed an issue where the event bus on main would be duplicated.
This commit is contained in:
parent
2eff3263d2
commit
e539cc26dd
|
@ -3,16 +3,14 @@ package com.github.apognu.otter
|
|||
import android.app.Application
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.github.apognu.otter.playback.QueueManager
|
||||
import com.github.apognu.otter.utils.Cache
|
||||
import com.github.apognu.otter.utils.Command
|
||||
import com.github.apognu.otter.utils.Event
|
||||
import com.github.apognu.otter.utils.Request
|
||||
import com.github.apognu.otter.utils.*
|
||||
import com.google.android.exoplayer2.database.ExoDatabaseProvider
|
||||
import com.google.android.exoplayer2.offline.DefaultDownloadIndex
|
||||
import com.google.android.exoplayer2.offline.DefaultDownloaderFactory
|
||||
import com.google.android.exoplayer2.offline.DownloadManager
|
||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper
|
||||
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
|
||||
import com.google.android.exoplayer2.upstream.cache.NoOpCacheEvictor
|
||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache
|
||||
import com.preference.PowerPreference
|
||||
import kotlinx.coroutines.channels.BroadcastChannel
|
||||
|
@ -36,6 +34,7 @@ class Otter : Application() {
|
|||
val progressBus: BroadcastChannel<Triple<Int, Int, Int>> = ConflatedBroadcastChannel()
|
||||
|
||||
private val exoDatabase: ExoDatabaseProvider by lazy { ExoDatabaseProvider(this) }
|
||||
|
||||
val exoCache: SimpleCache by lazy {
|
||||
PowerPreference.getDefaultFile().getInt("media_cache_size", 1).toLong().let {
|
||||
SimpleCache(
|
||||
|
@ -45,8 +44,17 @@ class Otter : Application() {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
val exoDownloadCache: SimpleCache by lazy {
|
||||
SimpleCache(
|
||||
cacheDir.resolve("downloads"),
|
||||
NoOpCacheEvictor(),
|
||||
exoDatabase
|
||||
)
|
||||
}
|
||||
|
||||
val exoDownloadManager: DownloadManager by lazy {
|
||||
DownloaderConstructorHelper(exoCache, QueueManager.factory(this)).run {
|
||||
DownloaderConstructorHelper(exoDownloadCache, QueueManager.factory(this)).run {
|
||||
DownloadManager(this@Otter, DefaultDownloadIndex(exoDatabase), DefaultDownloaderFactory(this))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,9 +41,11 @@ import kotlinx.android.synthetic.main.partial_now_playing.*
|
|||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.random.Random
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
enum class ResultCode(val code: Int) {
|
||||
|
@ -53,6 +55,8 @@ class MainActivity : AppCompatActivity() {
|
|||
private val favoriteRepository = FavoritesRepository(this)
|
||||
private val favoriteCheckRepository = FavoritedRepository(this)
|
||||
|
||||
private var bus: Job? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
@ -69,10 +73,6 @@ class MainActivity : AppCompatActivity() {
|
|||
.beginTransaction()
|
||||
.replace(R.id.container, BrowseFragment())
|
||||
.commit()
|
||||
|
||||
watchEventBus()
|
||||
|
||||
CommandBus.send(Command.RefreshService)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -116,6 +116,19 @@ class MainActivity : AppCompatActivity() {
|
|||
landscape_queue?.let {
|
||||
supportFragmentManager.beginTransaction().replace(R.id.landscape_queue, LandscapeQueueFragment()).commit()
|
||||
}
|
||||
|
||||
if (bus == null) {
|
||||
watchEventBus()
|
||||
}
|
||||
|
||||
CommandBus.send(Command.RefreshService)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
|
||||
bus?.cancel()
|
||||
bus = null
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
|
@ -212,7 +225,7 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
@SuppressLint("NewApi")
|
||||
private fun watchEventBus() {
|
||||
GlobalScope.launch(Main) {
|
||||
bus = GlobalScope.launch(Main) {
|
||||
EventBus.get().collect { message ->
|
||||
when (message) {
|
||||
is Event.LogOut -> {
|
||||
|
|
|
@ -2,6 +2,8 @@ package com.github.apognu.otter.adapters
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.view.Gravity
|
||||
|
@ -74,11 +76,23 @@ class FavoritesAdapter(private val context: Context?, private val favoriteListen
|
|||
false -> holder.favorite.setColorFilter(context.getColor(R.color.colorSelected))
|
||||
}
|
||||
|
||||
when (favorite.downloaded) {
|
||||
when (favorite.cached || favorite.downloaded) {
|
||||
true -> holder.title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.downloaded, 0, 0, 0)
|
||||
false -> holder.title.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
if (favorite.cached && !favorite.downloaded) {
|
||||
holder.title.compoundDrawables.forEach {
|
||||
it?.colorFilter = PorterDuffColorFilter(context.getColor(R.color.cached), PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
|
||||
if (favorite.downloaded) {
|
||||
holder.title.compoundDrawables.forEach {
|
||||
it?.colorFilter = PorterDuffColorFilter(context.getColor(R.color.downloaded), PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
|
||||
holder.favorite.setOnClickListener {
|
||||
favoriteListener.onToggleFavorite(favorite.id, !favorite.favorite)
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@ package com.github.apognu.otter.adapters
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.view.*
|
||||
|
@ -95,10 +94,22 @@ class TracksAdapter(private val context: Context?, private val favoriteListener:
|
|||
}
|
||||
}
|
||||
|
||||
when (track.downloaded) {
|
||||
when (track.cached || track.downloaded) {
|
||||
true -> holder.title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.downloaded, 0, 0, 0)
|
||||
false -> holder.title.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
if (track.cached && !track.downloaded) {
|
||||
holder.title.compoundDrawables.forEach {
|
||||
it?.colorFilter = PorterDuffColorFilter(context.getColor(R.color.cached), PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
|
||||
if (track.downloaded) {
|
||||
holder.title.compoundDrawables.forEach {
|
||||
it?.colorFilter = PorterDuffColorFilter(context.getColor(R.color.downloaded), PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
holder.actions.setOnClickListener {
|
||||
|
|
|
@ -9,6 +9,8 @@ import com.github.kittinunf.fuel.gson.gsonDeserializerOf
|
|||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory
|
||||
import com.google.android.exoplayer2.upstream.FileDataSource
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory
|
||||
import com.google.android.exoplayer2.util.Util
|
||||
import com.google.gson.Gson
|
||||
|
@ -28,7 +30,16 @@ class QueueManager(val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
return CacheDataSourceFactory(Otter.get().exoCache, http)
|
||||
val playbackCache = CacheDataSourceFactory(Otter.get().exoCache, http)
|
||||
|
||||
return CacheDataSourceFactory(
|
||||
Otter.get().exoDownloadCache,
|
||||
playbackCache,
|
||||
FileDataSource.Factory(),
|
||||
null,
|
||||
CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR,
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.github.apognu.otter.repositories
|
||||
|
||||
import android.content.Context
|
||||
import com.github.apognu.otter.Otter
|
||||
import com.github.apognu.otter.utils.*
|
||||
import com.github.kittinunf.fuel.Fuel
|
||||
import com.github.kittinunf.fuel.coroutines.awaitByteArrayResponseResult
|
||||
|
@ -26,6 +27,13 @@ class FavoritesRepository(override val context: Context?) : Repository<Track, Tr
|
|||
data.map { track ->
|
||||
track.favorite = true
|
||||
track.downloaded = downloaded.contains(track.id)
|
||||
|
||||
track.bestUpload()?.let { upload ->
|
||||
val url = mustNormalizeUrl(upload.listen_url)
|
||||
|
||||
track.cached = Otter.get().exoCache.isCached(url, 0, upload.duration * 1000L)
|
||||
}
|
||||
|
||||
track
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.github.apognu.otter.repositories
|
||||
|
||||
import android.content.Context
|
||||
import com.github.apognu.otter.Otter
|
||||
import com.github.apognu.otter.utils.*
|
||||
import com.github.kittinunf.fuel.gson.gsonDeserializerOf
|
||||
import com.google.android.exoplayer2.offline.Download
|
||||
|
@ -48,6 +49,13 @@ class TracksRepository(override val context: Context?, albumId: Int) : Repositor
|
|||
data.map { track ->
|
||||
track.favorite = favorites.contains(track.id)
|
||||
track.downloaded = downloaded.contains(track.id)
|
||||
|
||||
track.bestUpload()?.let { upload ->
|
||||
val url = mustNormalizeUrl(upload.listen_url)
|
||||
|
||||
track.cached = Otter.get().exoCache.isCached(url, 0, upload.duration * 1000L)
|
||||
}
|
||||
|
||||
track
|
||||
}.sortedBy { it.position }
|
||||
}
|
||||
|
|
|
@ -100,6 +100,7 @@ data class Track(
|
|||
) : SearchResult {
|
||||
var current: Boolean = false
|
||||
var favorite: Boolean = false
|
||||
var cached: Boolean = false
|
||||
var downloaded: Boolean = false
|
||||
|
||||
data class Upload(
|
||||
|
|
|
@ -16,4 +16,7 @@
|
|||
|
||||
<color name="whiteWhileLight">#000000</color>
|
||||
<color name="blackWhileLight">#ffffff</color>
|
||||
|
||||
<color name="downloaded">@color/controlColor</color>
|
||||
<color name="cached">#aeaeae</color>
|
||||
</resources>
|
|
@ -18,4 +18,7 @@
|
|||
|
||||
<color name="whiteWhileLight">#ffffff</color>
|
||||
<color name="blackWhileLight">#000000</color>
|
||||
|
||||
<color name="downloaded">@color/colorPrimary</color>
|
||||
<color name="cached">#999999</color>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue