Skip to content

Commit

Permalink
Merge pull request #255 from THEOplayer/release/v3.6.0
Browse files Browse the repository at this point in the history
Release/v3.6.0
  • Loading branch information
tvanlaerhoven authored Feb 2, 2024
2 parents 73a28ad + 3453a1c commit 17d33e9
Show file tree
Hide file tree
Showing 20 changed files with 113 additions and 39 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ 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).

## [3.6.0] - 24-02-02

### 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

### Added

- Added the ability to toggle `keepScreenOn` on Android. By default, screen timeout is disabled while the player is visible.

## [3.4.2] - 23-12-22

### Fixed
Expand Down
6 changes: 0 additions & 6 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@
-->
<uses-permission android:name="android.permission.INTERNET" />

<!--
Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming.
Protection level: normal
-->
<uses-permission android:name="android.permission.WAKE_LOCK" />

<!--
Allows a regular application to use Service.startForeground.
Protection level: normal
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}")
}
}
}
7 changes: 7 additions & 0 deletions android/src/main/java/com/theoplayer/player/PlayerModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,13 @@ class PlayerModule(context: ReactApplicationContext) : ReactContextBaseJavaModul
}
}

@ReactMethod
fun setKeepScreenOn(tag: Int, value: Boolean) {
viewResolver.resolveViewByTag(tag) { view: ReactTHEOplayerView? ->
view?.playerContext?.playerView?.keepScreenOn = value
}
}

@ReactMethod
fun setTextTrackStyle(tag: Int, style: ReadableMap?) {
viewResolver.resolveViewByTag(tag) { view: ReactTHEOplayerView? ->
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ target 'ReactNativeTHEOplayer' do
use_react_native!(
:path => config[:reactNativePath],
:flipper_configuration => flipper_config,
:hermes_enabled => true,
)

pod 'react-native-theoplayer', :path => '../..'
Expand Down
6 changes: 3 additions & 3 deletions example/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions ios/THEOplayerRCTPlayerAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
if newPresentationMode == .fullscreen {
print(ERROR_MESSAGE_PLAYER_FULLSCREEN_UNSUPPORTED_FEATURE)
}
if let theView = self.bridge.uiManager.view(forReactTag: node) as? THEOplayerRCTView,
let player = theView.player {
else if let theView = self.bridge.uiManager.view(forReactTag: node) as? THEOplayerRCTView,
let player = theView.player {
if player.presentationMode != newPresentationMode {
if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] Changing TheoPlayer to \(presentationMode)") }
player.presentationMode = newPresentationMode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class THEOplayerRCTBroadcastEventHandler: DefaultEventDispatcher {
}
}

public class DefaultEventDispatcher: NSObject, THEOplayerSDK.EventDispatcherProtocol, THEOplayerSDK.DispatchDispatch {
public class DefaultEventDispatcher: NSObject, THEOplayerSDK.EventDispatcherProtocol {
private var eventListeners = [String: [EventListenerWrapper]]()

public func addEventListener<E>(type: THEOplayerSDK.EventType<E>, listener: @escaping (_ : E) -> ()) -> THEOplayerSDK.EventListener {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-theoplayer",
"version": "3.4.2",
"version": "3.6.0",
"description": "A THEOplayer video component for react-native.",
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand Down
9 changes: 9 additions & 0 deletions src/api/player/THEOplayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,15 @@ export interface THEOplayer extends EventDispatcher<PlayerEventMap> {
*/
aspectRatio: AspectRatio;

/**
* Toggle the wake-lock on the player view. The screen will time out if disabled.
*
* @defaultValue `true`
* @remarks
* Only available on Android.
*/
keepScreenOn: boolean;

/**
* The active configuration for PiP.
*/
Expand Down
5 changes: 2 additions & 3 deletions src/internal/adapter/NativePlayerState.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { MediaTrack, PreloadType, PresentationMode, SourceDescription, TextTrack, TimeRange } from 'react-native-theoplayer';
import type { BackgroundAudioConfiguration } from '../../api/backgroundAudio/BackgroundAudioConfiguration';
import type { PiPConfiguration } from 'react-native-theoplayer';
import type { AspectRatio } from 'react-native-theoplayer';
import type { PiPConfiguration, AspectRatio, BackgroundAudioConfiguration } from 'react-native-theoplayer';

export interface NativePlayerState {
source: SourceDescription | undefined;
Expand All @@ -20,6 +18,7 @@ export interface NativePlayerState {
playbackRate: number;
preload: PreloadType;
aspectRatio: AspectRatio;
keepScreenOn: boolean;
audioTracks: MediaTrack[];
videoTracks: MediaTrack[];
textTracks: TextTrack[];
Expand Down
12 changes: 12 additions & 0 deletions src/internal/adapter/THEOplayerAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const defaultPlayerState: NativePlayerState = {
playbackRate: 1,
preload: 'none',
aspectRatio: AspectRatio.FIT,
keepScreenOn: true,
audioTracks: [],
videoTracks: [],
textTracks: [],
Expand Down Expand Up @@ -502,6 +503,17 @@ export class THEOplayerAdapter extends DefaultEventDispatcher<PlayerEventMap> im
NativeModules.PlayerModule.setAspectRatio(this._view.nativeHandle, ratio);
}

get keepScreenOn(): boolean {
return this._state.keepScreenOn;
}

set keepScreenOn(value: boolean) {
this._state.keepScreenOn = value;
if (Platform.OS === 'android') {
NativeModules.PlayerModule.setKeepScreenOn(this._view.nativeHandle, value);
}
}

pause(): void {
if (this.hasValidSource()) {
this._state.paused = true;
Expand Down
8 changes: 8 additions & 0 deletions src/internal/adapter/THEOplayerWebAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,14 @@ export class THEOplayerWebAdapter extends DefaultEventDispatcher<PlayerEventMap>
// unused
}

get keepScreenOn(): boolean {
return false;
}

set keepScreenOn(_screenOn: boolean) {
// unused
}

get duration(): number {
return this._player ? this._player.duration * 1e3 : NaN;
}
Expand Down

0 comments on commit 17d33e9

Please sign in to comment.