bitmovin-player-react-native 0.4.0 → 0.5.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/README.md +249 -1
- package/RNBitmovinPlayer.podspec +3 -1
- package/android/build.gradle +3 -2
- package/android/src/main/java/com/bitmovin/player/reactnative/AnalyticsModule.kt +154 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +45 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +25 -4
- package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewPackage.kt +3 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +172 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/extensions/Any.kt +27 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReactContextExtension.kt +8 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/extensions/String.kt +8 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerBridge.kt +37 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerModule.kt +73 -0
- package/ios/AnalyticsModule.m +14 -0
- package/ios/AnalyticsModule.swift +180 -0
- package/ios/Event+JSON.swift +11 -0
- package/ios/FullscreenHandlerBridge.swift +33 -0
- package/ios/FullscreenHandlerModule.m +9 -0
- package/ios/FullscreenHandlerModule.swift +71 -0
- package/ios/RCTConvert+BitmovinPlayer.swift +174 -0
- package/ios/RNPlayerView+PlayerListener.swift +5 -1
- package/ios/RNPlayerView+UserInterfaceListener.swift +16 -0
- package/ios/RNPlayerView.swift +5 -0
- package/ios/RNPlayerViewManager.m +6 -0
- package/ios/RNPlayerViewManager.swift +21 -0
- package/lib/index.d.ts +498 -51
- package/lib/index.js +186 -42
- package/lib/index.mjs +167 -26
- package/package.json +1 -1
- package/src/analytics/collector.ts +97 -0
- package/src/analytics/config.ts +218 -0
- package/src/analytics/index.ts +2 -0
- package/src/components/PlayerView/events.ts +10 -0
- package/src/components/PlayerView/index.tsx +38 -1
- package/src/components/PlayerView/native.ts +4 -1
- package/src/events.ts +43 -0
- package/src/index.ts +2 -0
- package/src/media.ts +33 -0
- package/src/player.ts +21 -0
- package/src/source.ts +4 -0
- package/src/styleConfig.ts +87 -0
- package/src/ui/fullscreenhandler.ts +19 -0
- package/src/ui/fullscreenhandlerbridge.ts +59 -0
package/README.md
CHANGED
|
@@ -32,8 +32,20 @@ Official React Native bindings for Bitmovin's mobile Player SDKs.
|
|
|
32
32
|
- [Enabling DRM protection](#enabling-drm-protection)
|
|
33
33
|
- [Prepare hooks](#prepare-hooks)
|
|
34
34
|
- [Adding external subtitle tracks](#adding-external-subtitle-tracks)
|
|
35
|
+
- [Adding external thumbnail track](#adding-external-thumbnail-track)
|
|
35
36
|
- [Enabling Picture in Picture mode](#enabling-picture-in-picture-mode)
|
|
37
|
+
- [Android](#android)
|
|
38
|
+
- [iOS](#ios)
|
|
39
|
+
- [Showing the Picture in Picture UI option](#showing-the-picture-in-picture-ui-option)
|
|
40
|
+
- [Supported Picture in Picture events](#supported-picture-in-picture-events)
|
|
41
|
+
- [Customize HTML UI](#customize-html-ui-android-and-ios-only)
|
|
42
|
+
- [Setting up fullscreen handling](#setting-up-fullscreen-handling)
|
|
43
|
+
- [Supported fullscreen related events](#supported-fullscreen-related-events)
|
|
36
44
|
- [Setting up ads](#setting-up-ads)
|
|
45
|
+
- [Static ads configuration](#static-ads-configuration)
|
|
46
|
+
- [Dynamic ads scheduling](#dynamic-ads-scheduling)
|
|
47
|
+
- [Supported ads events](#supported-ads-events)
|
|
48
|
+
- [Setting up analytics](#setting-up-analytics)
|
|
37
49
|
- [Contributing](#contributing)
|
|
38
50
|
|
|
39
51
|
## Platform Support
|
|
@@ -56,7 +68,7 @@ Features of the native mobile Player SDKs are progressively being implemented in
|
|
|
56
68
|
| -------------------------------- | ----------------------------------------- |
|
|
57
69
|
| Playback of DRM-protected assets | :white_check_mark: Available since v0.2.0 |
|
|
58
70
|
| Subtitles & Captions | :white_check_mark: Available since v0.2.0 |
|
|
59
|
-
| Advertising | :
|
|
71
|
+
| Advertising | :white_check_mark: Available since v0.4.0 |
|
|
60
72
|
| Playlist API | :x: Not available |
|
|
61
73
|
| Offline Playback | :x: Not available |
|
|
62
74
|
| Analytics | :x: Coming Q1 2023 |
|
|
@@ -516,6 +528,38 @@ The supported `PlayerView` events for subtitles are:
|
|
|
516
528
|
|
|
517
529
|
You might check out a complete subtitle example in the [`example/`](https://github.com/bitmovin/bitmovin-player-react-native/tree/development/example) app.
|
|
518
530
|
|
|
531
|
+
### Adding external thumbnail track
|
|
532
|
+
|
|
533
|
+
Thumbnail seeking is a must have for any video longer than a few minutes. It increases usability and the general QoE [(Quality of Experience)](https://bitmovin.com/ultra-high-definition-quality-experience-mpeg-dash-part-1/) dramatically.
|
|
534
|
+
|
|
535
|
+
Setting up is simple with the Bitmovin Player. Thumbnails are loaded into the timeline as a track. All you need to do is to tell the player the location of the thumbnail file:
|
|
536
|
+
|
|
537
|
+
```typescript
|
|
538
|
+
import { Platform } from 'react-native';
|
|
539
|
+
import {
|
|
540
|
+
SourceConfig,
|
|
541
|
+
SourceType,
|
|
542
|
+
SubtitleFormat,
|
|
543
|
+
} from 'bitmovin-player-react-native';
|
|
544
|
+
|
|
545
|
+
// Source config with an external subtitle track.
|
|
546
|
+
const config: SourceConfig = {
|
|
547
|
+
url:
|
|
548
|
+
Platform.OS === 'ios'
|
|
549
|
+
? 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8'
|
|
550
|
+
: 'https://bitmovin-a.akamaihd.net/content/sintel/sintel.mpd',
|
|
551
|
+
type: Platform.OS === 'ios' ? SourceType.HLS : SourceType.DASH,
|
|
552
|
+
poster: 'https://bitmovin-a.akamaihd.net/content/sintel/poster.png',
|
|
553
|
+
// External thumbnail track url to be added to this source.
|
|
554
|
+
thumbnailTrack:
|
|
555
|
+
'https://cdn.bitmovin.com/content/assets/art-of-motion-dash-hls-progressive/thumbnails/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.vtt',
|
|
556
|
+
};
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
What’s required for a video player with thumbnails
|
|
560
|
+
|
|
561
|
+
Adaptive Streaming relies on encoding your video into several groups of files (streams) at various resolutions, while thumbnails also need to be generated in the encoding process. The encoder creates a set of thumbnail images and combines them into a single image file (“Sprite”). For more information on encoding your videos, have a look at our [Cloud Encoding Service](https://bitmovin.com/encoding/).
|
|
562
|
+
|
|
519
563
|
### Enabling Picture in Picture mode
|
|
520
564
|
|
|
521
565
|
In order to make use of the Picture in Picture functionalities provided by the player, it's first necessary to configure your native application to properly support PiP.
|
|
@@ -612,6 +656,143 @@ The supported Picture in Picture events on `PlayerView` are:
|
|
|
612
656
|
|
|
613
657
|
Check [`events.ts`](https://github.com/bitmovin/bitmovin-player-react-native/blob/development/src/components/PlayerView/events.ts) for more information about them.
|
|
614
658
|
|
|
659
|
+
### Customize HTML UI (Android and iOS only)
|
|
660
|
+
|
|
661
|
+
The Bitmovin Player SDKs use the open source [Bitmovin Player Web UI](https://github.com/bitmovin/bitmovin-player-ui) on all platforms, except tvOS.
|
|
662
|
+
The UI is customizable in multiple ways.
|
|
663
|
+
|
|
664
|
+
#### Custom implementation
|
|
665
|
+
|
|
666
|
+
Since the Bitmovin Player Web UI is open source, it can be forked and modified to tailor to any application's needs.
|
|
667
|
+
See [Cusomizing the UI](https://github.com/bitmovin/bitmovin-player-ui#customizing-the-ui) section for details.
|
|
668
|
+
|
|
669
|
+
In case a custom implementation of the Player UI is desired, configure the hosted JS and CSS files via the `StyleConfig` as shown in the following example:
|
|
670
|
+
|
|
671
|
+
```ts
|
|
672
|
+
const player = usePlayer({
|
|
673
|
+
styleConfig: {
|
|
674
|
+
playerUiCss: 'CUSTOM_UI_CSS_URL',
|
|
675
|
+
playerUiJs: 'CUSTOM_UI_JS_URL',
|
|
676
|
+
},
|
|
677
|
+
});
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
#### Custom CSS
|
|
681
|
+
|
|
682
|
+
Customization of the default built-in Bitmovin Player UI is possible via providing custom styling CSS by only configuring `playerUiCss` as shown in the following example:
|
|
683
|
+
|
|
684
|
+
```ts
|
|
685
|
+
const player = usePlayer({
|
|
686
|
+
styleConfig: {
|
|
687
|
+
playerUiCss: 'CUSTOM_UI_CSS_URL',
|
|
688
|
+
},
|
|
689
|
+
});
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
#### Supplemental CSS
|
|
693
|
+
|
|
694
|
+
In case the usage of the default Bitmovin Player UI is sufficient with minor additional styling, it can be achieved via providing the URL to the additional CSS stylesheet via `supplementalPlayerUiCss`.
|
|
695
|
+
|
|
696
|
+
```ts
|
|
697
|
+
const player = usePlayer({
|
|
698
|
+
styleConfig: {
|
|
699
|
+
supplementalPlayerUiCss: 'SUPPLEMENTAL_UI_CSS_URL',
|
|
700
|
+
},
|
|
701
|
+
});
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### Setting up fullscreen handling
|
|
705
|
+
|
|
706
|
+
In order to enable the player to support fullscreen and show the fullscreen button when using the Bitmovin Player Web UI, a `FullscreenHandler` needs to be implemented.
|
|
707
|
+
Its responsibility is to update the UI when transitioning between fullscreen and non-fullscreen states.
|
|
708
|
+
The player view itself does not update it's presentation as the meaning of fullscreen is determined by the application integrating our library.
|
|
709
|
+
|
|
710
|
+
Here are the basics of enabling fullscreen support:
|
|
711
|
+
|
|
712
|
+
```typescript
|
|
713
|
+
// Define a handler to take care of fullscreen transitions
|
|
714
|
+
class SampleFullscreenHandler implements FullscreenHandler {
|
|
715
|
+
isFullscreenActive: boolean = true;
|
|
716
|
+
onFullscreen: (fullscreenMode: boolean) => void;
|
|
717
|
+
|
|
718
|
+
constructor(
|
|
719
|
+
isFullscreenActive: boolean,
|
|
720
|
+
onFullscreen: (fullscreenMode: boolean) => void
|
|
721
|
+
) {
|
|
722
|
+
this.isFullscreenActive = isFullscreenActive;
|
|
723
|
+
this.onFullscreen = onFullscreen;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
enterFullscreen(): void {
|
|
727
|
+
// Update UI state for fullscreen mode
|
|
728
|
+
this.onFullscreen(true);
|
|
729
|
+
this.isFullscreenActive = true;
|
|
730
|
+
console.log('enter fullscreen');
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
exitFullscreen(): void {
|
|
734
|
+
// Update UI state for non-fullscreen mode
|
|
735
|
+
this.onFullscreen(false);
|
|
736
|
+
this.isFullscreenActive = false;
|
|
737
|
+
console.log('exit fullscreen');
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
export default function BasicFullscreenHandling() {
|
|
742
|
+
// Set up player and other components
|
|
743
|
+
|
|
744
|
+
// Create SampleFullscreenHandler instance and enable it to update state
|
|
745
|
+
const [fullscreenMode, setFullscreenMode] = useState(false);
|
|
746
|
+
const fullscreenHandler = new SampleFullscreenHandler(
|
|
747
|
+
fullscreenMode,
|
|
748
|
+
setFullscreenMode
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
return (
|
|
752
|
+
<View>
|
|
753
|
+
<PlayerView
|
|
754
|
+
player={player}
|
|
755
|
+
style={fullscreenMode ? styles.playerFullscreen : styles.player}
|
|
756
|
+
fullscreenHandler={fullscreenHandler}
|
|
757
|
+
onFullscreenEnter={onFullscreenEnter}
|
|
758
|
+
onFullscreenExit={onFullscreenExit}
|
|
759
|
+
onFullscreenEnabled={onFullscreenEnabled}
|
|
760
|
+
onFullscreenDisabled={onFullscreenDisabled}
|
|
761
|
+
/>
|
|
762
|
+
</View>
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Define your styles
|
|
767
|
+
const styles = StyleSheet.create({
|
|
768
|
+
player: {
|
|
769
|
+
flex: 1,
|
|
770
|
+
backgroundColor: 'black',
|
|
771
|
+
},
|
|
772
|
+
playerFullscreen: {
|
|
773
|
+
position: 'absolute',
|
|
774
|
+
top: 0,
|
|
775
|
+
right: 0,
|
|
776
|
+
bottom: 0,
|
|
777
|
+
left: 0,
|
|
778
|
+
backgroundColor: 'black',
|
|
779
|
+
},
|
|
780
|
+
});
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
Check [`BasicFullscreenHandling.tsx`](https://github.com/bitmovin/bitmovin-player-react-native/blob/development/example/src/screens/BasicFullscreenHandling.tsx) for a full example implementation.
|
|
784
|
+
|
|
785
|
+
#### Supported fullscreen related events
|
|
786
|
+
|
|
787
|
+
The supported fullscreen events on `PlayerView` are:
|
|
788
|
+
|
|
789
|
+
- `onFullscreenEnter`
|
|
790
|
+
- `onFullscreenExit`
|
|
791
|
+
- `onFullscreenEnabled`
|
|
792
|
+
- `onFullscreenDisabled`
|
|
793
|
+
|
|
794
|
+
Check [`events.ts`](https://github.com/bitmovin/bitmovin-player-react-native/blob/development/src/components/PlayerView/events.ts) for more information about them.
|
|
795
|
+
|
|
615
796
|
### Setting up ads
|
|
616
797
|
|
|
617
798
|
The Bitmovin Player SDKs are capable of displaying Ads out of the box and there are two ways they can be
|
|
@@ -700,6 +881,73 @@ The supported `PlayerView` events for ads are:
|
|
|
700
881
|
|
|
701
882
|
You can check out a complete ads example in the [`example/`](https://github.com/bitmovin/bitmovin-player-react-native/tree/development/example) app.
|
|
702
883
|
|
|
884
|
+
### Setting up analytics
|
|
885
|
+
|
|
886
|
+
Each `Player` instance has an associated analytics collector that can be configured to send analytics information about it. By default,
|
|
887
|
+
the associated collector is disabled unless an `analyticsConfig` option is specified. So in order to get analytics up and running, add the following configuration options to your `PlayerConfig`:
|
|
888
|
+
|
|
889
|
+
```typescript
|
|
890
|
+
const player = usePlayer({
|
|
891
|
+
analyticsConfig: {
|
|
892
|
+
// Bitmovin analytics key from the Analytics Dashboard
|
|
893
|
+
key: '<ANALYTICS-KEY>', // `key` is the only required parameter.
|
|
894
|
+
// Bitmovin player license key
|
|
895
|
+
playerKey: '<BITMOVIN-PLAYER-KEY>',
|
|
896
|
+
// Asset CDN provider. Check out `CdnProvider` on `src/analytics/config.ts` for more options.
|
|
897
|
+
cdnProvider: CdnProvider.AKAMAI,
|
|
898
|
+
// User-defined user ID.
|
|
899
|
+
customUserId: 'Custom user ID',
|
|
900
|
+
// Whether the user ID should be randomly generated or not. Default value is false.
|
|
901
|
+
randomizeUserId: false,
|
|
902
|
+
// Experiment name that'll appear at the Analytics Dashboard.
|
|
903
|
+
experimentName: 'Experiment name',
|
|
904
|
+
// Video ID on your server
|
|
905
|
+
videoId: 'MyVideoId',
|
|
906
|
+
// Video title
|
|
907
|
+
title: 'Art of Motion',
|
|
908
|
+
// Whether this is a live stream video. Default is false.
|
|
909
|
+
isLive: false,
|
|
910
|
+
// Whether collector should also collect statistics about ads
|
|
911
|
+
// Can be changed to `true` in case `advertisingConfig` is also present.
|
|
912
|
+
// Default is false.
|
|
913
|
+
ads: false,
|
|
914
|
+
// Navigation breadcrumb.
|
|
915
|
+
// The path taken by the user inside your application.
|
|
916
|
+
path: '/examples/basic_analytics',
|
|
917
|
+
// List of custom data fields to be registered at the Analytics Dashboard.
|
|
918
|
+
// Useful to customize collection with your own data along with the SDK.
|
|
919
|
+
customData1: 'Custom data field 1',
|
|
920
|
+
customData2: 'Custom data field 2',
|
|
921
|
+
customData3: 'Custom data field 3',
|
|
922
|
+
customData4: 'Custom data field 4',
|
|
923
|
+
customData5: 'Custom data field 5',
|
|
924
|
+
// Usage of customData properties are supported up to 30 fields
|
|
925
|
+
customData30: 'Custom data field 30',
|
|
926
|
+
},
|
|
927
|
+
});
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
And that's it. Now you should start receiving analytics information about your `player` instance on the [Analytics Dashboard](https://bitmovin.com/dashboard/analytics).
|
|
931
|
+
|
|
932
|
+
Optionally, you can also access the configured `analyticsCollector` object in order to get some information (like `userId`) or
|
|
933
|
+
update your custom data during runtime:
|
|
934
|
+
|
|
935
|
+
```typescript
|
|
936
|
+
// Get the current user id.
|
|
937
|
+
const userId = await player.analyticsCollector?.getUserId();
|
|
938
|
+
|
|
939
|
+
// Get the current custom data config.
|
|
940
|
+
const customData = await player.analyticsCollector?.getCustomData();
|
|
941
|
+
|
|
942
|
+
// Update the current custom data config.
|
|
943
|
+
player.analyticsCollector?.setCustomDataOnce({
|
|
944
|
+
customData2: 'Updated custom data field 2',
|
|
945
|
+
customData4: 'Updated custom data field 4',
|
|
946
|
+
});
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
You can check out a complete analytics example in the [`example/`](https://github.com/bitmovin/bitmovin-player-react-native/tree/development/example) app.
|
|
950
|
+
|
|
703
951
|
## Contributing
|
|
704
952
|
|
|
705
953
|
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
|
package/RNBitmovinPlayer.podspec
CHANGED
|
@@ -19,7 +19,9 @@ 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.
|
|
22
|
+
s.dependency "BitmovinPlayer", "3.32.0"
|
|
23
|
+
s.dependency "BitmovinAnalyticsCollector/Core", "2.9.4"
|
|
24
|
+
s.dependency "BitmovinAnalyticsCollector/BitmovinPlayer", "2.9.4"
|
|
23
25
|
s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.17.0"
|
|
24
26
|
s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.6.1"
|
|
25
27
|
end
|
package/android/build.gradle
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
buildscript {
|
|
2
2
|
ext {
|
|
3
|
-
kotlinVersion = '1.7.
|
|
3
|
+
kotlinVersion = '1.7.21'
|
|
4
4
|
androidToolsVersion = '7.0.4'
|
|
5
5
|
}
|
|
6
6
|
repositories {
|
|
@@ -52,6 +52,7 @@ dependencies {
|
|
|
52
52
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
|
53
53
|
implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.26.0'
|
|
54
54
|
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
|
|
55
|
-
implementation 'com.bitmovin.
|
|
55
|
+
implementation 'com.bitmovin.analytics:collector-bitmovin-player:2.12.1'
|
|
56
|
+
implementation 'com.bitmovin.player:player:3.26.1'
|
|
56
57
|
implementation 'com.facebook.react:react-native:+'
|
|
57
58
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
package com.bitmovin.player.reactnative
|
|
2
|
+
|
|
3
|
+
import com.bitmovin.analytics.bitmovin.player.BitmovinPlayerCollector
|
|
4
|
+
import com.bitmovin.player.reactnative.converter.JsonConverter
|
|
5
|
+
import com.facebook.react.bridge.*
|
|
6
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
7
|
+
import com.facebook.react.uimanager.UIManagerModule
|
|
8
|
+
|
|
9
|
+
private const val MODULE_NAME = "AnalyticsModule"
|
|
10
|
+
|
|
11
|
+
@ReactModule(name = MODULE_NAME)
|
|
12
|
+
class AnalyticsModule(private val context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
|
|
13
|
+
/**
|
|
14
|
+
* In-memory mapping from `nativeId`s to `BitmovinPlayerCollector` instances.
|
|
15
|
+
*/
|
|
16
|
+
private val collectors: Registry<BitmovinPlayerCollector> = mutableMapOf()
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* JS exported module name.
|
|
20
|
+
*/
|
|
21
|
+
override fun getName() = MODULE_NAME
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Fetches the `BitmovinPlayerCollector` instance associated with `nativeId` from the internal registry.
|
|
25
|
+
* @param nativeId `BitmovinPlayerCollector` instance ID.
|
|
26
|
+
* @return The associated `BitmovinPlayerCollector` instance or `null`.
|
|
27
|
+
*/
|
|
28
|
+
fun getCollector(nativeId: NativeId?): BitmovinPlayerCollector? {
|
|
29
|
+
if (nativeId == null) {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
return collectors[nativeId]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates a new `BitmovinPlayerCollector` instance inside the internal registry using the provided `config` object.
|
|
37
|
+
* @param config `BitmovinAnalyticsConfig` object received from JS.
|
|
38
|
+
*/
|
|
39
|
+
@ReactMethod
|
|
40
|
+
fun initWithConfig(nativeId: NativeId, config: ReadableMap?) {
|
|
41
|
+
uiManager()?.addUIBlock { _ ->
|
|
42
|
+
JsonConverter.toAnalyticsConfig(config)?.let {
|
|
43
|
+
collectors[nativeId] = BitmovinPlayerCollector(it, context)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Detaches and removes the given `BitmovinPlayerCollector` from the internal registry.
|
|
50
|
+
* @param nativeId Native Id of the collector instance.
|
|
51
|
+
*/
|
|
52
|
+
@ReactMethod
|
|
53
|
+
fun destroy(nativeId: NativeId) {
|
|
54
|
+
uiManager()?.addUIBlock {
|
|
55
|
+
if (collectors.containsKey(nativeId)) {
|
|
56
|
+
collectors[nativeId]?.detachPlayer()
|
|
57
|
+
collectors.remove(nativeId)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Attaches a `BitmovinPlayerCollector` to the `Player` instance with native Id equal to `playerId`.
|
|
64
|
+
* @param nativeId Native Id of the collector instance.
|
|
65
|
+
* @param playerId Native Id of the player instance.
|
|
66
|
+
*/
|
|
67
|
+
@ReactMethod
|
|
68
|
+
fun attach(nativeId: NativeId, playerId: NativeId) {
|
|
69
|
+
uiManager()?.addUIBlock { _ ->
|
|
70
|
+
playerModule()?.getPlayer(playerId)?.let {
|
|
71
|
+
collectors[nativeId]?.attachPlayer(it)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Detaches the player object from a `BitmovinPlayerCollector` instance.
|
|
78
|
+
* @param nativeId Native Id of the collector instance.
|
|
79
|
+
*/
|
|
80
|
+
@ReactMethod
|
|
81
|
+
fun detach(nativeId: NativeId) {
|
|
82
|
+
uiManager()?.addUIBlock { _ ->
|
|
83
|
+
collectors[nativeId]?.detachPlayer()
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Updates the custom data config for a `BitmovinPlayerCollector` instance.
|
|
89
|
+
* @param nativeId Native Id of the collector instance.
|
|
90
|
+
* @param json Custom data config json.
|
|
91
|
+
*/
|
|
92
|
+
@ReactMethod
|
|
93
|
+
fun setCustomDataOnce(nativeId: NativeId, json: ReadableMap?) {
|
|
94
|
+
uiManager()?.addUIBlock { _ ->
|
|
95
|
+
JsonConverter.toAnalyticsCustomData(json)?.let {
|
|
96
|
+
collectors[nativeId]?.setCustomDataOnce(it)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Sets the custom data config for a `BitmovinPlayerCollector` instance.
|
|
103
|
+
* @param nativeId Native Id of the collector instance.
|
|
104
|
+
* @param json Custom data config json.
|
|
105
|
+
*/
|
|
106
|
+
@ReactMethod
|
|
107
|
+
fun setCustomData(nativeId: NativeId, json: ReadableMap?) {
|
|
108
|
+
uiManager()?.addUIBlock { _ ->
|
|
109
|
+
JsonConverter.toAnalyticsCustomData(json)?.let {
|
|
110
|
+
collectors[nativeId]?.customData = it
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Gets the current custom data config for a `BitmovinPlayerCollector` instance.
|
|
117
|
+
* @param nativeId Native Id of the the collector instance.
|
|
118
|
+
* @param promise JS promise object.
|
|
119
|
+
*/
|
|
120
|
+
@ReactMethod
|
|
121
|
+
fun getCustomData(nativeId: NativeId, promise: Promise) {
|
|
122
|
+
uiManager()?.addUIBlock { _ ->
|
|
123
|
+
collectors[nativeId]?.let {
|
|
124
|
+
promise.resolve(JsonConverter.fromAnalyticsCustomData(it.customData))
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Gets the current user Id for a `BitmovinPlayerCollector` instance.
|
|
131
|
+
* @param nativeId Native Id of the the collector instance.
|
|
132
|
+
* @param promise JS promise object.
|
|
133
|
+
*/
|
|
134
|
+
@ReactMethod
|
|
135
|
+
fun getUserId(nativeId: NativeId, promise: Promise) {
|
|
136
|
+
uiManager()?.addUIBlock { _ ->
|
|
137
|
+
collectors[nativeId]?.let {
|
|
138
|
+
promise.resolve(it.userId)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Helper function that gets the instantiated `UIManagerModule` from modules registry.
|
|
145
|
+
*/
|
|
146
|
+
private fun uiManager(): UIManagerModule? =
|
|
147
|
+
context.getNativeModule(UIManagerModule::class.java)
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Helper function that gets the instantiated `PlayerModule` from modules registry.
|
|
151
|
+
*/
|
|
152
|
+
private fun playerModule(): PlayerModule? =
|
|
153
|
+
context.getNativeModule(PlayerModule::class.java)
|
|
154
|
+
}
|
|
@@ -451,6 +451,41 @@ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context)
|
|
|
451
451
|
emitEvent("adStarted", it)
|
|
452
452
|
}
|
|
453
453
|
|
|
454
|
+
/**
|
|
455
|
+
* `onVideoPlaybackQualityChanged` event callback.
|
|
456
|
+
*/
|
|
457
|
+
private val onVideoPlaybackQualityChanged: (PlayerEvent.VideoPlaybackQualityChanged) -> Unit = {
|
|
458
|
+
emitEvent("videoPlaybackQualityChanged", it)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* `onFullscreenEnabled` event callback.
|
|
463
|
+
*/
|
|
464
|
+
private val onFullscreenEnabled: (PlayerEvent.FullscreenEnabled) -> Unit = {
|
|
465
|
+
emitEvent("fullscreenEnabled", it)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* `onFullscreenEnabled` event callback.
|
|
470
|
+
*/
|
|
471
|
+
private val onFullscreenDisabled: (PlayerEvent.FullscreenDisabled) -> Unit = {
|
|
472
|
+
emitEvent("fullscreenDisabled", it)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* `onFullscreenEnter` event callback.
|
|
477
|
+
*/
|
|
478
|
+
private val onFullscreenEnter: (PlayerEvent.FullscreenEnter) -> Unit = {
|
|
479
|
+
emitEvent("fullscreenEnter", it)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* `onFullscreenEnter` event callback.
|
|
484
|
+
*/
|
|
485
|
+
private val onFullscreenExit: (PlayerEvent.FullscreenExit) -> Unit = {
|
|
486
|
+
emitEvent("fullscreenExit", it)
|
|
487
|
+
}
|
|
488
|
+
|
|
454
489
|
/**
|
|
455
490
|
* Start listening and emitting player events as bubbling events to the js side.
|
|
456
491
|
*/
|
|
@@ -491,11 +526,16 @@ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context)
|
|
|
491
526
|
on(onAdScheduled)
|
|
492
527
|
on(onAdSkipped)
|
|
493
528
|
on(onAdStarted)
|
|
529
|
+
on(onVideoPlaybackQualityChanged)
|
|
494
530
|
}
|
|
495
531
|
playerView?.apply {
|
|
496
532
|
on(onPictureInPictureAvailabilityChanged)
|
|
497
533
|
on(onPictureInPictureEnter)
|
|
498
534
|
on(onPictureInPictureExit)
|
|
535
|
+
on(onFullscreenEnabled)
|
|
536
|
+
on(onFullscreenDisabled)
|
|
537
|
+
on(onFullscreenEnter)
|
|
538
|
+
on(onFullscreenExit)
|
|
499
539
|
}
|
|
500
540
|
}
|
|
501
541
|
|
|
@@ -539,11 +579,16 @@ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context)
|
|
|
539
579
|
off(onAdScheduled)
|
|
540
580
|
off(onAdSkipped)
|
|
541
581
|
off(onAdStarted)
|
|
582
|
+
off(onVideoPlaybackQualityChanged)
|
|
542
583
|
}
|
|
543
584
|
playerView?.apply {
|
|
544
585
|
off(onPictureInPictureAvailabilityChanged)
|
|
545
586
|
off(onPictureInPictureEnter)
|
|
546
587
|
off(onPictureInPictureExit)
|
|
588
|
+
off(onFullscreenEnabled)
|
|
589
|
+
off(onFullscreenDisabled)
|
|
590
|
+
off(onFullscreenEnter)
|
|
591
|
+
off(onFullscreenExit)
|
|
547
592
|
}
|
|
548
593
|
}
|
|
549
594
|
|
|
@@ -4,6 +4,9 @@ import android.os.Handler
|
|
|
4
4
|
import android.os.Looper
|
|
5
5
|
import android.view.ViewGroup.LayoutParams
|
|
6
6
|
import com.bitmovin.player.PlayerView
|
|
7
|
+
import com.bitmovin.player.reactnative.extensions.getModule
|
|
8
|
+
import com.bitmovin.player.reactnative.ui.FullscreenHandlerBridge
|
|
9
|
+
import com.bitmovin.player.reactnative.ui.FullscreenHandlerModule
|
|
7
10
|
import com.bitmovin.player.reactnative.ui.RNPictureInPictureHandler
|
|
8
11
|
import com.facebook.react.bridge.*
|
|
9
12
|
import com.facebook.react.module.annotations.ReactModule
|
|
@@ -19,6 +22,7 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
19
22
|
*/
|
|
20
23
|
enum class Commands {
|
|
21
24
|
ATTACH_PLAYER,
|
|
25
|
+
ATTACH_FULLSCREEN_BRIDGE
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
/**
|
|
@@ -91,6 +95,11 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
91
95
|
"adScheduled" to "onAdScheduled",
|
|
92
96
|
"adSkipped" to "onAdSkipped",
|
|
93
97
|
"adStarted" to "onAdStarted",
|
|
98
|
+
"videoPlaybackQualityChanged" to "onVideoPlaybackQualityChanged",
|
|
99
|
+
"fullscreenEnabled" to "onFullscreenEnabled",
|
|
100
|
+
"fullscreenDisabled" to "onFullscreenDisabled",
|
|
101
|
+
"fullscreenEnter" to "onFullscreenEnter",
|
|
102
|
+
"fullscreenExit" to "onFullscreenExit",
|
|
94
103
|
)
|
|
95
104
|
|
|
96
105
|
/**
|
|
@@ -112,7 +121,8 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
112
121
|
* @return map between names (used in js) and command ids (used in native code).
|
|
113
122
|
*/
|
|
114
123
|
override fun getCommandsMap(): MutableMap<String, Int> = mutableMapOf(
|
|
115
|
-
"attachPlayer" to Commands.ATTACH_PLAYER.ordinal
|
|
124
|
+
"attachPlayer" to Commands.ATTACH_PLAYER.ordinal,
|
|
125
|
+
"attachFullscreenBridge" to Commands.ATTACH_FULLSCREEN_BRIDGE.ordinal,
|
|
116
126
|
)
|
|
117
127
|
|
|
118
128
|
/**
|
|
@@ -126,11 +136,22 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
126
136
|
commandId?.toInt()?.let {
|
|
127
137
|
when (it) {
|
|
128
138
|
Commands.ATTACH_PLAYER.ordinal -> attachPlayer(view, args?.getString(1), args?.getMap(2))
|
|
139
|
+
Commands.ATTACH_FULLSCREEN_BRIDGE.ordinal -> args?.getString(1)?.let { fullscreenBridgeId ->
|
|
140
|
+
attachFullscreenBridge(view, fullscreenBridgeId)
|
|
141
|
+
}
|
|
129
142
|
else -> {}
|
|
130
143
|
}
|
|
131
144
|
}
|
|
132
145
|
}
|
|
133
146
|
|
|
147
|
+
private fun attachFullscreenBridge(view: RNPlayerView, fullscreenBridgeId: NativeId) {
|
|
148
|
+
Handler(Looper.getMainLooper()).post {
|
|
149
|
+
view.playerView?.setFullscreenHandler(
|
|
150
|
+
context.getModule<FullscreenHandlerModule>()?.getInstance(fullscreenBridgeId)
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
134
155
|
/**
|
|
135
156
|
* Set the `Player` instance for the target view using `playerId`.
|
|
136
157
|
* @param view Target `RNPlayerView`.
|
|
@@ -149,7 +170,8 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
149
170
|
val playerView = PlayerView(context, player)
|
|
150
171
|
playerView.layoutParams = LayoutParams(
|
|
151
172
|
LayoutParams.MATCH_PARENT,
|
|
152
|
-
LayoutParams.MATCH_PARENT
|
|
173
|
+
LayoutParams.MATCH_PARENT
|
|
174
|
+
)
|
|
153
175
|
view.addPlayerView(playerView)
|
|
154
176
|
}
|
|
155
177
|
}
|
|
@@ -158,6 +180,5 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
158
180
|
/**
|
|
159
181
|
* Helper function that gets the instantiated `PlayerModule` from modules registry.
|
|
160
182
|
*/
|
|
161
|
-
private fun getPlayerModule(): PlayerModule? =
|
|
162
|
-
context.getNativeModule(PlayerModule::class.java)
|
|
183
|
+
private fun getPlayerModule(): PlayerModule? = context.getModule()
|
|
163
184
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.bitmovin.player.reactnative
|
|
2
2
|
|
|
3
3
|
import android.view.View
|
|
4
|
+
import com.bitmovin.player.reactnative.ui.FullscreenHandlerModule
|
|
4
5
|
import com.facebook.react.ReactPackage
|
|
5
6
|
import com.facebook.react.bridge.NativeModule
|
|
6
7
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
@@ -21,7 +22,9 @@ class RNPlayerViewPackage : ReactPackage {
|
|
|
21
22
|
PlayerModule(reactContext),
|
|
22
23
|
SourceModule(reactContext),
|
|
23
24
|
DrmModule(reactContext),
|
|
25
|
+
AnalyticsModule(reactContext),
|
|
24
26
|
RNPlayerViewManager(reactContext),
|
|
27
|
+
FullscreenHandlerModule(reactContext)
|
|
25
28
|
)
|
|
26
29
|
}
|
|
27
30
|
|