Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*

Check warning on line 1 in app/src/main/java/org/akanework/gramophone/logic/GramophoneExtensions.kt

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (beta)

❌ Getting worse: Global Conditionals

The global code outside of functions increases in cyclomatic complexity from 31 to 37, threshold = 10. The code has become too complex as it contains many conditional statements (e.g. if, for, while) across its implementation, leading to lower code health. Avoid adding more.

Check warning on line 1 in app/src/main/java/org/akanework/gramophone/logic/GramophoneExtensions.kt

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (beta)

❌ New issue: Primitive Obsession

In this module, 50.0% of all function arguments are primitive types, threshold = 30.0%. The functions in this file have too many primitive types (e.g. int, double, float) in their function argument lists. Using many primitive types lead to the code smell Primitive Obsession. Avoid adding more primitive arguments.
* Copyright (C) 2024 Akane Foundation
*
* Gramophone is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -59,12 +59,14 @@
import androidx.core.view.children
import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMargins
import androidx.media3.common.BundleListRetriever
import androidx.media3.common.C
import androidx.media3.common.Format
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.Tracks
import androidx.media3.common.util.Log
import androidx.media3.exoplayer.source.ShuffleOrder
import androidx.media3.session.MediaController
import androidx.media3.session.SessionCommand
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
Expand All @@ -75,6 +77,13 @@
import org.akanework.gramophone.R
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_GET_AUDIO_FORMAT
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_GET_LYRICS
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_QB_DEL
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_QB_GET_INACTIVE_LIST
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_QB_GET_QUEUE_FOR_UI
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_QB_LOAD_QUEUE
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_QB_PIN_QUEUE
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_QB_REORDER
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_QB_UNPIN_QUEUE
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_QUERY_TIMER
import org.akanework.gramophone.logic.GramophonePlaybackService.Companion.SERVICE_SET_TIMER
import org.akanework.gramophone.logic.utils.AfFormatInfo
Expand Down Expand Up @@ -337,6 +346,129 @@
)
}

fun MediaController.getInactiveQueues(): List<MultiQueueObject> =
sendCustomCommand(
SessionCommand(SERVICE_QB_GET_INACTIVE_LIST, Bundle.EMPTY),
Bundle.EMPTY
).get().extras.run {
val binder = getBinder("allQueues")!!
BundleListRetriever.getList(binder).map {
MultiQueueObject.fromBundle(it)
}
}

fun MediaController.getQueue(index: Int = C.INDEX_UNSET): MultiQueueObject? =
sendCustomCommand(
SessionCommand(SERVICE_QB_GET_QUEUE_FOR_UI, Bundle.EMPTY).apply {
customExtras.putInt("index", index)
}, Bundle.EMPTY
).get().extras.run {
val binder = getBinder("allQueues")!!
BundleListRetriever.getList(binder).map {
MultiQueueObject.fromBundle(it)
}.firstOrNull()
}


fun shuffledItems(
items: List<MediaItem>,
order: ShuffleOrder
): List<MediaItem> {
val result = mutableListOf<MediaItem>()

var i = order.firstIndex
while (i != C.INDEX_UNSET) {
result.add(items[i])
i = order.getNextIndex(i)
}

return result
}

fun shuffledIndices(order: ShuffleOrder): MutableList<Int> {
val result = mutableListOf<Int>()

var i = order.firstIndex
while (i != C.INDEX_UNSET) {
result.add(i)
i = order.getNextIndex(i)
}

return result
}

fun MediaController.getQueueForUi(index: Int = -1): Pair<MutableList<Int>, MultiQueueObject>? {
if (index == -1) {
return null
}
return sendCustomCommand(
SessionCommand(SERVICE_QB_GET_QUEUE_FOR_UI, Bundle.EMPTY).apply {
customExtras.putInt("index", index)
}, Bundle.EMPTY
).get().extras.run {
val binder = getBinder("allQueues")!!
BundleListRetriever.getList(binder).map {
val mq = MultiQueueObject.fromBundle(it)
val indexes: MutableList<Int> = if (mq.shuffleOrder == null) {
(0 until mq.getSize()).toMutableList()
} else {
getIntArray("shuffleIndexes")!!.toMutableList()
}

Pair(indexes, mq)
}.firstOrNull()
}
}

fun MediaController.loadQueue(index: Int) {
sendCustomCommand(
SessionCommand(SERVICE_QB_LOAD_QUEUE, Bundle.EMPTY).apply {
customExtras.putInt("index", index)
}, Bundle.EMPTY
)
}

fun MediaController.pinQueue(index: Int) {
sendCustomCommand(
SessionCommand(SERVICE_QB_PIN_QUEUE, Bundle.EMPTY).apply {
customExtras.putInt("index", index)
}, Bundle.EMPTY
)
}


fun MediaController.unQueue(index: Int) {
sendCustomCommand(
SessionCommand(SERVICE_QB_UNPIN_QUEUE, Bundle.EMPTY).apply {
customExtras.putInt("index", index)
}, Bundle.EMPTY
)
}


fun MediaController.deleteQueue(index: Int): Boolean =
sendCustomCommand(
SessionCommand(SERVICE_QB_DEL, Bundle.EMPTY).apply {
customExtras.putInt("index", index)
}, Bundle.EMPTY
).get().extras.run {
if (containsKey("status"))
getBoolean("status")
else throw IllegalArgumentException("expected status to be set")
}

fun MediaController.reorderQueue(from: Int, to: Int): Boolean =
sendCustomCommand(
SessionCommand(SERVICE_QB_REORDER, Bundle.EMPTY).apply {
customExtras.putInt("from", from)
customExtras.putInt("to", to)
}, Bundle.EMPTY
).get().extras.run {
if (containsKey("status"))
getBoolean("status")
else throw IllegalArgumentException("expected status to be set")
}

fun Tracks.getFirstSelectedTrackFormatByType(type: @C.TrackType Int): Format? {
for (i in groups) {
if (i.type == type) {
Expand Down Expand Up @@ -503,6 +635,16 @@
it.unconsumeIfNeeded()
})

fun Context.supportsWideScreen() : Boolean {
val config = resources.configuration
return config.screenWidthDp >= 780
}

fun Context.isTablet() : Boolean {
val config = resources.configuration
return config.smallestScreenWidthDp >= 780
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sw600dp is tablet i think

}

val Context.gramophoneApplication
get() = this.applicationContext as GramophoneApplication

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import androidx.core.content.IntentCompat
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.AudioAttributes
import androidx.media3.common.BundleListRetriever
import androidx.media3.common.C
import androidx.media3.common.DeviceInfo
import androidx.media3.common.Format
Expand Down Expand Up @@ -122,6 +123,7 @@
import org.akanework.gramophone.logic.utils.ReplayGainUtil
import org.akanework.gramophone.logic.utils.SemanticLyrics
import org.akanework.gramophone.logic.utils.exoplayer.EndedWorkaroundPlayer
import org.akanework.gramophone.logic.utils.exoplayer.EndedWorkaroundPlayer.Companion.queueWithTitle
import org.akanework.gramophone.logic.utils.exoplayer.GramophoneExtractorsFactory
import org.akanework.gramophone.logic.utils.exoplayer.GramophoneMediaSourceFactory
import org.akanework.gramophone.logic.utils.exoplayer.GramophoneRenderFactory
Expand Down Expand Up @@ -153,11 +155,21 @@
const val PENDING_INTENT_NOTIFY_ID = 1
const val PENDING_INTENT_WIDGET_ID = 2
const val PENDING_INTENT_FAVE_ID = 3

const val SERVICE_SET_TIMER = "set_timer"
const val SERVICE_QUERY_TIMER = "query_timer"
const val SERVICE_GET_AUDIO_FORMAT = "get_audio_format"
const val SERVICE_GET_LYRICS = "get_lyrics"
const val SERVICE_TIMER_CHANGED = "changed_timer"

const val SERVICE_QB_GET_INACTIVE_LIST = "qb_get_inactive_list"
const val SERVICE_QB_LOAD_QUEUE = "qb_load"
const val SERVICE_QB_GET_QUEUE_FOR_UI = "qb_get_queue_for_ui"
const val SERVICE_QB_DEL = "qb_delete"
const val SERVICE_QB_REORDER = "qb_reorder"
const val SERVICE_QB_PIN_QUEUE ="qb_pin_queue"
const val SERVICE_QB_UNPIN_QUEUE ="qb_unpin_queue"

var instanceForWidgetAndLyricsOnly: GramophonePlaybackService? = null
}

Expand All @@ -168,6 +180,7 @@
val endedWorkaroundPlayer
get() = mediaSession?.player as EndedWorkaroundPlayer?
private var controller: MediaBrowser? = null
lateinit var qb: QueueBoard
private val sendLyrics = Runnable { scheduleSendingLyrics(false) }
var lyrics: SemanticLyrics? = null
private set
Expand Down Expand Up @@ -273,279 +286,283 @@
handler = Handler(Looper.getMainLooper())
nm = NotificationManagerCompat.from(this)
prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
qb = QueueBoard(this)
setListener(this)
setMediaNotificationProvider(
MeiZuLyricsMediaNotificationProvider(this) { lastSentHighlightedLyric }
)
setForegroundServiceTimeoutMs(120000)
setShowNotificationForEmptyPlayer(SHOW_NOTIFICATION_FOR_EMPTY_PLAYER_AFTER_STOP_OR_ERROR)
nm.createNotificationChannel(
NotificationChannelCompat.Builder(
NOTIFY_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_HIGH
).apply {
setName(getString(R.string.error_in_bg))
setVibrationEnabled(true)
setVibrationPattern(longArrayOf(0L, 200L))
setLightsEnabled(false)
setShowBadge(false)
setSound(null, null)
}.build()
)

customCommands =
listOf(
CommandButton.Builder(CommandButton.ICON_SHUFFLE_OFF) // shuffle currently disabled, click will enable
.setDisplayName(getString(R.string.shuffle))
.setPlayerCommand(Player.COMMAND_SET_SHUFFLE_MODE, true)
.build(),
CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON) // shuffle currently enabled, click will disable
.setDisplayName(getString(R.string.shuffle))
.setPlayerCommand(Player.COMMAND_SET_SHUFFLE_MODE, false)
.build(),
CommandButton.Builder(CommandButton.ICON_REPEAT_OFF) // repeat currently disabled, click will repeat all
.setDisplayName(getString(R.string.repeat_mode))
.setPlayerCommand(Player.COMMAND_SET_REPEAT_MODE, Player.REPEAT_MODE_ALL)
.build(),
CommandButton.Builder(CommandButton.ICON_REPEAT_ALL) // repeat all currently enabled, click will repeat one
.setDisplayName(getString(R.string.repeat_mode))
.setPlayerCommand(Player.COMMAND_SET_REPEAT_MODE, Player.REPEAT_MODE_ONE)
.build(),
CommandButton.Builder(CommandButton.ICON_REPEAT_ONE) // repeat one currently enabled, click will disable
.setDisplayName(getString(R.string.repeat_mode))
.setPlayerCommand(Player.COMMAND_SET_REPEAT_MODE, Player.REPEAT_MODE_OFF)
.build(),
)
afFormatTracker = AfFormatTracker(this, playbackHandler, handler)
afFormatTracker.formatChangedCallback = { format, period ->
if (period != null) {
handler.post {
val currentPeriod = controller?.currentPeriodIndex?.takeIf {
it != C.INDEX_UNSET &&
(controller?.currentTimeline?.periodCount ?: 0) > it
}
?.let { controller!!.currentTimeline.getUidOfPeriod(it) }
if (currentPeriod != period) {
if (format != null) {
pendingAfTrackFormats[period] = format
} else {
pendingAfTrackFormats.remove(period)
}
} else {
afTrackFormat = format?.let { period to it }
mediaSession?.broadcastCustomCommand(
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
Bundle.EMPTY
)
}
}
} else {
Log.e(TAG, "mediaPeriodId is NULL in formatChangedCallback!!")
}
}
rgAp = ReplayGainAudioProcessor()
prefs.registerOnSharedPreferenceChangeListener(this)
onSharedPreferenceChanged(prefs, null) // read initial values
val player = EndedWorkaroundPlayer(
ExoPlayer.Builder(
exoPlayer = ExoPlayer.Builder(
this,
GramophoneRenderFactory(
this, rgAp, this::onAudioSinkInputFormatChanged,
afFormatTracker::setAudioSink
)
.setPcmEncodingRestrictionLifted(true)
.setEnableDecoderFallback(true)
.setEnableAudioTrackPlaybackParams(true)
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON),
GramophoneMediaSourceFactory(
DefaultDataSource.Factory(this),
GramophoneExtractorsFactory().also {
it.setConstantBitrateSeekingEnabled(true)
if (prefs.getBooleanStrict("mp3_index_seeking", false))
it.setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING)
})
)
.setWakeMode(C.WAKE_MODE_LOCAL)
.setAudioAttributes(
AudioAttributes
.Builder()
.setUsage(C.USAGE_MEDIA)
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
.build(), true
)
.setHandleAudioBecomingNoisy(true)
.setTrackSelector(DefaultTrackSelector(this).apply {
setParameters(
buildUponParameters()
.setAllowInvalidateSelectionsOnRendererCapabilitiesChange(true)
.setAudioOffloadPreferences(
TrackSelectionParameters.AudioOffloadPreferences.Builder()
.apply {
val config =
prefs.getStringStrict("offload", "0")?.toIntOrNull()
if (config != null && config > 0 && Flags.OFFLOAD) {
rgAp.setOffloadEnabled(true)
setAudioOffloadMode(TrackSelectionParameters.AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_ENABLED)
setIsGaplessSupportRequired(config == 2)
}
}
.build()))
})
.setPlaybackLooper(internalPlaybackThread.looper)
.build()
.build(),
queueBoard = qb,
)
player.exoPlayer.addAnalyticsListener(EventLogger())
player.exoPlayer.addAnalyticsListener(afFormatTracker)
player.exoPlayer.addAnalyticsListener(this)
player.exoPlayer.setShuffleOrder(CircularShuffleOrder(player, 0, 0, Random.nextLong()))
lastPlayedManager = LastPlayedManager(this, player)
lastPlayedManager.allowSavingState = false

mediaSession =
MediaLibrarySession
.Builder(this, player, this)
// CacheBitmapLoader is required for MeiZuLyricsMediaNotificationProvider
.setBitmapLoader(CacheBitmapLoader(object : BitmapLoader {
// Coil-based bitmap loader to reuse Coil's caching and to make sure we use
// the same cover art as the rest of the app, ie MediaStore's cover

private val limit by lazy { MediaSession.getBitmapDimensionLimit(this@GramophonePlaybackService) }

// the suppression is correct, we want identity of the byte array as it will
// stay the same over one song's lifetime
@Suppress("KotlinArrayHashCode")
override fun decodeBitmap(data: ByteArray): ListenableFuture<Bitmap> {
return CallbackToFutureAdapter.getFuture { completer ->
imageLoader.enqueue(
ImageRequest.Builder(this@GramophonePlaybackService)
.data(data)
.memoryCacheKey(data.hashCode().toString())
.size(limit, limit)
.allowHardware(false)
.target(
onStart = { _ ->
// We don't need or want a placeholder.
},
onSuccess = { result ->
completer.set((result as BitmapImage).bitmap)
},
onError = { _ ->
completer.setException(
Exception(
"coil onError called for byte array"
)
)
}
)
.build())
.also {
completer.addCancellationListener(
{ it.dispose() },
ContextCompat.getMainExecutor(
this@GramophonePlaybackService
)
)
}
"coil load for ${data.hashCode()}"
}
}

override fun loadBitmap(
uri: Uri
): ListenableFuture<Bitmap> {
return CallbackToFutureAdapter.getFuture { completer ->
imageLoader.enqueue(
ImageRequest.Builder(this@GramophonePlaybackService)
.data(uri)
.size(limit, limit)
.allowHardware(false)
.target(
onStart = { _ ->
// We don't need or want a placeholder.
},
onSuccess = { result ->
completer.set((result as BitmapImage).bitmap)
},
onError = { _ ->
completer.setException(
Exception(
"coil onError called" +
" (normal if no album art exists)"
)
)
}
)
.build())
.also {
completer.addCancellationListener(
{ it.dispose() },
ContextCompat.getMainExecutor(
this@GramophonePlaybackService
)
)
}
"coil load for $uri"
}
}

override fun supportsMimeType(mimeType: String): Boolean {
return isBitmapFactorySupportedMimeType(mimeType)
}

override fun loadBitmapFromMetadata(metadata: MediaMetadata): ListenableFuture<Bitmap>? {
return metadata.artworkUri?.let { loadBitmap(it) }
}
}))
.setSessionActivity(
PendingIntent.getActivity(
this,
PENDING_INTENT_SESSION_ID,
Intent(this, MainActivity::class.java),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
)
)
.setSystemUiPlaybackResumptionOptIn(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
.build()
addSession(mediaSession!!)
controller = MediaBrowser.Builder(this, mediaSession!!.token).buildAsync().get()
controller!!.addListener(this)
if (controller!!.audioSessionId != C.AUDIO_SESSION_ID_UNSET) {
onAudioSessionIdChanged(controller!!.audioSessionId)
}
ContextCompat.registerReceiver(
this,
seekReceiver,
IntentFilter("$packageName.SEEK_TO"),
@SuppressLint("WrongConstant") // why is this needed?
ContextCompat.RECEIVER_NOT_EXPORTED
)
ContextCompat.registerReceiver(
this,
btReceiver,
IntentFilter("android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED"),
@SuppressLint("WrongConstant") // why is this needed?
ContextCompat.RECEIVER_EXPORTED
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O /* before 8, only sbc was supported */) {
proxy = BtCodecInfo.getCodec(this) {
Log.d(TAG, "first bluetooth codec config $btInfo")
btInfo = it
mediaSession?.broadcastCustomCommand(
SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY),
Bundle.EMPTY
)
}
}
scope.launch {
lastPlayedManager.restore { items, factory ->
if (mediaSession == null) return@restore
if (items != null) {
if (endedWorkaroundPlayer?.nextShuffleOrder != null)
throw IllegalStateException("shuffleFactory was found orphaned")
endedWorkaroundPlayer?.nextShuffleOrder = factory.toFactory()
val list = runBlocking { mapMediaItemsForFavorites(items.mediaItems) }
try {
mediaSession?.player?.setMediaItems(
list, items.startIndex, items.startPositionMs
queueWithTitle(list, "lastPlayedManager"),
items.startIndex,
items.startPositionMs

Check warning on line 565 in app/src/main/java/org/akanework/gramophone/logic/GramophonePlaybackService.kt

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (beta)

❌ Getting worse: Complex Method

GramophonePlaybackService.onCreate already has high cyclomatic complexity, and now it increases in Lines of Code from 315 to 319. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
)
} catch (e: IllegalSeekPositionException) {
try {
Expand Down Expand Up @@ -777,6 +794,13 @@
availableSessionCommands.add(SessionCommand(SERVICE_QUERY_TIMER, Bundle.EMPTY))
availableSessionCommands.add(SessionCommand(SERVICE_GET_LYRICS, Bundle.EMPTY))
availableSessionCommands.add(SessionCommand(SERVICE_GET_AUDIO_FORMAT, Bundle.EMPTY))
availableSessionCommands.add(SessionCommand(SERVICE_QB_GET_INACTIVE_LIST, Bundle.EMPTY))
availableSessionCommands.add(SessionCommand(SERVICE_QB_GET_QUEUE_FOR_UI, Bundle.EMPTY))
availableSessionCommands.add(SessionCommand(SERVICE_QB_LOAD_QUEUE, Bundle.EMPTY))
availableSessionCommands.add(SessionCommand(SERVICE_QB_DEL, Bundle.EMPTY))
availableSessionCommands.add(SessionCommand(SERVICE_QB_REORDER, Bundle.EMPTY))
availableSessionCommands.add(SessionCommand(SERVICE_QB_PIN_QUEUE, Bundle.EMPTY))
availableSessionCommands.add(SessionCommand(SERVICE_QB_UNPIN_QUEUE, Bundle.EMPTY))
return builder.setAvailableSessionCommands(availableSessionCommands.build()).build()
}

Expand Down Expand Up @@ -959,6 +983,64 @@
}
}

SERVICE_QB_GET_INACTIVE_LIST -> {
SessionResult(SessionResult.RESULT_SUCCESS).also { res ->
val queueList: List<MultiQueueObject> = qb.getInactiveQueues()
val binder = BundleListRetriever(queueList.map { it.toBundle() })
res.extras.putBinder("allQueues", binder)
}
}

SERVICE_QB_GET_QUEUE_FOR_UI -> {
SessionResult(SessionResult.RESULT_SUCCESS).also { res ->
val index = customCommand.customExtras.getInt("index")
val queueList: List<MultiQueueObject> = qb.getQueue(index)
val binder = BundleListRetriever(queueList.map { it.toBundle() })
res.extras.putBinder("allQueues", binder)

// assume ui does not expect shuffleIndexes if shuffle is off
if (!queueList.isEmpty()) {
val mq = queueList.first()
val factory =
CircularShuffleOrder.Persistent.deserialize(mq.shuffleOrder)
.toFactory()
val shuffleOrder = factory(0, mq.getSize(), endedWorkaroundPlayer!!)
val shuffleIndexesList: List<Int> = shuffledIndices(shuffleOrder)
res.extras.putIntArray("shuffleIndexes", shuffleIndexesList.toIntArray())
}
}
}

SERVICE_QB_LOAD_QUEUE -> {
val index = customCommand.customExtras.getInt("index")
qb.commitQueue(index)
SessionResult(SessionResult.RESULT_SUCCESS)
}

SERVICE_QB_PIN_QUEUE -> {
val index = customCommand.customExtras.getInt("index")
qb.pinQueue(index)
SessionResult(SessionResult.RESULT_SUCCESS).also { res ->
res.extras.putBoolean("status", false)
}
}

SERVICE_QB_UNPIN_QUEUE -> {
val index = customCommand.customExtras.getInt("index")
qb.unpinQueue(index)
SessionResult(SessionResult.RESULT_SUCCESS).also { res ->
res.extras.putBoolean("status", false)
}
}

SERVICE_QB_DEL -> {
val index = customCommand.customExtras.getInt("index")
qb.deleteQueue(index)
SessionResult(SessionResult.RESULT_SUCCESS).also { res ->
res.extras.putBoolean("status", false)
}
}

Check warning on line 1043 in app/src/main/java/org/akanework/gramophone/logic/GramophonePlaybackService.kt

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (beta)

❌ Getting worse: Complex Method

GramophonePlaybackService.onCustomCommand increases in cyclomatic complexity from 9 to 10, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
else -> {
SessionResult(SessionError.ERROR_BAD_VALUE)
}
Expand Down
Loading
Loading