diff --git a/CHANGELOG.md b/CHANGELOG.md index fa9acc00b..4148fb16b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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). +## [8.3.0] - 24-09-30 + +### Added + +- Added the option to specify the AVAudioSession mode for iOS (moviePlayback versus spokenAudio). + ## [8.2.2] - 24-09-28 - Fixed a build issue on iOS for applications that run without the GOOGLE_IMA feature flag diff --git a/android/build.gradle b/android/build.gradle index f2ff43c7f..3bc815446 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -106,6 +106,10 @@ repositories { maven { url "https://maven.theoplayer.com/releases" } } +// The minimum supported THEOplayer version is 8.1.0 +def theoplayer_sdk_version = safeExtGet('THEOplayer_sdk', '[8.1.0, 9.0.0)') +def theoplayer_mediasession_version = safeExtGet('THEOplayer_mediasession', '[8.1.0, 9.0.0)') + dependencies { //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" // From node_modules @@ -114,10 +118,6 @@ dependencies { implementation "androidx.core:core-ktx:${safeExtGet('corektxVersion', '1.10.1')}" implementation "com.google.code.gson:gson:2.11.0" - // The minimum supported THEOplayer version is 8.1.0 - def theoplayer_sdk_version = safeExtGet('THEOplayer_sdk', '[8.1.0, 9.0.0)') - def theoplayer_mediasession_version = safeExtGet('THEOplayer_mediasession', '[8.1.0, 9.0.0)') - println("Using THEOplayer (${versionString(theoplayer_sdk_version)})") implementation "com.theoplayer.theoplayer-sdk-android:core:${theoplayer_sdk_version}" implementation "com.theoplayer.theoplayer-sdk-android:ads-wrapper:8.0.0" @@ -151,3 +151,13 @@ dependencies { compileOnly "com.theoplayer.theoplayer-sdk-android:integration-cast:${theoplayer_sdk_version}" } } + +// Make sure to align all ads extension versions +configurations.configureEach { + resolutionStrategy { + force "com.theoplayer.theoplayer-sdk-android:integration-ads:${theoplayer_sdk_version}" + force "com.theoplayer.theoplayer-sdk-android:integration-ads-ima:${theoplayer_sdk_version}" + force "com.theoplayer.theoplayer-sdk-android:integration-ads-dai:${theoplayer_sdk_version}" + } +} + diff --git a/ios/THEOplayerRCTPlayerAPI.swift b/ios/THEOplayerRCTPlayerAPI.swift index 5342e1f0f..2a3a2ce98 100644 --- a/ios/THEOplayerRCTPlayerAPI.swift +++ b/ios/THEOplayerRCTPlayerAPI.swift @@ -224,6 +224,9 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule { var backgroundAudio = BackgroundAudioConfig() backgroundAudio.enabled = configDict["enabled"] as? Bool ?? false backgroundAudio.shouldResumeAfterInterruption = configDict["shouldResumeAfterInterruption"] as? Bool ?? false + if let audioSessionModeString = configDict["audioSessionMode"] as? String { + backgroundAudio.audioSessionMode = THEOplayerRCTTypeUtils.audioSessionModeFromString(audioSessionModeString) + } return backgroundAudio } diff --git a/ios/THEOplayerRCTTypeUtils.swift b/ios/THEOplayerRCTTypeUtils.swift index d7ed2d3cc..a3b81e6e1 100644 --- a/ios/THEOplayerRCTTypeUtils.swift +++ b/ios/THEOplayerRCTTypeUtils.swift @@ -1,6 +1,7 @@ // THEOplayerRCTTypeUtils.swift import Foundation +import AVKit import THEOplayerSDK let NAN_VALUE: Double = -1.0 @@ -111,6 +112,17 @@ class THEOplayerRCTTypeUtils { return AdIntegrationKind.custom } } + + class func audioSessionModeFromString(_ audioSessionModeString: String) -> AVAudioSession.Mode { + switch audioSessionModeString { + case "moviePlayback": + return .moviePlayback + case "spokenAudio": + return .spokenAudio + default: + return .moviePlayback + } + } #if os(iOS) class func cacheStatusToString(_ status: CacheStatus) -> String { diff --git a/ios/THEOplayerRCTView.swift b/ios/THEOplayerRCTView.swift index 3a20621d8..7703267fe 100644 --- a/ios/THEOplayerRCTView.swift +++ b/ios/THEOplayerRCTView.swift @@ -36,6 +36,7 @@ public class THEOplayerRCTView: UIView { var backgroundAudioConfig = BackgroundAudioConfig() { didSet { self.updateInterruptionNotifications() + self.updateAVAudioSessionMode() } } diff --git a/ios/backgroundAudio/THEOplayerRCTView+BackgroundAudioConfig.swift b/ios/backgroundAudio/THEOplayerRCTView+BackgroundAudioConfig.swift index d2d08c20e..4d2ff6728 100644 --- a/ios/backgroundAudio/THEOplayerRCTView+BackgroundAudioConfig.swift +++ b/ios/backgroundAudio/THEOplayerRCTView+BackgroundAudioConfig.swift @@ -3,10 +3,12 @@ import Foundation import THEOplayerSDK import AVFAudio +import AVKit struct BackgroundAudioConfig { var enabled: Bool = false var shouldResumeAfterInterruption: Bool = false + var audioSessionMode: AVAudioSession.Mode = .moviePlayback } extension THEOplayerRCTView: BackgroundPlaybackDelegate { @@ -46,6 +48,17 @@ extension THEOplayerRCTView: BackgroundPlaybackDelegate { } } + func updateAVAudioSessionMode() { + do { + THEOplayer.automaticallyManageAudioSession = (self.backgroundAudioConfig.audioSessionMode == .moviePlayback) + try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: self.backgroundAudioConfig.audioSessionMode) + if self.backgroundAudioConfig.audioSessionMode != .moviePlayback { + print("[NATIVE] AVAudioSession mode updated to \(self.backgroundAudioConfig.audioSessionMode.rawValue)") + } + } catch { + print("[NATIVE] Unable to update AVAudioSession mode to \(self.backgroundAudioConfig.audioSessionMode.rawValue): ", error) + } + } @objc func handleInterruption(notification: Notification) { guard let userInfo = notification.userInfo, diff --git a/package-lock.json b/package-lock.json index 18a9cfb73..e7b81fcf3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-native-theoplayer", - "version": "8.2.2", + "version": "8.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "react-native-theoplayer", - "version": "8.2.2", + "version": "8.3.0", "license": "SEE LICENSE AT https://www.theoplayer.com/terms", "dependencies": { "buffer": "^6.0.3" diff --git a/package.json b/package.json index aff5fabc7..112ef45e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-theoplayer", - "version": "8.2.2", + "version": "8.3.0", "description": "A THEOplayer video component for react-native.", "main": "lib/commonjs/index", "module": "lib/module/index", diff --git a/src/api/backgroundAudio/BackgroundAudioConfiguration.ts b/src/api/backgroundAudio/BackgroundAudioConfiguration.ts index b013a8b05..d08d9e0db 100644 --- a/src/api/backgroundAudio/BackgroundAudioConfiguration.ts +++ b/src/api/backgroundAudio/BackgroundAudioConfiguration.ts @@ -12,10 +12,23 @@ export interface BackgroundAudioConfiguration { readonly enabled?: boolean; /** - * Whether background audio should be resumed after an interruption (e.g. incoming call ended). + * Whether background audio should be resumed after an interruption on iOS (e.g. incoming call). * * @defaultValue `false` - * @remark Applies to iOS only, impacting behaviour for handling interruptions while on the lockscreen. + * @remark Applies to iOS only, impacting behaviour for handling interruptions. */ readonly shouldResumeAfterInterruption?: boolean; + + /** + * Specify the mode for the native iOS AVAudioSession. + * + * @defaultValue `AudioSessionMode.MOVIE_PLAYBACK` + * @remark Applies to iOS only, impacting behaviour for handling interruptions. + */ + readonly audioSessionMode?: AudioSessionMode; +} + +export enum AudioSessionMode { + MOVIE_PLAYBACK = 'moviePlayback', + SPOKEN_AUDIO = 'spokenAudio' }