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:
casey langen 2017-11-26 15:13:16 -08:00
parent 5d9e24019d
commit 33d3c1544c
30 changed files with 250 additions and 160 deletions

View File

@ -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
}
}

View File

@ -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 */
}

View File

@ -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 */
}

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -16,4 +16,8 @@ annotation class ServiceScope
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class DataScope
annotation class DataScope
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class PlaybackScope

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -0,0 +1,7 @@
package io.casey.musikcube.remote.service.gapless.db;
import android.arch.persistence.room.Dao;
@Dao
public class GaplessDao {
}

View File

@ -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() {
}

View File

@ -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)

View File

@ -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()

View File

@ -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 ->

View File

@ -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 ->

View File

@ -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)

View File

@ -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()

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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")
}
}
}

View File

@ -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;

View File

@ -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)

View File

@ -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()
}

View File

@ -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.")
}
}
}

View File

@ -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?) {

View File

@ -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() {

View File

@ -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) {

View File

@ -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)