Skip to content

Commit

Permalink
Merge pull request #3 from mvn-hungtruong-dn/feature/update-options-n…
Browse files Browse the repository at this point in the history
…otification

feat: update options notification
  • Loading branch information
mvn-hungtruong-dn authored Jun 28, 2023
2 parents cc1f9a3 + 4076010 commit c22844a
Showing 1 changed file with 165 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ import com.google.android.exoplayer2.ui.PlayerNotificationManager
import com.google.android.exoplayer2.ui.PlayerNotificationManager.CustomActionReceiver
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch

Expand All @@ -50,7 +52,62 @@ class NotificationManager internal constructor(
private val playerEventHolder: PlayerEventHolder,
private val androidNotificationDebounceInterval: Long
) : PlayerNotificationManager.NotificationListener {
private lateinit var descriptionAdapter: PlayerNotificationManager.MediaDescriptionAdapter
private var pendingIntent: PendingIntent? = null
private val descriptionAdapter = object : PlayerNotificationManager.MediaDescriptionAdapter {
override fun getCurrentContentTitle(player: Player): CharSequence {
return notificationMetadata?.title
?: player.mediaMetadata.title
?: ""
}

override fun createCurrentContentIntent(player: Player): PendingIntent? {
return pendingIntent
}

override fun getCurrentContentText(player: Player): CharSequence? {
return notificationMetadata?.artist
?: player.mediaMetadata.artist
?: player.mediaMetadata.albumArtist
?: ""
}

override fun getCurrentSubText(player: Player): CharSequence? {
return player.mediaMetadata.displayTitle
}

override fun getCurrentLargeIcon(
player: Player,
callback: PlayerNotificationManager.BitmapCallback,
): Bitmap? {
val holder = getCurrentItemHolder() ?: return null
val source = notificationMetadata?.artworkUrl ?: player.mediaMetadata.artworkUri
val data = player.mediaMetadata.artworkData

if (notificationMetadata?.artworkUrl == null && data != null) {
return BitmapFactory.decodeByteArray(data, 0, data.size)
}

if (source == null) {
return null
}

if (holder.artworkBitmap != null) {
return holder.artworkBitmap
}

context.imageLoader.enqueue(
ImageRequest.Builder(context)
.data(source)
.target { result ->
val bitmap = (result as BitmapDrawable).bitmap
holder.artworkBitmap = bitmap
callback.onBitmap(bitmap)
}
.build()
)
return holder.artworkBitmap
}
}
private var internalNotificationManager: PlayerNotificationManager? = null
private val scope = MainScope()
private val buttons = mutableSetOf<NotificationButton?>()
Expand Down Expand Up @@ -332,6 +389,8 @@ class NotificationManager internal constructor(
* Create a media player notification that automatically updates.
*
* **NOTE:** You should only call this once. Subsequent calls will result in an error.
*
* Update: Only update when call createNotification again. If you want to update the ResourceId of the buttons, call hideNotification before invoke createNotification function.
*/
fun createNotification(config: NotificationConfig) = scope.launch {
buttons.apply {
Expand All @@ -342,6 +401,75 @@ class NotificationManager internal constructor(
stopIcon = null
forwardIcon = null
rewindIcon = null

updateMediaSessionPlaybackActions()

pendingIntent = config.pendingIntent
showPlayPauseButton = false
showForwardButton = false
showRewindButton = false
showNextButton = false
showPreviousButton = false
showStopButton = false
if (internalNotificationManager == null) {
internalNotificationManager =
PlayerNotificationManager.Builder(context, NOTIFICATION_ID, CHANNEL_ID)
.apply {
setChannelNameResourceId(R.string.playback_channel_name)
setMediaDescriptionAdapter(descriptionAdapter)
setCustomActionReceiver(customActionReceiver)
setNotificationListener(this@NotificationManager)

for (button in buttons) {
if (button == null) continue
when (button) {
is NotificationButton.PLAY_PAUSE -> {
button.playIcon?.let { setPlayActionIconResourceId(it) }
button.pauseIcon?.let { setPauseActionIconResourceId(it) }
}

is NotificationButton.STOP -> button.icon?.let {
setStopActionIconResourceId(
it
)
}

is NotificationButton.FORWARD -> button.icon?.let {
setFastForwardActionIconResourceId(
it
)
}

is NotificationButton.BACKWARD -> button.icon?.let {
setRewindActionIconResourceId(
it
)
}

is NotificationButton.NEXT -> button.icon?.let {
setNextActionIconResourceId(
it
)
}

is NotificationButton.PREVIOUS -> button.icon?.let {
setPreviousActionIconResourceId(
it
)
}

else -> {}
}
}
}.build().apply {
setMediaSessionToken(mediaSession.sessionToken)
setPlayer(player)
}
}
setupInternalNotificationManager(config)
}

private fun updateMediaSessionPlaybackActions() {
mediaSessionConnector.setEnabledPlaybackActions(
buttons.fold(
PlaybackStateCompat.ACTION_SET_REPEAT_MODE
Expand Down Expand Up @@ -378,62 +506,6 @@ class NotificationManager internal constructor(
}
}
)
descriptionAdapter = object : PlayerNotificationManager.MediaDescriptionAdapter {
override fun getCurrentContentTitle(player: Player): CharSequence {
return notificationMetadata?.title
?: player.mediaMetadata.title
?: ""
}

override fun createCurrentContentIntent(player: Player): PendingIntent? {
return config.pendingIntent
}

override fun getCurrentContentText(player: Player): CharSequence? {
return notificationMetadata?.artist
?: player.mediaMetadata.artist
?: player.mediaMetadata.albumArtist
?: ""
}

override fun getCurrentSubText(player: Player): CharSequence? {
return player.mediaMetadata.displayTitle
}

override fun getCurrentLargeIcon(
player: Player,
callback: PlayerNotificationManager.BitmapCallback,
): Bitmap? {
val holder = getCurrentItemHolder() ?: return null
val source = notificationMetadata?.artworkUrl ?: player.mediaMetadata.artworkUri
val data = player.mediaMetadata.artworkData

if (notificationMetadata?.artworkUrl == null && data != null) {
return BitmapFactory.decodeByteArray(data, 0, data.size)
}

if (source == null) {
return null
}

if (holder.artworkBitmap != null) {
return holder.artworkBitmap
}

context.imageLoader.enqueue(
ImageRequest.Builder(context)
.data(source)
.target { result ->
val bitmap = (result as BitmapDrawable).bitmap
holder.artworkBitmap = bitmap
callback.onBitmap(bitmap)
}
.build()
)
return holder.artworkBitmap
}
}

if (needsCustomActionsToAddMissingButtons) {
val customActionProviders = buttons
.sortedBy {
Expand Down Expand Up @@ -465,109 +537,55 @@ class NotificationManager internal constructor(
}
mediaSessionConnector.setCustomActionProviders(*customActionProviders.toTypedArray())
}
}

internalNotificationManager =
PlayerNotificationManager.Builder(context, NOTIFICATION_ID, CHANNEL_ID)
.apply {
setChannelNameResourceId(R.string.playback_channel_name)
setMediaDescriptionAdapter(descriptionAdapter)
setCustomActionReceiver(customActionReceiver)
setNotificationListener(this@NotificationManager)

internalNotificationManager.apply {
showPlayPauseButton = false
showForwardButton = false
showRewindButton = false
showNextButton = false
showPreviousButton = false
showStopButton = false
private fun setupInternalNotificationManager(config: NotificationConfig) {
internalNotificationManager?.run {
setColor(config.accentColor ?: Color.TRANSPARENT)
config.smallIcon?.let { setSmallIcon(it) }
for (button in buttons) {
if (button == null) continue
when (button) {
is NotificationButton.PLAY_PAUSE -> {
showPlayPauseButton = true
}

for (button in buttons) {
if (button == null) continue
when (button) {
is NotificationButton.PLAY_PAUSE -> {
button.playIcon?.let { setPlayActionIconResourceId(it) }
button.pauseIcon?.let { setPauseActionIconResourceId(it) }
}

is NotificationButton.STOP -> button.icon?.let {
setStopActionIconResourceId(
it
)
}

is NotificationButton.FORWARD -> button.icon?.let {
setFastForwardActionIconResourceId(
it
)
}

is NotificationButton.BACKWARD -> button.icon?.let {
setRewindActionIconResourceId(
it
)
}

is NotificationButton.NEXT -> button.icon?.let {
setNextActionIconResourceId(
it
)
}

is NotificationButton.PREVIOUS -> button.icon?.let {
setPreviousActionIconResourceId(
it
)
}

else -> {}
}
is NotificationButton.STOP -> {
showStopButton = true
}
}.build().apply {
setColor(config.accentColor ?: Color.TRANSPARENT)
config.smallIcon?.let { setSmallIcon(it) }
for (button in buttons) {
if (button == null) continue
when (button) {
is NotificationButton.PLAY_PAUSE -> {
showPlayPauseButton = true
}

is NotificationButton.STOP -> {
showStopButton = true
}

is NotificationButton.FORWARD -> {
showForwardButton = true
showForwardButtonCompact = button.isCompact
}

is NotificationButton.BACKWARD -> {
showRewindButton = true
showRewindButtonCompact = button.isCompact
}
is NotificationButton.FORWARD -> {
showForwardButton = true
showForwardButtonCompact = button.isCompact
}

is NotificationButton.NEXT -> {
showNextButton = true
showNextButtonCompact = button.isCompact
}
is NotificationButton.BACKWARD -> {
showRewindButton = true
showRewindButtonCompact = button.isCompact
}

is NotificationButton.PREVIOUS -> {
showPreviousButton = true
showPreviousButtonCompact = button.isCompact
}
is NotificationButton.NEXT -> {
showNextButton = true
showNextButtonCompact = button.isCompact
}

else -> {}
}
is NotificationButton.PREVIOUS -> {
showPreviousButton = true
showPreviousButtonCompact = button.isCompact
}
setMediaSessionToken(mediaSession.sessionToken)
setPlayer(player)

else -> {}
}
}
}
}

fun hideNotification() = scope.launch {
internalNotificationManager?.setPlayer(null)
flow<Unit> {
internalNotificationManager?.setPlayer(null)
internalNotificationManager = null
delay(1000L)
}
}

override fun onNotificationPosted(
Expand Down

0 comments on commit c22844a

Please sign in to comment.