diff --git a/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsView.kt b/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsView.kt index f1030f8..1841487 100644 --- a/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsView.kt +++ b/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsView.kt @@ -1,5 +1,7 @@ package com.amazonaws.ivs.reactnative.player +import android.app.Activity +import android.app.PictureInPictureParams import android.content.pm.PackageManager import android.net.Uri import android.widget.FrameLayout @@ -14,10 +16,6 @@ import com.facebook.react.uimanager.events.RCTEventEmitter import java.util.* import java.util.concurrent.TimeUnit import kotlin.concurrent.timerTask -import android.app.PictureInPictureParams -import android.app.Activity -import androidx.annotation.RequiresApi - class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(context), LifecycleEventListener { private var playerView: PlayerView? = null @@ -30,6 +28,8 @@ class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(conte private var lastBitrate: Long? = null private var lastDuration: Long? = null private var finishedLoading: Boolean = false + private var pipEnabled: Boolean = false + private var isInBackground: Boolean = false enum class Events(private val mName: String) { STATE_CHANGED("onPlayerStateChange"), @@ -200,6 +200,11 @@ class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(conte player?.isAutoQualityMode = autoQualityMode } + fun setPipEnabled(_pipEnabled: Boolean) { + pipEnabled = _pipEnabled + if (!pipEnabled) togglePip() + } + fun onTextCue(cue: TextCue) { val reactContext = context as ReactContext @@ -429,29 +434,43 @@ class AmazonIvsView(private val context: ThemedReactContext) : FrameLayout(conte } } + fun togglePip() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && + context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) + ) { + val activity: Activity? = context.currentActivity + val hasToBuild = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + + if (!pipEnabled) { + val isInPip = + if (hasToBuild) activity!!.isInPictureInPictureMode + else activity!!.isInPictureInPictureMode + if (isInPip) { + activity?.moveTaskToBack(false) + } + return + } - fun togglePip(){ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N - && context.packageManager - .hasSystemFeature( - PackageManager.FEATURE_PICTURE_IN_PICTURE)) { - val activity: Activity? = context.currentActivity - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val params = PictureInPictureParams.Builder() - activity?.enterPictureInPictureMode(params.build()); + if (hasToBuild) { + val params = PictureInPictureParams.Builder().build() + activity?.enterPictureInPictureMode(params) } else { - activity?.enterPictureInPictureMode(); + activity?.enterPictureInPictureMode() } - } } - override fun onHostResume() { + isInBackground = false } - override fun onHostPause() {} + override fun onHostPause() { + if (pipEnabled) { + isInBackground = true + togglePip() + } + } override fun onHostDestroy() { cleanup() diff --git a/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsViewManager.kt b/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsViewManager.kt index a1b0e30..519a03a 100644 --- a/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsViewManager.kt +++ b/android/src/main/java/com/amazonaws/ivs/reactnative/player/AmazonIvsViewManager.kt @@ -117,6 +117,11 @@ class AmazonIvsViewManager : SimpleViewManager() { view.setAutoQualityMode(autoQualityMode) } + @ReactProp(name = "pipEnabled") + fun setPipEnabled(view: AmazonIvsView, pipEnabled: Boolean) { + view.setPipEnabled(pipEnabled) + } + @ReactProp(name = "maxBitrate") fun setMaxBitrate(view: AmazonIvsView, bitrate: Double) { view.setMaxBitrate(bitrate) diff --git a/ios/AmazonIvsManager.m b/ios/AmazonIvsManager.m index 65319cc..59ce41f 100644 --- a/ios/AmazonIvsManager.m +++ b/ios/AmazonIvsManager.m @@ -10,6 +10,7 @@ @interface RCT_EXTERN_MODULE(AmazonIvsManager, RCTViewManager) RCT_EXPORT_VIEW_PROPERTY(initialBufferDuration, double) RCT_EXPORT_VIEW_PROPERTY(autoMaxQuality, NSDictionary) RCT_EXPORT_VIEW_PROPERTY(autoQualityMode, BOOL) +RCT_EXPORT_VIEW_PROPERTY(pipEnabled, BOOL) RCT_EXPORT_VIEW_PROPERTY(playbackRate, double) RCT_EXPORT_VIEW_PROPERTY(logLevel, NSNumber) RCT_EXPORT_VIEW_PROPERTY(progressInterval, NSNumber) diff --git a/ios/AmazonIvsView.swift b/ios/AmazonIvsView.swift index 9a92544..9f208b9 100644 --- a/ios/AmazonIvsView.swift +++ b/ios/AmazonIvsView.swift @@ -54,6 +54,7 @@ class AmazonIvsView: UIView, IVSPlayer.Delegate { self.loop = player.looping self.liveLowLatency = player.isLiveLowLatency self.autoQualityMode = player.autoQualityMode + self.pipEnabled = false self.playbackRate = Double(player.playbackRate) self.logLevel = NSNumber(value: player.logLevel.rawValue) self.progressInterval = 1 @@ -139,6 +140,24 @@ class AmazonIvsView: UIView, IVSPlayer.Delegate { } } + @objc var pipEnabled: Bool { + didSet { + guard #available(iOS 15, *), AVPictureInPictureController.isPictureInPictureSupported() else { + return + } + if self.pipController != nil { + self.pipController!.canStartPictureInPictureAutomaticallyFromInline = pipEnabled + self.togglePip() + if !self.pipEnabled { + self.pipController = nil + } + } else { + self.preparePictureInPicture() + } + } + } + + @objc var autoMaxQuality: NSDictionary? { didSet { let quality = findQuality(quality: autoMaxQuality) @@ -259,7 +278,7 @@ class AmazonIvsView: UIView, IVSPlayer.Delegate { } if pipController.isPictureInPictureActive { pipController.stopPictureInPicture() - } else { + } else if self.pipEnabled { pipController.startPictureInPicture() } } @@ -453,7 +472,9 @@ class AmazonIvsView: UIView, IVSPlayer.Delegate { return } - + if !self.pipEnabled { + return + } if let existingController = self.pipController { if existingController.ivsPlayerLayer == playerView.playerLayer { return @@ -466,7 +487,7 @@ class AmazonIvsView: UIView, IVSPlayer.Delegate { } self.pipController = pipController - pipController.canStartPictureInPictureAutomaticallyFromInline = true + pipController.canStartPictureInPictureAutomaticallyFromInline = self.pipEnabled } } diff --git a/src/IVSPlayer.tsx b/src/IVSPlayer.tsx index 768c21f..3c6af7d 100644 --- a/src/IVSPlayer.tsx +++ b/src/IVSPlayer.tsx @@ -37,6 +37,7 @@ type IVSPlayerProps = { resizeMode?: ResizeMode; logLevel?: LogLevel; progressInterval?: number; + pipEnabled?: boolean; volume?: number; quality?: Quality | null; autoMaxQuality?: Quality | null; @@ -95,6 +96,7 @@ export type Props = { breakpoints?: number[]; maxBitrate?: number; initialBufferDuration?: number; + pipEnabled?: boolean; onSeek?(position: number): void; onData?(data: PlayerData): void; onVideoStatistics?(data: VideoData): void; @@ -132,6 +134,7 @@ const IVSPlayerContainer = React.forwardRef( autoplay = true, liveLowLatency, playbackRate, + pipEnabled, logLevel, progressInterval, volume, @@ -352,6 +355,7 @@ const IVSPlayerContainer = React.forwardRef( autoQualityMode={autoQualityMode} breakpoints={breakpoints} maxBitrate={maxBitrate} + pipEnabled={pipEnabled} onVideoStatistics={ onVideoStatistics ? onVideoStatisticsHandler : undefined }