bitmovin-player-react-native 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/RNBitmovinPlayer.podspec +3 -3
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +32 -64
- package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +5 -3
- package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +3 -1
- package/android/src/main/java/com/bitmovin/player/reactnative/ui/RNPictureInPictureHandler.kt +39 -180
- package/ios/RCTConvert+BitmovinPlayer.swift +5 -0
- package/lib/index.d.mts +12 -0
- package/lib/index.d.ts +12 -0
- package/package.json +1 -1
- package/src/tweaksConfig.ts +12 -0
package/RNBitmovinPlayer.podspec
CHANGED
|
@@ -19,7 +19,7 @@ Pod::Spec.new do |s|
|
|
|
19
19
|
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
20
20
|
|
|
21
21
|
s.dependency "React-Core"
|
|
22
|
-
s.dependency "BitmovinPlayer", "3.
|
|
23
|
-
s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.
|
|
24
|
-
s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.
|
|
22
|
+
s.dependency "BitmovinPlayer", "3.60.0"
|
|
23
|
+
s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.19.1"
|
|
24
|
+
s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.9.2"
|
|
25
25
|
end
|
package/android/build.gradle
CHANGED
|
@@ -103,5 +103,5 @@ dependencies {
|
|
|
103
103
|
// Bitmovin
|
|
104
104
|
implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.31.0'
|
|
105
105
|
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
|
|
106
|
-
implementation 'com.bitmovin.player:player:3.
|
|
106
|
+
implementation 'com.bitmovin.player:player:3.65.0+jason'
|
|
107
107
|
}
|
|
@@ -2,8 +2,7 @@ package com.bitmovin.player.reactnative
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.res.Configuration
|
|
5
|
-
import android.
|
|
6
|
-
import android.view.View
|
|
5
|
+
import android.os.Build
|
|
7
6
|
import android.view.ViewGroup
|
|
8
7
|
import android.widget.FrameLayout
|
|
9
8
|
import androidx.lifecycle.DefaultLifecycleObserver
|
|
@@ -17,8 +16,6 @@ import com.bitmovin.player.api.event.SourceEvent
|
|
|
17
16
|
import com.bitmovin.player.api.ui.PlayerViewConfig
|
|
18
17
|
import com.bitmovin.player.api.ui.StyleConfig
|
|
19
18
|
import com.bitmovin.player.reactnative.converter.toJson
|
|
20
|
-
import com.bitmovin.player.reactnative.ui.RNPictureInPictureDelegate
|
|
21
|
-
import com.bitmovin.player.reactnative.ui.RNPictureInPictureHandler
|
|
22
19
|
import com.facebook.react.ReactActivity
|
|
23
20
|
import com.facebook.react.bridge.*
|
|
24
21
|
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
@@ -101,7 +98,7 @@ private val EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING_UI = mapOf<KClass<out Event
|
|
|
101
98
|
@SuppressLint("ViewConstructor")
|
|
102
99
|
class RNPlayerView(
|
|
103
100
|
private val context: ReactApplicationContext,
|
|
104
|
-
) : FrameLayout(context)
|
|
101
|
+
) : FrameLayout(context) {
|
|
105
102
|
private val activityLifecycle = (context.currentActivity as? ReactActivity)?.lifecycle
|
|
106
103
|
?: error("Trying to create an instance of ${this::class.simpleName} while not attached to a ReactActivity")
|
|
107
104
|
|
|
@@ -153,7 +150,6 @@ class RNPlayerView(
|
|
|
153
150
|
|
|
154
151
|
private var _playerView: PlayerView? = null
|
|
155
152
|
set(value) {
|
|
156
|
-
field?.removeOnLayoutChangeListener(this)
|
|
157
153
|
field = value
|
|
158
154
|
viewEventRelay.eventEmitter = field
|
|
159
155
|
playerEventRelay.eventEmitter = field?.player
|
|
@@ -176,11 +172,6 @@ class RNPlayerView(
|
|
|
176
172
|
playerEventRelay.eventEmitter = value
|
|
177
173
|
}
|
|
178
174
|
|
|
179
|
-
/**
|
|
180
|
-
* Object that handles PiP mode changes in React Native.
|
|
181
|
-
*/
|
|
182
|
-
var pictureInPictureHandler: RNPictureInPictureHandler? = null
|
|
183
|
-
|
|
184
175
|
/**
|
|
185
176
|
* Configures the visual presentation and behaviour of the [playerView].
|
|
186
177
|
*/
|
|
@@ -214,11 +205,6 @@ class RNPlayerView(
|
|
|
214
205
|
(playerView.parent as ViewGroup?)?.removeView(playerView)
|
|
215
206
|
addView(playerView, 0)
|
|
216
207
|
}
|
|
217
|
-
pictureInPictureHandler?.let {
|
|
218
|
-
it.setDelegate(this)
|
|
219
|
-
playerView.setPictureInPictureHandler(it)
|
|
220
|
-
playerView.addOnLayoutChangeListener(this)
|
|
221
|
-
}
|
|
222
208
|
}
|
|
223
209
|
|
|
224
210
|
/**
|
|
@@ -232,61 +218,38 @@ class RNPlayerView(
|
|
|
232
218
|
addView(subtitleView)
|
|
233
219
|
}
|
|
234
220
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Called when the player has just entered PiP mode.
|
|
245
|
-
*/
|
|
246
|
-
override fun onEnterPictureInPicture() {
|
|
247
|
-
// Nothing to do
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Called when the player has just exited PiP mode.
|
|
252
|
-
*/
|
|
253
|
-
override fun onExitPictureInPicture() {
|
|
254
|
-
// Explicitly call `exitPictureInPicture()` on PlayerView when exiting PiP state, otherwise
|
|
255
|
-
// the `PictureInPictureExit` event won't get dispatched.
|
|
256
|
-
playerView?.exitPictureInPicture()
|
|
221
|
+
private fun isInPictureInPictureMode(): Boolean {
|
|
222
|
+
val activity = context.currentActivity ?: return false
|
|
223
|
+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
224
|
+
activity.isInPictureInPictureMode
|
|
225
|
+
} else {
|
|
226
|
+
false
|
|
227
|
+
}
|
|
257
228
|
}
|
|
258
229
|
|
|
259
|
-
|
|
260
|
-
* Called when the player's PiP mode changes with a new configuration object.
|
|
261
|
-
*/
|
|
262
|
-
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration?) {
|
|
263
|
-
playerView?.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
|
|
264
|
-
}
|
|
230
|
+
private var isCurrentActivityInPictureInPictureMode: Boolean = isInPictureInPictureMode()
|
|
265
231
|
|
|
266
232
|
/**
|
|
267
|
-
* Called whenever
|
|
233
|
+
* Called whenever this view's activity configuration changes.
|
|
268
234
|
*/
|
|
269
|
-
override fun
|
|
270
|
-
|
|
235
|
+
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
236
|
+
super.onConfigurationChanged(newConfig)
|
|
237
|
+
if (isCurrentActivityInPictureInPictureMode != isInPictureInPictureMode()) {
|
|
238
|
+
isCurrentActivityInPictureInPictureMode = isInPictureInPictureMode()
|
|
239
|
+
onPictureInPictureModeChanged(isCurrentActivityInPictureInPictureMode, newConfig)
|
|
240
|
+
}
|
|
271
241
|
}
|
|
272
242
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
override fun onLayoutChange(
|
|
277
|
-
view: View?,
|
|
278
|
-
left: Int,
|
|
279
|
-
top: Int,
|
|
280
|
-
right: Int,
|
|
281
|
-
bottom: Int,
|
|
282
|
-
oldLeft: Int,
|
|
283
|
-
oldTop: Int,
|
|
284
|
-
oldRight: Int,
|
|
285
|
-
oldBottom: Int,
|
|
243
|
+
private fun onPictureInPictureModeChanged(
|
|
244
|
+
isInPictureInPictureMode: Boolean,
|
|
245
|
+
newConfig: Configuration,
|
|
286
246
|
) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
247
|
+
val playerView = playerView ?: return
|
|
248
|
+
playerView.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
|
|
249
|
+
if (isInPictureInPictureMode) {
|
|
250
|
+
playerView.enterPictureInPicture()
|
|
251
|
+
} else {
|
|
252
|
+
playerView.exitPictureInPicture()
|
|
290
253
|
}
|
|
291
254
|
}
|
|
292
255
|
|
|
@@ -341,7 +304,7 @@ class RNPlayerView(
|
|
|
341
304
|
*/
|
|
342
305
|
data class RNPlayerViewConfigWrapper(
|
|
343
306
|
val playerViewConfig: PlayerViewConfig?,
|
|
344
|
-
val pictureInPictureConfig:
|
|
307
|
+
val pictureInPictureConfig: PictureInPictureConfig?,
|
|
345
308
|
)
|
|
346
309
|
|
|
347
310
|
data class RNStyleConfigWrapper(
|
|
@@ -352,3 +315,8 @@ data class RNStyleConfigWrapper(
|
|
|
352
315
|
enum class UserInterfaceType {
|
|
353
316
|
Bitmovin, Subtitle
|
|
354
317
|
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Configuration type for picture in picture behaviors.
|
|
321
|
+
*/
|
|
322
|
+
data class PictureInPictureConfig(val isEnabled: Boolean)
|
|
@@ -164,6 +164,7 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
164
164
|
val command = commandId?.toInt()?.toCommand() ?: throw IllegalArgumentException(
|
|
165
165
|
"The received command is not supported by the Bitmovin Player View",
|
|
166
166
|
)
|
|
167
|
+
|
|
167
168
|
fun <T> T?.require(): T = this ?: throw InvalidParameterException("Missing parameter")
|
|
168
169
|
when (command) {
|
|
169
170
|
Commands.ATTACH_PLAYER -> attachPlayer(view, args?.getString(1).require(), args?.getMap(2))
|
|
@@ -172,6 +173,7 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
172
173
|
view,
|
|
173
174
|
args?.getString(1).require(),
|
|
174
175
|
)
|
|
176
|
+
|
|
175
177
|
Commands.SET_FULLSCREEN -> setFullscreen(view, args?.getBoolean(1).require())
|
|
176
178
|
Commands.SET_SCALING_MODE -> setScalingMode(view, args?.getString(1).require())
|
|
177
179
|
Commands.SET_PICTURE_IN_PICTURE -> setPictureInPicture(view, args?.getBoolean(1).require())
|
|
@@ -246,9 +248,6 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
246
248
|
val playbackConfig = playerConfig?.getMap("playbackConfig")
|
|
247
249
|
val isPictureInPictureEnabled = view.config?.pictureInPictureConfig?.isEnabled == true ||
|
|
248
250
|
playbackConfig?.getBooleanOrNull("isPictureInPictureEnabled") == true
|
|
249
|
-
val pictureInPictureHandler = view.pictureInPictureHandler ?: RNPictureInPictureHandler(context)
|
|
250
|
-
view.pictureInPictureHandler = pictureInPictureHandler
|
|
251
|
-
view.pictureInPictureHandler?.isPictureInPictureEnabled = isPictureInPictureEnabled
|
|
252
251
|
|
|
253
252
|
val rnStyleConfigWrapper = playerConfig?.toRNStyleConfigWrapperFromPlayerConfig()
|
|
254
253
|
val configuredPlayerViewConfig = view.config?.playerViewConfig ?: PlayerViewConfig()
|
|
@@ -272,6 +271,9 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
272
271
|
LayoutParams.MATCH_PARENT,
|
|
273
272
|
LayoutParams.MATCH_PARENT,
|
|
274
273
|
)
|
|
274
|
+
if (isPictureInPictureEnabled) {
|
|
275
|
+
playerView.setPictureInPictureHandler(RNPictureInPictureHandler(currentActivity, player))
|
|
276
|
+
}
|
|
275
277
|
view.setPlayerView(playerView)
|
|
276
278
|
attachCustomMessageHandlerBridge(view)
|
|
277
279
|
}
|
|
@@ -47,6 +47,7 @@ import com.bitmovin.player.api.ui.ScalingMode
|
|
|
47
47
|
import com.bitmovin.player.api.ui.StyleConfig
|
|
48
48
|
import com.bitmovin.player.api.ui.UiConfig
|
|
49
49
|
import com.bitmovin.player.reactnative.BitmovinCastManagerOptions
|
|
50
|
+
import com.bitmovin.player.reactnative.PictureInPictureConfig
|
|
50
51
|
import com.bitmovin.player.reactnative.RNBufferLevels
|
|
51
52
|
import com.bitmovin.player.reactnative.RNPlayerViewConfigWrapper
|
|
52
53
|
import com.bitmovin.player.reactnative.RNStyleConfigWrapper
|
|
@@ -70,7 +71,6 @@ import com.bitmovin.player.reactnative.extensions.withInt
|
|
|
70
71
|
import com.bitmovin.player.reactnative.extensions.withMap
|
|
71
72
|
import com.bitmovin.player.reactnative.extensions.withString
|
|
72
73
|
import com.bitmovin.player.reactnative.extensions.withStringArray
|
|
73
|
-
import com.bitmovin.player.reactnative.ui.RNPictureInPictureHandler.PictureInPictureConfig
|
|
74
74
|
import com.facebook.react.bridge.*
|
|
75
75
|
import java.util.UUID
|
|
76
76
|
|
|
@@ -217,6 +217,7 @@ fun ReadableMap.toAdSource(): AdSource? {
|
|
|
217
217
|
* Converts any JS string into an `AdSourceType` enum value.
|
|
218
218
|
*/
|
|
219
219
|
private fun String.toAdSourceType(): AdSourceType? = when (this) {
|
|
220
|
+
"bitmovin" -> AdSourceType.Bitmovin
|
|
220
221
|
"ima" -> AdSourceType.Ima
|
|
221
222
|
"progressive" -> AdSourceType.Progressive
|
|
222
223
|
"unknown" -> AdSourceType.Unknown
|
|
@@ -626,6 +627,7 @@ fun AdSource.toJson(): WritableMap = Arguments.createMap().apply {
|
|
|
626
627
|
* Converts any `AdSourceType` value into its json representation.
|
|
627
628
|
*/
|
|
628
629
|
fun AdSourceType.toJson(): String = when (this) {
|
|
630
|
+
AdSourceType.Bitmovin -> "bitmovin"
|
|
629
631
|
AdSourceType.Ima -> "ima"
|
|
630
632
|
AdSourceType.Unknown -> "unknown"
|
|
631
633
|
AdSourceType.Progressive -> "progressive"
|
package/android/src/main/java/com/bitmovin/player/reactnative/ui/RNPictureInPictureHandler.kt
CHANGED
|
@@ -1,200 +1,59 @@
|
|
|
1
1
|
package com.bitmovin.player.reactnative.ui
|
|
2
2
|
|
|
3
|
+
import android.app.Activity
|
|
3
4
|
import android.app.PictureInPictureParams
|
|
4
|
-
import android.content.pm.PackageManager
|
|
5
|
-
import android.content.res.Configuration
|
|
6
|
-
import android.graphics.Rect
|
|
7
5
|
import android.os.Build
|
|
6
|
+
import android.util.Log
|
|
8
7
|
import android.util.Rational
|
|
9
8
|
import androidx.annotation.RequiresApi
|
|
10
|
-
import
|
|
11
|
-
import com.bitmovin.player.
|
|
12
|
-
|
|
9
|
+
import com.bitmovin.player.api.Player
|
|
10
|
+
import com.bitmovin.player.ui.DefaultPictureInPictureHandler
|
|
11
|
+
|
|
12
|
+
private const val TAG = "RNPiPHandler"
|
|
13
|
+
|
|
14
|
+
class RNPictureInPictureHandler(
|
|
15
|
+
private val activity: Activity,
|
|
16
|
+
private val player: Player,
|
|
17
|
+
) : DefaultPictureInPictureHandler(activity, player) {
|
|
18
|
+
// Current PiP implementation on the native side requires playerView.exitPictureInPicture() to be called
|
|
19
|
+
// for `PictureInPictureExit` event to be emitted.
|
|
20
|
+
// Additionally, the event is only emitted if `isPictureInPicture` is true. At the point in time we call
|
|
21
|
+
// playerView.exitPictureInPicture() the activity will already have exited the PiP mode,
|
|
22
|
+
// and thus the event won't be emitted. To work around this we keep track of the PiP state ourselves.
|
|
23
|
+
private var _isPictureInPicture = false
|
|
13
24
|
|
|
14
|
-
/**
|
|
15
|
-
* Delegate object for `RNPictureInPictureHandler`. It delegates all view logic that needs
|
|
16
|
-
* to be performed during each PiP state to this object.
|
|
17
|
-
*/
|
|
18
|
-
interface RNPictureInPictureDelegate {
|
|
19
|
-
/**
|
|
20
|
-
* Called whenever the handler's `isInPictureInPictureMode` changes to `true`.
|
|
21
|
-
*/
|
|
22
|
-
fun onExitPictureInPicture()
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Called whenever the handler's `isInPictureInPictureMode` changes to `false`.
|
|
26
|
-
*/
|
|
27
|
-
fun onEnterPictureInPicture()
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Called whenever the activity's PiP mode state changes with the new resources configuration.
|
|
31
|
-
*/
|
|
32
|
-
fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration?)
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Called whenever the handler needs to compute a new `sourceRectHint` for PiP params.
|
|
36
|
-
* The passed rect reference is expected to be fulfilled with the PlayerView's global visible
|
|
37
|
-
* rect.
|
|
38
|
-
*/
|
|
39
|
-
fun setSourceRectHint(sourceRectHint: Rect)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Custom PictureInPictureHandler` concrete implementation designed for React Native. It relies on
|
|
44
|
-
* React Native's application context to manage the application's PiP state. Can be subclassed in
|
|
45
|
-
* order to provide custom PiP capabilities.
|
|
46
|
-
*/
|
|
47
|
-
open class RNPictureInPictureHandler(val context: ReactApplicationContext) : PictureInPictureHandler {
|
|
48
|
-
/**
|
|
49
|
-
* Configuration type for picture in picture behaviors.
|
|
50
|
-
*/
|
|
51
|
-
data class PictureInPictureConfig(val isEnabled: Boolean)
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* PiP delegate object that contains the view logic to be performed on each PiP state change.
|
|
55
|
-
*/
|
|
56
|
-
private var delegate: RNPictureInPictureDelegate? = null
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Whether the user has enabled PiP support via `isPictureInPictureEnabled` playback configuration in JS.
|
|
60
|
-
*/
|
|
61
|
-
var isPictureInPictureEnabled = false
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Whether this view is currently in PiP mode.
|
|
65
|
-
*/
|
|
66
|
-
private var isInPictureInPictureMode = false
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Whether the current Android version supports PiP mode.
|
|
70
|
-
*/
|
|
71
|
-
private val isPictureInPictureSupported: Boolean
|
|
72
|
-
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
|
|
73
|
-
context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Whether the picture in picture feature is available and enabled.
|
|
77
|
-
*/
|
|
78
|
-
override val isPictureInPictureAvailable: Boolean
|
|
79
|
-
get() = isPictureInPictureEnabled && isPictureInPictureSupported
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Whether this view is currently in PiP mode. Required for PictureInPictureHandler interface.
|
|
83
|
-
*/
|
|
84
25
|
override val isPictureInPicture: Boolean
|
|
85
|
-
get() =
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Current React activity computed property.
|
|
89
|
-
*/
|
|
90
|
-
private val currentActivity: AppCompatActivity?
|
|
91
|
-
get() {
|
|
92
|
-
if (context.hasCurrentActivity()) {
|
|
93
|
-
return context.currentActivity as AppCompatActivity
|
|
94
|
-
}
|
|
95
|
-
return null
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Sets the new delegate object and update the activity's PiP parameters accordingly.
|
|
100
|
-
*/
|
|
101
|
-
open fun setDelegate(delegate: RNPictureInPictureDelegate?) {
|
|
102
|
-
this.delegate = delegate
|
|
103
|
-
// Update the activity's PiP params once the delegate has been set.
|
|
104
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isPictureInPictureAvailable) {
|
|
105
|
-
applyPictureInPictureParams()
|
|
106
|
-
}
|
|
107
|
-
}
|
|
26
|
+
get() = _isPictureInPicture
|
|
108
27
|
|
|
109
|
-
|
|
110
|
-
* Called whenever bitmovin's `PlayerView` needs to enter PiP mode.
|
|
111
|
-
*/
|
|
28
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
112
29
|
override fun enterPictureInPicture() {
|
|
113
|
-
if (isPictureInPictureAvailable) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
it.enterPictureInPictureMode()
|
|
117
|
-
}
|
|
30
|
+
if (!isPictureInPictureAvailable) {
|
|
31
|
+
Log.w(TAG, "Calling enterPictureInPicture without PiP support.")
|
|
32
|
+
return
|
|
118
33
|
}
|
|
119
|
-
}
|
|
120
34
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
*/
|
|
124
|
-
override fun exitPictureInPicture() {
|
|
125
|
-
if (isPictureInPictureAvailable) {
|
|
126
|
-
currentActivity?.supportActionBar?.show()
|
|
35
|
+
if (isPictureInPicture) {
|
|
36
|
+
return
|
|
127
37
|
}
|
|
128
|
-
}
|
|
129
38
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
handlePictureInPictureModeChanges(newConfig)
|
|
137
|
-
}
|
|
138
|
-
}
|
|
39
|
+
// The default implementation doesn't properly handle the case where source isn't loaded yet.
|
|
40
|
+
// To work around it we just use a 16:9 aspect ratio if we cannot calculate it from `playbackVideoData`.
|
|
41
|
+
val aspectRatio =
|
|
42
|
+
player.playbackVideoData
|
|
43
|
+
?.let { Rational(it.width, it.height) }
|
|
44
|
+
?: Rational(16, 9)
|
|
139
45
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
@RequiresApi(Build.VERSION_CODES.N)
|
|
145
|
-
private fun handlePictureInPictureModeChanges(newConfig: Configuration?) = currentActivity?.let {
|
|
146
|
-
if (isInPictureInPictureMode != it.isInPictureInPictureMode) {
|
|
147
|
-
delegate?.onPictureInPictureModeChanged(it.isInPictureInPictureMode, newConfig)
|
|
148
|
-
if (it.isInPictureInPictureMode) {
|
|
149
|
-
delegate?.onEnterPictureInPicture()
|
|
150
|
-
} else {
|
|
151
|
-
delegate?.onExitPictureInPicture()
|
|
152
|
-
}
|
|
153
|
-
isInPictureInPictureMode = it.isInPictureInPictureMode
|
|
154
|
-
}
|
|
155
|
-
}
|
|
46
|
+
val params =
|
|
47
|
+
PictureInPictureParams.Builder()
|
|
48
|
+
.setAspectRatio(aspectRatio)
|
|
49
|
+
.build()
|
|
156
50
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
*
|
|
160
|
-
* You can read more about the recommended settings for PiP here:
|
|
161
|
-
* - https://developer.android.com/develop/ui/views/picture-in-picture#smoother-transition
|
|
162
|
-
* - https://developer.android.com/develop/ui/views/picture-in-picture#smoother-exit
|
|
163
|
-
*/
|
|
164
|
-
@RequiresApi(Build.VERSION_CODES.O)
|
|
165
|
-
private fun applyPictureInPictureParams() = currentActivity?.let {
|
|
166
|
-
// See also: https://developer.android.com/develop/ui/views/picture-in-picture#smoother-transition
|
|
167
|
-
val sourceRectHint = Rect()
|
|
168
|
-
delegate?.setSourceRectHint(sourceRectHint)
|
|
169
|
-
val ratio = Rational(16, 9)
|
|
170
|
-
val params = PictureInPictureParams.Builder()
|
|
171
|
-
.setAspectRatio(ratio)
|
|
172
|
-
.setSourceRectHint(sourceRectHint)
|
|
173
|
-
when {
|
|
174
|
-
// See also: https://developer.android.com/develop/ui/views/picture-in-picture#smoother-exit
|
|
175
|
-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ->
|
|
176
|
-
params.setAutoEnterEnabled(true).setSeamlessResizeEnabled(true)
|
|
177
|
-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ->
|
|
178
|
-
params.setExpandedAspectRatio(ratio)
|
|
179
|
-
}
|
|
180
|
-
it.setPictureInPictureParams(params.build())
|
|
51
|
+
activity.enterPictureInPictureMode(params)
|
|
52
|
+
_isPictureInPicture = true
|
|
181
53
|
}
|
|
182
54
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
open fun updateSourceRectHint() {
|
|
187
|
-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !isPictureInPictureAvailable) {
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
currentActivity?.let {
|
|
191
|
-
val sourceRectHint = Rect()
|
|
192
|
-
delegate?.setSourceRectHint(sourceRectHint)
|
|
193
|
-
it.setPictureInPictureParams(
|
|
194
|
-
PictureInPictureParams.Builder()
|
|
195
|
-
.setSourceRectHint(sourceRectHint)
|
|
196
|
-
.build(),
|
|
197
|
-
)
|
|
198
|
-
}
|
|
55
|
+
override fun exitPictureInPicture() {
|
|
56
|
+
super.exitPictureInPicture()
|
|
57
|
+
_isPictureInPicture = false
|
|
199
58
|
}
|
|
200
59
|
}
|
|
@@ -159,6 +159,11 @@ extension RCTConvert {
|
|
|
159
159
|
break
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
|
+
#if !os(tvOS)
|
|
163
|
+
if let updatesNowPlayingInfoCenter = json["updatesNowPlayingInfoCenter"] as? Bool {
|
|
164
|
+
tweaksConfig.updatesNowPlayingInfoCenter = updatesNowPlayingInfoCenter
|
|
165
|
+
}
|
|
166
|
+
#endif
|
|
162
167
|
return tweaksConfig
|
|
163
168
|
}
|
|
164
169
|
|
package/lib/index.d.mts
CHANGED
|
@@ -2786,6 +2786,18 @@ interface TweaksConfig {
|
|
|
2786
2786
|
* @platform Android
|
|
2787
2787
|
*/
|
|
2788
2788
|
preferSoftwareDecodingForAds?: boolean;
|
|
2789
|
+
/**
|
|
2790
|
+
* Determines whether `AVKit` should update Now Playing information automatically when using System UI.
|
|
2791
|
+
*
|
|
2792
|
+
* - If set to `false`, the automatic updates of Now Playing Info sent by `AVKit` are disabled.
|
|
2793
|
+
* This prevents interference with manual updates you may want to perform.
|
|
2794
|
+
* - If set to `true`, the default behaviour is maintained, allowing `AVKit` to handle Now Playing updates.
|
|
2795
|
+
*
|
|
2796
|
+
* Default is `true`.
|
|
2797
|
+
*
|
|
2798
|
+
* @platform iOS
|
|
2799
|
+
*/
|
|
2800
|
+
updatesNowPlayingInfoCenter?: boolean;
|
|
2789
2801
|
}
|
|
2790
2802
|
|
|
2791
2803
|
/**
|
package/lib/index.d.ts
CHANGED
|
@@ -2786,6 +2786,18 @@ interface TweaksConfig {
|
|
|
2786
2786
|
* @platform Android
|
|
2787
2787
|
*/
|
|
2788
2788
|
preferSoftwareDecodingForAds?: boolean;
|
|
2789
|
+
/**
|
|
2790
|
+
* Determines whether `AVKit` should update Now Playing information automatically when using System UI.
|
|
2791
|
+
*
|
|
2792
|
+
* - If set to `false`, the automatic updates of Now Playing Info sent by `AVKit` are disabled.
|
|
2793
|
+
* This prevents interference with manual updates you may want to perform.
|
|
2794
|
+
* - If set to `true`, the default behaviour is maintained, allowing `AVKit` to handle Now Playing updates.
|
|
2795
|
+
*
|
|
2796
|
+
* Default is `true`.
|
|
2797
|
+
*
|
|
2798
|
+
* @platform iOS
|
|
2799
|
+
*/
|
|
2800
|
+
updatesNowPlayingInfoCenter?: boolean;
|
|
2789
2801
|
}
|
|
2790
2802
|
|
|
2791
2803
|
/**
|
package/package.json
CHANGED
package/src/tweaksConfig.ts
CHANGED
|
@@ -157,4 +157,16 @@ export interface TweaksConfig {
|
|
|
157
157
|
* @platform Android
|
|
158
158
|
*/
|
|
159
159
|
preferSoftwareDecodingForAds?: boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Determines whether `AVKit` should update Now Playing information automatically when using System UI.
|
|
162
|
+
*
|
|
163
|
+
* - If set to `false`, the automatic updates of Now Playing Info sent by `AVKit` are disabled.
|
|
164
|
+
* This prevents interference with manual updates you may want to perform.
|
|
165
|
+
* - If set to `true`, the default behaviour is maintained, allowing `AVKit` to handle Now Playing updates.
|
|
166
|
+
*
|
|
167
|
+
* Default is `true`.
|
|
168
|
+
*
|
|
169
|
+
* @platform iOS
|
|
170
|
+
*/
|
|
171
|
+
updatesNowPlayingInfoCenter?: boolean;
|
|
160
172
|
}
|