Skip to content

Commit

Permalink
Merge pull request #253 from THEOplayer/bugfix/android_foregroundserv…
Browse files Browse the repository at this point in the history
…ice_exception

Bugfix/android foregroundservice exception
  • Loading branch information
tvanlaerhoven authored Feb 2, 2024
2 parents 093fe63 + 88002f9 commit 59d5f64
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 23 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Unreleased
## [Unreleased]

### Fixed
### Fixed

- Fixed a build issue on the iOS bridge caused by the deprecated DispatchDispatch protocol.
- Fixed an issue on Android where the `MediaPlaybackService` would sometimes crash with a `ForegroundServiceDidNotStartInTimeException` exception.

### Added

- Added the ability to override both small and large notification icons in Android with `ic_notification_small` and `ic_notification_large` resources respectively.

## [3.5.0] - 24-01-30

Expand Down
2 changes: 1 addition & 1 deletion android/src/main/java/com/theoplayer/PlayerEventEmitter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ class PlayerEventEmitter internal constructor(
)
payload.putMap(EVENT_PROP_VERSION, WritableNativeMap().apply {
putString(EVENT_PROP_VERSION, THEOplayerGlobal.getVersion())
putString(EVENT_PROP_SUITE_VERSION, THEOplayerGlobal.getPlayerSuiteVersion())
putString(EVENT_PROP_SUITE_VERSION, "")
})

// Notify the player is ready
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ open class DefaultEventDispatcher: EventDispatcher<Event<*>> {

fun dispatchEvent(event: Event<*>) {
_listeners[event.type]?.forEach { listener ->
(listener as EventListener<Event<*>>).handleEvent(event)
@Suppress("UNCHECKED_CAST")
(listener as EventListener<Event<*>>).handleEvent(event)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class CastEventAdapter(private val castApi: Cast, private val emitter: Emitter)

private fun serializeError(error: CastError): WritableMap {
val errorPayload = Arguments.createMap()
@Suppress("SENSELESS_NULL_IN_WHEN")
errorPayload.putString(
EVENT_PROP_ERROR_CODE,
when (error.errorCode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,12 @@ class ContentProtectionModule(private val context: ReactApplicationContext) :
}

@ReactMethod
@Suppress("UNUSED_PARAMETER")
fun addListener(eventName: String?) {
}

@ReactMethod
@Suppress("UNUSED_PARAMETER")
fun removeListeners(count: Int?) {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.support.v4.media.session.MediaSessionCompat
Expand Down Expand Up @@ -195,3 +196,13 @@ fun fetchImageFromUri(uri: Uri?, block: (Bitmap?) -> Unit) {
)
}
}

fun loadPlaceHolderIcon(context: Context, res: Int = R.drawable.ic_notification_large): Bitmap? {
return try {
BitmapFactory.decodeResource(context.resources, res)
} catch(e: Exception) {
// Make sure we never crash on trying to decode a possibly overridden icon resource.
Log.w(TAG, "Failed to decode placeHolderIcon: ${e.message}")
null
}
}
50 changes: 31 additions & 19 deletions android/src/main/java/com/theoplayer/media/MediaPlaybackService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.theoplayer.media
import android.app.*
import android.content.Intent
import android.content.pm.ServiceInfo
import android.graphics.Bitmap
import android.os.Binder
import android.os.Build
import android.os.Bundle
Expand Down Expand Up @@ -96,6 +97,8 @@ class MediaPlaybackService : MediaBrowserServiceCompat() {
Log.w(TAG, "Failed to start foreground service: ${e.message}")
}

// Quickly post a notification and already call startForeground. This has to happen within 5s
// after creating the service to avoid a ForegroundServiceDidNotStartInTimeException
updateNotification(PlaybackStateCompat.STATE_PLAYING)
}

Expand Down Expand Up @@ -222,6 +225,7 @@ class MediaPlaybackService : MediaBrowserServiceCompat() {
}
}

@Suppress("UNUSED_PARAMETER")
private fun allowBrowsing(clientPackageName: String, clientUid: Int): Boolean {
// Only allow browsing from the same package
return TextUtils.equals(clientPackageName, packageName)
Expand Down Expand Up @@ -264,26 +268,13 @@ class MediaPlaybackService : MediaBrowserServiceCompat() {
// When a service runs in the foreground, it must display a notification, ideally
// with one or more transport controls. The notification should also include useful
// information from the session's metadata.
// Fetch large icon asynchronously

// Get the foreground service started in time before fetching an icon.
startForegroundWithPlaybackState(playbackState, loadPlaceHolderIcon(this))

// Fetch the correct large icon asynchronously.
fetchImageFromUri(mediaSession.controller.metadata?.description?.iconUri) { largeIcon ->
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
NOTIFICATION_ID,
notificationBuilder.build(playbackState, largeIcon, enableMediaControls),
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
)
} else {
startForeground(
NOTIFICATION_ID,
notificationBuilder.build(playbackState, largeIcon, enableMediaControls)
)
}
} catch (e: IllegalStateException) {
// Make sure that app does not crash in case anything goes wrong with starting the service.
// https://issuetracker.google.com/issues/229000935
Log.w(TAG, "Failed to start foreground service: ${e.message}")
}
startForegroundWithPlaybackState(playbackState, largeIcon)
}
}
PlaybackStateCompat.STATE_STOPPED -> {
Expand All @@ -303,4 +294,25 @@ class MediaPlaybackService : MediaBrowserServiceCompat() {
}
}
}

private fun startForegroundWithPlaybackState(@PlaybackStateCompat.State playbackState: Int, largeIcon: Bitmap? = null) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
NOTIFICATION_ID,
notificationBuilder.build(playbackState, largeIcon, enableMediaControls),
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
)
} else {
startForeground(
NOTIFICATION_ID,
notificationBuilder.build(playbackState, largeIcon, enableMediaControls)
)
}
} catch (e: IllegalStateException) {
// Make sure that app does not crash in case anything goes wrong with starting the service.
// https://issuetracker.google.com/issues/229000935
Log.w(TAG, "Failed to start foreground service: ${e.message}")
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 59d5f64

Please sign in to comment.