mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-29 19:20:28 +00:00
Massive, project-wide warning cleanup.
This commit is contained in:
parent
60a55f0cff
commit
89fc15fe72
@ -10,6 +10,7 @@ import io.casey.musikcube.remote.injection.ServiceModule
|
||||
import io.casey.musikcube.remote.service.gapless.GaplessHeaderService
|
||||
import io.casey.musikcube.remote.service.playback.impl.streaming.db.OfflineDb
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs
|
||||
import io.casey.musikcube.remote.ui.shared.extension.getString
|
||||
import io.fabric.sdk.android.Fabric
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
@ -24,7 +25,7 @@ class Application : android.app.Application() {
|
||||
super.onCreate()
|
||||
|
||||
val prefs = getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
|
||||
deviceId = prefs.getString(Prefs.Key.DEVICE_ID, "")
|
||||
deviceId = prefs.getString(Prefs.Key.DEVICE_ID) ?: ""
|
||||
if (deviceId.isBlank()) {
|
||||
deviceId = UUID.randomUUID().toString()
|
||||
prefs.edit().putString(Prefs.Key.DEVICE_ID, deviceId).apply()
|
||||
|
@ -11,7 +11,7 @@ class MixinSet : MixinBase() {
|
||||
private var bundle = Bundle()
|
||||
|
||||
fun <T> add(mixin: IMixin): T {
|
||||
components.put(mixin.javaClass, mixin)
|
||||
components[mixin.javaClass] = mixin
|
||||
|
||||
when (state) {
|
||||
State.Created ->
|
||||
@ -33,10 +33,12 @@ class MixinSet : MixinBase() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
return mixin as T
|
||||
}
|
||||
|
||||
fun <T: IMixin> get(cls: Class<out T>): T? = components.get(cls) as T?
|
||||
@Suppress("unchecked_cast")
|
||||
fun <T: IMixin> get(cls: Class<out T>): T? = components[cls] as T?
|
||||
|
||||
override fun onCreate(bundle: Bundle) {
|
||||
super.onCreate(bundle)
|
||||
|
@ -73,6 +73,7 @@ abstract class ViewModel<T>(protected val runner: Runner? = null): Runner.TaskCa
|
||||
private val idToInstance = mutableMapOf<Long, ViewModel<*>>()
|
||||
|
||||
fun <T: ViewModel<*>> restore(id: Long): T? {
|
||||
@Suppress("unchecked_cast")
|
||||
val instance: T? = idToInstance[id] as T?
|
||||
if (instance != null) {
|
||||
handler.removeCallbacks(instance.cleanup)
|
||||
|
@ -12,8 +12,8 @@ import com.bumptech.glide.module.AppGlideModule
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs
|
||||
import okhttp3.*
|
||||
import java.io.InputStream
|
||||
import io.casey.musikcube.remote.ui.shared.model.albumart.canIntercept as canInterceptArtwork
|
||||
import io.casey.musikcube.remote.ui.shared.model.albumart.intercept as interceptArtwork
|
||||
import io.casey.musikcube.remote.ui.shared.util.AlbumArtLookup.canIntercept as canInterceptArtwork
|
||||
import io.casey.musikcube.remote.ui.shared.util.AlbumArtLookup.intercept as interceptArtwork
|
||||
|
||||
@GlideModule
|
||||
class GlideModule : AppGlideModule() {
|
||||
|
@ -13,7 +13,6 @@ import io.casey.musikcube.remote.service.gapless.db.GaplessDb
|
||||
import io.casey.musikcube.remote.service.gapless.db.GaplessTrack
|
||||
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.*
|
||||
import javax.inject.Inject
|
||||
@ -78,11 +77,11 @@ class StreamProxy(private val context: Context) {
|
||||
proxy = HttpProxyCacheServer.Builder(context.applicationContext)
|
||||
.cacheDirectory(cachePath)
|
||||
.maxCacheSize(CACHE_SETTING_TO_BYTES[diskCacheIndex] ?: MINIMUM_CACHE_SIZE_BYTES)
|
||||
.headerInjector { _ ->
|
||||
.headerInjector {
|
||||
val headers = HashMap<String, String>()
|
||||
val userPass = "default:" + prefs.getString(Prefs.Key.PASSWORD, Prefs.Default.PASSWORD)!!
|
||||
val encoded = Base64.encodeToString(userPass.toByteArray(), Base64.NO_WRAP)
|
||||
headers.put("Authorization", "Basic " + encoded)
|
||||
headers["Authorization"] = "Basic $encoded"
|
||||
headers
|
||||
}
|
||||
.headerReceiver { url: String, headers: Map<String, List<String>> ->
|
||||
@ -105,10 +104,10 @@ class StreamProxy(private val context: Context) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val BYTES_PER_MEGABYTE = 1048576L
|
||||
private val BYTES_PER_GIGABYTE = 1073741824L
|
||||
private val ESTIMATED_LENGTH = "X-musikcube-Estimated-Content-Length"
|
||||
val MINIMUM_CACHE_SIZE_BYTES = BYTES_PER_MEGABYTE * 128
|
||||
private const val BYTES_PER_MEGABYTE = 1048576L
|
||||
private const val BYTES_PER_GIGABYTE = 1073741824L
|
||||
private const val ESTIMATED_LENGTH = "X-musikcube-Estimated-Content-Length"
|
||||
const val MINIMUM_CACHE_SIZE_BYTES = BYTES_PER_MEGABYTE * 128
|
||||
|
||||
val CACHE_SETTING_TO_BYTES: MutableMap<Int, Long> = mutableMapOf(
|
||||
0 to MINIMUM_CACHE_SIZE_BYTES,
|
||||
@ -127,15 +126,14 @@ class StreamProxy(private val context: Context) {
|
||||
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 = ""
|
||||
val params = when (uri?.query.isNullOrBlank()) {
|
||||
true -> ""
|
||||
false ->
|
||||
"-" + uri!!.query!!
|
||||
.replace("?", "-")
|
||||
.replace("&", "-")
|
||||
.replace("=", "-")
|
||||
|
||||
}
|
||||
|
||||
return@gen "${segments[2]}$params"
|
||||
|
@ -184,7 +184,7 @@ class StreamingPlaybackService(context: Context) : IPlaybackService {
|
||||
val context = QueryContext(Messages.Request.PlaySnapshotTracks)
|
||||
val type = PlayQueueType.Snapshot
|
||||
|
||||
service.queryContext?.let { _ ->
|
||||
service.queryContext?.let {
|
||||
dataProvider.snapshotPlayQueue().subscribeBy(
|
||||
onNext = {
|
||||
resetPlayContextAndQueryFactory()
|
||||
@ -347,7 +347,7 @@ class StreamingPlaybackService(context: Context) : IPlaybackService {
|
||||
override var state = PlaybackState.Stopped
|
||||
private set(value) {
|
||||
if (field !== value) {
|
||||
Log.d(TAG, "state = " + state)
|
||||
Log.d(TAG, "state=$state")
|
||||
field = value
|
||||
notifyEventListeners()
|
||||
}
|
||||
@ -366,10 +366,10 @@ class StreamingPlaybackService(context: Context) : IPlaybackService {
|
||||
}
|
||||
|
||||
override fun toggleRepeatMode() {
|
||||
when (repeatMode) {
|
||||
RepeatMode.None -> repeatMode = RepeatMode.List
|
||||
RepeatMode.List -> repeatMode = RepeatMode.Track
|
||||
else -> repeatMode = RepeatMode.None
|
||||
repeatMode = when (repeatMode) {
|
||||
RepeatMode.None -> RepeatMode.List
|
||||
RepeatMode.List -> RepeatMode.Track
|
||||
else -> RepeatMode.None
|
||||
}
|
||||
|
||||
this.prefs.edit().putString(REPEAT_MODE_PREF, repeatMode.toString()).apply()
|
||||
@ -585,13 +585,10 @@ class StreamingPlaybackService(context: Context) : IPlaybackService {
|
||||
else if (!userInitiated && repeatMode === RepeatMode.Track) {
|
||||
return currentIndex
|
||||
}
|
||||
else {
|
||||
if (currentIndex + 1 >= count) {
|
||||
return if (repeatMode === RepeatMode.List) 0 else -1
|
||||
}
|
||||
else {
|
||||
return currentIndex + 1
|
||||
}
|
||||
|
||||
return when (currentIndex + 1 >= count) {
|
||||
true -> if (repeatMode === RepeatMode.List) 0 else -1
|
||||
false -> currentIndex + 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -714,6 +711,7 @@ class StreamingPlaybackService(context: Context) : IPlaybackService {
|
||||
|
||||
val countMessage = playlistQueryFactory.count() ?: return
|
||||
|
||||
@Suppress("unused")
|
||||
countMessage
|
||||
.concatMap { count ->
|
||||
getCurrentAndNextTrackMessages(playContext, count)
|
||||
@ -763,6 +761,7 @@ class StreamingPlaybackService(context: Context) : IPlaybackService {
|
||||
val query = playlistQueryFactory.page(start, count)
|
||||
|
||||
if (query != null) {
|
||||
@Suppress("unused")
|
||||
query.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
|
@ -33,11 +33,11 @@ import io.casey.musikcube.remote.service.websocket.model.ITrack
|
||||
import io.casey.musikcube.remote.ui.home.activity.MainActivity
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs
|
||||
import io.casey.musikcube.remote.ui.shared.extension.fallback
|
||||
import io.casey.musikcube.remote.ui.shared.model.albumart.Size
|
||||
import io.casey.musikcube.remote.ui.shared.util.Size
|
||||
import io.casey.musikcube.remote.util.Debouncer
|
||||
import io.casey.musikcube.remote.util.Strings
|
||||
import android.support.v4.app.NotificationCompat.Action as NotifAction
|
||||
import io.casey.musikcube.remote.ui.shared.model.albumart.getUrl as getAlbumArtUrl
|
||||
import io.casey.musikcube.remote.ui.shared.util.AlbumArtLookup.getUrl as getAlbumArtUrl
|
||||
|
||||
/**
|
||||
* a service used to interact with all of the system media-related components -- notifications,
|
||||
|
@ -10,6 +10,7 @@ import android.util.Log
|
||||
import com.neovisionaries.ws.client.*
|
||||
import io.casey.musikcube.remote.BuildConfig
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs
|
||||
import io.casey.musikcube.remote.ui.shared.extension.getString
|
||||
import io.casey.musikcube.remote.ui.shared.util.NetworkUtil
|
||||
import io.casey.musikcube.remote.util.Preconditions
|
||||
import io.reactivex.Observable
|
||||
@ -137,7 +138,7 @@ class WebSocketService constructor(private val context: Context) {
|
||||
private set(newState) {
|
||||
Preconditions.throwIfNotOnMainThread()
|
||||
|
||||
Log.d(TAG, "state = " + newState)
|
||||
Log.d(TAG, "state=$newState")
|
||||
|
||||
if (state == State.Disconnected) {
|
||||
serverVersion = -1
|
||||
@ -158,6 +159,7 @@ class WebSocketService constructor(private val context: Context) {
|
||||
interceptors.add(interceptor)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun removeInterceptor(interceptor: (SocketMessage, Responder) -> Boolean) {
|
||||
Preconditions.throwIfNotOnMainThread()
|
||||
interceptors.remove(interceptor)
|
||||
@ -230,7 +232,7 @@ class WebSocketService constructor(private val context: Context) {
|
||||
|
||||
val ping = SocketMessage.Builder.request(Messages.Request.Ping).build()
|
||||
|
||||
send(ping, INTERNAL_CLIENT) { _: SocketMessage ->
|
||||
send(ping, INTERNAL_CLIENT) {
|
||||
handler.removeMessages(MESSAGE_PING_TIMEOUT)
|
||||
handler.sendEmptyMessageDelayed(MESSAGE_SCHEDULE_PING, PING_INTERVAL_MILLIS)
|
||||
}
|
||||
@ -239,7 +241,9 @@ class WebSocketService constructor(private val context: Context) {
|
||||
|
||||
fun cancelMessages(client: Client) {
|
||||
Preconditions.throwIfNotOnMainThread()
|
||||
removeCallbacks({ mrd: MessageResultDescriptor -> mrd.client === client })
|
||||
removeCallbacks { mrd: MessageResultDescriptor ->
|
||||
mrd.client === client
|
||||
}
|
||||
}
|
||||
|
||||
fun send(message: SocketMessage,
|
||||
@ -282,14 +286,12 @@ class WebSocketService constructor(private val context: Context) {
|
||||
mrd.callback = callback
|
||||
mrd.type = Type.Callback
|
||||
mrd.intercepted = intercepted
|
||||
messageCallbacks.put(message.id, mrd)
|
||||
messageCallbacks[message.id] = mrd
|
||||
}
|
||||
|
||||
if (!intercepted) {
|
||||
socket?.sendText(message.toString())
|
||||
}
|
||||
else {
|
||||
Log.d(TAG, "send: message intercepted with id " + id.toString())
|
||||
when (intercepted) {
|
||||
true -> Log.d(TAG, "send: message intercepted with id=$id")
|
||||
false -> socket?.sendText(message.toString())
|
||||
}
|
||||
|
||||
return id
|
||||
@ -346,21 +348,22 @@ class WebSocketService constructor(private val context: Context) {
|
||||
subject.onError(ex)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
subject.doOnDispose { cancelMessage(mrd.id) }
|
||||
|
||||
if (!intercepted) {
|
||||
socket?.sendText(message.toString())
|
||||
}
|
||||
|
||||
messageCallbacks.put(message.id, mrd)
|
||||
messageCallbacks[message.id] = mrd
|
||||
|
||||
return subject
|
||||
}
|
||||
|
||||
fun hasValidConnection(): Boolean {
|
||||
val addr = prefs.getString(Prefs.Key.ADDRESS, "")
|
||||
val address = prefs.getString(Prefs.Key.ADDRESS) ?: ""
|
||||
val port = prefs.getInt(Prefs.Key.MAIN_PORT, -1)
|
||||
return addr.isNotEmpty() && port >= 0
|
||||
return address.isNotEmpty() && port >= 0
|
||||
}
|
||||
|
||||
private fun disconnect(autoReconnect: Boolean) {
|
||||
@ -390,25 +393,27 @@ class WebSocketService constructor(private val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeNonInterceptedCallbacks() {
|
||||
removeCallbacks({ mrd -> !mrd.intercepted })
|
||||
}
|
||||
private fun removeNonInterceptedCallbacks() =
|
||||
removeCallbacks {
|
||||
mrd -> !mrd.intercepted
|
||||
}
|
||||
|
||||
private fun removeInternalCallbacks() {
|
||||
removeCallbacks({ mrd: MessageResultDescriptor -> mrd.client === INTERNAL_CLIENT })
|
||||
}
|
||||
private fun removeInternalCallbacks() =
|
||||
removeCallbacks {
|
||||
mrd: MessageResultDescriptor -> mrd.client === INTERNAL_CLIENT
|
||||
}
|
||||
|
||||
private fun removeExpiredCallbacks() {
|
||||
val now = System.currentTimeMillis()
|
||||
|
||||
removeCallbacks({ mrd: MessageResultDescriptor -> now - mrd.enqueueTime > CALLBACK_TIMEOUT_MILLIS })
|
||||
removeCallbacks {
|
||||
mrd: MessageResultDescriptor -> now - mrd.enqueueTime > CALLBACK_TIMEOUT_MILLIS
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeCallbacksForClient(client: Client) {
|
||||
removeCallbacks({ mrd: MessageResultDescriptor ->
|
||||
private fun removeCallbacksForClient(client: Client) =
|
||||
removeCallbacks { mrd: MessageResultDescriptor ->
|
||||
mrd.client === client
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeCallbacks(predicate: (MessageResultDescriptor) -> Boolean) {
|
||||
val it = messageCallbacks.entries.iterator()
|
||||
@ -595,25 +600,25 @@ class WebSocketService constructor(private val context: Context) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "WebSocketService"
|
||||
private const val TAG = "WebSocketService"
|
||||
|
||||
private val AUTO_RECONNECT_INTERVAL_MILLIS = 2000L
|
||||
private val CALLBACK_TIMEOUT_MILLIS = 30000L
|
||||
private val CONNECTION_TIMEOUT_MILLIS = 5000
|
||||
private val PING_INTERVAL_MILLIS = 3500L
|
||||
private val AUTO_CONNECT_FAILSAFE_DELAY_MILLIS = 2000L
|
||||
private val AUTO_DISCONNECT_DELAY_MILLIS = 10000L
|
||||
private val FLAG_AUTHENTICATION_FAILED = 0xbeef
|
||||
private val WEBSOCKET_FLAG_POLICY_VIOLATION = 1008
|
||||
private val MINIMUM_SUPPORTED_API_VERSION = 15
|
||||
private const val AUTO_RECONNECT_INTERVAL_MILLIS = 2000L
|
||||
private const val CALLBACK_TIMEOUT_MILLIS = 30000L
|
||||
private const val CONNECTION_TIMEOUT_MILLIS = 5000
|
||||
private const val PING_INTERVAL_MILLIS = 3500L
|
||||
private const val AUTO_CONNECT_FAILSAFE_DELAY_MILLIS = 2000L
|
||||
private const val AUTO_DISCONNECT_DELAY_MILLIS = 10000L
|
||||
private const val FLAG_AUTHENTICATION_FAILED = 0xbeef
|
||||
private const val WEBSOCKET_FLAG_POLICY_VIOLATION = 1008
|
||||
private const val MINIMUM_SUPPORTED_API_VERSION = 15
|
||||
|
||||
private val MESSAGE_BASE = 0xcafedead.toInt()
|
||||
private val MESSAGE_CONNECT_THREAD_FINISHED = MESSAGE_BASE + 0
|
||||
private val MESSAGE_RECEIVED = MESSAGE_BASE + 1
|
||||
private val MESSAGE_REMOVE_OLD_CALLBACKS = MESSAGE_BASE + 2
|
||||
private val MESSAGE_AUTO_RECONNECT = MESSAGE_BASE + 3
|
||||
private val MESSAGE_SCHEDULE_PING = MESSAGE_BASE + 4
|
||||
private val MESSAGE_PING_TIMEOUT = MESSAGE_BASE + 5
|
||||
private const val MESSAGE_BASE = 0xcafedead.toInt()
|
||||
private const val MESSAGE_CONNECT_THREAD_FINISHED = MESSAGE_BASE + 0
|
||||
private const val MESSAGE_RECEIVED = MESSAGE_BASE + 1
|
||||
private const val MESSAGE_REMOVE_OLD_CALLBACKS = MESSAGE_BASE + 2
|
||||
private const val MESSAGE_AUTO_RECONNECT = MESSAGE_BASE + 3
|
||||
private const val MESSAGE_SCHEDULE_PING = MESSAGE_BASE + 4
|
||||
private const val MESSAGE_PING_TIMEOUT = MESSAGE_BASE + 5
|
||||
|
||||
private val DISCONNECT_ON_PING_TIMEOUT = !BuildConfig.DEBUG
|
||||
|
||||
|
@ -17,8 +17,8 @@ import io.casey.musikcube.remote.ui.shared.extension.fallback
|
||||
import io.casey.musikcube.remote.ui.shared.extension.getColorCompat
|
||||
import io.casey.musikcube.remote.ui.shared.extension.titleEllipsizeMode
|
||||
import io.casey.musikcube.remote.ui.shared.mixin.PlaybackMixin
|
||||
import io.casey.musikcube.remote.ui.shared.model.albumart.Size
|
||||
import io.casey.musikcube.remote.ui.shared.model.albumart.getUrl
|
||||
import io.casey.musikcube.remote.ui.shared.util.Size
|
||||
import io.casey.musikcube.remote.ui.shared.util.AlbumArtLookup.getUrl
|
||||
|
||||
class AlbumBrowseAdapter(private val listener: EventListener,
|
||||
private val playback: PlaybackMixin,
|
||||
|
@ -39,12 +39,12 @@ import io.casey.musikcube.remote.ui.albums.activity.AlbumBrowseActivity
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs
|
||||
import io.casey.musikcube.remote.ui.shared.extension.fallback
|
||||
import io.casey.musikcube.remote.ui.shared.extension.getColorCompat
|
||||
import io.casey.musikcube.remote.ui.shared.model.albumart.Size
|
||||
import io.casey.musikcube.remote.ui.shared.util.Size
|
||||
import io.casey.musikcube.remote.ui.tracks.activity.TrackListActivity
|
||||
import io.casey.musikcube.remote.util.Strings
|
||||
import org.json.JSONArray
|
||||
import javax.inject.Inject
|
||||
import io.casey.musikcube.remote.ui.shared.model.albumart.getUrl as getAlbumArtUrl
|
||||
import io.casey.musikcube.remote.ui.shared.util.AlbumArtLookup.getUrl as getAlbumArtUrl
|
||||
|
||||
class MainMetadataView : FrameLayout {
|
||||
@Inject lateinit var wss: WebSocketService
|
||||
|
@ -22,6 +22,7 @@ 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 java.lang.IllegalArgumentException
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val EXTRA_CONNECTION = "extra_connection"
|
||||
@ -197,19 +198,22 @@ private class RenameTask(val db: ConnectionsDb, val connection: Connection, val
|
||||
class ConfirmDeleteDialog : DialogFragment() {
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val connection = arguments!!.getParcelable<Connection>(EXTRA_CONNECTION)
|
||||
val message = getString(R.string.settings_confirm_delete_message, connection.name)
|
||||
|
||||
val dlg = AlertDialog.Builder(activity!!)
|
||||
.setTitle(R.string.settings_confirm_delete_title)
|
||||
.setMessage(message)
|
||||
.setNegativeButton(R.string.button_no, null)
|
||||
.setPositiveButton(R.string.button_yes) { _, _ ->
|
||||
(activity as ConnectionsActivity).delete(connection)
|
||||
return when (connection == null) {
|
||||
true -> throw IllegalArgumentException("invalid connection")
|
||||
else -> {
|
||||
AlertDialog.Builder(activity!!)
|
||||
.setTitle(R.string.settings_confirm_delete_title)
|
||||
.setMessage(getString(R.string.settings_confirm_delete_message, connection.name))
|
||||
.setNegativeButton(R.string.button_no, null)
|
||||
.setPositiveButton(R.string.button_yes) { _, _ ->
|
||||
(activity as ConnectionsActivity).delete(connection)
|
||||
}
|
||||
.create().apply {
|
||||
setCancelable(false)
|
||||
}
|
||||
}
|
||||
.create()
|
||||
|
||||
dlg.setCancelable(false)
|
||||
return dlg
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -228,26 +232,29 @@ class RenameDialog : DialogFragment() {
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val connection = arguments!!.getParcelable<Connection>(EXTRA_CONNECTION)
|
||||
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = inflater.inflate(R.layout.dialog_edit, null)
|
||||
val edit = view.findViewById<EditText>(R.id.edit)
|
||||
return when (connection == null) {
|
||||
true -> throw IllegalArgumentException("invalid connection")
|
||||
else -> {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = inflater.inflate(R.layout.dialog_edit, null)
|
||||
val edit = view.findViewById<EditText>(R.id.edit)
|
||||
|
||||
edit.setText(connection.name)
|
||||
edit.selectAll()
|
||||
edit.setText(connection.name)
|
||||
edit.selectAll()
|
||||
|
||||
val dlg = AlertDialog.Builder(activity!!)
|
||||
.setTitle(R.string.settings_save_as_title)
|
||||
.setNegativeButton(R.string.button_cancel, null)
|
||||
.setPositiveButton(R.string.button_save) { _, _ ->
|
||||
val name = edit.text.toString()
|
||||
(activity as ConnectionsActivity).rename(connection, name)
|
||||
}
|
||||
.create()
|
||||
|
||||
dlg.setView(view)
|
||||
dlg.setCancelable(false)
|
||||
|
||||
return dlg
|
||||
AlertDialog.Builder(activity!!)
|
||||
.setTitle(R.string.settings_save_as_title)
|
||||
.setNegativeButton(R.string.button_cancel, null)
|
||||
.setPositiveButton(R.string.button_save) { _, _ ->
|
||||
val name = edit.text.toString()
|
||||
(activity as ConnectionsActivity).rename(connection, name)
|
||||
}
|
||||
.create().apply {
|
||||
setView(view)
|
||||
setCancelable(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -52,6 +52,7 @@ class RemoteEqActivity: BaseActivity() {
|
||||
}
|
||||
|
||||
override fun <T : ViewModel<*>> createViewModel(): T? {
|
||||
@Suppress("unchecked_cast")
|
||||
return RemoteEqViewModel() as T
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,7 @@ class RemoteSettingsActivity: BaseActivity() {
|
||||
}
|
||||
|
||||
override fun <T : ViewModel<*>> createViewModel(): T? {
|
||||
@Suppress("unchecked_cast")
|
||||
return RemoteSettingsViewModel() as T
|
||||
}
|
||||
|
||||
@ -209,6 +210,8 @@ class RemoteSettingsActivity: BaseActivity() {
|
||||
ViewModelState.Saved -> {
|
||||
finish()
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ 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.lang.IllegalArgumentException
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import io.casey.musikcube.remote.ui.settings.constants.Prefs.Default as Defaults
|
||||
@ -107,7 +108,7 @@ class SettingsActivity : BaseActivity() {
|
||||
|
||||
private fun rebindUi() {
|
||||
/* connection info */
|
||||
addressText.setTextAndMoveCursorToEnd(prefs.getString(Keys.ADDRESS, Defaults.ADDRESS))
|
||||
addressText.setTextAndMoveCursorToEnd(prefs.getString(Keys.ADDRESS) ?: Defaults.ADDRESS)
|
||||
|
||||
portText.setTextAndMoveCursorToEnd(String.format(
|
||||
Locale.ENGLISH, "%d", prefs.getInt(Keys.MAIN_PORT, Defaults.MAIN_PORT)))
|
||||
@ -115,7 +116,7 @@ class SettingsActivity : BaseActivity() {
|
||||
httpPortText.setTextAndMoveCursorToEnd(String.format(
|
||||
Locale.ENGLISH, "%d", prefs.getInt(Keys.AUDIO_PORT, Defaults.AUDIO_PORT)))
|
||||
|
||||
passwordText.setTextAndMoveCursorToEnd(prefs.getString(Keys.PASSWORD, Defaults.PASSWORD))
|
||||
passwordText.setTextAndMoveCursorToEnd(prefs.getString(Keys.PASSWORD) ?: Defaults.PASSWORD)
|
||||
|
||||
/* bitrate */
|
||||
val bitrates = ArrayAdapter.createFromResource(
|
||||
@ -402,10 +403,15 @@ class SettingsActivity : BaseActivity() {
|
||||
.setMessage(R.string.settings_confirm_overwrite_message)
|
||||
.setNegativeButton(R.string.button_no, null)
|
||||
.setPositiveButton(R.string.button_yes) { _, _ ->
|
||||
val connection = arguments!!.getParcelable<Connection>(EXTRA_CONNECTION)
|
||||
val db = (activity as SettingsActivity).connectionsDb
|
||||
val saveAs = SaveAsTask(db, connection, true)
|
||||
(activity as SettingsActivity).runner.run(SaveAsTask.nameFor(connection), saveAs)
|
||||
val connection = arguments?.getParcelable<Connection>(EXTRA_CONNECTION)
|
||||
when (connection) {
|
||||
null -> throw IllegalArgumentException("invalid connection")
|
||||
else -> {
|
||||
val db = (activity as SettingsActivity).connectionsDb
|
||||
val saveAs = SaveAsTask(db, connection, true)
|
||||
(activity as SettingsActivity).runner.run(SaveAsTask.nameFor(connection), saveAs)
|
||||
}
|
||||
}
|
||||
}
|
||||
.create()
|
||||
|
||||
|
@ -18,9 +18,9 @@ class Connection : Parcelable {
|
||||
constructor()
|
||||
|
||||
constructor(source: Parcel) {
|
||||
name = source.readString()
|
||||
hostname = source.readString()
|
||||
password = source.readString()
|
||||
name = source.readString() ?: ""
|
||||
hostname = source.readString() ?: ""
|
||||
password = source.readString() ?: ""
|
||||
httpPort = source.readInt()
|
||||
wssPort = source.readInt()
|
||||
ssl = (source.readInt() == 1)
|
||||
@ -49,6 +49,7 @@ class Connection : Parcelable {
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Suppress("unused")
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.Creator<Connection> = object: Parcelable.Creator<Connection> {
|
||||
override fun createFromParcel(source: Parcel?): Connection {
|
||||
|
@ -129,6 +129,9 @@ abstract class BaseActivity : AppCompatActivity(), ViewModel.Provider, Runner.Ta
|
||||
|
||||
protected open val transitionType = Transition.Horizontal
|
||||
|
||||
protected val extras: Bundle
|
||||
get() = intent?.extras ?: Bundle()
|
||||
|
||||
override fun <T: ViewModel<*>> createViewModel(): T? = null
|
||||
protected fun <T: ViewModel<*>> getViewModel(): T? = mixin(ViewModelMixin::class.java)?.get<T>() as T
|
||||
protected fun <T: IMixin> mixin(mixin: T): T = mixins.add(mixin)
|
||||
|
@ -237,10 +237,10 @@ fun AppCompatActivity.showSnackbar(viewId: Int, stringId: Int, buttonText: Strin
|
||||
showSnackbar(this.findViewById<View>(viewId), stringId, buttonText, buttonCb)
|
||||
|
||||
fun fallback(input: String?, fallback: String): String =
|
||||
if (input.isNullOrEmpty()) fallback else input!!
|
||||
if (input.isNullOrEmpty()) fallback else input
|
||||
|
||||
fun fallback(input: String?, fallback: Int): String =
|
||||
if (input.isNullOrEmpty()) Application.instance.getString(fallback) else input!!
|
||||
if (input.isNullOrEmpty()) Application.instance.getString(fallback) else input
|
||||
|
||||
fun AppCompatActivity.slideNextUp() = overridePendingTransition(R.anim.slide_up, R.anim.stay_put)
|
||||
|
||||
@ -277,6 +277,12 @@ fun titleEllipsizeMode(prefs: SharedPreferences): TextUtils.TruncateAt {
|
||||
}
|
||||
}
|
||||
|
||||
fun SharedPreferences.getString(key: String): String? =
|
||||
when (!this.contains(key)) {
|
||||
true -> null
|
||||
else -> this.getString(key, "")
|
||||
}
|
||||
|
||||
inline fun <reified T> FragmentManager.find(tag: String): T {
|
||||
return findFragmentByTag(tag) as T
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import io.casey.musikcube.remote.ui.settings.constants.Prefs
|
||||
|
||||
class PlaybackMixin(var listener: (() -> Unit)? = { }): MixinBase() {
|
||||
private lateinit var prefs: SharedPreferences
|
||||
private val context = Application.instance!!
|
||||
private val context = Application.instance
|
||||
|
||||
var service: IPlaybackService = PlaybackServiceFactory.instance(context)
|
||||
private set
|
||||
|
@ -11,6 +11,7 @@ class ViewModelMixin(private val provider: ViewModel.Provider): MixinBase() {
|
||||
if (viewModel == null) {
|
||||
viewModel = provider.createViewModel()
|
||||
}
|
||||
@Suppress("unchecked_cast")
|
||||
return viewModel as T?
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ abstract class BaseSlidingWindow(
|
||||
disposables.add(dataProvider.observePlayQueue()
|
||||
.subscribe({ requery() }, { /* error */ }))
|
||||
|
||||
recyclerView.setStateChangeListener(fastScrollStateChangeListener)
|
||||
recyclerView.setOnFastScrollStateChangeListener(fastScrollStateChangeListener)
|
||||
recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||
connected = true
|
||||
fastScrollerActive = false
|
||||
|
@ -1,4 +1,4 @@
|
||||
package io.casey.musikcube.remote.ui.shared.model.albumart
|
||||
package io.casey.musikcube.remote.ui.shared.util
|
||||
|
||||
import android.content.Context
|
||||
import android.util.LruCache
|
||||
@ -44,47 +44,19 @@ private val badPatterns = arrayOf(
|
||||
|
||||
/* http://www.last.fm/group/Last.fm+Web+Services/forum/21604/_/522900 -- it's ok to
|
||||
put our key in the code */
|
||||
private val lastFmFormatUrl =
|
||||
private const val LASTFM_FORMAT_URL =
|
||||
"http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=" +
|
||||
"502c69bd3f9946e8e0beee4fcb28c4cd&artist=%s&album=%s&format=json&size=%s"
|
||||
|
||||
private val urlCache = LruCache<String, String>(500)
|
||||
private val badUrlCache = LruCache<String, Boolean>(100)
|
||||
private val inFlight = mutableMapOf<String, CountDownLatch>()
|
||||
private val httpClient = OkHttpClient.Builder().build()
|
||||
|
||||
private val prefs by lazy {
|
||||
Application.instance.getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
fun getUrl(album: IAlbum, size: Size = Size.Small): String? {
|
||||
return getThumbnailUrl(album.thumbnailId)
|
||||
?: getUrl(album.albumArtist, album.name, size)
|
||||
}
|
||||
|
||||
fun getUrl(track: ITrack, size: Size = Size.Small): String? {
|
||||
return getThumbnailUrl(track.thumbnailId)
|
||||
?: getUrl(track.artist, track.album, size)
|
||||
}
|
||||
|
||||
fun getUrl(artist: String = "", album: String = "", size: Size = Size.Small): String? {
|
||||
if (!prefs.getBoolean(Prefs.Key.LASTFM_ENABLED, Prefs.Default.LASTFM_ENABLED)) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (artist.isBlank() || album.isBlank()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return String.format(lastFmFormatUrl, dejunk(artist), dejunk(album), size.key)
|
||||
}
|
||||
|
||||
fun canIntercept(request: Request): Boolean {
|
||||
return request.url().host() == "ws.audioscrobbler.com" &&
|
||||
request.url().queryParameter("method") == "album.getinfo"
|
||||
}
|
||||
|
||||
private val httpClient = OkHttpClient.Builder().build()
|
||||
|
||||
private fun executeWithRetries(req: Request): Response? {
|
||||
var count = 0
|
||||
var result: Response? = null
|
||||
@ -103,109 +75,6 @@ private fun executeWithRetries(req: Request): Response? {
|
||||
return result
|
||||
}
|
||||
|
||||
fun intercept(req: Request): Request? {
|
||||
val url = req.url()
|
||||
|
||||
var imageUrl = urlCache[url.toString()] ?: ""
|
||||
val desiredSize = Size.from(url.queryParameter("size"))
|
||||
var badUrl = false
|
||||
var pending: CountDownLatch? = null
|
||||
|
||||
synchronized(urlCache) {
|
||||
badUrl = badUrlCache.get(url.toString()) ?: false
|
||||
pending = inFlight[url.toString()]
|
||||
}
|
||||
|
||||
/* let's see if there's already another request for this URL in flight. if there is,
|
||||
let it finish, as it'll wind up in the cache and we won't have to make multiple
|
||||
requests against the backend */
|
||||
if (pending != null) {
|
||||
pending?.await()
|
||||
pending = null
|
||||
|
||||
synchronized(urlCache) {
|
||||
imageUrl = urlCache[url.toString()] ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
/* depending on the above, we may have an imageUrl! if we do, we're good. otherwise,
|
||||
let's setup the countdown latch so subsequent requests wait... */
|
||||
if (imageUrl.isBlank() && !badUrl) {
|
||||
synchronized(urlCache) {
|
||||
pending = CountDownLatch(1)
|
||||
inFlight.put(url.toString(), pending!!)
|
||||
}
|
||||
}
|
||||
|
||||
if (imageUrl.isBlank() && !badUrl) {
|
||||
val response = executeWithRetries(req)
|
||||
|
||||
if (response != null) {
|
||||
val images = mutableListOf<Pair<Size, String>>()
|
||||
|
||||
try {
|
||||
val json = JSONObject(response.body()?.string())
|
||||
val imagesJson = json.getJSONObject("album").getJSONArray("image")
|
||||
for (i in 0 until imagesJson.length()) {
|
||||
val imageJson = imagesJson.getJSONObject(i)
|
||||
val size = Size.from(imageJson.optString("size", ""))
|
||||
val resolvedUrl = imageJson.optString("#text", "")
|
||||
if (Strings.notEmpty(resolvedUrl)) {
|
||||
images.add(Pair<Size, String>(size, resolvedUrl))
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ex: JSONException) {
|
||||
badUrlCache.put(url.toString(), true)
|
||||
}
|
||||
|
||||
if (images.size > 0) {
|
||||
/* find the image with the closest to the requested size.
|
||||
exact match preferred. */
|
||||
var closest = images[0]
|
||||
var lastDelta = Integer.MAX_VALUE
|
||||
for (check in images) {
|
||||
if (check.first == desiredSize) {
|
||||
closest = check
|
||||
break
|
||||
}
|
||||
else {
|
||||
val delta = Math.abs(desiredSize.order - check.first.order)
|
||||
if (lastDelta > delta) {
|
||||
closest = check
|
||||
lastDelta = delta
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imageUrl = closest.second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result: Request? = null
|
||||
|
||||
if (imageUrl.isNotBlank()) {
|
||||
synchronized(urlCache) {
|
||||
urlCache.put(url.toString(), imageUrl)
|
||||
}
|
||||
|
||||
if (desiredSize == Size.Mega) {
|
||||
imageUrl = imageUrl.replace("/i/u/300x300", "/i/u/600x600")
|
||||
}
|
||||
|
||||
result = Request.Builder().url(imageUrl).build()
|
||||
}
|
||||
|
||||
synchronized(urlCache) {
|
||||
if (pending != null) {
|
||||
pending?.countDown()
|
||||
inFlight.remove(url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getThumbnailUrl(id: Long): String? {
|
||||
if (id > 0) {
|
||||
@ -223,5 +92,138 @@ private fun dejunk(album: String): String {
|
||||
for (pattern in badPatterns) {
|
||||
result = pattern.matcher(result).replaceAll("")
|
||||
}
|
||||
@Suppress("deprecation")
|
||||
return URLEncoder.encode(result.trim { it.isWhitespace() })
|
||||
}
|
||||
|
||||
object AlbumArtLookup {
|
||||
fun getUrl(album: IAlbum, size: Size = Size.Small): String? {
|
||||
return getThumbnailUrl(album.thumbnailId)
|
||||
?: getUrl(album.albumArtist, album.name, size)
|
||||
}
|
||||
|
||||
fun getUrl(track: ITrack, size: Size = Size.Small): String? {
|
||||
return getThumbnailUrl(track.thumbnailId)
|
||||
?: getUrl(track.artist, track.album, size)
|
||||
}
|
||||
|
||||
fun getUrl(artist: String = "", album: String = "", size: Size = Size.Small): String? {
|
||||
if (!prefs.getBoolean(Prefs.Key.LASTFM_ENABLED, Prefs.Default.LASTFM_ENABLED)) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (artist.isBlank() || album.isBlank()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return String.format(LASTFM_FORMAT_URL, dejunk(artist), dejunk(album), size.key)
|
||||
}
|
||||
|
||||
fun canIntercept(request: Request): Boolean {
|
||||
return request.url().host() == "ws.audioscrobbler.com" &&
|
||||
request.url().queryParameter("method") == "album.getinfo"
|
||||
}
|
||||
|
||||
fun intercept(req: Request): Request? {
|
||||
val url = req.url()
|
||||
|
||||
var imageUrl = urlCache[url.toString()] ?: ""
|
||||
val desiredSize = Size.from(url.queryParameter("size"))
|
||||
var badUrl: Boolean
|
||||
var pending: CountDownLatch?
|
||||
|
||||
synchronized(urlCache) {
|
||||
badUrl = badUrlCache.get(url.toString()) ?: false
|
||||
pending = inFlight[url.toString()]
|
||||
}
|
||||
|
||||
/* let's see if there's already another request for this URL in flight. if there is,
|
||||
let it finish, as it'll wind up in the cache and we won't have to make multiple
|
||||
requests against the backend */
|
||||
if (pending != null) {
|
||||
pending?.await()
|
||||
pending = null
|
||||
|
||||
synchronized(urlCache) {
|
||||
imageUrl = urlCache[url.toString()] ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
/* depending on the above, we may have an imageUrl! if we do, we're good. otherwise,
|
||||
let's setup the countdown latch so subsequent requests wait... */
|
||||
if (imageUrl.isBlank() && !badUrl) {
|
||||
synchronized(urlCache) {
|
||||
pending = CountDownLatch(1)
|
||||
inFlight.put(url.toString(), pending!!)
|
||||
}
|
||||
}
|
||||
|
||||
if (imageUrl.isBlank() && !badUrl) {
|
||||
val response = executeWithRetries(req)
|
||||
|
||||
if (response != null) {
|
||||
val images = mutableListOf<Pair<Size, String>>()
|
||||
|
||||
try {
|
||||
val json = JSONObject(response.body()?.string())
|
||||
val imagesJson = json.getJSONObject("album").getJSONArray("image")
|
||||
for (i in 0 until imagesJson.length()) {
|
||||
val imageJson = imagesJson.getJSONObject(i)
|
||||
val size = Size.from(imageJson.optString("size", ""))
|
||||
val resolvedUrl = imageJson.optString("#text", "")
|
||||
if (Strings.notEmpty(resolvedUrl)) {
|
||||
images.add(Pair<Size, String>(size, resolvedUrl))
|
||||
}
|
||||
}
|
||||
} catch (ex: JSONException) {
|
||||
badUrlCache.put(url.toString(), true)
|
||||
}
|
||||
|
||||
if (images.size > 0) {
|
||||
/* find the image with the closest to the requested size.
|
||||
exact match preferred. */
|
||||
var closest = images[0]
|
||||
var lastDelta = Integer.MAX_VALUE
|
||||
for (check in images) {
|
||||
if (check.first == desiredSize) {
|
||||
closest = check
|
||||
break
|
||||
}
|
||||
else {
|
||||
val delta = Math.abs(desiredSize.order - check.first.order)
|
||||
if (lastDelta > delta) {
|
||||
closest = check
|
||||
lastDelta = delta
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imageUrl = closest.second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result: Request? = null
|
||||
|
||||
if (imageUrl.isNotBlank()) {
|
||||
synchronized(urlCache) {
|
||||
urlCache.put(url.toString(), imageUrl)
|
||||
}
|
||||
|
||||
if (desiredSize == Size.Mega) {
|
||||
imageUrl = imageUrl.replace("/i/u/300x300", "/i/u/600x600")
|
||||
}
|
||||
|
||||
result = Request.Builder().url(imageUrl).build()
|
||||
}
|
||||
|
||||
synchronized(urlCache) {
|
||||
if (pending != null) {
|
||||
pending?.countDown()
|
||||
inFlight.remove(url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
@ -31,16 +31,16 @@ class EmptyListView : FrameLayout {
|
||||
private var viewOfflineButton: View? = null
|
||||
private var reconnectButton: View? = null
|
||||
|
||||
constructor(context: Context?)
|
||||
constructor(context: Context)
|
||||
: super(context) {
|
||||
initialize()
|
||||
}
|
||||
|
||||
constructor(context: Context?, attrs: AttributeSet?)
|
||||
constructor(context: Context, attrs: AttributeSet?)
|
||||
: super(context, attrs) {
|
||||
initialize()
|
||||
}
|
||||
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
|
||||
: super(context, attrs, defStyleAttr) {
|
||||
initialize()
|
||||
}
|
||||
@ -115,11 +115,11 @@ class EmptyListView : FrameLayout {
|
||||
|
||||
addView(mainView)
|
||||
|
||||
reconnectButton?.setOnClickListener { _ ->
|
||||
reconnectButton?.setOnClickListener {
|
||||
wss.reconnect()
|
||||
}
|
||||
|
||||
viewOfflineButton?.setOnClickListener { _ ->
|
||||
viewOfflineButton?.setOnClickListener {
|
||||
val activity = context as Activity
|
||||
activity.startActivity(TrackListActivity.getOfflineStartIntent(activity))
|
||||
activity.finish()
|
||||
|
@ -33,7 +33,7 @@ class EditPlaylistActivity: BaseActivity() {
|
||||
mixin(ViewModelMixin(this))
|
||||
data = mixin(DataProviderMixin())
|
||||
super.onCreate(savedInstanceState)
|
||||
playlistName = intent.extras.getString(EXTRA_PLAYLIST_NAME, "-")
|
||||
playlistName = extras.getString(EXTRA_PLAYLIST_NAME, "-")
|
||||
title = getString(R.string.playlist_edit_activity, playlistName)
|
||||
setContentView(R.layout.recycler_view_activity)
|
||||
viewModel = getViewModel()!!
|
||||
@ -80,7 +80,8 @@ class EditPlaylistActivity: BaseActivity() {
|
||||
}
|
||||
|
||||
override fun <T: ViewModel<*>> createViewModel(): T? {
|
||||
return EditPlaylistViewModel(intent.extras.getLong(EXTRA_PLAYLIST_ID, -1L)) as T
|
||||
@Suppress("unchecked_cast")
|
||||
return EditPlaylistViewModel(extras.getLong(EXTRA_PLAYLIST_ID, -1L)) as T
|
||||
}
|
||||
|
||||
private fun saveAndFinish() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user