mirror of
https://github.com/clangen/musikcube.git
synced 2025-01-02 11:58:27 +00:00
More improvements to dependency injection -- databases and more service
related things are injected with correct scoping rules. Removed remaining static state from Application subclass.
This commit is contained in:
parent
5d9e24019d
commit
33d3c1544c
@ -1,15 +1,11 @@
|
||||
package io.casey.musikcube.remote
|
||||
|
||||
import android.arch.persistence.room.Room
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import com.facebook.stetho.Stetho
|
||||
import io.casey.musikcube.remote.injection.AppComponent
|
||||
import io.casey.musikcube.remote.injection.AppModule
|
||||
import io.casey.musikcube.remote.injection.DaggerAppComponent
|
||||
import io.casey.musikcube.remote.injection.ServiceModule
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.StreamProxy
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.offline.OfflineDb
|
||||
import io.casey.musikcube.remote.ui.settings.model.ConnectionsDb
|
||||
import io.casey.musikcube.remote.ui.shared.util.NetworkUtil
|
||||
import io.fabric.sdk.android.Fabric
|
||||
|
||||
@ -32,17 +28,6 @@ class Application : android.app.Application() {
|
||||
}
|
||||
|
||||
NetworkUtil.init()
|
||||
StreamProxy.init(this)
|
||||
|
||||
offlineDb = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
OfflineDb::class.java,
|
||||
"offline").build()
|
||||
|
||||
connectionsDb = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
ConnectionsDb::class.java,
|
||||
"connections").build()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -50,11 +35,5 @@ class Application : android.app.Application() {
|
||||
|
||||
var instance: Application? = null
|
||||
private set
|
||||
|
||||
var offlineDb: OfflineDb? = null
|
||||
private set
|
||||
|
||||
var connectionsDb: ConnectionsDb? = null
|
||||
private set
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,24 @@
|
||||
package io.casey.musikcube.remote.injection
|
||||
|
||||
import android.content.Context
|
||||
import dagger.Component
|
||||
import io.casey.musikcube.remote.service.gapless.GaplessHeaderService
|
||||
import io.casey.musikcube.remote.service.gapless.db.GaplessDb
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.StreamProxy
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.db.OfflineDb
|
||||
import io.casey.musikcube.remote.service.websocket.WebSocketService
|
||||
import io.casey.musikcube.remote.service.websocket.model.IDataProvider
|
||||
import io.casey.musikcube.remote.ui.settings.model.ConnectionsDb
|
||||
|
||||
@ApplicationScope
|
||||
@Component(modules = arrayOf(AppModule::class, ServiceModule::class))
|
||||
@Component(modules = arrayOf(AppModule::class, DataModule::class, ServiceModule::class))
|
||||
interface AppComponent {
|
||||
fun webSocketService(): WebSocketService
|
||||
fun webSocketService(): WebSocketService /* via ServiceModule */
|
||||
fun gaplessHeaderService(): GaplessHeaderService /* via ServiceModule */
|
||||
fun streamProxy(): StreamProxy /* via ServiceModule */
|
||||
fun offlineDb(): OfflineDb /* via DataModule */
|
||||
fun gaplessDb(): GaplessDb /* via DataModule */
|
||||
fun connectionsDb(): ConnectionsDb /* via DataModule */
|
||||
fun dataProvider(): IDataProvider /* via DataModule */
|
||||
fun context(): Context /* via AppModule */
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
package io.casey.musikcube.remote.injection
|
||||
|
||||
import android.content.Context
|
||||
import dagger.Component
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.offline.OfflineDb
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.db.OfflineDb
|
||||
|
||||
@DataScope
|
||||
@Component(
|
||||
dependencies = arrayOf(AppComponent::class),
|
||||
modules = arrayOf())
|
||||
modules = arrayOf(DataModule::class))
|
||||
interface DataComponent {
|
||||
fun inject(db: OfflineDb)
|
||||
|
||||
fun context(): Context /* via AppComponent */
|
||||
}
|
@ -1,13 +1,36 @@
|
||||
package io.casey.musikcube.remote.injection
|
||||
|
||||
import android.arch.persistence.room.Room
|
||||
import android.content.Context
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.casey.musikcube.remote.service.gapless.db.GaplessDb
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.db.OfflineDb
|
||||
import io.casey.musikcube.remote.service.websocket.WebSocketService
|
||||
import io.casey.musikcube.remote.service.websocket.model.IDataProvider
|
||||
import io.casey.musikcube.remote.service.websocket.model.impl.remote.RemoteDataProvider
|
||||
import io.casey.musikcube.remote.ui.settings.model.ConnectionsDb
|
||||
|
||||
@Module
|
||||
class DataModule {
|
||||
@Provides
|
||||
fun providesDataProvider(wss: WebSocketService): IDataProvider = RemoteDataProvider(wss)
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun providesOfflineDb(context: Context): OfflineDb {
|
||||
return Room.databaseBuilder(context, OfflineDb::class.java, "offline").build()
|
||||
}
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun providesConnectionsDb(context: Context): ConnectionsDb {
|
||||
return Room.databaseBuilder(context, ConnectionsDb::class.java, "connections").build()
|
||||
}
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun providesGaplessDb(context: Context): GaplessDb {
|
||||
return Room.databaseBuilder(context, GaplessDb::class.java, "gapless").build()
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package io.casey.musikcube.remote.injection
|
||||
|
||||
import dagger.Component
|
||||
import io.casey.musikcube.remote.service.playback.PlayerWrapper
|
||||
|
||||
@PlaybackScope
|
||||
@Component(dependencies = arrayOf(AppComponent::class))
|
||||
interface PlaybackComponent {
|
||||
fun inject(playerWrapper: PlayerWrapper)
|
||||
}
|
@ -16,4 +16,8 @@ annotation class ServiceScope
|
||||
|
||||
@Scope
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class DataScope
|
||||
annotation class DataScope
|
||||
|
||||
@Scope
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class PlaybackScope
|
@ -1,14 +1,14 @@
|
||||
package io.casey.musikcube.remote.injection
|
||||
|
||||
import dagger.Component
|
||||
import io.casey.musikcube.remote.service.gapless.GaplessHeaderService
|
||||
import io.casey.musikcube.remote.service.playback.impl.remote.RemotePlaybackService
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.StreamingPlaybackService
|
||||
|
||||
@ServiceScope
|
||||
@Component(
|
||||
dependencies = arrayOf(AppComponent::class),
|
||||
modules = arrayOf(DataModule::class))
|
||||
@Component(dependencies = arrayOf(AppComponent::class))
|
||||
interface ServiceComponent {
|
||||
fun inject(service: StreamingPlaybackService)
|
||||
fun inject(service: RemotePlaybackService)
|
||||
fun inject(service: GaplessHeaderService)
|
||||
}
|
@ -3,6 +3,8 @@ package io.casey.musikcube.remote.injection
|
||||
import android.content.Context
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.casey.musikcube.remote.service.gapless.GaplessHeaderService
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.StreamProxy
|
||||
import io.casey.musikcube.remote.service.websocket.WebSocketService
|
||||
|
||||
@Module
|
||||
@ -12,4 +14,16 @@ class ServiceModule {
|
||||
fun providesWebSocketService(context: Context): WebSocketService {
|
||||
return WebSocketService(context)
|
||||
}
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun providesGaplessHeaderService(): GaplessHeaderService {
|
||||
return GaplessHeaderService()
|
||||
}
|
||||
|
||||
@ApplicationScope
|
||||
@Provides
|
||||
fun providesStreamProxy(context: Context): StreamProxy {
|
||||
return StreamProxy(context)
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,7 @@ import io.casey.musikcube.remote.ui.shared.view.EmptyListView
|
||||
import io.casey.musikcube.remote.ui.tracks.activity.TrackListActivity
|
||||
|
||||
@ViewScope
|
||||
@Component(
|
||||
dependencies = arrayOf(AppComponent::class),
|
||||
modules = arrayOf(DataModule::class))
|
||||
@Component(dependencies = arrayOf(AppComponent::class))
|
||||
interface ViewComponent {
|
||||
fun inject(activity: ConnectionsActivity)
|
||||
fun inject(activity: MainActivity)
|
||||
|
@ -0,0 +1,17 @@
|
||||
package io.casey.musikcube.remote.service.gapless
|
||||
|
||||
import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.injection.DaggerServiceComponent
|
||||
import io.casey.musikcube.remote.injection.DataModule
|
||||
import io.casey.musikcube.remote.service.gapless.db.GaplessDb
|
||||
import javax.inject.Inject
|
||||
|
||||
class GaplessHeaderService {
|
||||
@Inject lateinit var db: GaplessDb
|
||||
|
||||
init {
|
||||
DaggerServiceComponent.builder()
|
||||
.appComponent(Application.appComponent)
|
||||
.build().inject(this)
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package io.casey.musikcube.remote.service.gapless.db;
|
||||
|
||||
import android.arch.persistence.room.Dao;
|
||||
|
||||
@Dao
|
||||
public class GaplessDao {
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package io.casey.musikcube.remote.service.gapless.db
|
||||
|
||||
import android.arch.persistence.room.Database
|
||||
import android.arch.persistence.room.RoomDatabase
|
||||
|
||||
@Database(entities = arrayOf(GaplessTrack::class), version = 1)
|
||||
abstract class GaplessDb: RoomDatabase() {
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package io.casey.musikcube.remote.service.gapless.db
|
||||
|
||||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
|
||||
@Entity
|
||||
data class GaplessTrack(@PrimaryKey val id: Long, val url: String, val state: Int)
|
@ -2,10 +2,13 @@ package io.casey.musikcube.remote.service.playback
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.injection.*
|
||||
import io.casey.musikcube.remote.service.playback.impl.player.ExoPlayerWrapper
|
||||
import io.casey.musikcube.remote.service.playback.impl.player.GaplessExoPlayerWrapper
|
||||
import io.casey.musikcube.remote.service.playback.impl.player.MediaPlayerWrapper
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.offline.OfflineTrack
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.StreamProxy
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.db.OfflineDb
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.db.OfflineTrack
|
||||
import io.casey.musikcube.remote.service.websocket.model.ITrack
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs
|
||||
import io.casey.musikcube.remote.util.Preconditions
|
||||
@ -13,8 +16,18 @@ import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class PlayerWrapper {
|
||||
@Inject lateinit var offlineDb: OfflineDb
|
||||
@Inject lateinit var streamProxy: StreamProxy
|
||||
|
||||
init {
|
||||
DaggerPlaybackComponent.builder()
|
||||
.appComponent(Application.appComponent)
|
||||
.build().inject(this)
|
||||
}
|
||||
|
||||
private enum class Type(prefIndex: Int) {
|
||||
ExoPlayer(0), ExoPlayerGapless(1), MediaPlayer(2);
|
||||
|
||||
@ -69,6 +82,19 @@ abstract class PlayerWrapper {
|
||||
this.listener?.invoke(this, this.state)
|
||||
}
|
||||
|
||||
protected fun storeOffline(uri: String, metadata: ITrack) {
|
||||
Single.fromCallable {
|
||||
val track = OfflineTrack()
|
||||
if (track.fromJSONObject(uri, metadata.toJson())) {
|
||||
offlineDb.trackDao().insertTrack(track)
|
||||
offlineDb.prune()
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TYPE = Type.ExoPlayer
|
||||
private val DUCK_COEF = 0.2f /* volume = 20% when ducked */
|
||||
@ -79,19 +105,6 @@ abstract class PlayerWrapper {
|
||||
private var globalMuted = false
|
||||
private var preDuckGlobalVolume = DUCK_NONE
|
||||
|
||||
fun storeOffline(uri: String, metadata: ITrack) {
|
||||
Single.fromCallable {
|
||||
val track = OfflineTrack()
|
||||
if (track.fromJSONObject(uri, metadata.toJson())) {
|
||||
Application.offlineDb?.trackDao()?.insertTrack(track)
|
||||
Application.offlineDb?.prune()
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
fun duck() {
|
||||
Preconditions.throwIfNotOnMainThread()
|
||||
|
||||
|
@ -60,7 +60,7 @@ class ExoPlayerWrapper : PlayerWrapper() {
|
||||
if (!dead()) {
|
||||
this.metadata = metadata
|
||||
this.originalUri = uri
|
||||
this.proxyUri = StreamProxy.getProxyUrl(uri)
|
||||
this.proxyUri = streamProxy.getProxyUrl(uri)
|
||||
Log.d("ExoPlayerWrapper", "originalUri: ${this.originalUri} proxyUri: ${this.proxyUri}")
|
||||
|
||||
addCacheListener()
|
||||
@ -79,7 +79,7 @@ class ExoPlayerWrapper : PlayerWrapper() {
|
||||
if (!dead()) {
|
||||
this.metadata = metadata
|
||||
this.originalUri = uri
|
||||
this.proxyUri = StreamProxy.getProxyUrl(uri)
|
||||
this.proxyUri = streamProxy.getProxyUrl(uri)
|
||||
Log.d("ExoPlayerWrapper", "originalUri: ${this.originalUri} proxyUri: ${this.proxyUri}")
|
||||
|
||||
this.prefetch = true
|
||||
@ -200,7 +200,7 @@ class ExoPlayerWrapper : PlayerWrapper() {
|
||||
}
|
||||
|
||||
private fun addCacheListener() {
|
||||
if (StreamProxy.isCached(this.originalUri!!)) {
|
||||
if (streamProxy.isCached(this.originalUri!!)) {
|
||||
percentAvailable = 100
|
||||
|
||||
if (originalUri != null && metadata != null) {
|
||||
@ -208,12 +208,12 @@ class ExoPlayerWrapper : PlayerWrapper() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
StreamProxy.registerCacheListener(this.cacheListener, this.originalUri!!)
|
||||
streamProxy.registerCacheListener(this.cacheListener, this.originalUri!!)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeCacheListener() {
|
||||
StreamProxy.unregisterCacheListener(this.cacheListener)
|
||||
streamProxy.unregisterCacheListener(this.cacheListener)
|
||||
}
|
||||
|
||||
private val cacheListener = CacheListener { _: File, _: String, percent: Int ->
|
||||
|
@ -51,7 +51,7 @@ class GaplessExoPlayerWrapper : PlayerWrapper() {
|
||||
|
||||
this.metadata = metadata
|
||||
this.originalUri = uri
|
||||
this.proxyUri = StreamProxy.getProxyUrl(uri)
|
||||
this.proxyUri = streamProxy.getProxyUrl(uri)
|
||||
|
||||
addCacheListener()
|
||||
|
||||
@ -71,7 +71,7 @@ class GaplessExoPlayerWrapper : PlayerWrapper() {
|
||||
|
||||
this.metadata = metadata
|
||||
this.originalUri = uri
|
||||
this.proxyUri = StreamProxy.getProxyUrl(uri)
|
||||
this.proxyUri = streamProxy.getProxyUrl(uri)
|
||||
this.prefetch = true
|
||||
this.source = ExtractorMediaSource(Uri.parse(proxyUri), sourceFactory, extractorsFactory, null, null)
|
||||
|
||||
@ -177,7 +177,7 @@ class GaplessExoPlayerWrapper : PlayerWrapper() {
|
||||
}
|
||||
|
||||
private fun addCacheListener() {
|
||||
if (StreamProxy.isCached(this.originalUri!!)) {
|
||||
if (streamProxy.isCached(this.originalUri!!)) {
|
||||
percentAvailable = 100
|
||||
|
||||
if (originalUri != null && metadata != null) {
|
||||
@ -185,12 +185,12 @@ class GaplessExoPlayerWrapper : PlayerWrapper() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
StreamProxy.registerCacheListener(this.cacheListener, this.originalUri!!)
|
||||
streamProxy.registerCacheListener(this.cacheListener, this.originalUri!!)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeCacheListener() {
|
||||
StreamProxy.unregisterCacheListener(this.cacheListener)
|
||||
streamProxy.unregisterCacheListener(this.cacheListener)
|
||||
}
|
||||
|
||||
private val cacheListener = CacheListener { _: File, _: String, percent: Int ->
|
||||
|
@ -45,7 +45,7 @@ class MediaPlayerWrapper : PlayerWrapper() {
|
||||
|
||||
this.metadata = metadata
|
||||
this.originalUri = uri
|
||||
this.proxyUri = StreamProxy.getProxyUrl(uri)
|
||||
this.proxyUri = streamProxy.getProxyUrl(uri)
|
||||
|
||||
player.setDataSource(context, Uri.parse(proxyUri), headers)
|
||||
player.setAudioStreamType(AudioManager.STREAM_MUSIC)
|
||||
|
@ -3,7 +3,6 @@ package io.casey.musikcube.remote.service.playback.impl.remote
|
||||
import android.os.Handler
|
||||
import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.injection.DaggerServiceComponent
|
||||
import io.casey.musikcube.remote.injection.DataModule
|
||||
import io.casey.musikcube.remote.service.playback.IPlaybackService
|
||||
import io.casey.musikcube.remote.service.playback.PlaybackState
|
||||
import io.casey.musikcube.remote.service.playback.RepeatMode
|
||||
@ -149,7 +148,6 @@ class RemotePlaybackService : IPlaybackService {
|
||||
init {
|
||||
DaggerServiceComponent.builder()
|
||||
.appComponent(Application.appComponent)
|
||||
.dataModule(DataModule())
|
||||
.build().inject(this)
|
||||
|
||||
reset()
|
||||
|
@ -7,18 +7,46 @@ import android.util.Base64
|
||||
import com.danikula.videocache.CacheListener
|
||||
import com.danikula.videocache.HttpProxyCacheServer
|
||||
import com.danikula.videocache.file.Md5FileNameGenerator
|
||||
import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs
|
||||
import io.casey.musikcube.remote.ui.shared.util.NetworkUtil
|
||||
import io.casey.musikcube.remote.util.Strings
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class StreamProxy private constructor(context: Context) {
|
||||
private val proxy: HttpProxyCacheServer
|
||||
class StreamProxy(private val context: Context) {
|
||||
private lateinit var proxy: HttpProxyCacheServer
|
||||
private val prefs: SharedPreferences = context.getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
|
||||
|
||||
init {
|
||||
restart()
|
||||
}
|
||||
|
||||
@Synchronized fun registerCacheListener(cl: CacheListener, uri: String) {
|
||||
proxy.registerCacheListener(cl, uri) /* let it throw */
|
||||
}
|
||||
|
||||
@Synchronized fun unregisterCacheListener(cl: CacheListener) {
|
||||
proxy.unregisterCacheListener(cl)
|
||||
}
|
||||
|
||||
@Synchronized fun isCached(url: String): Boolean {
|
||||
return proxy.isCached(url)
|
||||
}
|
||||
|
||||
@Synchronized fun getProxyUrl(url: String): String {
|
||||
return proxy.getProxyUrl(url)
|
||||
}
|
||||
|
||||
@Synchronized fun getProxyFilename(url: String): String {
|
||||
return FILENAME_GENERATOR(url)
|
||||
}
|
||||
|
||||
@Synchronized fun reload() {
|
||||
proxy.shutdown()
|
||||
restart()
|
||||
}
|
||||
|
||||
private fun restart() {
|
||||
if (this.prefs.getBoolean(Prefs.Key.CERT_VALIDATION_DISABLED, Prefs.Default.CERT_VALIDATION_DISABLED)) {
|
||||
NetworkUtil.disableCertificateValidation()
|
||||
}
|
||||
@ -45,40 +73,13 @@ class StreamProxy private constructor(context: Context) {
|
||||
headers.put("Authorization", "Basic " + encoded)
|
||||
headers
|
||||
}
|
||||
.fileNameGenerator gen@ { url ->
|
||||
try {
|
||||
val uri = Uri.parse(url)
|
||||
/* format matches: audio/external_id/<id> */
|
||||
val segments = uri.pathSegments
|
||||
if (segments.size == 3 && "external_id" == segments[1]) {
|
||||
/* url params, hyphen separated */
|
||||
var params = uri.query
|
||||
if (Strings.notEmpty(params)) {
|
||||
params = "-" + params
|
||||
.replace("?", "-")
|
||||
.replace("&", "-")
|
||||
.replace("=", "-")
|
||||
}
|
||||
else {
|
||||
params = ""
|
||||
}
|
||||
|
||||
return@gen "${segments[2]}$params"
|
||||
}
|
||||
}
|
||||
catch (ex: Exception) {
|
||||
/* eh... */
|
||||
}
|
||||
|
||||
return@gen DEFAULT_FILENAME_GENERATOR.generate(url)
|
||||
}
|
||||
.fileNameGenerator(FILENAME_GENERATOR)
|
||||
.build()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ENABLED = true
|
||||
val BYTES_PER_MEGABYTE = 1048576L
|
||||
val BYTES_PER_GIGABYTE = 1073741824L
|
||||
private val BYTES_PER_MEGABYTE = 1048576L
|
||||
private val BYTES_PER_GIGABYTE = 1073741824L
|
||||
val MINIMUM_CACHE_SIZE_BYTES = BYTES_PER_MEGABYTE * 128
|
||||
|
||||
val CACHE_SETTING_TO_BYTES: MutableMap<Int, Long> = mutableMapOf(
|
||||
@ -91,34 +92,32 @@ class StreamProxy private constructor(context: Context) {
|
||||
|
||||
private val DEFAULT_FILENAME_GENERATOR = Md5FileNameGenerator()
|
||||
|
||||
private var INSTANCE: StreamProxy? = null
|
||||
private val FILENAME_GENERATOR: (String) -> String = gen@ { url ->
|
||||
try {
|
||||
val uri = Uri.parse(url)
|
||||
/* format matches: audio/external_id/<id> */
|
||||
val segments = uri.pathSegments
|
||||
if (segments.size == 3 && "external_id" == segments[1]) {
|
||||
/* url params, hyphen separated */
|
||||
var params = uri.query
|
||||
if (Strings.notEmpty(params)) {
|
||||
params = "-" + params
|
||||
.replace("?", "-")
|
||||
.replace("&", "-")
|
||||
.replace("=", "-")
|
||||
}
|
||||
else {
|
||||
params = ""
|
||||
}
|
||||
|
||||
@Synchronized fun init(context: Context) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = StreamProxy(context.applicationContext)
|
||||
return@gen "${segments[2]}$params"
|
||||
}
|
||||
}
|
||||
catch (ex: Exception) {
|
||||
/* eh... */
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized fun registerCacheListener(cl: CacheListener, uri: String) {
|
||||
INSTANCE?.proxy?.registerCacheListener(cl, uri) /* let it throw */
|
||||
}
|
||||
|
||||
@Synchronized fun unregisterCacheListener(cl: CacheListener) {
|
||||
INSTANCE?.proxy?.unregisterCacheListener(cl)
|
||||
}
|
||||
|
||||
@Synchronized fun isCached(url: String): Boolean {
|
||||
return INSTANCE?.proxy?.isCached(url)!!
|
||||
}
|
||||
|
||||
@Synchronized fun getProxyUrl(url: String): String {
|
||||
init(Application.instance!!)
|
||||
return if (ENABLED) INSTANCE?.proxy?.getProxyUrl(url)!! else url
|
||||
}
|
||||
|
||||
@Synchronized fun reload() {
|
||||
INSTANCE?.proxy?.shutdown()
|
||||
INSTANCE = null
|
||||
return@gen DEFAULT_FILENAME_GENERATOR.generate(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import android.util.Log
|
||||
import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.R
|
||||
import io.casey.musikcube.remote.injection.DaggerServiceComponent
|
||||
import io.casey.musikcube.remote.injection.DataModule
|
||||
import io.casey.musikcube.remote.service.playback.IPlaybackService
|
||||
import io.casey.musikcube.remote.service.playback.PlaybackState
|
||||
import io.casey.musikcube.remote.service.playback.PlayerWrapper
|
||||
@ -141,7 +140,6 @@ class StreamingPlaybackService(context: Context) : IPlaybackService {
|
||||
init {
|
||||
DaggerServiceComponent.builder()
|
||||
.appComponent(Application.appComponent)
|
||||
.dataModule(DataModule())
|
||||
.build().inject(this)
|
||||
}
|
||||
|
||||
@ -516,8 +514,8 @@ class StreamingPlaybackService(context: Context) : IPlaybackService {
|
||||
/* transcoding bitrate, if selected by the user */
|
||||
var bitrateQueryParam = ""
|
||||
val bitrateIndex = prefs.getInt(
|
||||
Prefs.Key.TRANSCODER_BITRATE_INDEX,
|
||||
Prefs.Default.TRANSCODER_BITRATE_INDEX)
|
||||
Prefs.Key.TRANSCODER_BITRATE_INDEX,
|
||||
Prefs.Default.TRANSCODER_BITRATE_INDEX)
|
||||
|
||||
if (bitrateIndex > 0) {
|
||||
val r = Application.instance!!.resources
|
||||
|
@ -1,4 +1,4 @@
|
||||
package io.casey.musikcube.remote.service.playback.impl.streaming.offline
|
||||
package io.casey.musikcube.remote.service.playback.impl.streaming.db
|
||||
|
||||
import android.arch.persistence.room.Database
|
||||
import android.arch.persistence.room.RoomDatabase
|
||||
@ -20,9 +20,9 @@ import javax.inject.Inject
|
||||
@Database(entities = arrayOf(OfflineTrack::class), version = 1)
|
||||
abstract class OfflineDb : RoomDatabase() {
|
||||
@Inject lateinit var wss: WebSocketService
|
||||
@Inject lateinit var streamProxy: StreamProxy
|
||||
|
||||
init {
|
||||
|
||||
DaggerDataComponent.builder()
|
||||
.appComponent(Application.appComponent)
|
||||
.build().inject(this)
|
||||
@ -50,7 +50,7 @@ abstract class OfflineDb : RoomDatabase() {
|
||||
val toDelete = ArrayList<String>()
|
||||
|
||||
uris.forEach {
|
||||
if (!StreamProxy.isCached(it)) {
|
||||
if (!streamProxy.isCached(it)) {
|
||||
toDelete.add(it)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package io.casey.musikcube.remote.service.playback.impl.streaming.offline
|
||||
package io.casey.musikcube.remote.service.playback.impl.streaming.db
|
||||
|
||||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
@ -66,9 +66,9 @@ class OfflineTrack {
|
||||
json.put(Metadata.Track.EXTERNAL_ID, externalId)
|
||||
json.put(Metadata.Track.URI, uri)
|
||||
return json
|
||||
} catch (ex: JSONException) {
|
||||
}
|
||||
catch (ex: JSONException) {
|
||||
throw RuntimeException("json serialization error")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package io.casey.musikcube.remote.service.playback.impl.streaming.offline;
|
||||
package io.casey.musikcube.remote.service.playback.impl.streaming.db;
|
||||
|
||||
import android.arch.persistence.room.Dao;
|
||||
import android.arch.persistence.room.Insert;
|
@ -26,7 +26,6 @@ import com.bumptech.glide.request.target.Target
|
||||
import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.R
|
||||
import io.casey.musikcube.remote.injection.DaggerViewComponent
|
||||
import io.casey.musikcube.remote.injection.DataModule
|
||||
import io.casey.musikcube.remote.injection.GlideApp
|
||||
import io.casey.musikcube.remote.service.playback.IPlaybackService
|
||||
import io.casey.musikcube.remote.service.playback.PlaybackServiceFactory
|
||||
@ -291,7 +290,6 @@ class MainMetadataView : FrameLayout {
|
||||
private fun init() {
|
||||
DaggerViewComponent.builder()
|
||||
.appComponent(Application.appComponent)
|
||||
.dataModule(DataModule())
|
||||
.build().inject(this)
|
||||
|
||||
this.prefs = context.getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
|
||||
|
@ -17,15 +17,18 @@ import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import com.uacf.taskrunner.Task
|
||||
import com.uacf.taskrunner.Tasks
|
||||
import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.R
|
||||
import io.casey.musikcube.remote.ui.settings.model.Connection
|
||||
import io.casey.musikcube.remote.ui.settings.model.ConnectionsDb
|
||||
import io.casey.musikcube.remote.ui.shared.activity.BaseActivity
|
||||
import io.casey.musikcube.remote.ui.shared.extension.*
|
||||
import javax.inject.Inject
|
||||
|
||||
private val EXTRA_CONNECTION = "extra_connection"
|
||||
|
||||
class ConnectionsActivity : BaseActivity() {
|
||||
@Inject lateinit var connectionsDb: ConnectionsDb
|
||||
|
||||
private lateinit var recycler: RecyclerView
|
||||
private lateinit var emptyText: View
|
||||
private lateinit var adapter: Adapter
|
||||
@ -51,7 +54,7 @@ class ConnectionsActivity : BaseActivity() {
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
runner.run(LoadTask.NAME, LoadTask())
|
||||
runner.run(LoadTask.NAME, LoadTask(connectionsDb))
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@ -74,11 +77,11 @@ class ConnectionsActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
fun rename(connection: Connection, name: String) {
|
||||
runner.run(RenameTask.NAME, RenameTask(connection, name))
|
||||
runner.run(RenameTask.NAME, RenameTask(connectionsDb, connection, name))
|
||||
}
|
||||
|
||||
fun delete(connection: Connection) {
|
||||
runner.run(DeleteTask.NAME, DeleteTask(connection))
|
||||
runner.run(DeleteTask.NAME, DeleteTask(connectionsDb, connection))
|
||||
}
|
||||
|
||||
private val itemClickListener: (View) -> Unit = { view: View ->
|
||||
@ -155,9 +158,9 @@ private class Adapter(val clickListener: (View) -> Unit,
|
||||
}
|
||||
}
|
||||
|
||||
private class LoadTask : Tasks.Blocking<List<Connection>, Exception>() {
|
||||
private class LoadTask(val db: ConnectionsDb) : Tasks.Blocking<List<Connection>, Exception>() {
|
||||
override fun exec(context: Context?): List<Connection> {
|
||||
return Application.connectionsDb?.connectionsDao()?.query()!!
|
||||
return db.connectionsDao().query()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -165,9 +168,9 @@ private class LoadTask : Tasks.Blocking<List<Connection>, Exception>() {
|
||||
}
|
||||
}
|
||||
|
||||
private class DeleteTask(val connection: Connection) : Tasks.Blocking<List<Connection>, Exception>() {
|
||||
private class DeleteTask(val db: ConnectionsDb, val connection: Connection) : Tasks.Blocking<List<Connection>, Exception>() {
|
||||
override fun exec(context: Context?): List<Connection> {
|
||||
val dao = Application.connectionsDb?.connectionsDao()!!
|
||||
val dao = db.connectionsDao()
|
||||
dao.delete(connection.name)
|
||||
return dao.query()
|
||||
}
|
||||
@ -177,11 +180,11 @@ private class DeleteTask(val connection: Connection) : Tasks.Blocking<List<Conne
|
||||
}
|
||||
}
|
||||
|
||||
private class RenameTask(val connection: Connection, val name:String)
|
||||
private class RenameTask(val db: ConnectionsDb, val connection: Connection, val name:String)
|
||||
: Tasks.Blocking<List<Connection>, Exception>()
|
||||
{
|
||||
override fun exec(context: Context?): List<Connection> {
|
||||
val dao = Application.connectionsDb?.connectionsDao()!!
|
||||
val dao = db.connectionsDao()
|
||||
dao.rename(connection.name, name)
|
||||
return dao.query()
|
||||
}
|
||||
|
@ -15,21 +15,25 @@ import android.view.View
|
||||
import android.widget.*
|
||||
import com.uacf.taskrunner.Task
|
||||
import com.uacf.taskrunner.Tasks
|
||||
import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.R
|
||||
import io.casey.musikcube.remote.service.playback.PlayerWrapper
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.StreamProxy
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs
|
||||
import io.casey.musikcube.remote.ui.settings.model.Connection
|
||||
import io.casey.musikcube.remote.ui.settings.model.ConnectionsDb
|
||||
import io.casey.musikcube.remote.ui.shared.activity.BaseActivity
|
||||
import io.casey.musikcube.remote.ui.shared.extension.*
|
||||
import io.casey.musikcube.remote.ui.shared.mixin.DataProviderMixin
|
||||
import io.casey.musikcube.remote.ui.shared.mixin.PlaybackMixin
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs.Default as Defaults
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs.Key as Keys
|
||||
|
||||
class SettingsActivity : BaseActivity() {
|
||||
@Inject lateinit var connectionsDb: ConnectionsDb
|
||||
@Inject lateinit var streamProxy: StreamProxy
|
||||
|
||||
private lateinit var addressText: EditText
|
||||
private lateinit var portText: EditText
|
||||
private lateinit var httpPortText: EditText
|
||||
@ -243,7 +247,7 @@ class SettingsActivity : BaseActivity() {
|
||||
connection.noValidate = certCheckbox.isChecked
|
||||
|
||||
if (connection.valid) {
|
||||
runner.run(SaveAsTask.nameFor(connection), SaveAsTask(connection))
|
||||
runner.run(SaveAsTask.nameFor(connection), SaveAsTask(connectionsDb, connection))
|
||||
}
|
||||
else {
|
||||
showInvalidConnectionDialog()
|
||||
@ -290,7 +294,7 @@ class SettingsActivity : BaseActivity() {
|
||||
playback.service.stop()
|
||||
}
|
||||
|
||||
StreamProxy.reload()
|
||||
streamProxy.reload()
|
||||
data.wss.disconnect()
|
||||
|
||||
finish()
|
||||
@ -408,7 +412,8 @@ class SettingsActivity : BaseActivity() {
|
||||
.setNegativeButton(R.string.button_no, null)
|
||||
.setPositiveButton(R.string.button_yes) { _, _ ->
|
||||
val connection = arguments.getParcelable<Connection>(EXTRA_CONNECTION)
|
||||
val saveAs = SaveAsTask(connection, true)
|
||||
val db = (activity as SettingsActivity).connectionsDb
|
||||
val saveAs = SaveAsTask(db, connection, true)
|
||||
(activity as SettingsActivity).runner.run(SaveAsTask.nameFor(connection), saveAs)
|
||||
}
|
||||
.create()
|
||||
@ -479,14 +484,15 @@ class SettingsActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private class SaveAsTask(val connection: Connection,
|
||||
private class SaveAsTask(val db: ConnectionsDb,
|
||||
val connection: Connection,
|
||||
val overwrite: Boolean = false)
|
||||
: Tasks.Blocking<SaveAsTask.Result, Exception>()
|
||||
{
|
||||
enum class Result { Exists, Added }
|
||||
|
||||
override fun exec(context: Context?): Result {
|
||||
val dao = Application.connectionsDb?.connectionsDao()!!
|
||||
val dao = db.connectionsDao()
|
||||
|
||||
if (!overwrite) {
|
||||
val existing: Connection? = dao.query(connection.name)
|
||||
@ -503,11 +509,11 @@ private class SaveAsTask(val connection: Connection,
|
||||
val NAME = "SaveAsTask"
|
||||
|
||||
fun nameFor(connection: Connection): String {
|
||||
return "${NAME}.${connection.name}"
|
||||
return "$NAME.${connection.name}"
|
||||
}
|
||||
|
||||
fun match(name: String?): Boolean {
|
||||
return name != null && name.startsWith("${NAME}.")
|
||||
return name != null && name.startsWith("$NAME.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ import io.casey.musikcube.remote.framework.IMixin
|
||||
import io.casey.musikcube.remote.framework.MixinSet
|
||||
import io.casey.musikcube.remote.framework.ViewModel
|
||||
import io.casey.musikcube.remote.injection.DaggerViewComponent
|
||||
import io.casey.musikcube.remote.injection.DataModule
|
||||
import io.casey.musikcube.remote.injection.ViewComponent
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs
|
||||
import io.casey.musikcube.remote.ui.shared.extension.hideKeyboard
|
||||
@ -33,7 +32,6 @@ abstract class BaseActivity : AppCompatActivity(), ViewModel.Provider, Runner.Ta
|
||||
protected val component: ViewComponent =
|
||||
DaggerViewComponent.builder()
|
||||
.appComponent(Application.appComponent)
|
||||
.dataModule(DataModule())
|
||||
.build()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -4,7 +4,6 @@ import android.os.Bundle
|
||||
import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.framework.MixinBase
|
||||
import io.casey.musikcube.remote.injection.DaggerViewComponent
|
||||
import io.casey.musikcube.remote.injection.DataModule
|
||||
import io.casey.musikcube.remote.service.websocket.WebSocketService
|
||||
import io.casey.musikcube.remote.service.websocket.model.IDataProvider
|
||||
import javax.inject.Inject
|
||||
@ -18,9 +17,7 @@ class DataProviderMixin : MixinBase() {
|
||||
|
||||
DaggerViewComponent.builder()
|
||||
.appComponent(Application.appComponent)
|
||||
.dataModule(DataModule())
|
||||
.build()
|
||||
.inject(this)
|
||||
.build().inject(this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -14,7 +14,6 @@ import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.R
|
||||
import io.casey.musikcube.remote.framework.MixinBase
|
||||
import io.casey.musikcube.remote.injection.DaggerViewComponent
|
||||
import io.casey.musikcube.remote.injection.DataModule
|
||||
import io.casey.musikcube.remote.service.playback.PlaybackServiceFactory
|
||||
import io.casey.musikcube.remote.service.websocket.Messages
|
||||
import io.casey.musikcube.remote.service.websocket.model.IAlbum
|
||||
@ -50,9 +49,7 @@ class ItemContextMenuMixin(private val activity: AppCompatActivity,
|
||||
init {
|
||||
DaggerViewComponent.builder()
|
||||
.appComponent(Application.appComponent)
|
||||
.dataModule(DataModule())
|
||||
.build()
|
||||
.inject(this)
|
||||
.build().inject(this)
|
||||
}
|
||||
|
||||
override fun onCreate(bundle: Bundle) {
|
||||
|
@ -10,7 +10,6 @@ import android.widget.TextView
|
||||
import io.casey.musikcube.remote.Application
|
||||
import io.casey.musikcube.remote.R
|
||||
import io.casey.musikcube.remote.injection.DaggerViewComponent
|
||||
import io.casey.musikcube.remote.injection.DataModule
|
||||
import io.casey.musikcube.remote.service.playback.PlaybackServiceFactory
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.StreamingPlaybackService
|
||||
import io.casey.musikcube.remote.service.websocket.WebSocketService
|
||||
@ -103,7 +102,6 @@ class EmptyListView : FrameLayout {
|
||||
private fun initialize() {
|
||||
DaggerViewComponent.builder()
|
||||
.appComponent(Application.appComponent)
|
||||
.dataModule(DataModule())
|
||||
.build().inject(this)
|
||||
|
||||
val inflater = LayoutInflater.from(context)
|
||||
|
Loading…
Reference in New Issue
Block a user