Skip to content

Commit b93707d

Browse files
committed
feat: fix missing some case in media handler
1 parent 97b4e15 commit b93707d

10 files changed

Lines changed: 168 additions & 50 deletions

File tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.maxrave.simpmusic
22

33
import android.content.Context
4+
import com.maxrave.domain.data.player.PlayerError
45

56
// Sent crash to Sentry
67
fun reportCrash(throwable: Throwable) {
@@ -9,5 +10,5 @@ fun reportCrash(throwable: Throwable) {
910
fun configCrashlytics(applicationContext: Context) {
1011
}
1112

12-
fun pushPlayerError(error: Throwable) {
13+
fun pushPlayerError(error: PlayerError) {
1314
}

app/src/full/java/com/maxrave/simpmusic/Crashlytics.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.maxrave.simpmusic
22

33
import android.content.Context
44
import android.util.Log
5+
import com.maxrave.domain.data.player.PlayerError
56
import io.sentry.Sentry
67
import io.sentry.android.core.SentryAndroid
78

@@ -18,8 +19,8 @@ fun configCrashlytics(applicationContext: Context) {
1819
}
1920
}
2021

21-
fun pushPlayerError(error: Throwable) {
22+
fun pushPlayerError(error: PlayerError) {
2223
Sentry.withScope { scope ->
23-
Sentry.captureException(error)
24+
Sentry.captureMessage("Player Error: ${error.message}, code: ${error.errorCode}, code name: ${error.errorCodeName}")
2425
}
2526
}

app/src/main/java/com/maxrave/simpmusic/ui/MainActivity.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ import com.maxrave.domain.mediaservice.handler.MediaPlayerHandler
8585
import com.maxrave.logger.Logger
8686
import com.maxrave.simpmusic.di.viewModelModule
8787
import com.maxrave.simpmusic.extension.copy
88+
import com.maxrave.simpmusic.pushPlayerError
8889
import com.maxrave.simpmusic.ui.component.AppBottomNavigationBar
8990
import com.maxrave.simpmusic.ui.component.AppNavigationRail
9091
import com.maxrave.simpmusic.ui.component.LiquidGlassAppBottomNavigationBar
@@ -738,6 +739,9 @@ class MainActivity : AppCompatActivity() {
738739

739740
private fun startMusicService() {
740741
mediaPlayerHandler.startMediaService(this, serviceConnection)
742+
mediaPlayerHandler.pushPlayerError = { it ->
743+
pushPlayerError(it)
744+
}
741745
viewModel.isServiceRunning = true
742746
shouldUnbind = true
743747
Logger.d("Service", "Service started")

core/data/src/main/java/com/maxrave/data/di/MediaHandlerModule.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ val mediaHandlerModule =
1919
streamRepository = get(),
2020
localPlaylistRepository = get(),
2121
coroutineScope = get(named(Config.SERVICE_SCOPE)),
22-
updateWidget = {},
23-
updatePlayStatusForWidget = { _, _ -> },
24-
setNotificationLayout = { _, _, _ -> },
25-
pushPlayerError = { _ -> },
2622
)
2723
}
2824
}

core/data/src/main/java/com/maxrave/data/mediaservice/MediaServiceHandlerImpl.kt

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import com.maxrave.domain.data.model.browse.album.Track
3030
import com.maxrave.domain.data.model.mediaService.SponsorSkipSegments
3131
import com.maxrave.domain.data.model.searchResult.songs.Artist
3232
import com.maxrave.domain.data.model.streams.YouTubeWatchEndpoint
33+
import com.maxrave.domain.data.player.GenericCommandButton
3334
import com.maxrave.domain.data.player.GenericMediaItem
3435
import com.maxrave.domain.data.player.GenericMediaMetadata
3536
import com.maxrave.domain.data.player.GenericPlaybackParameters
@@ -104,14 +105,11 @@ internal class MediaServiceHandlerImpl(
104105
private val streamRepository: StreamRepository,
105106
private val localPlaylistRepository: LocalPlaylistRepository,
106107
private val coroutineScope: CoroutineScope,
107-
private val updateWidget: (GenericMediaItem?) -> Unit,
108-
private val updatePlayStatusForWidget: (Context, Boolean) -> Unit,
109-
private val setNotificationLayout: (liked: Boolean, isShuffle: Boolean, repeatState: RepeatState) -> Unit,
110-
private val pushPlayerError: (s: PlayerError) -> Unit,
111108
) : MediaPlayerHandler,
112109
MediaPlayerListener {
113110
override val player: MediaPlayerInterface = inputPlayer
114-
111+
override var onUpdateNotification: (List<GenericCommandButton>) -> Unit = {}
112+
override var pushPlayerError: (PlayerError) -> Unit = {}
115113
private val _simpleMediaState = MutableStateFlow<SimpleMediaState>(SimpleMediaState.Initial)
116114
override val simpleMediaState: StateFlow<SimpleMediaState> = _simpleMediaState.asStateFlow()
117115

@@ -255,6 +253,12 @@ internal class MediaServiceHandlerImpl(
255253
}
256254
mayBeRestoreQueue()
257255
coroutineScope.launch {
256+
val controlStateJob =
257+
launch {
258+
controlState.collectLatest {
259+
updateNotification()
260+
}
261+
}
258262
val skipSegmentsJob =
259263
launch {
260264
simpleMediaState
@@ -325,6 +329,7 @@ internal class MediaServiceHandlerImpl(
325329
Logger.w(TAG, "Playback current speed: ${player.playbackParameters.speed}, Pitch: ${player.playbackParameters.pitch}")
326330
}
327331
}
332+
controlStateJob.join()
328333
skipSegmentsJob.join()
329334
playbackJob.join()
330335
playbackSpeedPitchJob.join()
@@ -347,7 +352,6 @@ internal class MediaServiceHandlerImpl(
347352
track = track,
348353
)
349354
}
350-
updateWidget(mediaItem)
351355
_format.value = null
352356
_skipSegments.value = null
353357
getDataOfNowPlayingTrackStateJob?.cancel()
@@ -611,10 +615,13 @@ internal class MediaServiceHandlerImpl(
611615
?.liked ?: false
612616
Logger.w("Check liked", liked.toString())
613617
_controlState.value = _controlState.value.copy(isLiked = liked)
614-
setNotificationLayout.invoke(
615-
liked,
616-
controlState.value.isShuffle,
617-
controlState.value.repeatState,
618+
onUpdateNotification.invoke(
619+
listOf(
620+
GenericCommandButton.Like(liked),
621+
GenericCommandButton.Repeat(repeatState = _controlState.value.repeatState),
622+
GenericCommandButton.Radio,
623+
GenericCommandButton.Shuffle(isShuffled = _controlState.value.isShuffle),
624+
),
618625
)
619626
}
620627
}
@@ -689,7 +696,6 @@ internal class MediaServiceHandlerImpl(
689696
player.shuffleModeEnabled = true
690697
_controlState.value = _controlState.value.copy(isShuffle = true)
691698
}
692-
updateNotification()
693699
}
694700

695701
PlayerEvent.Repeat -> {
@@ -789,13 +795,11 @@ internal class MediaServiceHandlerImpl(
789795
if (!(controlState.first().isLiked)) 1 else 0,
790796
)
791797
delay(200)
792-
updateNotification()
793798
}
794799
}
795800

796801
override fun like(liked: Boolean) {
797802
_controlState.value = _controlState.value.copy(isLiked = liked)
798-
updateNotification()
799803
}
800804

801805
override fun resetSongAndQueue() {
@@ -2000,10 +2004,6 @@ internal class MediaServiceHandlerImpl(
20002004

20012005
override fun onIsPlayingChanged(isPlaying: Boolean) {
20022006
_controlState.value = _controlState.value.copy(isPlaying = isPlaying)
2003-
updatePlayStatusForWidget(
2004-
context,
2005-
isPlaying,
2006-
)
20072007
if (isPlaying) {
20082008
startProgressUpdate()
20092009
} else {
@@ -2113,7 +2113,6 @@ internal class MediaServiceHandlerImpl(
21132113
}
21142114
}
21152115
updateNextPreviousTrackAvailability()
2116-
updateNotification()
21172116
}
21182117

21192118
override fun onRepeatModeChanged(repeatMode: Int) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.maxrave.domain.data.player
2+
3+
import com.maxrave.domain.mediaservice.handler.RepeatState
4+
5+
sealed class GenericCommandButton {
6+
data class Like(
7+
val isLiked: Boolean,
8+
) : GenericCommandButton()
9+
10+
data class Shuffle(
11+
val isShuffled: Boolean,
12+
) : GenericCommandButton()
13+
14+
data class Repeat(
15+
val repeatState: RepeatState,
16+
) : GenericCommandButton()
17+
18+
data object Radio : GenericCommandButton()
19+
}

core/domain/src/main/java/com/maxrave/domain/extension/AllExt.kt

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package com.maxrave.domain.extension
22

3-
import android.R.attr.name
4-
import android.R.attr.path
5-
import android.R.attr.value
63
import com.maxrave.common.MERGING_DATA_TYPE
74
import com.maxrave.domain.data.entities.SongEntity
85
import com.maxrave.domain.data.model.browse.album.Track
@@ -85,38 +82,38 @@ fun Track.toGenericMediaItem(): GenericMediaItem {
8582
)
8683
}
8784

88-
fun CookieItem.Content.toNetscapeFormat(
89-
includeSubdomains: Boolean = true
90-
): String {
85+
fun CookieItem.Content.toNetscapeFormat(includeSubdomains: Boolean = true): String {
9186
with(this) {
92-
val stringList = listOf(
93-
domain,
94-
includeSubdomains.toString().uppercase(),
95-
path,
96-
isSecure.toString().uppercase(),
97-
expiresUtc.toString(),
98-
name,
99-
value
100-
)
87+
val stringList =
88+
listOf(
89+
domain,
90+
includeSubdomains.toString().uppercase(),
91+
path,
92+
isSecure.toString().uppercase(),
93+
expiresUtc.toString(),
94+
name,
95+
value,
96+
)
10197

10298
val builder = StringBuilder(stringList.first())
10399

104100
for (s in stringList.subList(1, stringList.size)) {
105101
if (s.isNotEmpty()) {
106-
if (builder.isNotEmpty())
102+
if (builder.isNotEmpty()) {
107103
builder.append("\u0009")
104+
}
108105
builder.append(s)
109106
}
110107
}
111108
return builder.toString()
112109
}
113110
}
114111

115-
fun CookieItem.toNetScapeString(): String {
116-
return "# Netscape HTTP Cookie File\n" +
117-
"# WebView Generated by the SimpMusic app\n" +
118-
"# This is a generated file! Do not edit.\n\n" +
119-
this.content.fold(StringBuilder("")) { acc, cookie ->
120-
acc.append(cookie.toNetscapeFormat()).append("\n")
121-
}.toString()
122-
}
112+
fun CookieItem.toNetScapeString(): String =
113+
"# Netscape HTTP Cookie File\n" +
114+
"# WebView Generated by the SimpMusic app\n" +
115+
"# This is a generated file! Do not edit.\n\n" +
116+
this.content
117+
.fold(StringBuilder("")) { acc, cookie ->
118+
acc.append(cookie.toNetscapeFormat()).append("\n")
119+
}.toString()

core/domain/src/main/java/com/maxrave/domain/mediaservice/handler/MediaPlayerHandler.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import com.maxrave.domain.data.entities.NewFormatEntity
88
import com.maxrave.domain.data.entities.SongEntity
99
import com.maxrave.domain.data.model.browse.album.Track
1010
import com.maxrave.domain.data.model.mediaService.SponsorSkipSegments
11+
import com.maxrave.domain.data.player.GenericCommandButton
1112
import com.maxrave.domain.data.player.GenericMediaItem
13+
import com.maxrave.domain.data.player.PlayerError
1214
import com.maxrave.domain.mediaservice.player.MediaPlayerInterface
1315
import kotlinx.coroutines.flow.StateFlow
1416

@@ -30,6 +32,10 @@ interface MediaPlayerHandler {
3032
val format: StateFlow<NewFormatEntity?>
3133
val currentSongIndex: StateFlow<Int>
3234

35+
// Listeners
36+
var onUpdateNotification: (List<GenericCommandButton>) -> Unit
37+
var pushPlayerError: (PlayerError) -> Unit
38+
3339
// Playback control
3440
suspend fun onPlayerEvent(playerEvent: PlayerEvent)
3541

media/media3/src/main/java/com/maxrave/media3/extension/Media3Ext.kt

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
package com.maxrave.media3.extension
22

3+
import android.content.Context
4+
import android.os.Bundle
35
import androidx.core.net.toUri
46
import androidx.media3.common.MediaItem
57
import androidx.media3.common.MediaMetadata
68
import androidx.media3.common.util.UnstableApi
9+
import androidx.media3.session.CommandButton
10+
import androidx.media3.session.SessionCommand
11+
import com.maxrave.common.MEDIA_CUSTOM_COMMAND
712
import com.maxrave.common.MERGING_DATA_TYPE
13+
import com.maxrave.common.R
814
import com.maxrave.domain.data.entities.SongEntity
915
import com.maxrave.domain.data.model.browse.album.Track
16+
import com.maxrave.domain.data.player.GenericCommandButton
17+
import com.maxrave.domain.mediaservice.handler.RepeatState
1018
import com.maxrave.domain.utils.connectArtists
1119
import com.maxrave.domain.utils.toListName
1220

@@ -106,4 +114,82 @@ fun List<Track>.toMediaItems(): List<MediaItem> {
106114
fun MediaItem.isSong(): Boolean = this.mediaMetadata.description?.contains(MERGING_DATA_TYPE.SONG) == true
107115

108116
@UnstableApi
109-
fun MediaItem.isVideo(): Boolean = this.mediaMetadata.description?.contains(MERGING_DATA_TYPE.VIDEO) == true
117+
fun MediaItem.isVideo(): Boolean = this.mediaMetadata.description?.contains(MERGING_DATA_TYPE.VIDEO) == true
118+
119+
fun GenericCommandButton.toCommandButton(context: Context): CommandButton =
120+
when (this) {
121+
is GenericCommandButton.Like -> {
122+
val liked = this.isLiked
123+
CommandButton
124+
.Builder(
125+
if (liked) {
126+
CommandButton.ICON_HEART_FILLED
127+
} else {
128+
CommandButton.ICON_HEART_UNFILLED
129+
},
130+
).setDisplayName(
131+
if (liked) {
132+
context.getString(R.string.liked)
133+
} else {
134+
context.getString(
135+
R.string.like,
136+
)
137+
},
138+
).setSessionCommand(SessionCommand(MEDIA_CUSTOM_COMMAND.LIKE, Bundle()))
139+
.build()
140+
}
141+
GenericCommandButton.Radio -> {
142+
CommandButton
143+
.Builder(
144+
CommandButton.ICON_RADIO,
145+
).setDisplayName(context.getString(R.string.radio))
146+
.setSessionCommand(
147+
SessionCommand(
148+
MEDIA_CUSTOM_COMMAND.RADIO,
149+
Bundle(),
150+
),
151+
).build()
152+
}
153+
is GenericCommandButton.Repeat -> {
154+
val repeatMode = this.repeatState
155+
CommandButton
156+
.Builder(
157+
when (repeatMode) {
158+
RepeatState.One -> CommandButton.ICON_REPEAT_ONE
159+
160+
RepeatState.All -> CommandButton.ICON_REPEAT_ALL
161+
162+
else -> CommandButton.ICON_REPEAT_OFF
163+
},
164+
).setDisplayName(
165+
when (repeatMode) {
166+
RepeatState.One -> context.getString(R.string.repeat_one)
167+
168+
RepeatState.All -> context.getString(R.string.repeat_all)
169+
170+
else -> context.getString(R.string.repeat_off)
171+
},
172+
).setSessionCommand(
173+
SessionCommand(
174+
MEDIA_CUSTOM_COMMAND.REPEAT,
175+
Bundle(),
176+
),
177+
).build()
178+
}
179+
is GenericCommandButton.Shuffle -> {
180+
CommandButton
181+
.Builder(
182+
if (this.isShuffled) {
183+
CommandButton.ICON_SHUFFLE_ON
184+
} else {
185+
CommandButton.ICON_SHUFFLE_OFF
186+
},
187+
).setDisplayName(context.getString(R.string.shuffle))
188+
.setSessionCommand(
189+
SessionCommand(
190+
MEDIA_CUSTOM_COMMAND.SHUFFLE,
191+
Bundle(),
192+
),
193+
).build()
194+
}
195+
}

0 commit comments

Comments
 (0)