Moved to dependency injection for WebSocketService. Also downgraded

support libraries from 26.0.0-beta2 to beta1. There seem to be issues
drawing toolbar icons in beta2.
This commit is contained in:
casey langen 2017-07-14 17:21:41 -07:00
parent 2a2688f1bf
commit fdb1d10235
19 changed files with 121 additions and 46 deletions

View File

@ -11,6 +11,7 @@ buildscript {
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 26
@ -59,7 +60,7 @@ kapt {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
@ -68,11 +69,11 @@ dependencies {
implementation(name:'videocache-2.8.0-pre', ext:'aar')
implementation 'org.slf4j:slf4j-android:1.7.21'
implementation "android.arch.persistence.room:runtime:1.0.0-alpha3"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha3"
kapt "android.arch.persistence.room:compiler:1.0.0-alpha3"
implementation "android.arch.persistence.room:runtime:1.0.0-alpha4"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha4"
kapt "android.arch.persistence.room:compiler:1.0.0-alpha4"
provided 'org.glassfish:javax.annotation:10.0-b28'
compileOnly 'org.glassfish:javax.annotation:10.0-b28'
implementation 'com.google.dagger:dagger:2.11'
implementation 'com.google.dagger:dagger-android:2.11'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
@ -90,14 +91,14 @@ dependencies {
implementation 'com.github.pluscubed:recycler-fast-scroll:0.3.2@aar'
implementation 'com.facebook.stetho:stetho:1.5.0'
implementation 'com.android.support:appcompat-v7:26.0.0-beta2'
implementation 'com.android.support:recyclerview-v7:26.0.0-beta2'
implementation 'com.android.support:design:26.0.0-beta2'
implementation 'com.android.support:appcompat-v7:26.0.0-beta1'
implementation 'com.android.support:recyclerview-v7:26.0.0-beta1'
implementation 'com.android.support:design:26.0.0-beta1'
implementation('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
transitive = true
}
testCompile 'junit:junit:4.12'
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
testImplementation 'junit:junit:4.12'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
}

View File

@ -2,13 +2,13 @@ package io.casey.musikcube.remote
import android.app.Activity
import android.arch.persistence.room.Room
import android.content.Context
import com.crashlytics.android.Crashlytics
import com.facebook.stetho.Stetho
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasActivityInjector
import io.casey.musikcube.remote.injection.DaggerMainComponent
import io.casey.musikcube.remote.injection.MainComponent
import io.casey.musikcube.remote.injection.MainModule
import io.casey.musikcube.remote.offline.OfflineDb
import io.casey.musikcube.remote.playback.StreamProxy
@ -24,7 +24,8 @@ class Application : android.app.Application(), HasActivityInjector {
super.onCreate()
DaggerMainComponent.builder().mainModule(MainModule()).build().inject(this)
mainComponent = DaggerMainComponent.builder().mainModule(MainModule()).build()
mainComponent.inject(this)
if (BuildConfig.DEBUG) {
Stetho.initializeWithDefaults(this)
@ -47,7 +48,9 @@ class Application : android.app.Application(), HasActivityInjector {
}
companion object {
var instance: Context? = null
lateinit var mainComponent: MainComponent
var instance: Application? = null
private set
var offlineDb: OfflineDb? = null

View File

@ -4,6 +4,7 @@ import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle
import android.os.Handler
@ -110,7 +111,7 @@ class MainActivity : WebSocketActivityBase() {
menu.findItem(R.id.action_genres).isEnabled = connected
menu.findItem(R.id.action_remote_toggle).setIcon(
if (streaming) R.mipmap.ic_toolbar_streaming else R.mipmap.ic_toolbar_remote)
if (streaming) R.drawable.ic_toolbar_streaming else R.drawable.ic_toolbar_remote)
return super.onPrepareOptionsMenu(menu)
}

View File

@ -3,9 +3,28 @@ package io.casey.musikcube.remote.injection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import io.casey.musikcube.remote.MainActivity
import io.casey.musikcube.remote.ui.activity.*
@Module
abstract class ActivityModule {
@ContributesAndroidInjector
abstract fun contributeMainActivityInjector(): MainActivity
abstract fun mainActivityInjector(): MainActivity
@ContributesAndroidInjector
abstract fun baseActivityInject(): WebSocketActivityBase
@ContributesAndroidInjector
abstract fun settingsInjector(): SettingsActivity
@ContributesAndroidInjector
abstract fun albumBrowseInject(): AlbumBrowseActivity
@ContributesAndroidInjector
abstract fun categoryBrowseInject(): CategoryBrowseActivity
@ContributesAndroidInjector
abstract fun playQueueInject(): PlayQueueActivity
@ContributesAndroidInjector
abstract fun trackListInject(): TrackListActivity
}

View File

@ -4,8 +4,24 @@ import dagger.Component
import dagger.android.AndroidInjectionModule
import dagger.android.AndroidInjector
import io.casey.musikcube.remote.Application
import io.casey.musikcube.remote.offline.OfflineDb
import io.casey.musikcube.remote.playback.RemotePlaybackService
import io.casey.musikcube.remote.playback.StreamingPlaybackService
import io.casey.musikcube.remote.ui.view.EmptyListView
import io.casey.musikcube.remote.ui.view.MainMetadataView
import javax.inject.Singleton
@Singleton
@Component(modules = arrayOf(AndroidInjectionModule::class, MainModule::class, ActivityModule::class))
interface MainComponent : AndroidInjector<Application> {
/* views */
fun inject(view: EmptyListView)
fun inject(view: MainMetadataView)
/* services */
fun inject(service: StreamingPlaybackService)
fun inject(service: RemotePlaybackService)
/* data */
fun inject(db: OfflineDb)
}

View File

@ -1,8 +1,22 @@
package io.casey.musikcube.remote.injection
import android.content.Context
import dagger.Module
import dagger.Provides
import io.casey.musikcube.remote.Application
import io.casey.musikcube.remote.websocket.WebSocketService
import javax.inject.Singleton
@Module
class MainModule {
@Provides
fun providesContext(): Context {
return Application.instance!!
}
@Provides
@Singleton
fun providesWebSocketService(context: Context): WebSocketService {
return WebSocketService(context)
}
}

View File

@ -14,22 +14,26 @@ import io.reactivex.schedulers.Schedulers
import org.json.JSONArray
import org.json.JSONObject
import java.util.*
import javax.inject.Inject
@Database(entities = arrayOf(OfflineTrack::class), version = 1)
abstract class OfflineDb : RoomDatabase() {
@Inject lateinit var wss: WebSocketService
init {
WebSocketService.getInstance(Application.instance!!)
.addInterceptor({ message, responder ->
var result = false
if (Messages.Request.QueryTracksByCategory.matches(message.name)) {
val category = message.getStringOption(Messages.Key.CATEGORY)
if (Messages.Category.OFFLINE == category) {
queryTracks(message, responder)
result = true
}
Application.mainComponent.inject(this)
wss.addInterceptor({ message, responder ->
var result = false
if (Messages.Request.QueryTracksByCategory.matches(message.name)) {
val category = message.getStringOption(Messages.Key.CATEGORY)
if (Messages.Category.OFFLINE == category) {
queryTracks(message, responder)
result = true
}
result
})
}
result
})
prune()
}

View File

@ -2,12 +2,14 @@ package io.casey.musikcube.remote.playback
import android.content.Context
import android.os.Handler
import io.casey.musikcube.remote.Application
import io.casey.musikcube.remote.ui.model.TrackListSlidingWindow
import io.casey.musikcube.remote.websocket.Messages
import io.casey.musikcube.remote.websocket.SocketMessage
import io.casey.musikcube.remote.websocket.WebSocketService
import org.json.JSONObject
import java.util.*
import javax.inject.Inject
class RemotePlaybackService(context: Context) : PlaybackService {
private interface Key {
@ -89,8 +91,8 @@ class RemotePlaybackService(context: Context) : PlaybackService {
}
}
@Inject lateinit var wss: WebSocketService
private val handler = Handler()
private val wss: WebSocketService = WebSocketService.getInstance(context.applicationContext)
private val listeners = HashSet<() -> Unit>()
private val estimatedTime = EstimatedPosition()
@ -142,6 +144,7 @@ class RemotePlaybackService(context: Context) : PlaybackService {
private var track: JSONObject = JSONObject()
init {
Application.mainComponent.inject(this)
reset()
}

View File

@ -22,9 +22,10 @@ import org.json.JSONArray
import org.json.JSONObject
import java.net.URLEncoder
import java.util.*
import javax.inject.Inject
class StreamingPlaybackService(context: Context) : PlaybackService {
private val wss: WebSocketService = WebSocketService.getInstance(context.applicationContext)
@Inject lateinit var wss: WebSocketService
private val prefs: SharedPreferences = context.getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
private val listeners = HashSet<() -> Unit>()
private var params: QueueParams? = null
@ -136,6 +137,10 @@ class StreamingPlaybackService(context: Context) : PlaybackService {
}
}
init {
Application.mainComponent.inject(this)
}
@Synchronized override fun connect(listener: () -> Unit) {
listeners.add(listener)
if (listeners.size == 1) {

View File

@ -255,7 +255,7 @@ class SystemService : Service() {
applicationContext, 1, MainActivity.getStartIntent(this), 0)
val notification = NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_notification)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(artist + " - " + album)
.setContentIntent(contentIntent)

View File

@ -12,6 +12,7 @@ import android.support.v7.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import android.widget.*
import dagger.android.AndroidInjection
import io.casey.musikcube.remote.R
import io.casey.musikcube.remote.playback.PlayerWrapper
import io.casey.musikcube.remote.playback.StreamProxy
@ -21,6 +22,7 @@ import io.casey.musikcube.remote.ui.extension.setTextAndMoveCursorToEnd
import io.casey.musikcube.remote.websocket.Prefs
import io.casey.musikcube.remote.websocket.WebSocketService
import java.util.*
import javax.inject.Inject
import io.casey.musikcube.remote.websocket.Prefs.Default as Defaults
import io.casey.musikcube.remote.websocket.Prefs.Key as Keys
@ -38,7 +40,10 @@ class SettingsActivity : AppCompatActivity() {
private lateinit var cacheSpinner: Spinner
private lateinit var prefs: SharedPreferences
@Inject lateinit var wss: WebSocketService
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
prefs = this.getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
setContentView(R.layout.activity_settings)
@ -178,7 +183,7 @@ class SettingsActivity : AppCompatActivity() {
}
StreamProxy.reload()
WebSocketService.getInstance(this).disconnect()
wss.disconnect()
finish()
}

View File

@ -11,23 +11,25 @@ import android.view.MenuItem
import com.uacf.taskrunner.LifecycleDelegate
import com.uacf.taskrunner.Runner
import com.uacf.taskrunner.Task
import dagger.android.AndroidInjection
import io.casey.musikcube.remote.playback.PlaybackService
import io.casey.musikcube.remote.playback.PlaybackServiceFactory
import io.casey.musikcube.remote.websocket.Prefs
import io.casey.musikcube.remote.websocket.WebSocketService
import javax.inject.Inject
abstract class WebSocketActivityBase : AppCompatActivity(), Runner.TaskCallbacks {
private lateinit var runnerDelegate: LifecycleDelegate
private lateinit var prefs: SharedPreferences
private lateinit var wss: WebSocketService
@Inject lateinit var wss: WebSocketService
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
volumeControlStream = AudioManager.STREAM_MUSIC
runnerDelegate = LifecycleDelegate(this, this, javaClass, null)
runnerDelegate.onCreate(savedInstanceState)
wss = WebSocketService.getInstance(this)
playbackService = PlaybackServiceFactory.instance(this)
prefs = getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
}

View File

@ -7,17 +7,21 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import io.casey.musikcube.remote.Application
import io.casey.musikcube.remote.R
import io.casey.musikcube.remote.playback.PlaybackServiceFactory
import io.casey.musikcube.remote.playback.StreamingPlaybackService
import io.casey.musikcube.remote.ui.activity.TrackListActivity
import io.casey.musikcube.remote.ui.extension.setVisible
import io.casey.musikcube.remote.websocket.WebSocketService
import javax.inject.Inject
import io.casey.musikcube.remote.websocket.WebSocketService.State as WebSocketState
class EmptyListView : FrameLayout {
enum class Capability { OnlineOnly, OfflineOk }
@Inject lateinit var wss: WebSocketService
private var mainView: View? = null
private var emptyTextView: TextView? = null
private var emptyContainer: View? = null
@ -81,6 +85,8 @@ class EmptyListView : FrameLayout {
}
private fun initialize() {
Application.mainComponent.inject(this)
val inflater = LayoutInflater.from(context)
mainView = inflater.inflate(R.layout.empty_list_view, this, false)
@ -93,7 +99,7 @@ class EmptyListView : FrameLayout {
addView(mainView)
reconnectButton?.setOnClickListener { _ ->
WebSocketService.getInstance(context).reconnect()
wss.reconnect()
}
viewOfflineButton?.setOnClickListener { _ ->

View File

@ -21,6 +21,8 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.drawable.GlideDrawable
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import dagger.android.AndroidInjection
import io.casey.musikcube.remote.Application
import io.casey.musikcube.remote.R
import io.casey.musikcube.remote.playback.*
import io.casey.musikcube.remote.ui.activity.AlbumBrowseActivity
@ -33,9 +35,10 @@ import io.casey.musikcube.remote.websocket.Prefs
import io.casey.musikcube.remote.websocket.SocketMessage
import io.casey.musikcube.remote.websocket.WebSocketService
import org.json.JSONArray
import javax.inject.Inject
class MainMetadataView : FrameLayout {
private var wss: WebSocketService? = null
@Inject lateinit var wss: WebSocketService
private var prefs: SharedPreferences? = null
private var isPaused = true
@ -300,8 +303,9 @@ class MainMetadataView : FrameLayout {
}
private fun init() {
Application.mainComponent.inject(this)
this.prefs = context.getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
this.wss = WebSocketService.getInstance(context)
val child = LayoutInflater.from(context).inflate(R.layout.main_metadata, this, false)

View File

@ -14,7 +14,7 @@ import io.reactivex.Observable
import java.util.*
import java.util.concurrent.atomic.AtomicLong
class WebSocketService private constructor(val context: Context) {
class WebSocketService constructor(val context: Context) {
interface Client {
fun onStateChanged(newState: State, oldState: State)
fun onMessageReceived(message: SocketMessage)
@ -581,17 +581,8 @@ class WebSocketService private constructor(val context: Context) {
private val MESSAGE_SCHEDULE_PING = MESSAGE_BASE + 4
private val MESSAGE_PING_EXPIRED = MESSAGE_BASE + 5
private var INSTANCE: WebSocketService? = null
private val NEXT_ID = AtomicLong(0)
@Synchronized fun getInstance(context: Context): WebSocketService {
if (INSTANCE == null) {
INSTANCE = WebSocketService(context)
}
return INSTANCE!!
}
private val INTERNAL_CLIENT = object : Client {
override fun onStateChanged(newState: State, oldState: State) {}
override fun onMessageReceived(message: SocketMessage) {}

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -7,7 +7,7 @@
android:id="@+id/action_remote_toggle"
app:showAsAction="always"
android:title="@string/menu_remote_toggle"
android:icon="@mipmap/ic_toolbar_remote" />
android:icon="@drawable/ic_toolbar_remote" />
<item
android:id="@+id/action_playlists"
@ -18,6 +18,7 @@
android:id="@+id/action_genres"
app:showAsAction="never"
android:title="@string/menu_genres"/>
<item
android:id="@+id/action_offline_tracks"
app:showAsAction="never"