Cleaned up PlaybackService interface to expose the currently playing

ITrack, instead of the awkward getPlayingString() and getPlayingLong()
methods.
This commit is contained in:
casey langen 2017-11-05 12:38:14 -08:00
parent c62daae9d5
commit 5c71000bcc
20 changed files with 104 additions and 107 deletions

View File

@ -18,7 +18,7 @@ import android.widget.CompoundButton
import android.widget.SeekBar
import android.widget.TextView
import io.casey.musikcube.remote.data.IDataProvider
import io.casey.musikcube.remote.playback.PlaybackService
import io.casey.musikcube.remote.playback.IPlaybackService
import io.casey.musikcube.remote.playback.PlaybackState
import io.casey.musikcube.remote.playback.RepeatMode
import io.casey.musikcube.remote.ui.activity.*
@ -36,7 +36,7 @@ import io.casey.musikcube.remote.websocket.WebSocketService
class MainActivity : WebSocketActivityBase() {
private val handler = Handler()
private lateinit var prefs: SharedPreferences
private var playback: PlaybackService? = null
private var playback: IPlaybackService? = null
private var updateCheck: UpdateCheck = UpdateCheck()
private var seekbarValue = -1

View File

@ -2,6 +2,6 @@ package io.casey.musikcube.remote.data
interface ICategoryValue {
val id: Long
val name: String
val value: String
val type: String
}

View File

@ -17,7 +17,6 @@ interface ITrack {
val artist: String
val artistId: Long
fun getString(key: String, default: String): String
fun getLong(key: String, default: Long): Long
fun getCategoryId(categoryType: String): Long
fun toJson(): JSONObject
}

View File

@ -7,8 +7,8 @@ import org.json.JSONObject
class RemoteAlbum(val json: JSONObject) : IAlbum {
override val id: Long get() = json.optLong(Metadata.Album.ID, -1)
override val name: String get() = json.optString(Metadata.Album.TITLE, "<unknown>")
override val albumArtist: String get() = json.optString(Metadata.Album.ALBUM_ARTIST, "<unknown>")
override val value: String get() = json.optString(Metadata.Album.TITLE, "")
override val albumArtist: String get() = json.optString(Metadata.Album.ALBUM_ARTIST, "")
override val albumArtistId: Long get() = json.optLong(Metadata.Album.ALBUM_ARTIST_ID, -1)
override val type: String get() = Messages.Category.ALBUM
}

View File

@ -6,8 +6,8 @@ import org.json.JSONObject
class RemoteAlbumArtist(private val json: JSONObject) : IAlbumArtist {
override val id: Long
get() = json.optLong(Messages.Key.ID)
override val name: String
get() = json.optLong(Messages.Key.ID, -1)
override val value: String
get() = json.optString(Messages.Key.VALUE, "")
override val type: String
get() = Messages.Category.ALBUM_ARTIST

View File

@ -8,8 +8,8 @@ class RemoteCategoryValue(private val categoryType: String,
private val json: JSONObject) : ICategoryValue
{
override val id: Long
get() = json.optLong(Messages.Key.ID)
override val name: String
get() = json.optLong(Messages.Key.ID, -1)
override val value: String
get() = json.optString(Messages.Key.VALUE, "")
override val type: String
get() = categoryType

View File

@ -2,6 +2,7 @@ package io.casey.musikcube.remote.data.impl.remote
import io.casey.musikcube.remote.data.ITrack
import io.casey.musikcube.remote.playback.Metadata
import io.casey.musikcube.remote.websocket.Messages
import org.json.JSONObject
class RemoteTrack(val json: JSONObject) : ITrack {
@ -12,35 +13,44 @@ class RemoteTrack(val json: JSONObject) : ITrack {
override val uri: String
get() = json.optString(Metadata.Track.URI, "")
override val title: String
get() = json.optString(Metadata.Track.TITLE, "<no name>")
get() = json.optString(Metadata.Track.TITLE, "")
override val album: String
get() = json.optString(Metadata.Track.ALBUM, "<no album>")
get() = json.optString(Metadata.Track.ALBUM, "")
override val albumId: Long
get() = json.optLong(Metadata.Track.ALBUM_ID, -1)
override val albumArtist: String
get() = json.optString(Metadata.Track.ALBUM_ARTIST, "<no album artist>")
get() = json.optString(Metadata.Track.ALBUM_ARTIST, "")
override val albumArtistId: Long
get() = json.optLong(Metadata.Track.ALBUM_ARTIST_ID, -1)
override val genre: String
get() = json.optString(Metadata.Track.GENRE, "<no genre>")
get() = json.optString(Metadata.Track.GENRE, "")
override val trackNum: Int
get() = json.optInt(Metadata.Track.TRACK_NUM, 0)
override val genreId: Long
get() = json.optLong(Metadata.Track.GENRE_ID, -1)
override val artist: String
get() = json.optString(Metadata.Track.ARTIST, "<no artist>")
get() = json.optString(Metadata.Track.ARTIST, "")
override val artistId: Long
get() = json.optLong(Metadata.Track.ARTIST_ID, -1)
override fun getString(key: String, default: String): String {
return json.optString(key, default)
override fun getCategoryId(categoryType: String): Long {
val idKey = CATEGORY_NAME_TO_ID[categoryType]
if (idKey != null && idKey.isNotEmpty()) {
return json.optLong(idKey, -1L)
}
override fun getLong(key: String, default: Long): Long {
return json.optLong(key, default)
return -1L
}
override fun toJson(): JSONObject {
return JSONObject(json.toString())
}
companion object {
private val CATEGORY_NAME_TO_ID: Map<String, String> = mapOf(
Messages.Category.ALBUM_ARTIST to Metadata.Track.ALBUM_ARTIST_ID,
Messages.Category.GENRE to Metadata.Track.GENRE_ID,
Messages.Category.ARTIST to Metadata.Track.ARTIST_ID,
Messages.Category.ALBUM to Metadata.Track.ALBUM_ID,
Messages.Category.PLAYLISTS to Metadata.Track.ALBUM_ID)
}
}

View File

@ -1,8 +1,9 @@
package io.casey.musikcube.remote.playback
import io.casey.musikcube.remote.data.ITrack
import io.casey.musikcube.remote.ui.model.TrackListSlidingWindow
interface PlaybackService {
interface IPlaybackService {
fun connect(listener: () -> Unit)
fun disconnect(listener: () -> Unit)
@ -46,6 +47,5 @@ interface PlaybackService {
val playlistQueryFactory: TrackListSlidingWindow.QueryFactory
fun getTrackString(key: String, defaultValue: String): String
fun getTrackLong(key: String, defaultValue: Long): Long
val playingTrack: ITrack
}

View File

@ -10,7 +10,7 @@ object PlaybackServiceFactory {
private var remote: RemotePlaybackService? = null
private var prefs: SharedPreferences? = null
@Synchronized fun instance(context: Context): PlaybackService {
@Synchronized fun instance(context: Context): IPlaybackService {
init(context)
if (prefs!!.getBoolean(Prefs.Key.STREAMING_PLAYBACK, Prefs.Default.STREAMING_PLAYBACK)) {

View File

@ -4,6 +4,7 @@ import android.os.Handler
import io.casey.musikcube.remote.Application
import io.casey.musikcube.remote.data.IDataProvider
import io.casey.musikcube.remote.data.ITrack
import io.casey.musikcube.remote.data.impl.remote.RemoteTrack
import io.casey.musikcube.remote.injection.DaggerServiceComponent
import io.casey.musikcube.remote.injection.DataModule
import io.casey.musikcube.remote.ui.model.TrackListSlidingWindow
@ -15,7 +16,7 @@ import org.json.JSONObject
import java.util.*
import javax.inject.Inject
class RemotePlaybackService : PlaybackService {
class RemotePlaybackService : IPlaybackService {
private interface Key {
companion object {
val STATE = "state"
@ -288,19 +289,8 @@ class RemotePlaybackService : PlaybackService {
override val bufferedTime: Double
get() = duration
override fun getTrackString(key: String, defaultValue: String): String {
if (track.has(key)) {
return track.optString(key, defaultValue)
}
return defaultValue
}
override fun getTrackLong(key: String, defaultValue: Long): Long {
if (track.has(key)) {
return track.optLong(key, defaultValue)
}
return defaultValue
}
override val playingTrack: ITrack
get() = RemoteTrack(track)
private fun reset() {
playbackState = PlaybackState.Stopped

View File

@ -12,6 +12,7 @@ import io.casey.musikcube.remote.Application
import io.casey.musikcube.remote.R
import io.casey.musikcube.remote.data.IDataProvider
import io.casey.musikcube.remote.data.ITrack
import io.casey.musikcube.remote.data.impl.remote.RemoteTrack
import io.casey.musikcube.remote.injection.DaggerServiceComponent
import io.casey.musikcube.remote.injection.DataModule
import io.casey.musikcube.remote.ui.model.TrackListSlidingWindow
@ -20,11 +21,12 @@ import io.casey.musikcube.remote.websocket.Messages
import io.casey.musikcube.remote.websocket.Prefs
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import org.json.JSONObject
import java.net.URLEncoder
import java.util.*
import javax.inject.Inject
class StreamingPlaybackService(context: Context) : PlaybackService {
class StreamingPlaybackService(context: Context) : IPlaybackService {
@Inject lateinit var dataProvider: IDataProvider
private val prefs: SharedPreferences = context.getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
@ -352,12 +354,10 @@ class StreamingPlaybackService(context: Context) : PlaybackService {
notifyEventListeners()
}
override fun getTrackString(key: String, defaultValue: String): String {
return playContext.currentMetadata?.getString(key, defaultValue) ?: defaultValue
}
override fun getTrackLong(key: String, defaultValue: Long): Long {
return playContext.currentMetadata?.getLong(key, defaultValue) ?: defaultValue
override val playingTrack: ITrack
get() {
val playing: ITrack? = playContext.currentMetadata
return playing ?: RemoteTrack(JSONObject())
}
override val bufferedTime: Double /* ms -> sec */
@ -567,11 +567,7 @@ class StreamingPlaybackService(context: Context) : PlaybackService {
}
else {
if (currentIndex + 1 >= count) {
if (repeatMode === RepeatMode.List) {
return 0
} else {
return -1
}
return if (repeatMode === RepeatMode.List) 0 else -1
}
else {
return currentIndex + 1

View File

@ -23,6 +23,7 @@ import com.bumptech.glide.request.target.Target
import io.casey.musikcube.remote.Application
import io.casey.musikcube.remote.MainActivity
import io.casey.musikcube.remote.R
import io.casey.musikcube.remote.ui.extension.fallback
import io.casey.musikcube.remote.ui.model.AlbumArtModel
import io.casey.musikcube.remote.util.Debouncer
import io.casey.musikcube.remote.util.Strings
@ -188,9 +189,10 @@ class SystemService : Service() {
else -> { }
}
title = playback?.getTrackString(Metadata.Track.TITLE, "-")!!
album = playback?.getTrackString(Metadata.Track.ALBUM, "-")!!
artist = playback?.getTrackString(Metadata.Track.ARTIST, "-")!!
val playing = playback!!.playingTrack
title = fallback(playing.title, "-")
album = fallback(playing.album, "-")
artist = fallback(playing.artist, "-")
duration = ((playback?.duration ?: 0.0) * 1000).toInt()
}

View File

@ -117,7 +117,7 @@ class AlbumBrowseActivity : WebSocketActivityBase(), Filterable {
val album = view.tag as IAlbum
val intent = TrackListActivity.getStartIntent(
this@AlbumBrowseActivity, Messages.Category.ALBUM, album.id, album.name)
this@AlbumBrowseActivity, Messages.Category.ALBUM, album.id, album.value)
startActivityForResult(intent, Navigation.RequestCode.ALBUM_TRACKS_ACTIVITY)
}
@ -127,7 +127,8 @@ class AlbumBrowseActivity : WebSocketActivityBase(), Filterable {
private val subtitle = itemView.findViewById<TextView>(R.id.subtitle)
internal fun bind(album: IAlbum) {
val playingId = transport.playbackService?.getTrackLong(Metadata.Track.ALBUM_ID, -1L) ?: -1L
val playing = transport.playbackService!!.playingTrack
val playingId = playing.albumId
var titleColor = R.color.theme_foreground
var subtitleColor = R.color.theme_disabled_foreground
@ -137,10 +138,10 @@ class AlbumBrowseActivity : WebSocketActivityBase(), Filterable {
subtitleColor = R.color.theme_yellow
}
title.text = album.name
title.text = fallback(album.value, "-")
title.setTextColor(getColorCompat(titleColor))
subtitle.text = album.albumArtist
subtitle.text = fallback(album.albumArtist, "-")
subtitle.setTextColor(getColorCompat(subtitleColor))
itemView.tag = album
}
@ -197,7 +198,7 @@ class AlbumBrowseActivity : WebSocketActivityBase(), Filterable {
}
fun getStartIntent(context: Context, categoryName: String, categoryValue: ICategoryValue): Intent {
return getStartIntent(context, categoryName, categoryValue.id, categoryValue.name)
return getStartIntent(context, categoryName, categoryValue.id, categoryValue.value)
}
}
}

View File

@ -165,7 +165,7 @@ class CategoryBrowseActivity : WebSocketActivityBase(), Filterable {
private fun navigateToTracks(entry: ICategoryValue) {
val categoryId = entry.id
val value = entry.name
val value = entry.value
val intent = TrackListActivity.getStartIntent(this, category, categoryId, value)
startActivityForResult(intent, Navigation.RequestCode.CATEGORY_TRACKS_ACTIVITY)
}
@ -178,12 +178,8 @@ class CategoryBrowseActivity : WebSocketActivityBase(), Filterable {
}
internal fun bind(entry: ICategoryValue) {
var playingId: Long = -1
val idKey = CATEGORY_NAME_TO_ID[category]
if (idKey != null && idKey.isNotEmpty()) {
playingId = transport.playbackService?.getTrackLong(idKey, -1) ?: -1L
}
val playing = transport.playbackService?.playingTrack
val playingId = playing?.getCategoryId(category) ?: -1
var titleColor = R.color.theme_foreground
if (playingId != -1L && entry.id == playingId) {
@ -191,7 +187,7 @@ class CategoryBrowseActivity : WebSocketActivityBase(), Filterable {
}
/* note optString only does a null check! */
var value = entry.name
var value = entry.value
value = if (Strings.empty(value)) getString(R.string.unknown_value) else value
title.text = value
@ -230,13 +226,6 @@ class CategoryBrowseActivity : WebSocketActivityBase(), Filterable {
private val EXTRA_CATEGORY = "extra_category"
private val EXTRA_DEEP_LINK_TYPE = "extra_deep_link_type"
private val CATEGORY_NAME_TO_ID: Map<String, String> = mapOf(
Messages.Category.ALBUM_ARTIST to Metadata.Track.ALBUM_ARTIST_ID,
Messages.Category.GENRE to Metadata.Track.GENRE_ID,
Messages.Category.ARTIST to Metadata.Track.ARTIST_ID,
Messages.Category.ALBUM to Metadata.Track.ALBUM_ID,
Messages.Category.PLAYLISTS to Metadata.Track.ALBUM_ID)
private val CATEGORY_NAME_TO_TITLE: Map<String, Int> = mapOf(
Messages.Category.ALBUM_ARTIST to R.string.artists_title,
Messages.Category.GENRE to R.string.genres_title,

View File

@ -13,7 +13,7 @@ import io.casey.musikcube.remote.R
import io.casey.musikcube.remote.data.IDataProvider
import io.casey.musikcube.remote.data.ITrack
import io.casey.musikcube.remote.playback.Metadata
import io.casey.musikcube.remote.playback.PlaybackService
import io.casey.musikcube.remote.playback.IPlaybackService
import io.casey.musikcube.remote.ui.extension.*
import io.casey.musikcube.remote.ui.model.TrackListSlidingWindow
import io.casey.musikcube.remote.ui.view.EmptyListView
@ -21,7 +21,7 @@ import io.casey.musikcube.remote.ui.view.EmptyListView
class PlayQueueActivity : WebSocketActivityBase() {
private var adapter: Adapter = Adapter()
private var offlineQueue: Boolean = false
private var playback: PlaybackService? = null
private var playback: IPlaybackService? = null
private lateinit var tracks: TrackListSlidingWindow
private lateinit var emptyView: EmptyListView
@ -113,16 +113,17 @@ class PlayQueueActivity : WebSocketActivityBase() {
subtitle.text = "-"
}
else {
val playing = playback!!.playingTrack
val entryExternalId = entry.externalId
val playingExternalId = playback?.getTrackString(Metadata.Track.EXTERNAL_ID, "")
val playingExternalId = playing.externalId
if (entryExternalId == playingExternalId) {
titleColor = R.color.theme_green
subtitleColor = R.color.theme_yellow
}
title.text = entry.title
subtitle.text = entry.albumArtist
title.text = fallback(entry.title, "-")
subtitle.text = fallback(entry.albumArtist, "-")
}
title.setTextColor(getColorCompat(titleColor))

View File

@ -150,16 +150,17 @@ class TrackListActivity : WebSocketActivityBase(), Filterable {
var subtitleColor = R.color.theme_disabled_foreground
if (track != null) {
val playing = transport.playbackService!!.playingTrack
val entryExternalId = track.externalId
val playingExternalId = transport.playbackService?.getTrackString(Metadata.Track.EXTERNAL_ID, "")
val playingExternalId = playing.externalId
if (entryExternalId == playingExternalId) {
titleColor = R.color.theme_green
subtitleColor = R.color.theme_yellow
}
title.text = track.title
subtitle.text = track.albumArtist
title.text = fallback(track.title, "-")
subtitle.text = fallback(track.albumArtist, "-")
}
else {
title.text = "-"

View File

@ -13,7 +13,7 @@ import com.uacf.taskrunner.Task
import io.casey.musikcube.remote.Application
import io.casey.musikcube.remote.data.IDataProvider
import io.casey.musikcube.remote.injection.*
import io.casey.musikcube.remote.playback.PlaybackService
import io.casey.musikcube.remote.playback.IPlaybackService
import io.casey.musikcube.remote.playback.PlaybackServiceFactory
import io.casey.musikcube.remote.ui.extension.hideKeyboard
import io.casey.musikcube.remote.websocket.Prefs
@ -137,7 +137,7 @@ abstract class WebSocketActivityBase : AppCompatActivity(), Runner.TaskCallbacks
return wss
}
protected var playbackService: PlaybackService? = null
protected var playbackService: IPlaybackService? = null
private set
protected val runner: Runner

View File

@ -190,3 +190,7 @@ fun AppCompatActivity.showSnackbar(view: View, stringId: Int) {
fun AppCompatActivity.showSnackbar(viewId: Int, stringId: Int) {
this.showSnackbar(this.findViewById<View>(viewId), stringId)
}
fun fallback(input: String?, fallback: String): String {
return if (input.isNullOrEmpty()) fallback else input!!
}

View File

@ -11,10 +11,11 @@ import android.widget.TextView
import io.casey.musikcube.remote.MainActivity
import io.casey.musikcube.remote.R
import io.casey.musikcube.remote.playback.Metadata
import io.casey.musikcube.remote.playback.PlaybackService
import io.casey.musikcube.remote.playback.IPlaybackService
import io.casey.musikcube.remote.playback.PlaybackServiceFactory
import io.casey.musikcube.remote.playback.PlaybackState
import io.casey.musikcube.remote.ui.activity.PlayQueueActivity
import io.casey.musikcube.remote.ui.extension.fallback
import io.casey.musikcube.remote.ui.extension.getColorCompat
class TransportFragment : Fragment() {
@ -53,7 +54,7 @@ class TransportFragment : Fragment() {
this.playbackService?.connect(playbackListener)
}
var playbackService: PlaybackService? = null
var playbackService: IPlaybackService? = null
private set
var modelChangedListener: OnModelChangedListener? = null
@ -112,10 +113,9 @@ class TransportFragment : Fragment() {
title?.setText(R.string.transport_not_playing)
}
else {
title?.setTextColor(getColorCompat(R.color.theme_green))
val defaultValue = getString(if (buffering) R.string.buffering else R.string.unknown_title)
title?.text = playbackService?.getTrackString(Metadata.Track.TITLE, defaultValue)
title?.text = fallback(playbackService?.playingTrack?.title, defaultValue)
title?.setTextColor(getColorCompat(R.color.theme_green))
}
}

View File

@ -25,10 +25,10 @@ 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.AppModule
import io.casey.musikcube.remote.playback.*
import io.casey.musikcube.remote.ui.activity.AlbumBrowseActivity
import io.casey.musikcube.remote.ui.activity.TrackListActivity
import io.casey.musikcube.remote.ui.extension.fallback
import io.casey.musikcube.remote.ui.extension.getColorCompat
import io.casey.musikcube.remote.ui.model.AlbumArtModel
import io.casey.musikcube.remote.util.Strings
@ -102,13 +102,14 @@ class MainMetadataView : FrameLayout {
visibility = View.VISIBLE
val playback = playbackService
val playing = playbackService.playingTrack
val buffering = playback.playbackState == PlaybackState.Buffering
val streaming = playback is StreamingPlaybackService
val artist = playback.getTrackString(Metadata.Track.ARTIST, "")
val album = playback.getTrackString(Metadata.Track.ALBUM, "")
val title = playback.getTrackString(Metadata.Track.TITLE, "")
val artist = fallback(playing.artist, "")
val album = fallback(playing.album, "")
val title = fallback(playing.title, "")
/* we don't display the volume amount when we're streaming -- the system has
overlays for drawing volume. */
@ -151,7 +152,7 @@ class MainMetadataView : FrameLayout {
}
}
private val playbackService: PlaybackService
private val playbackService: IPlaybackService
get() = PlaybackServiceFactory.instance(context)
private fun getString(resId: Int): String {
@ -181,14 +182,17 @@ class MainMetadataView : FrameLayout {
}
}
private fun rebindAlbumArtistWithArtTextView(playback: PlaybackService) {
private fun rebindAlbumArtistWithArtTextView(playback: IPlaybackService) {
val playing = playback.playingTrack
val buffering = playback.playbackState == PlaybackState.Buffering
val artist = playback.getTrackString(
Metadata.Track.ARTIST, getString(if (buffering) R.string.buffering else R.string.unknown_artist))
val artist = fallback(
playing.artist,
getString(if (buffering) R.string.buffering else R.string.unknown_artist))
val album = playback.getTrackString(
Metadata.Track.ALBUM, getString(if (buffering) R.string.buffering else R.string.unknown_album))
val album = fallback(
playing.album,
getString(if (buffering) R.string.buffering else R.string.unknown_album))
val albumColor = ForegroundColorSpan(getColorCompat(R.color.theme_orange))
@ -338,11 +342,11 @@ class MainMetadataView : FrameLayout {
private fun navigateToCurrentArtist() {
val context = context
val playback = playbackService
val playing = playbackService.playingTrack
val artistId = playback.getTrackLong(Metadata.Track.ARTIST_ID, -1)
val artistId = playing.artistId
if (artistId != -1L) {
val artistName = playback.getTrackString(Metadata.Track.ARTIST, "")
val artistName = fallback(playing.artist, "")
context.startActivity(AlbumBrowseActivity.getStartIntent(
context, Messages.Category.ARTIST, artistId, artistName))
}
@ -350,11 +354,11 @@ class MainMetadataView : FrameLayout {
private fun navigateToCurrentAlbum() {
val context = context
val playback = playbackService
val playing = playbackService.playingTrack
val albumId = playback.getTrackLong(Metadata.Track.ALBUM_ID, -1)
val albumId = playing.albumId
if (albumId != -1L) {
val albumName = playback.getTrackString(Metadata.Track.ALBUM, "")
val albumName = fallback(playing.album, "")
context.startActivity(TrackListActivity.getStartIntent(
context, Messages.Category.ALBUM, albumId, albumName))
}