69a7bc2d26
* docs: enable Android PIP * chore: change comments * feat(android): implement Android PictureInPicture * refactor: minor refactor code and apply lint * fix: rewrite pip action intent code for Android14 * fix: remove redundant codes * feat: add isInPictureInPicture flag for lifecycle handling - activity provide helper method for same purpose, but this flag makes code simple * feat: add pipFullscreenPlayerView for makes PIP include video only * fix: add manifest value checker for prevent crash * docs: add pictureInPicture prop's Android guide * fix: sync controller visibility * refactor: refining variable name * fix: check multi window mode when host pause - some OS version call onPause on multi-window mode * fix: handling when onStop is called while in multi-window mode * refactor: enhance PIP util codes * fix: fix FullscreenPlayerView constructor * refactor: add enterPictureInPictureOnLeave prop and pip methods - remove pictureInPicture boolean prop - add enterPictureInPictureOnLeave boolean prop - add enterPictureInPicture method - add exitPictureInPicture method * fix: fix lint error * fix: prevent audio play in background without playInBackground prop * fix: fix onDetachedFromWindow * docs: update docs for pip * fix(android): sync pip controller with external controller state - for media session * fix(ios): fix pip active fn variable reference * refactor(ios): refactor code * refactor(android): refactor codes * fix(android): fix lint error * refactor(android): refactor android pip logics * fix(android): fix flickering issue when stop picture in picture * fix(android): fix import * fix(android): fix picture in picture with fullscreen mode * fix(ios): fix syntax error * fix(android): fix Fragment managed code * refactor(android): remove redundant override lifecycle * fix(js): add PIP type definition for codegen * fix(android): fix syntax * chore(android): fix lint error * fix(ios): fix enter background handler * refactor(ios): remove redundant code * fix(ios): fix applicationDidEnterBackground for PIP * fix(android): fix onPictureInPictureStatusChanged * fix(ios): fix RCTPictureInPicture * refactor(android): Ignore exception for some device ignore pip checker - some device ignore PIP availability check, so we need to handle exception to prevent crash * fix(android): add hideWithoutPlayer fn into Kotlin ver * refactor(android): remove redundant code * fix(android): fix pip ratio to be calculated with correct ratio value * fix(android): fix crash issue when unmounting in PIP mode * fix(android): fix lint error * Update android/src/main/java/com/brentvatne/react/VideoManagerModule.kt * fix(android): fix lint error * fix(ios): fix lint error * fix(ios): fix lint error * feat(expo): add android picture in picture config within expo plugin * fix: Replace Fragment with androidx.activity - remove code that uses Fragment, which is a tricky implementation * fix: fix lint error * fix(android): disable auto enter when player released * fix(android): fix event handler to check based on Activity it's bound to --------- Co-authored-by: jonghun <jonghun@toss.im> Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
114 lines
4.0 KiB
Swift
114 lines
4.0 KiB
Swift
import AVFoundation
|
|
import React
|
|
|
|
@objc(RCTVideoManager)
|
|
class RCTVideoManager: RCTViewManager {
|
|
override func view() -> UIView {
|
|
return RCTVideo(eventDispatcher: (RCTBridge.current().eventDispatcher() as! RCTEventDispatcher))
|
|
}
|
|
|
|
func methodQueue() -> DispatchQueue {
|
|
return bridge.uiManager.methodQueue
|
|
}
|
|
|
|
func performOnVideoView(withReactTag reactTag: NSNumber, callback: @escaping (RCTVideo?) -> Void) {
|
|
DispatchQueue.main.async { [weak self] in
|
|
guard let self else {
|
|
callback(nil)
|
|
return
|
|
}
|
|
|
|
let view = self.bridge.uiManager.view(forReactTag: reactTag)
|
|
|
|
guard let videoView = view as? RCTVideo else {
|
|
DebugLog("Invalid view returned from registry, expecting RCTVideo, got: \(String(describing: self.view))")
|
|
callback(nil)
|
|
return
|
|
}
|
|
|
|
callback(videoView)
|
|
}
|
|
}
|
|
|
|
@objc(seekCmd:time:tolerance:)
|
|
func seekCmd(_ reactTag: NSNumber, time: NSNumber, tolerance: NSNumber) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.setSeek(time, tolerance)
|
|
})
|
|
}
|
|
|
|
@objc(setLicenseResultCmd:license:licenseUrl:)
|
|
func setLicenseResultCmd(_ reactTag: NSNumber, license: NSString, licenseUrl: NSString) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.setLicenseResult(license as String, licenseUrl as String)
|
|
})
|
|
}
|
|
|
|
@objc(setLicenseResultErrorCmd:error:licenseUrl:)
|
|
func setLicenseResultErrorCmd(_ reactTag: NSNumber, error: NSString, licenseUrl: NSString) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.setLicenseResultError(error as String, licenseUrl as String)
|
|
})
|
|
}
|
|
|
|
@objc(setPlayerPauseStateCmd:paused:)
|
|
func setPlayerPauseStateCmd(_ reactTag: NSNumber, paused: Bool) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.setPaused(paused)
|
|
})
|
|
}
|
|
|
|
@objc(setVolumeCmd:volume:)
|
|
func setVolumeCmd(_ reactTag: NSNumber, volume: Float) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.setVolume(volume)
|
|
})
|
|
}
|
|
|
|
@objc(setFullScreenCmd:fullscreen:)
|
|
func setFullScreenCmd(_ reactTag: NSNumber, fullScreen: Bool) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.setFullscreen(fullScreen)
|
|
})
|
|
}
|
|
|
|
@objc(enterPictureInPictureCmd:)
|
|
func enterPictureInPictureCmd(_ reactTag: NSNumber) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.enterPictureInPicture()
|
|
})
|
|
}
|
|
|
|
@objc(exitPictureInPictureCmd:)
|
|
func exitPictureInPictureCmd(_ reactTag: NSNumber) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.exitPictureInPicture()
|
|
})
|
|
}
|
|
|
|
@objc(setSourceCmd:source:)
|
|
func setSourceCmd(_ reactTag: NSNumber, source: NSDictionary) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.setSrc(source)
|
|
})
|
|
}
|
|
|
|
@objc(save:options:resolve:reject:)
|
|
func save(_ reactTag: NSNumber, options: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.save(options, resolve, reject)
|
|
})
|
|
}
|
|
|
|
@objc(getCurrentPosition:resolve:reject:)
|
|
func getCurrentPosition(_ reactTag: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
performOnVideoView(withReactTag: reactTag, callback: { videoView in
|
|
videoView?.getCurrentPlaybackTime(resolve, reject)
|
|
})
|
|
}
|
|
|
|
override class func requiresMainQueueSetup() -> Bool {
|
|
return true
|
|
}
|
|
}
|