├── .eslintrc
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ ├── implement.md
│ └── question.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .npmignore
├── CODE_OF_CONDUCT.md
├── Example
├── .babelrc
├── .editorconfig
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── .npmignore
├── .prettierrc.js
├── .watchmanconfig
├── Gemfile
├── Gemfile.lock
├── __tests__
│ └── App-test.tsx
├── android
│ ├── app
│ │ ├── build.gradle
│ │ ├── debug.keystore
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── assets
│ │ │ └── local_file.mp4
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── rnjwplayer
│ │ │ │ ├── CastOptionsProvider.java
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── MainApplication.kt
│ │ │ └── res
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_adaptive_back.png
│ │ │ ├── ic_launcher_adaptive_fore.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_round_adaptive_back.png
│ │ │ └── ic_launcher_round_adaptive_fore.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_adaptive_back.png
│ │ │ ├── ic_launcher_adaptive_fore.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_round_adaptive_back.png
│ │ │ └── ic_launcher_round_adaptive_fore.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_adaptive_back.png
│ │ │ ├── ic_launcher_adaptive_fore.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_round_adaptive_back.png
│ │ │ └── ic_launcher_round_adaptive_fore.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_adaptive_back.png
│ │ │ ├── ic_launcher_adaptive_fore.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_round_adaptive_back.png
│ │ │ └── ic_launcher_round_adaptive_fore.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_adaptive_back.png
│ │ │ ├── ic_launcher_adaptive_fore.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_launcher_round_adaptive_back.png
│ │ │ └── ic_launcher_round_adaptive_fore.png
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
├── app.json
├── app
│ ├── jsx
│ │ ├── App.tsx
│ │ ├── components
│ │ │ ├── AnimatedPlayer
│ │ │ │ ├── index.js
│ │ │ │ └── player.style.js
│ │ │ ├── Player.js
│ │ │ └── PlayerContainer.js
│ │ └── screens
│ │ │ ├── DRMExample.js
│ │ │ ├── Home.js
│ │ │ ├── ListExample.js
│ │ │ ├── LocalFileExample.js
│ │ │ ├── OnBeforeNextPlaylistItemExample.js
│ │ │ ├── PlayerInModal.js
│ │ │ ├── SingleExample.js
│ │ │ ├── SourcesExample.js
│ │ │ └── YoutubeExample.js
│ └── ui
│ │ └── styles
│ │ └── global.style.js
├── babel.config.js
├── index.js
├── ios
│ ├── .xcode.env
│ ├── AppDelegate.swift
│ ├── Podfile
│ ├── Podfile.lock
│ ├── RNJWPlayer.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── RNJWPlayer.xcscheme
│ ├── RNJWPlayer.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── RNJWPlayer
│ │ ├── Images.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── JW-rw-Logo_1024-1024.png
│ │ │ │ ├── JW-rw-Logo_1024-20.png
│ │ │ │ ├── JW-rw-Logo_1024-20@2x.png
│ │ │ │ ├── JW-rw-Logo_1024-20@3x.png
│ │ │ │ ├── JW-rw-Logo_1024-29.png
│ │ │ │ ├── JW-rw-Logo_1024-29@2x.png
│ │ │ │ ├── JW-rw-Logo_1024-29@3x.png
│ │ │ │ ├── JW-rw-Logo_1024-40.png
│ │ │ │ ├── JW-rw-Logo_1024-40@2x.png
│ │ │ │ ├── JW-rw-Logo_1024-40@3x.png
│ │ │ │ ├── JW-rw-Logo_1024-60@2x.png
│ │ │ │ ├── JW-rw-Logo_1024-60@3x.png
│ │ │ │ ├── JW-rw-Logo_1024-76.png
│ │ │ │ ├── JW-rw-Logo_1024-76@2x.png
│ │ │ │ └── JW-rw-Logo_1024-83.5@2x.png
│ │ │ ├── Contents.json
│ │ │ └── jwlogo.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── jwlogo-sketch.png
│ │ │ │ └── jwlogo.png
│ │ ├── Info.plist
│ │ ├── LaunchScreen.storyboard
│ │ └── PrivacyInfo.xcprivacy
│ └── local_file.mp4
├── jest.config.js
├── metro.config.js
├── package-lock.json
├── package.json
├── tsconfig.json
└── yarn.lock
├── LICENSE
├── README.md
├── RNJWPlayer.podspec
├── android
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── jwplayer
│ └── rnjwplayer
│ ├── ArrayUtil.java
│ ├── CastOptionsProvider.java
│ ├── MapUtil.java
│ ├── RNJWPlayer.java
│ ├── RNJWPlayerAds.java
│ ├── RNJWPlayerModule.java
│ ├── RNJWPlayerPackage.java
│ ├── RNJWPlayerView.java
│ ├── RNJWPlayerViewManager.java
│ ├── Util.java
│ └── WidevineCallback.java
├── badges
├── license.svg
└── version.svg
├── docs
├── legacy_readme.md
├── props.md
└── types.md
├── images
├── 1.png
├── 2.png
└── 3.png
├── index.d.ts
├── index.js
├── ios
├── RNJWPlayer.xcodeproj
│ └── project.pbxproj
└── RNJWPlayer
│ ├── RCTConvert+RNJWPlayer.swift
│ ├── RNJWPlayer-Bridging-Header.h
│ ├── RNJWPlayerAds.swift
│ ├── RNJWPlayerModels.swift
│ ├── RNJWPlayerView.swift
│ ├── RNJWPlayerViewController.swift
│ ├── RNJWPlayerViewManager.m
│ └── RNJWPlayerViewManager.swift
├── package.json
└── yarn.lock
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "parser": "babel-eslint",
4 | "plugins": [
5 | "react",
6 | "react-native"
7 | ],
8 | "ecmaFeatures": {
9 | "jsx": true,
10 | "es6": true,
11 | "classes": true
12 | },
13 | "rules": {
14 | "comma-dangle": [1, "always-multiline"],
15 | "no-underscore-dangle" : 0,
16 | "max-len": [1, 180, 4],
17 | "arrow-body-style": [0],
18 | "no-param-reassign": ["error", { "props": false }],
19 | "react/require-extension": "off",
20 | "react-native/no-unused-styles": 2,
21 | "react-native/split-platform-components": 2,
22 | "react-native/no-inline-styles": 2,
23 | "react-native/no-color-literals": 2,
24 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
25 | "react/prefer-stateless-function": [0],
26 | "jsx-quotes": ["error", "prefer-single"],
27 | "no-unused-expressions": ["error", { "allowShortCircuit": true }]
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # SDK Team
2 | * @jwplayer/team_sdks
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: "needs-grooming"
6 | assignees: '@jwplayer/team_sdks'
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Use the `Example` application as reference material and a starting place to reproduce any bugs.
15 |
16 | ***Steps to reproduce the behavior:***
17 | 1. Provide thorough
18 | 2. And complete
19 | 3. Steps for reproduction
20 | 4. That are reliably reproducible
21 |
22 | **Expected behavior**
23 | A clear and concise description of what you expected to happen.
24 |
25 | **Screenshots / Visual evidence**
26 | If applicable, add screenshots or recordings to help explain your problem. (Required if reproduction is not 100% reliable)
27 |
28 | **Desktop (please complete the following information):**
29 | If you are having a build issue, we would like to know about your machine.
30 | - Result of `npx react-native info`
31 |
32 | **Device(s) affected**
33 | - Device: [e.g. iPhone6]
34 | - OS: [e.g. iOS8.1]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
40 | **JWP Ticketing**
41 | To expedite resolution and maintain confidentiality, submit a JWP [request](https://support.jwplayer.com/hc/en-us/requests/new) in addition to this new `Issue`. A JWP request offers the following benefits:
42 | * Associates the `Issue` with your company
43 | * Permits securely share sensitive information related to the bug, request, or question
44 | * Enhances JWP's ability to track progress and provide timely updates
45 | * Enables you to track and manage multiple issues through a single platform
46 |
47 | | ℹ️ While `Issues` are valuable for open-source collaboration, a JWP request ensures that you will receive clear timelines and efficient support. |
48 | |:---|
49 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[FEAT]"
5 | labels: 'needs-grooming'
6 | assignees: '@jwplayer/team_sdks'
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
22 | **JWP Ticketing**
23 | To expedite resolution and maintain confidentiality, submit a JWP [request](https://support.jwplayer.com/hc/en-us/requests/new) in addition to this new `Issue`. A JWP request offers the following benefits:
24 | * Associates the `Issue` with your company
25 | * Permits securely share sensitive information related to the bug, request, or question
26 | * Enhances JWP's ability to track progress and provide timely updates
27 | * Enables you to track and manage multiple issues through a single platform
28 |
29 | | ℹ️ While `Issues` are valuable for open-source collaboration, a JWP request ensures that you will receive clear timelines and efficient support. |
30 | |:---|
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/implement.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Implementation
3 | about: Request an implementation of a native feature or upgrade of SDK
4 | title: "[IMPLEMENT]"
5 | labels: 'needs-grooming'
6 | assignees: '@jwplayer/team_sdks'
7 |
8 | ---
9 |
10 | **What needs to be implemented**
11 |
12 | **How should it be implemented**
13 |
14 | **Acceptance Criteria**
15 |
16 | **Additional context**
17 |
18 | **JWP Ticketing**
19 | To expedite resolution and maintain confidentiality, submit a JWP [request](https://support.jwplayer.com/hc/en-us/requests/new) in addition to this new `Issue`. A JWP request offers the following benefits:
20 | * Associates the `Issue` with your company
21 | * Permits securely share sensitive information related to the bug, request, or question
22 | * Enhances JWP's ability to track progress and provide timely updates
23 | * Enables you to track and manage multiple issues through a single platform
24 |
25 | | ℹ️ While `Issues` are valuable for open-source collaboration, a JWP request ensures that you will receive clear timelines and efficient support. |
26 | |:---|
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Question
3 | about: Ask a question about the project or product
4 | title: "[ASK]"
5 | labels: 'needs-grooming'
6 | assignees: '@jwplayer/team_sdks'
7 |
8 | ---
9 |
10 | **What isn't clear?**
11 | What information are we not providing that you think we should?
12 |
13 | **JWP Ticketing**
14 | To expedite resolution and maintain confidentiality, submit a JWP [request](https://support.jwplayer.com/hc/en-us/requests/new) in addition to this new `Issue`. A JWP request offers the following benefits:
15 | * Associates the `Issue` with your company
16 | * Permits securely share sensitive information related to the bug, request, or question
17 | * Enhances JWP's ability to track progress and provide timely updates
18 | * Enables you to track and manage multiple issues through a single platform
19 |
20 | | ℹ️ While `Issues` are valuable for open-source collaboration, a JWP request ensures that you will receive clear timelines and efficient support. |
21 | |:---|
22 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### What does this Pull Request do?
2 |
3 |
4 | ### Why is this Pull Request needed?
5 |
6 |
7 | ### Are there any points in the code the reviewer needs to double check?
8 |
9 |
10 | ### Are there any Pull Requests open in other repos which need to be merged with this?
11 |
12 |
13 | #### Addresses Issue(s):
14 |
15 | [GitHub Issue](https://github.com/jwplayer/jwplayer-react-native/issues/###)
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 | .history
5 |
6 | # Xcode
7 | #
8 | build/
9 | *.pbxuser
10 | !default.pbxuser
11 | *.mode1v3
12 | !default.mode1v3
13 | *.mode2v3
14 | !default.mode2v3
15 | *.perspectivev3
16 | !default.perspectivev3
17 | xcuserdata
18 | *.xccheckout
19 | *.moved-aside
20 | DerivedData
21 | *.hmap
22 | *.ipa
23 | *.xcuserstate
24 | project.xcworkspace
25 | ios/Pods
26 | ios/Podfile.lock
27 |
28 |
29 | # Android/IJ
30 | #
31 | *.iml
32 | .idea
33 | .gradle
34 | local.properties
35 | .classpath
36 | .project
37 | .settings/
38 |
39 | # node.js
40 | #
41 | node_modules/
42 | npm-debug.log
43 | package-lock.json
44 |
45 | # yarn
46 | yarn-error.log
47 |
48 | # BUCK
49 | buck-out/
50 | \.buckd/
51 | android/app/libs
52 | android/keystores/debug.keystore
53 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | Example
2 | images
3 | android/build
4 | android/.settings
5 | android/.classpath
6 | android/.project
7 | *.iml
8 | .eslintrc
9 | yarn-error.log
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | conduct@jwplayer.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
--------------------------------------------------------------------------------
/Example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | ["module:react-native-dotenv"]
4 | ]
5 | }
--------------------------------------------------------------------------------
/Example/.editorconfig:
--------------------------------------------------------------------------------
1 | # Windows files
2 | [*.bat]
3 | end_of_line = crlf
4 |
--------------------------------------------------------------------------------
/Example/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: '@react-native',
4 | };
5 |
--------------------------------------------------------------------------------
/Example/.gitattributes:
--------------------------------------------------------------------------------
1 | # Windows files should use crlf line endings
2 | # https://help.github.com/articles/dealing-with-line-endings/
3 | *.bat text eol=crlf
4 |
--------------------------------------------------------------------------------
/Example/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | **/.xcode.env.local
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 | .cxx/
34 | *.keystore
35 | !debug.keystore
36 | .kotlin/
37 |
38 | # node.js
39 | #
40 | node_modules/
41 | npm-debug.log
42 | yarn-error.log
43 |
44 | # fastlane
45 | #
46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
47 | # screenshots whenever they are needed.
48 | # For more information about the recommended setup visit:
49 | # https://docs.fastlane.tools/best-practices/source-control/
50 |
51 | */fastlane/report.xml
52 | */fastlane/Preview.html
53 | */fastlane/screenshots
54 | **/fastlane/test_output
55 |
56 | # Bundle artifact
57 | *.jsbundle
58 |
59 | # Ruby / CocoaPods
60 | **/Pods/
61 | /vendor/bundle/
62 |
63 | # Temporary files created by Metro to check the health of the file watcher
64 | .metro-health-check*
65 |
66 | # testing
67 | /coverage
68 |
69 | # Yarn
70 | .yarn/*
71 | !.yarn/patches
72 | !.yarn/plugins
73 | !.yarn/releases
74 | !.yarn/sdks
75 | !.yarn/versions
76 |
77 | # Environment
78 | .env*
--------------------------------------------------------------------------------
/Example/.npmignore:
--------------------------------------------------------------------------------
1 | Example
2 | images
--------------------------------------------------------------------------------
/Example/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | bracketSpacing: false,
3 | jsxBracketSameLine: true,
4 | singleQuote: true,
5 | trailingComma: 'all',
6 | arrowParens: 'avoid',
7 | };
8 |
--------------------------------------------------------------------------------
/Example/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/Example/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
4 | ruby ">= 2.6.10"
5 |
6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures.
7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
9 | gem 'xcodeproj', '< 1.26.0'
10 | gem 'concurrent-ruby', '< 1.3.4'
--------------------------------------------------------------------------------
/Example/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.6)
5 | rexml
6 | activesupport (7.0.8)
7 | concurrent-ruby (~> 1.0, >= 1.0.2)
8 | i18n (>= 1.6, < 2)
9 | minitest (>= 5.1)
10 | tzinfo (~> 2.0)
11 | addressable (2.8.6)
12 | public_suffix (>= 2.0.2, < 6.0)
13 | algoliasearch (1.27.5)
14 | httpclient (~> 2.8, >= 2.8.3)
15 | json (>= 1.5.1)
16 | atomos (0.1.3)
17 | claide (1.1.0)
18 | cocoapods (1.14.3)
19 | addressable (~> 2.8)
20 | claide (>= 1.0.2, < 2.0)
21 | cocoapods-core (= 1.14.3)
22 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
23 | cocoapods-downloader (>= 2.1, < 3.0)
24 | cocoapods-plugins (>= 1.0.0, < 2.0)
25 | cocoapods-search (>= 1.0.0, < 2.0)
26 | cocoapods-trunk (>= 1.6.0, < 2.0)
27 | cocoapods-try (>= 1.1.0, < 2.0)
28 | colored2 (~> 3.1)
29 | escape (~> 0.0.4)
30 | fourflusher (>= 2.3.0, < 3.0)
31 | gh_inspector (~> 1.0)
32 | molinillo (~> 0.8.0)
33 | nap (~> 1.0)
34 | ruby-macho (>= 2.3.0, < 3.0)
35 | xcodeproj (>= 1.23.0, < 2.0)
36 | cocoapods-core (1.14.3)
37 | activesupport (>= 5.0, < 8)
38 | addressable (~> 2.8)
39 | algoliasearch (~> 1.0)
40 | concurrent-ruby (~> 1.1)
41 | fuzzy_match (~> 2.0.4)
42 | nap (~> 1.0)
43 | netrc (~> 0.11)
44 | public_suffix (~> 4.0)
45 | typhoeus (~> 1.0)
46 | cocoapods-deintegrate (1.0.5)
47 | cocoapods-downloader (2.1)
48 | cocoapods-plugins (1.0.0)
49 | nap
50 | cocoapods-search (1.0.1)
51 | cocoapods-trunk (1.6.0)
52 | nap (>= 0.8, < 2.0)
53 | netrc (~> 0.11)
54 | cocoapods-try (1.2.0)
55 | colored2 (3.1.2)
56 | concurrent-ruby (1.2.2)
57 | escape (0.0.4)
58 | ethon (0.16.0)
59 | ffi (>= 1.15.0)
60 | ffi (1.16.3)
61 | fourflusher (2.3.1)
62 | fuzzy_match (2.0.4)
63 | gh_inspector (1.1.3)
64 | httpclient (2.8.3)
65 | i18n (1.14.1)
66 | concurrent-ruby (~> 1.0)
67 | json (2.7.1)
68 | minitest (5.20.0)
69 | molinillo (0.8.0)
70 | nanaimo (0.3.0)
71 | nap (1.1.0)
72 | netrc (0.11.0)
73 | public_suffix (4.0.7)
74 | rexml (3.2.6)
75 | ruby-macho (2.5.1)
76 | typhoeus (1.4.1)
77 | ethon (>= 0.9.0)
78 | tzinfo (2.0.6)
79 | concurrent-ruby (~> 1.0)
80 | xcodeproj (1.23.0)
81 | CFPropertyList (>= 2.3.3, < 4.0)
82 | atomos (~> 0.1.3)
83 | claide (>= 1.0.2, < 2.0)
84 | colored2 (~> 3.1)
85 | nanaimo (~> 0.3.0)
86 | rexml (~> 3.2.4)
87 |
88 | PLATFORMS
89 | x86_64-darwin-21
90 |
91 | DEPENDENCIES
92 | activesupport (>= 6.1.7.3, < 7.1.0)
93 | cocoapods (>= 1.11.3)
94 |
95 | RUBY VERSION
96 | ruby 3.1.2p20
97 |
98 | BUNDLED WITH
99 | 2.4.3
100 |
--------------------------------------------------------------------------------
/Example/__tests__/App-test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import React from 'react';
6 | import ReactTestRenderer from 'react-test-renderer';
7 | import App from '../App';
8 |
9 | test('renders correctly', async () => {
10 | await ReactTestRenderer.act(() => {
11 | ReactTestRenderer.create();
12 | });
13 | });
--------------------------------------------------------------------------------
/Example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "org.jetbrains.kotlin.android"
3 | apply plugin: "com.facebook.react"
4 |
5 | /**
6 | * This is the configuration block to customize your React Native Android app.
7 | * By default you don't need to apply any configuration, just uncomment the lines you need.
8 | */
9 | react {
10 | /* Folders */
11 | // The root of your project, i.e. where "package.json" lives. Default is '../..'
12 | // root = file("../../")
13 | // The folder where the react-native NPM package is. Default is ../../node_modules/react-native
14 | // reactNativeDir = file("../../node_modules/react-native")
15 | // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
16 | // codegenDir = file("../../node_modules/@react-native/codegen")
17 | // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
18 | // cliFile = file("../../node_modules/react-native/cli.js")
19 |
20 | /* Variants */
21 | // The list of variants to that are debuggable. For those we're going to
22 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
23 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
24 | // debuggableVariants = ["liteDebug", "prodDebug"]
25 |
26 | /* Bundling */
27 | // A list containing the node command and its flags. Default is just 'node'.
28 | // nodeExecutableAndArgs = ["node"]
29 | //
30 | // The command to run when bundling. By default is 'bundle'
31 | // bundleCommand = "ram-bundle"
32 | //
33 | // The path to the CLI configuration file. Default is empty.
34 | // bundleConfig = file(../rn-cli.config.js)
35 | //
36 | // The name of the generated asset file containing your JS bundle
37 | // bundleAssetName = "MyApplication.android.bundle"
38 | //
39 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
40 | // entryFile = file("../js/MyApplication.android.js")
41 | //
42 | // A list of extra flags to pass to the 'bundle' commands.
43 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
44 | // extraPackagerArgs = []
45 |
46 | /* Hermes Commands */
47 | // The hermes compiler command to run. By default it is 'hermesc'
48 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
49 | //
50 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
51 | // hermesFlags = ["-O", "-output-source-map"]
52 |
53 | /* Autolinking */
54 | autolinkLibrariesWithApp()
55 | }
56 |
57 | /**
58 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
59 | */
60 | def enableProguardInReleaseBuilds = false
61 |
62 | /**
63 | * The preferred build flavor of JavaScriptCore (JSC)
64 | *
65 | * For example, to use the international variant, you can use:
66 | * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+`
67 | *
68 | * The international variant includes ICU i18n library and necessary data
69 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
70 | * give correct results when using with locales other than en-US. Note that
71 | * this variant is about 6MiB larger per architecture than default.
72 | */
73 | def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
74 |
75 | android {
76 | ndkVersion rootProject.ext.ndkVersion
77 | buildToolsVersion rootProject.ext.buildToolsVersion
78 | compileSdk rootProject.ext.compileSdkVersion
79 |
80 | namespace "com.rnjwplayer"
81 | defaultConfig {
82 | applicationId "com.rnjwplayer"
83 | minSdkVersion rootProject.ext.minSdkVersion
84 | targetSdkVersion rootProject.ext.targetSdkVersion
85 | versionCode 1
86 | versionName "1.0"
87 | multiDexEnabled true
88 | }
89 | signingConfigs {
90 | debug {
91 | storeFile file('debug.keystore')
92 | storePassword 'android'
93 | keyAlias 'androiddebugkey'
94 | keyPassword 'android'
95 | }
96 | }
97 | buildTypes {
98 | debug {
99 | signingConfig signingConfigs.debug
100 | }
101 | release {
102 | // Caution! In production, you need to generate your own keystore file.
103 | // see https://reactnative.dev/docs/signed-apk-android.
104 | signingConfig signingConfigs.debug
105 | minifyEnabled enableProguardInReleaseBuilds
106 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
107 | }
108 | }
109 | }
110 |
111 | dependencies {
112 | // The version of react-native is set by the React Native Gradle Plugin
113 | implementation("com.facebook.react:react-android")
114 |
115 | if (hermesEnabled.toBoolean()) {
116 | implementation("com.facebook.react:hermes-android")
117 | } else {
118 | implementation jscFlavor
119 | }
120 |
121 | implementation "androidx.core:core-splashscreen:1.0.1"
122 | implementation 'com.google.android.gms:play-services-cast-framework:21.3.0'
123 | }
124 |
125 | apply from: file("../../node_modules/react-native-vector-icons/fonts.gradle")
126 |
--------------------------------------------------------------------------------
/Example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/debug.keystore
--------------------------------------------------------------------------------
/Example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/Example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/assets/local_file.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/assets/local_file.mp4
--------------------------------------------------------------------------------
/Example/android/app/src/main/java/com/rnjwplayer/CastOptionsProvider.java:
--------------------------------------------------------------------------------
1 | package com.rnjwplayer;
2 |
3 | import android.content.Context;
4 |
5 | import com.google.android.gms.cast.CastMediaControlIntent;
6 | import com.google.android.gms.cast.LaunchOptions;
7 | import com.google.android.gms.cast.framework.CastOptions;
8 | import com.google.android.gms.cast.framework.OptionsProvider;
9 | import com.google.android.gms.cast.framework.SessionProvider;
10 | import com.google.android.gms.cast.framework.media.CastMediaOptions;
11 | import com.google.android.gms.cast.framework.media.MediaIntentReceiver;
12 | import com.google.android.gms.cast.framework.media.NotificationOptions;
13 |
14 | import java.util.Arrays;
15 | import java.util.List;
16 | import java.util.Locale;
17 |
18 | public class CastOptionsProvider implements OptionsProvider {
19 |
20 | /**
21 | * The Application Id to use, currently the Default Media Receiver.
22 | */
23 | private static final String DEFAULT_APPLICATION_ID = CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID;
24 |
25 | @Override
26 | public CastOptions getCastOptions(Context context) {
27 | final NotificationOptions notificationOptions = new NotificationOptions.Builder()
28 | .setActions(Arrays.asList(
29 | MediaIntentReceiver.ACTION_SKIP_NEXT,
30 | MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK,
31 | MediaIntentReceiver.ACTION_STOP_CASTING), new int[] { 1, 2 })
32 | .setTargetActivityClassName(MainActivity.class.getName())
33 | .build();
34 |
35 | final CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
36 | .setNotificationOptions(notificationOptions)
37 | .build();
38 |
39 | final LaunchOptions launchOptions = new LaunchOptions.Builder()
40 | .setLocale(Locale.US)
41 | .build();
42 |
43 | return new CastOptions.Builder()
44 | .setReceiverApplicationId(DEFAULT_APPLICATION_ID)
45 | .setCastMediaOptions(mediaOptions)
46 | .setLaunchOptions(launchOptions)
47 | .build();
48 | }
49 |
50 | @Override
51 | public List getAdditionalSessionProviders(Context appContext) {
52 | return null;
53 | }
54 | }
--------------------------------------------------------------------------------
/Example/android/app/src/main/java/com/rnjwplayer/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.rnjwplayer
2 |
3 | import android.content.Intent
4 | import android.content.res.Configuration
5 | import com.facebook.react.ReactActivity
6 | import com.facebook.react.ReactActivityDelegate
7 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
8 | import com.facebook.react.defaults.DefaultReactActivityDelegate
9 | import com.zoontek.rnbootsplash.RNBootSplash
10 |
11 | class MainActivity : ReactActivity() {
12 | /**
13 | * Returns the name of the main component registered from JavaScript. This is used to schedule
14 | * rendering of the component.
15 | */
16 | override fun getMainComponentName(): String = "RNJWPlayer"
17 |
18 | /**
19 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
20 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
21 | */
22 | override fun createReactActivityDelegate(): ReactActivityDelegate =
23 | MainActivityDelegate(this, mainComponentName, fabricEnabled)
24 |
25 | class MainActivityDelegate(
26 | activity: ReactActivity,
27 | mainComponentName: String,
28 | fabricEnabled: Boolean
29 | ) :
30 | DefaultReactActivityDelegate(activity, mainComponentName, fabricEnabled) {
31 | override fun loadApp(appKey: String) {
32 | RNBootSplash.init(plainActivity, R.style.BootTheme)
33 | super.loadApp(appKey)
34 | }
35 | }
36 |
37 | override fun onConfigurationChanged(newConfig: Configuration) {
38 | super.onConfigurationChanged(newConfig)
39 | val intent = Intent("onConfigurationChanged")
40 | intent.putExtra("newConfig", newConfig)
41 | this.sendBroadcast(intent)
42 | }
43 |
44 | override fun onPictureInPictureModeChanged(
45 | isInPictureInPictureMode: Boolean,
46 | newConfig: Configuration
47 | ) {
48 | super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
49 | val intent = Intent("onPictureInPictureModeChanged")
50 | intent.putExtra("isInPictureInPictureMode", isInPictureInPictureMode)
51 | intent.putExtra("newConfig", newConfig)
52 | this.sendBroadcast(intent)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/java/com/rnjwplayer/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package com.rnjwplayer
2 |
3 | import android.app.Application
4 | import com.facebook.react.PackageList
5 | import com.facebook.react.ReactApplication
6 | import com.facebook.react.ReactHost
7 | import com.facebook.react.ReactNativeHost
8 | import com.facebook.react.ReactPackage
9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
11 | import com.facebook.react.defaults.DefaultReactNativeHost
12 | import com.facebook.react.soloader.OpenSourceMergedSoMapping
13 | import com.facebook.soloader.SoLoader
14 |
15 | class MainApplication : Application(), ReactApplication {
16 |
17 | override val reactNativeHost: ReactNativeHost =
18 | object : DefaultReactNativeHost(this) {
19 | override fun getPackages(): List =
20 | PackageList(this).packages.apply {
21 | // Packages that cannot be autolinked yet can be added manually here, for example:
22 | // add(MyReactNativePackage())
23 | }
24 |
25 | override fun getJSMainModuleName(): String = "index"
26 |
27 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
28 |
29 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
30 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
31 | }
32 |
33 | override val reactHost: ReactHost
34 | get() = getDefaultReactHost(applicationContext, reactNativeHost)
35 |
36 | override fun onCreate() {
37 | super.onCreate()
38 | SoLoader.init(this, OpenSourceMergedSoMapping)
39 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
40 | // If you opted-in for the New Architecture, we load the native entry point for this app.
41 | load()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round_adaptive_back.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round_adaptive_fore.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round_adaptive_back.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round_adaptive_fore.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round_adaptive_back.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round_adaptive_fore.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round_adaptive_back.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round_adaptive_fore.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_adaptive_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_adaptive_back.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_adaptive_fore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_adaptive_fore.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 | #EC0041
5 | #E02929
6 |
7 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RNJWPlayer
3 |
4 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | buildToolsVersion = "35.0.0"
4 | minSdkVersion = 24
5 | compileSdkVersion = 35
6 | targetSdkVersion = 35
7 | ndkVersion = "27.1.12297006"
8 | kotlinVersion = "2.0.21"
9 | RNJWPlayerUseGoogleCast = true
10 | RNJWPlayerUseGoogleIMA = true
11 | }
12 | repositories {
13 | google()
14 | mavenCentral()
15 | }
16 | dependencies {
17 | classpath('com.android.tools.build:gradle')
18 | classpath("com.facebook.react:react-native-gradle-plugin")
19 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
20 | }
21 | }
22 |
23 | allprojects {
24 | repositories {
25 | google()
26 | mavenCentral()
27 | maven {
28 | url 'https://mvn.jwplayer.com/content/repositories/releases/'
29 | }
30 | }
31 | }
32 |
33 | apply plugin: "com.facebook.react.rootproject"
34 |
--------------------------------------------------------------------------------
/Example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 |
25 | # Use this property to specify which architecture you want to build.
26 | # You can also override it from the CLI using
27 | # ./gradlew -PreactNativeArchitectures=x86_64
28 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
29 |
30 | # Use this property to enable support to the new architecture.
31 | # This will allow you to use TurboModules and the Fabric render in
32 | # your application. You should enable this flag either if you want
33 | # to write custom TurboModules/Fabric components OR use libraries that
34 | # are providing them.
35 | newArchEnabled=true
36 |
37 | # Use this property to enable or disable the Hermes JS engine.
38 | # If set to false, you will be using JSC instead.
39 | hermesEnabled=true
--------------------------------------------------------------------------------
/Example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/Example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
--------------------------------------------------------------------------------
/Example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$( ls -ld "$app_path" )
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | if ! command -v java >/dev/null 2>&1
137 | then
138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
139 |
140 | Please set the JAVA_HOME variable in your environment to match the
141 | location of your Java installation."
142 | fi
143 | fi
144 |
145 | # Increase the maximum file descriptors if we can.
146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
147 | case $MAX_FD in #(
148 | max*)
149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
150 | # shellcheck disable=SC2039,SC3045
151 | MAX_FD=$( ulimit -H -n ) ||
152 | warn "Could not query maximum file descriptor limit"
153 | esac
154 | case $MAX_FD in #(
155 | '' | soft) :;; #(
156 | *)
157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
158 | # shellcheck disable=SC2039,SC3045
159 | ulimit -n "$MAX_FD" ||
160 | warn "Could not set maximum file descriptor limit to $MAX_FD"
161 | esac
162 | fi
163 |
164 | # Collect all arguments for the java command, stacking in reverse order:
165 | # * args from the command line
166 | # * the main class name
167 | # * -classpath
168 | # * -D...appname settings
169 | # * --module-path (only if needed)
170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
171 |
172 | # For Cygwin or MSYS, switch paths to Windows format before running java
173 | if "$cygwin" || "$msys" ; then
174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
176 |
177 | JAVACMD=$( cygpath --unix "$JAVACMD" )
178 |
179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
180 | for arg do
181 | if
182 | case $arg in #(
183 | -*) false ;; # don't mess with options #(
184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
185 | [ -e "$t" ] ;; #(
186 | *) false ;;
187 | esac
188 | then
189 | arg=$( cygpath --path --ignore --mixed "$arg" )
190 | fi
191 | # Roll the args list around exactly as many times as the number of
192 | # args, so each arg winds up back in the position where it started, but
193 | # possibly modified.
194 | #
195 | # NB: a `for` loop captures its iteration list before it begins, so
196 | # changing the positional parameters here affects neither the number of
197 | # iterations, nor the values presented in `arg`.
198 | shift # remove old arg
199 | set -- "$@" "$arg" # push replacement arg
200 | done
201 | fi
202 |
203 |
204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
206 |
207 | # Collect all arguments for the java command:
208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
209 | # and any embedded shellness will be escaped.
210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
211 | # treated as '${Hostname}' itself on the command line.
212 |
213 | set -- \
214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
215 | -classpath "$CLASSPATH" \
216 | org.gradle.wrapper.GradleWrapperMain \
217 | "$@"
218 |
219 | # Stop when "xargs" is not available.
220 | if ! command -v xargs >/dev/null 2>&1
221 | then
222 | die "xargs is not available"
223 | fi
224 |
225 | # Use "xargs" to parse quoted args.
226 | #
227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
228 | #
229 | # In Bash we could simply go:
230 | #
231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
232 | # set -- "${ARGS[@]}" "$@"
233 | #
234 | # but POSIX shell has neither arrays nor command substitution, so instead we
235 | # post-process each arg (as a line of input to sed) to backslash-escape any
236 | # character that might be a shell metacharacter, then use eval to reverse
237 | # that process (while maintaining the separation between arguments), and wrap
238 | # the whole thing up as a single "set" statement.
239 | #
240 | # This will of course break if any of these variables contains a newline or
241 | # an unmatched quote.
242 | #
243 |
244 | eval "set -- $(
245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
246 | xargs -n1 |
247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
248 | tr '\n' ' '
249 | )" '"$@"'
250 |
251 | exec "$JAVACMD" "$@"
--------------------------------------------------------------------------------
/Example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
--------------------------------------------------------------------------------
/Example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
2 | plugins { id("com.facebook.react.settings") }
3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
4 | rootProject.name = 'RNJWPlayer'
5 | include ':app'
6 | includeBuild('../node_modules/@react-native/gradle-plugin')
--------------------------------------------------------------------------------
/Example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "RNJWPlayer",
3 | "displayName": "RNJWPlayer"
4 | }
--------------------------------------------------------------------------------
/Example/app/jsx/App.tsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import RNBootSplash from 'react-native-bootsplash';
3 |
4 | /* navigation */
5 | import {NavigationContainer} from '@react-navigation/native';
6 | import {createNativeStackNavigator} from '@react-navigation/native-stack';
7 |
8 | /* screens */
9 | import Home from './screens/Home';
10 | import ListExample from './screens/ListExample';
11 | import SingleExample from './screens/SingleExample';
12 | import OnBeforeNextPlaylistItemExample from './screens/OnBeforeNextPlaylistItemExample';
13 | import DRMExample from './screens/DRMExample';
14 | import LocalFileExample from './screens/LocalFileExample';
15 | import SourcesExample from './screens/SourcesExample';
16 | import YoutubeExample from './screens/YoutubeExample';
17 | import PlayerInModal from './screens/PlayerInModal';
18 |
19 | const Stack = createNativeStackNavigator();
20 |
21 | export default class App extends Component {
22 | render() {
23 | return (
24 | RNBootSplash.hide({fade: true})}>
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Example/app/jsx/components/AnimatedPlayer/player.style.js:
--------------------------------------------------------------------------------
1 | import {StyleSheet} from 'react-native';
2 | import {colors} from '../../../ui/styles/global.style';
3 |
4 | export default StyleSheet.create({
5 | mainCont: {
6 | position: 'absolute',
7 | left: 0,
8 | right: 0,
9 | borderTopColor: 'rgba(0, 0, 0, .3)',
10 | },
11 | subCont: {
12 | flexDirection: 'row',
13 | backgroundColor: 'transparent',
14 | },
15 | playerMainCont: {
16 | flex: 4,
17 | flexDirection: 'row',
18 | alignItems: 'center',
19 | overflow: 'hidden',
20 | },
21 | textCont: {
22 | flex: 1,
23 | marginHorizontal: 10,
24 | // width: SCREEN_WIDTH / 5,
25 | },
26 | trackTitle: {
27 | fontSize: 20,
28 | // color: 'lightgray',
29 | marginHorizontal: 10,
30 | marginTop: 20,
31 | fontWeight: '700',
32 | lineHeight: 23,
33 | },
34 | actionsCont: {
35 | flexDirection: 'row',
36 | justifyContent: 'space-between',
37 | alignItems: 'center',
38 | paddingHorizontal: 20,
39 | paddingVertical: 20,
40 | borderBottomColor: 'gray',
41 | borderBottomWidth: 1,
42 | },
43 | actionItem: {
44 | justifyContent: 'center',
45 | alignItems: 'center',
46 | },
47 | actionItemText: {
48 | fontSize: 12,
49 | marginTop: 2,
50 | // color: 'gray',
51 | },
52 | authorCont: {
53 | flexDirection: 'row',
54 | padding: 10,
55 | justifyContent: 'space-between',
56 | alignItems: 'center',
57 | borderBottomColor: 'gray',
58 | borderBottomWidth: 1,
59 | },
60 | subAuthorCont: {
61 | flex: 1,
62 | flexDirection: 'row',
63 | alignItems: 'center',
64 | justifyContent: 'space-between',
65 | },
66 | trackAuthor: {
67 | fontSize: 20,
68 | // color: 'lightgray',
69 | fontWeight: '300',
70 | },
71 | trackAuthorSmall: {
72 | fontSize: 13,
73 | // color: 'lightgray',
74 | fontWeight: '300',
75 | },
76 | series: {
77 | flex: 1,
78 | justifyContent: 'center',
79 | flexWrap: 'wrap',
80 | marginLeft: 10,
81 | marginRight: 5,
82 | },
83 | seriesName: {
84 | flex: 1,
85 | fontSize: 14,
86 | // color: 'lightgray',
87 | //marginLeft: 10,
88 | //marginRight: 5
89 | },
90 | subscribe: {
91 | flexDirection: 'row',
92 | alignItems: 'center',
93 | justifyContent: 'flex-end',
94 | },
95 | publishedText: {
96 | fontSize: 12,
97 | color: 'gray',
98 | // lineHeight: 22
99 | },
100 | authorButton: {
101 | flexDirection: 'row',
102 | marginLeft: 2,
103 | },
104 | navToAuthor: {
105 | marginLeft: 2,
106 | },
107 | publisherCont: {
108 | // flexDirection: 'row',
109 | // alignItems: 'center',
110 | margin: 10,
111 | },
112 | authorByCont: {
113 | flexDirection: 'row',
114 | },
115 | dropdown: {
116 | position: 'absolute',
117 | top: 20,
118 | left: 20,
119 | zIndex: 200,
120 | // backgroundColor: "rgba(0, 0, 0, .4)",
121 | borderRadius: 5,
122 | paddingVertical: 2,
123 | paddingHorizontal: 8,
124 | },
125 | image: {
126 | height: 50,
127 | width: 50,
128 | resizeMode: 'contain',
129 | },
130 | seriesSubscribe: {
131 | color: colors.red,
132 | },
133 | });
134 |
--------------------------------------------------------------------------------
/Example/app/jsx/components/Player.js:
--------------------------------------------------------------------------------
1 | import React, {forwardRef} from 'react';
2 | import {Platform} from 'react-native';
3 |
4 | import JWPlayer from '@jwplayer/jwplayer-react-native';
5 |
6 | import {IOS_API_KEY, ANDROID_API_KEY} from '@env';
7 |
8 | export default forwardRef((props, ref) => {
9 | const { onLayout, tag, config, style } = props;
10 |
11 | const newProps = Object.assign({}, props);
12 | delete newProps.ref;
13 | delete newProps.key;
14 | delete newProps.config;
15 | delete newProps.style;
16 |
17 | return (
18 | alert(e.nativeEvent?.error || 'Player Error.')}
34 | />
35 | );
36 | });
37 |
--------------------------------------------------------------------------------
/Example/app/jsx/components/PlayerContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Text, Pressable, View, Dimensions } from 'react-native';
3 | export const { height } = Dimensions.get('window');
4 |
5 | /* styles */
6 | import { globalStyles } from '../../ui/styles/global.style';
7 |
8 | const styles = StyleSheet.create({
9 | centeredView: {
10 | flex: 1,
11 | justifyContent: 'center',
12 | alignItems: 'center',
13 | marginTop: 22,
14 | },
15 | modalView: {
16 | marginTop: height / 3,
17 | height: 300
18 | },
19 | button: {
20 | borderRadius: 20,
21 | },
22 | buttonOpen: {
23 | backgroundColor: '#808080',
24 | },
25 | textStyle: {
26 | color: 'white',
27 | fontWeight: 'bold',
28 | fontSize: 18,
29 | margin: 30,
30 | textAlign: 'center',
31 | },
32 | });
33 |
34 | export default ({ children, text, onPress = null }) => {
35 | return (
36 |
37 |
38 | {children}
39 |
40 | {text}
41 | {onPress &&
44 | Button for onPress
45 |
46 | }
47 |
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/Example/app/jsx/screens/DRMExample.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useEffect, useState } from 'react';
2 | import { StyleSheet, Text, Linking, Platform } from 'react-native';
3 | import Player from '../components/Player';
4 | import DeviceInfo from 'react-native-device-info';
5 | import PlayerContainer from '../components/PlayerContainer';
6 |
7 | export default () => {
8 | const playerRef = useRef([]);
9 | const [isEmulator, setIsEmulator] = useState(true); // force true for potential race condition
10 |
11 | /**
12 | * `jwSignedConfigPlaylist` is an example of the return for a JW Platform signed URL as
13 | * described here https://docs.jwplayer.com/platform/docs/protection-studio-drm-generate-a-signed-content-url-for-drm-playback
14 | *
15 | * ** I do not currently work with iOS, use legacy example **
16 | *
17 | * For the legacy examples, see `renderIOSPlayer` and `renderAndroidPlayer`
18 | */
19 | const jwSignedConfigPlaylist = {
20 | "title": "Shaka Test",
21 | "image": "https://shaka-player-demo.appspot.com/assets/poster.jpg",
22 | "description": "test test",
23 | "playlist": [
24 | {
25 | "title": "Shaka Test",
26 | "mediaid": "testtest", // REQUIRED -- This style of config is intended to be used with JW Studio DRM
27 | "image": "https://shaka-player-demo.appspot.com/assets/poster.jpg",
28 | "description": "",
29 | "sources": [
30 | {
31 | "drm": {
32 | "widevine": {
33 | "url": "https://cwip-shaka-proxy.appspot.com/no_auth"
34 | }
35 | },
36 | "file": "https://storage.googleapis.com/shaka-demo-assets/sintel-widevine/dash.mpd",
37 | "type": "application/dash+xml"
38 | },
39 | {
40 | "drm": {
41 | "fairplay": {
42 | "processSpcUrl": "https://fps.ezdrm.com/api/licenses",
43 | "certificateUrl": "https://fps.ezdrm.com/demo/video/eleisure.cer"
44 | }
45 | },
46 | "file": "https://fps.ezdrm.com/demo/video/ezdrm.m3u8",
47 | "type": "application/vnd.apple.mpegurl"
48 | }
49 | ]
50 | }
51 | ]
52 | }
53 |
54 | // legacy
55 | const renderIOSPlayer = () => {
56 | /**
57 | * See https://docs.jwplayer.com/platform/docs/protection-studio-drm-generate-a-signed-content-url-for-drm-playback
58 | * for more information regarding generating below information and JWP Studio DRM.
59 | *
60 | * If using independent DRM provider, fill in the
61 | * SPC URL, and Certificate URL for the media asset.
62 | */
63 |
64 | // TODO fill in with your DRM content
65 | const ProcessSPCUrl = ''
66 | const CertificateUrl ='';
67 | const VideoFile = '';
68 |
69 | return (
70 |
89 | );
90 | };
91 |
92 | const renderPlayer = () => {
93 | return (
94 |
104 | );
105 | }
106 |
107 | // legacy
108 | const renderAndroidPlayer = () => {
109 | const AuthUrl = 'https://cwip-shaka-proxy.appspot.com/no_auth';
110 | const StreamUrl =
111 | 'https://storage.googleapis.com/shaka-demo-assets/sintel-widevine/dash.mpd';
112 |
113 | return (
114 |
132 | );
133 | };
134 |
135 | useEffect(() => {
136 | getIsEmulator();
137 | }, []);
138 |
139 | const getIsEmulator = async () => {
140 | const emulator = await DeviceInfo.isEmulator();
141 | setIsEmulator(emulator);
142 | };
143 |
144 | return (
145 |
149 | {"DRM Doesn't work in the simulator. Check out "}
150 |
153 | Linking.openURL(
154 | 'https://reactnative.dev/docs/running-on-device',
155 | )
156 | }>
157 | this
158 |
159 | {' link to run on a real device.'}
160 |
161 | ) : Platform.OS === 'ios' ? (
162 | renderIOSPlayer() // enforce legacy for now
163 | ) : (
164 | renderAndroidPlayer()
165 | )
166 | }
167 | text="Welcome to react-native-jw-media-player"
168 | />
169 | );
170 | };
171 |
172 | const styles = StyleSheet.create({
173 | text: {
174 | fontSize: 18,
175 | margin: 40,
176 | },
177 | errorText: {
178 | textAlign: 'center',
179 | color: 'white',
180 | padding: 20,
181 | fontSize: 17,
182 | },
183 | });
--------------------------------------------------------------------------------
/Example/app/jsx/screens/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, View, Text, TouchableOpacity, FlatList} from 'react-native';
3 | import Icons from 'react-native-vector-icons/FontAwesome5';
4 | import {useNavigation} from '@react-navigation/native';
5 |
6 | const SCREENS = ['Single', 'On Before Next Playlist Item', 'Modal', 'List', 'DRM', 'Local', 'Sources', 'Youtube'];
7 |
8 | export default () => {
9 | const navigation = useNavigation();
10 | return (
11 |
12 | `${index}`}
16 | data={SCREENS}
17 | renderItem={({item}) => (
18 | navigation.navigate(item)}
21 | style={styles.item}>
22 | Go to {item} example
23 |
24 |
25 | )}
26 | ItemSeparatorComponent={() => }
27 | />
28 |
29 | );
30 | };
31 |
32 | const styles = StyleSheet.create({
33 | container: {
34 | flex: 1,
35 | backgroundColor: 'white',
36 | },
37 | flatList: {
38 | paddingVertical: 25,
39 | },
40 | item: {
41 | flexDirection: 'row',
42 | alignItems: 'center',
43 | justifyContent: 'space-between',
44 | paddingHorizontal: 20,
45 | height: 25,
46 | },
47 | text: {
48 | fontSize: 15,
49 | },
50 | separator: {
51 | height: 1,
52 | backgroundColor: 'lightgray',
53 | marginVertical: 25,
54 | },
55 | });
56 |
--------------------------------------------------------------------------------
/Example/app/jsx/screens/ListExample.js:
--------------------------------------------------------------------------------
1 | import React, {useRef} from 'react';
2 | import {StyleSheet, View, Text, FlatList} from 'react-native';
3 | import Player from '../components/Player';
4 |
5 | /* styles */
6 | import {globalStyles} from '../../ui/styles/global.style';
7 |
8 | export default () => {
9 | const tags = ['JWPlayer-1', 'JWPlayer-2', 'JWPlayer-3'];
10 | const playerRef = useRef({});
11 |
12 | const onBeforePlay = tag => {
13 | tags.map(player => {
14 | if (player !== tag) {
15 | playerRef.current[player]?.pause();
16 | }
17 | });
18 | };
19 |
20 | const renderPlayer = (tag, index) => {
21 | return (
22 | (playerRef.current[tag] = el)}
24 | key={tag}
25 | style={globalStyles.player}
26 | config={{
27 | playlist: [
28 | {
29 | file:
30 | index % 2 === 0
31 | ? 'https://cdn.jwplayer.com/videos/CXz339Xh-sJF8m8CA.mp4'
32 | : 'https://playertest.longtailvideo.com/adaptive/oceans/oceans.m3u8',
33 | image:
34 | index % 2 === 0
35 | ? 'https://cdn.jwplayer.com/thumbs/CXz339Xh-720.jpg'
36 | : 'https://d3el35u4qe4frz.cloudfront.net/bkaovAYt-480.jpg',
37 | },
38 | ],
39 | }}
40 | onBeforePlay={() => onBeforePlay(tag)}
41 | />
42 | );
43 | };
44 |
45 | return (
46 | `${item}-${index}`}
49 | data={tags}
50 | renderItem={({item, index}) => (
51 |
52 | {renderPlayer(item, index)}
53 | This is {item}
54 |
55 | )}
56 | ItemSeparatorComponent={() => }
57 | />
58 | );
59 | };
60 |
61 | const styles = StyleSheet.create({
62 | text: {
63 | fontSize: 18,
64 | margin: 40,
65 | },
66 | flatList: {
67 | alignItems: 'center',
68 | justifyContent: 'center',
69 | backgroundColor: 'black',
70 | paddingVertical: 50,
71 | },
72 | separator: {
73 | height: 1,
74 | marginBottom: 50,
75 | backgroundColor: 'white',
76 | },
77 | });
78 |
--------------------------------------------------------------------------------
/Example/app/jsx/screens/LocalFileExample.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useEffect, useState } from 'react';
2 | import { Text, StyleSheet, Platform } from 'react-native';
3 | import Player from '../components/Player';
4 | import PlayerContainer from '../components/PlayerContainer';
5 |
6 | /* utils */
7 | var RNFS = require('react-native-fs');
8 |
9 | export default () => {
10 | const playerRef = useRef([]);
11 | const [localFile, setLocalFile] = useState(null);
12 |
13 | useEffect(() => {
14 | getLocalFile();
15 | }, []);
16 |
17 | const getLocalFile = async () => {
18 | if (Platform.OS === 'ios') {
19 | const data = await RNFS.readDir(RNFS.MainBundlePath)
20 | const local = data?.find(file => file.name === 'local_file.mp4')?.path;
21 | setLocalFile('file://' + local);
22 | } else {
23 | const data = await RNFS.readDirAssets('');
24 | const local = data?.find(file => file.name == 'local_file.mp4')?.path;
25 | setLocalFile('asset:///' + local);
26 | }
27 | };
28 |
29 | const renderPlayer = () => {
30 | return localFile ? (
31 |
46 | ) : (
47 | Failed to load local file.
48 | );
49 | };
50 |
51 | return (
52 |
56 | );
57 | };
58 |
59 | const styles = StyleSheet.create({
60 | errorText: {
61 | textAlign: 'center',
62 | color: 'white',
63 | padding: 20,
64 | fontSize: 17,
65 | },
66 | });
67 |
--------------------------------------------------------------------------------
/Example/app/jsx/screens/OnBeforeNextPlaylistItemExample.js:
--------------------------------------------------------------------------------
1 | import React, { useRef } from 'react';
2 | import { StatusBar } from 'react-native';
3 | import Player from '../components/Player';
4 | import PlayerContainer from '../components/PlayerContainer';
5 |
6 | export default () => {
7 | const playerRef = useRef([]);
8 |
9 | const onBeforeNextPlaylistItem = e => {
10 | console.log('onBeforeNextPlaylistItem was called with: ', e.nativeEvent);
11 | var playlistItem = JSON.parse(e.nativeEvent.playlistItem);
12 | var index = e.nativeEvent.index
13 |
14 | // Replace the 2nd item in the playlist with something new
15 | // This could also be a modified version of the current `playlistItem` raised in the callback
16 | if (index === 1) {
17 | playerRef.current.resolveNextPlaylistItem(otherItem);
18 | } else {
19 | playerRef.current.resolveNextPlaylistItem(playlistItem);
20 | }
21 | }
22 |
23 | const onFullScreen = () => {
24 | StatusBar.setHidden(true);
25 | };
26 |
27 | const onFullScreenExit = () => {
28 | StatusBar.setHidden(false);
29 | };
30 |
31 | let jwConfig = {
32 | "title": "Sample Playlist of Blender Projects",
33 | "playlist": "https://cdn.jwplayer.com/v2/playlists/NbzQThwn?format=json"
34 | }
35 |
36 | let otherItem = {
37 | "title": "Injected playlist item",
38 | "file": "http://content.bitsontherun.com/videos/q1fx20VZ-52qL9xLP.mp4"
39 | }
40 |
41 | const renderPlayer = () => {
42 | return (
43 |
58 | );
59 | };
60 |
61 | return (
62 |
66 | );
67 | };
68 |
--------------------------------------------------------------------------------
/Example/app/jsx/screens/PlayerInModal.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState } from 'react';
2 | import Player from '../components/Player';
3 | import PlayerContainer from '../components/PlayerContainer';
4 | import { Alert, Modal, StyleSheet, Text, Pressable, View, StatusBar, Dimensions } from 'react-native';
5 | export const { height } = Dimensions.get('window');
6 |
7 |
8 | export default () => {
9 | const playerRef = useRef([]);
10 |
11 | const [modalVisible, setModalVisible] = useState(false);
12 |
13 | let jwConfig = {
14 | "title": "Basic Modal implementation",
15 | "fullScreenOnLandscape": true,
16 | "landscapeOnFullScreen": true,
17 | "playerInModal": true,
18 | "playlist": [
19 | {
20 | "file": "https://content.bitsontherun.com/videos/q1fx20VZ-52qL9xLP.mp4",
21 | }
22 | ]
23 | }
24 |
25 | return (
26 |
27 | {
32 | setModalVisible(!modalVisible);
33 | }}>
34 |
35 |
46 |
47 | setModalVisible(false)}>
50 | Hide Modal
51 |
52 |
53 | setModalVisible(true)}>
56 | Show Modal
57 |
58 |
59 |
60 | );
61 | };
62 | const styles = StyleSheet.create({
63 | centeredView: {
64 | flex: 1,
65 | justifyContent: 'center',
66 | alignItems: 'center',
67 | marginTop: 22,
68 | },
69 | modalView: {
70 | marginTop:height / 3,
71 | height: 300
72 | },
73 | button: {
74 | borderRadius: 20,
75 | padding: 10,
76 | elevation: 2,
77 | },
78 | buttonOpen: {
79 | backgroundColor: '#F194FF',
80 | },
81 | buttonClose: {
82 | backgroundColor: '#2196F3',
83 | },
84 | textStyle: {
85 | color: 'white',
86 | fontWeight: 'bold',
87 | textAlign: 'center',
88 | },
89 | });
--------------------------------------------------------------------------------
/Example/app/jsx/screens/SingleExample.js:
--------------------------------------------------------------------------------
1 | import React, {useRef} from 'react';
2 | import {Alert, StatusBar} from 'react-native';
3 | import Player from '../components/Player';
4 | import PlayerContainer from '../components/PlayerContainer';
5 |
6 | export default () => {
7 | const playerRef = useRef([]);
8 |
9 | const onTime = e => {
10 | // var {position, duration} = e.nativeEvent;
11 | // eslint-disable-line
12 | // console.log('onTime was called with: ', position, duration);
13 | };
14 |
15 | const onFullScreen = () => {
16 | StatusBar.setHidden(true);
17 | };
18 |
19 | const onFullScreenExit = () => {
20 | StatusBar.setHidden(false);
21 | };
22 |
23 | let jwConfig = {
24 | "title": "Single Inline Linear Preroll",
25 | "playlist": [
26 | {
27 | "title": "Single Inline Linear Preroll",
28 | "file": "https://content.bitsontherun.com/videos/q1fx20VZ-52qL9xLP.mp4",
29 | "adschedule": {
30 | "adBreak1": {
31 | "offset": "pre",
32 | "ad": {
33 | "source": "googima",
34 | "tag": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator="
35 | }
36 | }
37 | }
38 | }
39 | ],
40 | "advertising": {
41 | "client": "googima"
42 | }
43 | }
44 |
45 | const renderPlayer = () => {
46 | return (
47 |
61 | );
62 | };
63 |
64 | return (
65 | {
69 | // Add logic for a button press here
70 | Alert.alert('Button Pressed');
71 | }}
72 | />
73 | );
74 | };
75 |
--------------------------------------------------------------------------------
/Example/app/jsx/screens/SourcesExample.js:
--------------------------------------------------------------------------------
1 | import React, {useRef} from 'react';
2 | import Player from '../components/Player';
3 | import PlayerContainer from '../components/PlayerContainer';
4 |
5 | export default () => {
6 | const playerRef = useRef([]);
7 |
8 | const renderPlayer = () => {
9 | return (
10 |
35 | );
36 | };
37 |
38 | return (
39 |
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/Example/app/jsx/screens/YoutubeExample.js:
--------------------------------------------------------------------------------
1 | import React, {useRef, useState} from 'react';
2 | import {
3 | StyleSheet,
4 | View,
5 | Text,
6 | FlatList,
7 | TouchableOpacity,
8 | Image,
9 | } from 'react-native';
10 | import Player from '../components/AnimatedPlayer';
11 |
12 | export default () => {
13 | const [isVisible, setIsVisible] = useState(false);
14 | const [playerItem, setPlayerItem] = useState({});
15 | const data = [
16 | {
17 | title: 'JWPlayer-1',
18 | subtitle: 'Subtitle 1',
19 | file: 'http://content.bitsontherun.com/videos/bkaovAYt-injeKYZS.mp4',
20 | image: 'https://d3el35u4qe4frz.cloudfront.net/bkaovAYt-480.jpg',
21 | duration: 33,
22 | startTime: 10,
23 | author: 'Some Guy',
24 | series: 'Youtube sample 1',
25 | },
26 | {
27 | title: 'JWPlayer-2',
28 | subtitle: 'Subtitle 2',
29 | file: 'http://content.bitsontherun.com/videos/bkaovAYt-kNspJqnJ.mp4',
30 | image: 'https://d3el35u4qe4frz.cloudfront.net/bkaovAYt-480.jpg',
31 | duration: 33,
32 | author: 'Some other Guy',
33 | series: 'Youtube sample 2',
34 | },
35 | {
36 | title: 'JWPlayer-3',
37 | subtitle: 'Subtitle 3',
38 | file: 'https://playertest.longtailvideo.com/adaptive/oceans/oceans.m3u8',
39 | image: 'https://d3el35u4qe4frz.cloudfront.net/bkaovAYt-480.jpg',
40 | duration: 2000,
41 | author: 'A third Guy',
42 | series: 'Youtube sample 3',
43 | },
44 | ];
45 |
46 | return (
47 |
48 | `${title}-${index}`}
51 | data={data}
52 | renderItem={({item, index}) => (
53 | {
55 | setIsVisible(true);
56 | setPlayerItem(item);
57 | }}
58 | style={styles.itemContainer}>
59 |
60 |
61 | {item?.title}
62 | {item?.subtitle}
63 |
64 |
65 | )}
66 | ItemSeparatorComponent={() => }
67 | />
68 | {isVisible && playerItem && (
69 |
70 | )}
71 |
72 | );
73 | };
74 |
75 | const styles = StyleSheet.create({
76 | itemContainer: {
77 | flexDirection: 'row',
78 | alignItems: 'center',
79 | paddingHorizontal: 20,
80 | },
81 | image: {
82 | height: 50,
83 | width: 50,
84 | resizeMode: 'contain',
85 | // backgroundColor: 'lightgray',
86 | },
87 | textContainer: {
88 | margin: 20,
89 | },
90 | text: {
91 | fontSize: 18,
92 | },
93 | subtext: {
94 | fontSize: 15,
95 | },
96 | flatList: {
97 |
98 | },
99 | separator: {
100 | height: 1,
101 | backgroundColor: 'lightgray',
102 | },
103 | });
104 |
--------------------------------------------------------------------------------
/Example/app/ui/styles/global.style.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, Dimensions, Platform} from 'react-native';
3 |
4 | export const {width} = Dimensions.get('window');
5 |
6 | export const globalStyles = StyleSheet.create({
7 | container: {
8 | flex: 1,
9 | backgroundColor: 'white',
10 | },
11 | subContainer: {
12 | backgroundColor: 'black',
13 | alignItems: 'center',
14 | },
15 | playerContainer: {
16 | height: 300,
17 | width: width - 40,
18 | },
19 | player: {
20 | flex: 1,
21 | },
22 | text: {
23 | fontSize: 18,
24 | margin: 40,
25 | },
26 | });
27 |
28 | export const colors = {
29 | red: '#EC0041',
30 | };
31 |
--------------------------------------------------------------------------------
/Example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:@react-native/babel-preset'],
3 | plugins: [
4 | [
5 | 'module:react-native-dotenv',
6 | {
7 | envName: 'APP_ENV',
8 | moduleName: '@env',
9 | path: '.env',
10 | blocklist: null,
11 | allowlist: null,
12 | safe: false,
13 | allowUndefined: true,
14 | verbose: false,
15 | },
16 | ],
17 | ],
18 | };
19 |
--------------------------------------------------------------------------------
/Example/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import 'react-native-gesture-handler';
6 | import {AppRegistry} from 'react-native';
7 | import App from './app/jsx/App';
8 | import {name as appName} from './app.json';
9 |
10 | AppRegistry.registerComponent(appName, () => App);
11 |
--------------------------------------------------------------------------------
/Example/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/Example/ios/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import React
3 | import React_RCTAppDelegate
4 | import ReactAppDependencyProvider
5 | import RNBootSplash
6 | import GoogleCast
7 |
8 | @main
9 | class AppDelegate: RCTAppDelegate {
10 | override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
11 | self.moduleName = "RNJWPlayer"
12 | self.dependencyProvider = RCTAppDependencyProvider()
13 | /*:
14 | kGCKDefaultMediaReceiverApplicationID is a constant from the GoogleCast SDK, representing the default Chromecast receiver.
15 | - https://developers.google.com/android/reference/com/google/android/gms/cast/CastMediaControlIntent#public-static-final-string-default_media_receiver_application_id
16 | If using a custom Chromecast receiver, replace kGCKDefaultMediaReceiverApplicationID with your custom receiver's App ID string.
17 | - https://github.com/jwplayer/jwplayer-react-native?tab=readme-ov-file#casting
18 | */
19 | let discoveryCriteria = GCKDiscoveryCriteria(applicationID: kGCKDefaultMediaReceiverApplicationID)
20 | let options = GCKCastOptions(discoveryCriteria: discoveryCriteria)
21 | GCKCastContext.setSharedInstanceWith(options)
22 | // You can add your custom initial props in the dictionary below.
23 | // They will be passed down to the ViewController used by React Native.
24 | self.initialProps = [:]
25 |
26 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
27 | }
28 |
29 | override func sourceURL(for bridge: RCTBridge) -> URL? {
30 | self.bundleURL()
31 | }
32 |
33 | override func bundleURL() -> URL? {
34 | #if DEBUG
35 | RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
36 | #else
37 | Bundle.main.url(forResource: "main", withExtension: "jsbundle")
38 | #endif
39 | }
40 |
41 | override func customize(_ rootView: RCTRootView!) {
42 | super.customize(rootView)
43 | RNBootSplash.initWithStoryboard("LaunchScreen", rootView: rootView)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Resolve react_native_pods.rb with node to allow for hoisting
2 | require Pod::Executable.execute_command('node', ['-p',
3 | 'require.resolve(
4 | "react-native/scripts/react_native_pods.rb",
5 | {paths: [process.argv[1]]},
6 | )', __dir__]).strip
7 |
8 | platform :ios, min_ios_version_supported
9 | prepare_react_native_project!
10 |
11 | linkage = ENV['USE_FRAMEWORKS']
12 | if linkage != nil
13 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
14 | use_frameworks! :linkage => linkage.to_sym
15 | end
16 |
17 | $RNJWPlayerUseGoogleIMA = true
18 | $RNJWPlayerUseGoogleCast = true
19 |
20 | target 'RNJWPlayer' do
21 | config = use_native_modules!
22 |
23 | use_react_native!(
24 | :path => config[:reactNativePath],
25 | # An absolute path to your application root.
26 | :app_path => "#{Pod::Config.instance.installation_root}/.."
27 | )
28 |
29 | post_install do |installer|
30 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
31 | react_native_post_install(
32 | installer,
33 | config[:reactNativePath],
34 | :mac_catalyst_enabled => false,
35 | # :ccache_enabled => true
36 | )
37 | end
38 | end
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer.xcodeproj/xcshareddata/xcschemes/RNJWPlayer.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "JW-rw-Logo_1024-20@2x.png",
5 | "idiom" : "iphone",
6 | "scale" : "2x",
7 | "size" : "20x20"
8 | },
9 | {
10 | "filename" : "JW-rw-Logo_1024-20@3x.png",
11 | "idiom" : "iphone",
12 | "scale" : "3x",
13 | "size" : "20x20"
14 | },
15 | {
16 | "filename" : "JW-rw-Logo_1024-29@2x.png",
17 | "idiom" : "iphone",
18 | "scale" : "2x",
19 | "size" : "29x29"
20 | },
21 | {
22 | "filename" : "JW-rw-Logo_1024-29@3x.png",
23 | "idiom" : "iphone",
24 | "scale" : "3x",
25 | "size" : "29x29"
26 | },
27 | {
28 | "filename" : "JW-rw-Logo_1024-40@2x.png",
29 | "idiom" : "iphone",
30 | "scale" : "2x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "filename" : "JW-rw-Logo_1024-40@3x.png",
35 | "idiom" : "iphone",
36 | "scale" : "3x",
37 | "size" : "40x40"
38 | },
39 | {
40 | "filename" : "JW-rw-Logo_1024-60@2x.png",
41 | "idiom" : "iphone",
42 | "scale" : "2x",
43 | "size" : "60x60"
44 | },
45 | {
46 | "filename" : "JW-rw-Logo_1024-60@3x.png",
47 | "idiom" : "iphone",
48 | "scale" : "3x",
49 | "size" : "60x60"
50 | },
51 | {
52 | "filename" : "JW-rw-Logo_1024-20.png",
53 | "idiom" : "ipad",
54 | "scale" : "1x",
55 | "size" : "20x20"
56 | },
57 | {
58 | "filename" : "JW-rw-Logo_1024-20@2x.png",
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "20x20"
62 | },
63 | {
64 | "filename" : "JW-rw-Logo_1024-29.png",
65 | "idiom" : "ipad",
66 | "scale" : "1x",
67 | "size" : "29x29"
68 | },
69 | {
70 | "filename" : "JW-rw-Logo_1024-29@2x.png",
71 | "idiom" : "ipad",
72 | "scale" : "2x",
73 | "size" : "29x29"
74 | },
75 | {
76 | "filename" : "JW-rw-Logo_1024-40.png",
77 | "idiom" : "ipad",
78 | "scale" : "1x",
79 | "size" : "40x40"
80 | },
81 | {
82 | "filename" : "JW-rw-Logo_1024-40@2x.png",
83 | "idiom" : "ipad",
84 | "scale" : "2x",
85 | "size" : "40x40"
86 | },
87 | {
88 | "filename" : "JW-rw-Logo_1024-76.png",
89 | "idiom" : "ipad",
90 | "scale" : "1x",
91 | "size" : "76x76"
92 | },
93 | {
94 | "filename" : "JW-rw-Logo_1024-76@2x.png",
95 | "idiom" : "ipad",
96 | "scale" : "2x",
97 | "size" : "76x76"
98 | },
99 | {
100 | "filename" : "JW-rw-Logo_1024-83.5@2x.png",
101 | "idiom" : "ipad",
102 | "scale" : "2x",
103 | "size" : "83.5x83.5"
104 | },
105 | {
106 | "filename" : "JW-rw-Logo_1024-1024.png",
107 | "idiom" : "ios-marketing",
108 | "scale" : "1x",
109 | "size" : "1024x1024"
110 | }
111 | ],
112 | "info" : {
113 | "author" : "xcode",
114 | "version" : 1
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-1024.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-20.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-20@2x.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-20@3x.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-29.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-29@2x.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-29@3x.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-40.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-40@2x.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-40@3x.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-60@2x.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-60@3x.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-76.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-76@2x.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/AppIcon.appiconset/JW-rw-Logo_1024-83.5@2x.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/jwlogo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "jwlogo.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "jwlogo-sketch.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/jwlogo.imageset/jwlogo-sketch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/jwlogo.imageset/jwlogo-sketch.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Images.xcassets/jwlogo.imageset/jwlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/RNJWPlayer/Images.xcassets/jwlogo.imageset/jwlogo.png
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | RNJWPlayer
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 |
30 | NSAllowsArbitraryLoads
31 |
32 | NSAllowsLocalNetworking
33 |
34 |
35 | NSBluetoothAlwaysUsageDescription
36 | ${PRODUCT_NAME} uses bluetooth to discover Cast-enabled devices around you.
37 | NSBonjourServices
38 |
39 | _googlecast._tcp
40 | _CC1AD845._googlecast._tcp
41 |
42 | NSLocalNetworkUsageDescription
43 | ${PRODUCT_NAME} uses the local network to discover Cast-enabled devices on your WiFi
44 | network.
45 | NSLocationWhenInUseUsageDescription
46 |
47 | UIAppFonts
48 |
49 | AntDesign.ttf
50 | Entypo.ttf
51 | EvilIcons.ttf
52 | Feather.ttf
53 | FontAwesome.ttf
54 | FontAwesome5_Brands.ttf
55 | FontAwesome5_Regular.ttf
56 | FontAwesome5_Solid.ttf
57 | Foundation.ttf
58 | Ionicons.ttf
59 | MaterialIcons.ttf
60 | MaterialCommunityIcons.ttf
61 | SimpleLineIcons.ttf
62 | Octicons.ttf
63 | Zocial.ttf
64 | Fontisto.ttf
65 |
66 | UIBackgroundModes
67 |
68 | audio
69 |
70 | UILaunchStoryboardName
71 | LaunchScreen
72 | UIRequiredDeviceCapabilities
73 |
74 | arm64
75 |
76 | UISupportedInterfaceOrientations
77 |
78 | UIInterfaceOrientationPortrait
79 | UIInterfaceOrientationLandscapeLeft
80 | UIInterfaceOrientationLandscapeRight
81 |
82 | UIViewControllerBasedStatusBarAppearance
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
47 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/Example/ios/RNJWPlayer/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPrivacyAccessedAPITypes
6 |
7 |
8 | NSPrivacyAccessedAPIType
9 | NSPrivacyAccessedAPICategorySystemBootTime
10 | NSPrivacyAccessedAPITypeReasons
11 |
12 | 35F9.1
13 |
14 |
15 |
16 | NSPrivacyAccessedAPIType
17 | NSPrivacyAccessedAPICategoryUserDefaults
18 | NSPrivacyAccessedAPITypeReasons
19 |
20 | CA92.1
21 | C56D.1
22 |
23 |
24 |
25 | NSPrivacyAccessedAPIType
26 | NSPrivacyAccessedAPICategoryFileTimestamp
27 | NSPrivacyAccessedAPITypeReasons
28 |
29 | C617.1
30 |
31 |
32 |
33 | NSPrivacyAccessedAPIType
34 | NSPrivacyAccessedAPICategoryDiskSpace
35 | NSPrivacyAccessedAPITypeReasons
36 |
37 | 85F4.1
38 |
39 |
40 |
41 | NSPrivacyCollectedDataTypes
42 |
43 | NSPrivacyTracking
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Example/ios/local_file.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/Example/ios/local_file.mp4
--------------------------------------------------------------------------------
/Example/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'react-native',
3 | };
--------------------------------------------------------------------------------
/Example/metro.config.js:
--------------------------------------------------------------------------------
1 | const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
2 |
3 | /**
4 | * Metro configuration
5 | * https://reactnative.dev/docs/metro
6 | *
7 | * @type {import('@react-native/metro-config').MetroConfig}
8 | */
9 | const config = {};
10 |
11 | module.exports = mergeConfig(getDefaultConfig(__dirname), config);
--------------------------------------------------------------------------------
/Example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "RNJWPlayer",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "reinstall": "yarn purge-modules && yarn",
7 | "purge-modules": "rm -rf node_modules",
8 | "android": "react-native run-android",
9 | "ios": "react-native run-ios",
10 | "lint": "eslint .",
11 | "start": "react-native start",
12 | "test": "jest",
13 | "postinstall": "rm -rf ./node_modules/@jwplayer/jwplayer-react-native/Example ./node_modules/@jwplayer/jwplayer-react-native/images ./node_modules/@jwplayer/jwplayer-react-native/node_modules ./node_modules/@jwplayer/jwplayer-react-native/yarn.lock"
14 | },
15 | "dependencies": {
16 | "@jwplayer/jwplayer-react-native": "../../jwplayer-react-native",
17 | "@react-navigation/native": "7.0.19",
18 | "@react-navigation/native-stack": "7.3.3",
19 | "react": "19.0.0",
20 | "react-native": "0.78.1",
21 | "react-native-bootsplash": "6.3.4",
22 | "react-native-device-info": "14.0.4",
23 | "react-native-fs": "2.20.0",
24 | "react-native-gesture-handler": "2.24.0",
25 | "react-native-orientation-locker": "1.7.0",
26 | "react-native-safe-area-context": "5.3.0",
27 | "react-native-screens": "4.10.0",
28 | "react-native-vector-icons": "10.2.0",
29 | "rn-iphone-helper": "2.0.4"
30 | },
31 | "devDependencies": {
32 | "@babel/core": "^7.25.2",
33 | "@babel/preset-env": "^7.25.3",
34 | "@babel/runtime": "^7.25.0",
35 | "@react-native-community/cli": "15.0.1",
36 | "@react-native-community/cli-platform-android": "15.0.1",
37 | "@react-native-community/cli-platform-ios": "15.0.1",
38 | "@react-native/babel-preset": "0.78.1",
39 | "@react-native/eslint-config": "0.78.1",
40 | "@react-native/metro-config": "0.78.1",
41 | "@react-native/typescript-config": "0.78.1",
42 | "@types/jest": "^29.5.13",
43 | "@types/react": "^19.0.0",
44 | "@types/react-test-renderer": "^19.0.0",
45 | "eslint": "^8.19.0",
46 | "jest": "^29.6.3",
47 | "postinstall-postinstall": "^2.1.0",
48 | "prettier": "2.8.8",
49 | "react-native-dotenv": "3.4.11",
50 | "react-test-renderer": "19.0.0",
51 | "typescript": "5.0.4"
52 | },
53 | "engines": {
54 | "node": ">=18"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json"
3 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2024 JW Player
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/RNJWPlayer.podspec:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = 'RNJWPlayer'
7 | s.version = package['version']
8 | s.summary = package['description']
9 | s.license = package['license']
10 | s.authors = package['author']
11 | s.homepage = package['homepage']
12 | s.platform = :ios, "14.0"
13 | s.source = { :git => "https://github.com/jwplayer/jwplayer-react-native.git", :tag => "v#{s.version}" }
14 | s.source_files = "ios/RNJWPlayer/*.{h,m,swift}"
15 | s.dependency 'JWPlayerKit', '4.22.0'
16 | s.dependency 'React-Core'
17 | s.static_framework = true
18 | s.info_plist = {
19 | 'NSBluetoothAlwaysUsageDescription' => 'We will use your Bluetooth for media casting.',
20 | 'NSBluetoothPeripheralUsageDescription' => 'We will use your Bluetooth for media casting.',
21 | 'NSLocalNetworkUsageDescription' => 'We will use the local network to discover Cast-enabled devices on your WiFi network.',
22 | 'Privacy - Local Network Usage Description' => 'We will use the local network to discover Cast-enabled devices on your WiFi network.',
23 | 'NSMicrophoneUsageDescription' => 'We will use your Microphone for media casting.'
24 | }
25 | s.xcconfig = {
26 | 'OTHER_LDFLAGS': '-ObjC',
27 | }
28 |
29 | if defined?($RNJWPlayerUseGoogleCast)
30 | Pod::UI.puts "RNJWPlayer: enable Google Cast"
31 | s.dependency 'google-cast-sdk', '4.8.3'
32 | s.pod_target_xcconfig = {
33 | 'OTHER_SWIFT_FLAGS' => '$(inherited) -D USE_GOOGLE_CAST'
34 | }
35 | end
36 | if defined?($RNJWPlayerUseGoogleIMA)
37 | Pod::UI.puts "RNJWPlayer: enable IMA SDK"
38 | s.dependency 'GoogleAds-IMA-iOS-SDK', '3.22.1'
39 | s.pod_target_xcconfig = {
40 | 'OTHER_SWIFT_FLAGS' => '$(inherited) -D USE_GOOGLE_IMA'
41 | }
42 | end
43 |
44 | end
45 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.4.2'
9 | }
10 | }
11 |
12 | apply plugin: 'com.android.library'
13 |
14 | def safeExtGet(prop, fallback) {
15 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
16 | }
17 |
18 | def isNewArchitectureEnabled() {
19 | return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
20 | }
21 |
22 | def useIMA = safeExtGet("RNJWPlayerUseGoogleIMA", "")?.toBoolean() ?: false
23 | def useCast = safeExtGet("RNJWPlayerUseGoogleCast", "")?.toBoolean() ?: false
24 |
25 | android {
26 | compileSdkVersion safeExtGet('compileSdkVersion', 28)
27 | buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
28 |
29 | defaultConfig {
30 | minSdkVersion safeExtGet('minSdkVersion', 16)
31 | targetSdkVersion safeExtGet('targetSdkVersion', 28)
32 | versionCode 1
33 | versionName "1.0"
34 |
35 | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
36 |
37 | ndk {
38 | abiFilters "armeabi-v7a", "x86"
39 | }
40 | }
41 |
42 | buildTypes {
43 | debug {
44 | // Set the build config fields for the debug build type
45 | buildConfigField "boolean", "USE_IMA", useIMA.toString()
46 | buildConfigField "boolean", "USE_CAST", useCast.toString()
47 | // ... other debug configurations
48 | }
49 | release {
50 | // Set the build config fields for the release build type
51 | buildConfigField "boolean", "USE_IMA", useIMA.toString()
52 | buildConfigField "boolean", "USE_CAST", useCast.toString()
53 | // ... other release configurations
54 | }
55 | }
56 | lintOptions {
57 | warning 'InvalidPackage'
58 | }
59 | }
60 |
61 | allprojects {
62 | repositories {
63 | mavenLocal()
64 | mavenCentral()
65 | google()
66 | maven {
67 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
68 | url "$rootDir/../node_modules/react-native/android"
69 | }
70 | maven{
71 | url 'https://mvn.jwplayer.com/content/repositories/releases/'
72 | }
73 | }
74 | }
75 |
76 | def jwPlayerVersion = "4.20.0"
77 | def exoplayerVersion = "2.18.7" // Deprecated. Use Media3 when targeting JW SDK > 4.16.0
78 | def media3ExoVersion = "1.4.1"
79 |
80 | dependencies {
81 | implementation 'com.facebook.react:react-native:+'
82 | implementation 'com.google.code.gson:gson:2.9.0'
83 |
84 | // JWPlayer SDK
85 | implementation "com.jwplayer:jwplayer-core:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}"
86 | implementation "com.jwplayer:jwplayer-common:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}"
87 | implementation "com.jwplayer:jwplayer-ima:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}"
88 | implementation "com.jwplayer:jwplayer-chromecast:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}"
89 |
90 | // Ad dependencies
91 | if (useIMA) {
92 | implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.36.0'
93 | implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
94 | }
95 |
96 | // Cast dependencies
97 | if (useCast) {
98 | implementation 'com.google.android.gms:play-services-cast-framework:21.3.0'
99 | }
100 |
101 | // ExoPlayer dependencies
102 | implementation "androidx.media3:media3-common:${safeExtGet('media3ExoVersion', media3ExoVersion)}"
103 | implementation "androidx.media3:media3-exoplayer:${safeExtGet('media3ExoVersion', media3ExoVersion)}"
104 | implementation "androidx.media3:media3-exoplayer-dash:${safeExtGet('media3ExoVersion', media3ExoVersion)}"
105 | implementation "androidx.media3:media3-exoplayer-hls:${safeExtGet('media3ExoVersion', media3ExoVersion)}"
106 | implementation "androidx.media3:media3-exoplayer-smoothstreaming:${safeExtGet('media3ExoVersion', media3ExoVersion)}"
107 | implementation "androidx.media3:media3-ui:${safeExtGet('media3ExoVersion', media3ExoVersion)}"
108 |
109 | // JW Player Native UI dependencies
110 | implementation 'com.squareup.picasso:picasso:2.71828'
111 | implementation 'androidx.viewpager2:viewpager2:1.0.0'
112 | implementation 'com.android.volley:volley:1.2.1'
113 | implementation 'androidx.recyclerview:recyclerview:1.3.0'
114 | implementation 'androidx.mediarouter:mediarouter:1.4.0'
115 | implementation 'androidx.media:media:1.6.0'
116 | }
117 |
118 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/android/src/main/java/com/jwplayer/rnjwplayer/ArrayUtil.java:
--------------------------------------------------------------------------------
1 | package com.jwplayer.rnjwplayer;
2 |
3 | import com.facebook.react.bridge.Arguments;
4 | import com.facebook.react.bridge.ReadableArray;
5 | import com.facebook.react.bridge.ReadableType;
6 | import com.facebook.react.bridge.WritableArray;
7 |
8 | import java.util.Map;
9 |
10 | import org.json.JSONArray;
11 | import org.json.JSONObject;
12 | import org.json.JSONException;
13 |
14 | import android.util.Log;
15 |
16 | public class ArrayUtil {
17 |
18 | public static JSONArray toJSONArray(ReadableArray readableArray) throws JSONException {
19 | JSONArray jsonArray = new JSONArray();
20 |
21 | for (int i = 0; i < readableArray.size(); i++) {
22 | ReadableType type = readableArray.getType(i);
23 |
24 | switch (type) {
25 | case Null:
26 | jsonArray.put(i, null);
27 | break;
28 | case Boolean:
29 | jsonArray.put(i, readableArray.getBoolean(i));
30 | break;
31 | case Number:
32 | jsonArray.put(i, readableArray.getDouble(i));
33 | break;
34 | case String:
35 | jsonArray.put(i, readableArray.getString(i));
36 | break;
37 | case Map:
38 | jsonArray.put(i, MapUtil.toJSONObject(readableArray.getMap(i)));
39 | break;
40 | case Array:
41 | jsonArray.put(i, ArrayUtil.toJSONArray(readableArray.getArray(i)));
42 | break;
43 | }
44 | }
45 | return jsonArray;
46 | }
47 |
48 | public static Object[] toArray(JSONArray jsonArray) throws JSONException {
49 | Object[] array = new Object[jsonArray.length()];
50 |
51 | for (int i = 0; i < jsonArray.length(); i++) {
52 | Object value = jsonArray.get(i);
53 |
54 | if (value instanceof JSONObject) {
55 | value = MapUtil.toMap((JSONObject) value);
56 | }
57 | if (value instanceof JSONArray) {
58 | value = ArrayUtil.toArray((JSONArray) value);
59 | }
60 |
61 | array[i] = value;
62 | }
63 |
64 | return array;
65 | }
66 |
67 | public static Object[] toArray(ReadableArray readableArray) {
68 | Object[] array = new Object[readableArray.size()];
69 |
70 | for (int i = 0; i < readableArray.size(); i++) {
71 | ReadableType type = readableArray.getType(i);
72 |
73 | switch (type) {
74 | case Null:
75 | array[i] = null;
76 | break;
77 | case Boolean:
78 | array[i] = readableArray.getBoolean(i);
79 | break;
80 | case Number:
81 | array[i] = readableArray.getDouble(i);
82 | break;
83 | case String:
84 | array[i] = readableArray.getString(i);
85 | break;
86 | case Map:
87 | array[i] = MapUtil.toMap(readableArray.getMap(i));
88 | break;
89 | case Array:
90 | array[i] = ArrayUtil.toArray(readableArray.getArray(i));
91 | break;
92 | }
93 | }
94 |
95 | return array;
96 | }
97 |
98 | public static WritableArray toWritableArray(Object[] array) {
99 | WritableArray writableArray = Arguments.createArray();
100 |
101 | for (int i = 0; i < array.length; i++) {
102 | Object value = array[i];
103 |
104 | if (value == null) {
105 | writableArray.pushNull();
106 | }
107 | if (value instanceof Boolean) {
108 | writableArray.pushBoolean((Boolean) value);
109 | }
110 | if (value instanceof Double) {
111 | writableArray.pushDouble((Double) value);
112 | }
113 | if (value instanceof Integer) {
114 | writableArray.pushInt((Integer) value);
115 | }
116 | if (value instanceof String) {
117 | writableArray.pushString((String) value);
118 | }
119 | if (value instanceof Map) {
120 | writableArray.pushMap(MapUtil.toWritableMap((Map) value));
121 | }
122 | if (value.getClass().isArray()) {
123 | writableArray.pushArray(ArrayUtil.toWritableArray((Object[]) value));
124 | }
125 | }
126 |
127 | return writableArray;
128 | }
129 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/jwplayer/rnjwplayer/CastOptionsProvider.java:
--------------------------------------------------------------------------------
1 | package com.jwplayer.rnjwplayer;
2 |
3 | import android.content.Context;
4 |
5 | import com.google.android.gms.cast.CastMediaControlIntent;
6 | import com.google.android.gms.cast.LaunchOptions;
7 | import com.google.android.gms.cast.framework.CastOptions;
8 | import com.google.android.gms.cast.framework.OptionsProvider;
9 | import com.google.android.gms.cast.framework.SessionProvider;
10 | import com.google.android.gms.cast.framework.media.CastMediaOptions;
11 | import com.google.android.gms.cast.framework.media.MediaIntentReceiver;
12 | import com.google.android.gms.cast.framework.media.NotificationOptions;
13 |
14 | import java.util.Arrays;
15 | import java.util.List;
16 | import java.util.Locale;
17 |
18 |
19 | public class CastOptionsProvider implements OptionsProvider {
20 |
21 | /**
22 | * The Application Id to use, currently the Default Media Receiver.
23 | */
24 | private static final String DEFAULT_APPLICATION_ID = CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID;
25 |
26 | @Override
27 | public CastOptions getCastOptions(Context context) {
28 | final NotificationOptions notificationOptions = new NotificationOptions.Builder()
29 | .setActions(Arrays.asList(
30 | MediaIntentReceiver.ACTION_SKIP_NEXT,
31 | MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK,
32 | MediaIntentReceiver.ACTION_STOP_CASTING), new int[]{1, 2})
33 | .setTargetActivityClassName(RNJWPlayerView.class.getName())
34 | .build();
35 |
36 | final CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
37 | .setNotificationOptions(notificationOptions)
38 | .build();
39 |
40 | final LaunchOptions launchOptions = new LaunchOptions.Builder()
41 | .setLocale(Locale.US)
42 | .build();
43 |
44 | return new CastOptions.Builder()
45 | .setReceiverApplicationId(DEFAULT_APPLICATION_ID)
46 | .setCastMediaOptions(mediaOptions)
47 | .setLaunchOptions(launchOptions)
48 | .build();
49 | }
50 |
51 | @Override
52 | public List getAdditionalSessionProviders(Context appContext) {
53 | return null;
54 | }
55 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/jwplayer/rnjwplayer/MapUtil.java:
--------------------------------------------------------------------------------
1 | package com.jwplayer.rnjwplayer;
2 |
3 | import com.facebook.react.bridge.Arguments;
4 | import com.facebook.react.bridge.ReadableMap;
5 | import com.facebook.react.bridge.ReadableMapKeySetIterator;
6 | import com.facebook.react.bridge.ReadableType;
7 | import com.facebook.react.bridge.WritableMap;
8 |
9 | import java.util.Map;
10 | import java.util.HashMap;
11 | import java.util.Iterator;
12 |
13 | import org.json.JSONArray;
14 | import org.json.JSONObject;
15 | import org.json.JSONException;
16 |
17 | public class MapUtil {
18 |
19 | public static JSONObject toJSONObject(ReadableMap readableMap) throws JSONException {
20 | JSONObject jsonObject = new JSONObject();
21 |
22 | ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
23 |
24 | while (iterator.hasNextKey()) {
25 | String key = iterator.nextKey();
26 | ReadableType type = readableMap.getType(key);
27 |
28 | switch (type) {
29 | case Null:
30 | jsonObject.put(key, null);
31 | break;
32 | case Boolean:
33 | jsonObject.put(key, readableMap.getBoolean(key));
34 | break;
35 | case Number:
36 | jsonObject.put(key, readableMap.getDouble(key));
37 | break;
38 | case String:
39 | jsonObject.put(key, readableMap.getString(key));
40 | break;
41 | case Map:
42 | jsonObject.put(key, MapUtil.toJSONObject(readableMap.getMap(key)));
43 | break;
44 | case Array:
45 | jsonObject.put(key, ArrayUtil.toJSONArray(readableMap.getArray(key)));
46 | break;
47 | }
48 | }
49 |
50 | return jsonObject;
51 | }
52 |
53 | public static Map toMap(JSONObject jsonObject) throws JSONException {
54 | Map map = new HashMap<>();
55 | Iterator iterator = jsonObject.keys();
56 |
57 | while (iterator.hasNext()) {
58 | String key = iterator.next();
59 | Object value = jsonObject.get(key);
60 |
61 | if (value instanceof JSONObject) {
62 | value = MapUtil.toMap((JSONObject) value);
63 | }
64 | if (value instanceof JSONArray) {
65 | value = ArrayUtil.toArray((JSONArray) value);
66 | }
67 |
68 | map.put(key, value);
69 | }
70 |
71 | return map;
72 | }
73 |
74 | public static Map toMap(ReadableMap readableMap) {
75 | Map map = new HashMap<>();
76 | ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
77 |
78 | while (iterator.hasNextKey()) {
79 | String key = iterator.nextKey();
80 | ReadableType type = readableMap.getType(key);
81 |
82 | switch (type) {
83 | case Null:
84 | map.put(key, null);
85 | break;
86 | case Boolean:
87 | map.put(key, readableMap.getBoolean(key));
88 | break;
89 | case Number:
90 | map.put(key, readableMap.getDouble(key));
91 | break;
92 | case String:
93 | map.put(key, readableMap.getString(key));
94 | break;
95 | case Map:
96 | map.put(key, MapUtil.toMap(readableMap.getMap(key)));
97 | break;
98 | case Array:
99 | map.put(key, ArrayUtil.toArray(readableMap.getArray(key)));
100 | break;
101 | }
102 | }
103 |
104 | return map;
105 | }
106 |
107 | public static WritableMap toWritableMap(Map map) {
108 | WritableMap writableMap = Arguments.createMap();
109 | Iterator iterator = map.entrySet().iterator();
110 |
111 | while (iterator.hasNext()) {
112 | Map.Entry pair = (Map.Entry)iterator.next();
113 | Object value = pair.getValue();
114 |
115 | if (value == null) {
116 | writableMap.putNull((String) pair.getKey());
117 | } else if (value instanceof Boolean) {
118 | writableMap.putBoolean((String) pair.getKey(), (Boolean) value);
119 | } else if (value instanceof Double) {
120 | writableMap.putDouble((String) pair.getKey(), (Double) value);
121 | } else if (value instanceof Integer) {
122 | writableMap.putInt((String) pair.getKey(), (Integer) value);
123 | } else if (value instanceof String) {
124 | writableMap.putString((String) pair.getKey(), (String) value);
125 | } else if (value instanceof Map) {
126 | writableMap.putMap((String) pair.getKey(), MapUtil.toWritableMap((Map) value));
127 | } else if (value.getClass() != null && value.getClass().isArray()) {
128 | writableMap.putArray((String) pair.getKey(), ArrayUtil.toWritableArray((Object[]) value));
129 | }
130 |
131 | iterator.remove();
132 | }
133 |
134 | return writableMap;
135 | }
136 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayer.java:
--------------------------------------------------------------------------------
1 | package com.jwplayer.rnjwplayer;
2 |
3 |
4 | import android.content.Context;
5 | import android.content.res.Configuration;
6 | import android.view.KeyEvent;
7 |
8 | import com.jwplayer.pub.view.JWPlayerView;
9 |
10 | public class RNJWPlayer extends JWPlayerView {
11 | public Boolean fullScreenOnLandscape = false;
12 | public Boolean exitFullScreenOnPortrait = false;
13 |
14 | public RNJWPlayer(Context var1) {
15 | super(var1);
16 | }
17 |
18 | @Override
19 | public boolean dispatchKeyEvent(KeyEvent event) {
20 | // Exit fullscreen or perform the action requested
21 | if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && this.getPlayer().getFullscreen()) {
22 | if (event.getAction() == KeyEvent.ACTION_UP) {
23 | this.getPlayer().setFullscreen(false,false);
24 | }
25 | return true;
26 | }
27 | return super.dispatchKeyEvent(event);
28 | }
29 |
30 | @Override
31 | public void requestLayout() {
32 | super.requestLayout();
33 |
34 | // The spinner relies on a measure + layout pass happening after it calls requestLayout().
35 | // Without this, the widget never actually changes the selection and doesn't call the
36 | // appropriate listeners. Since we override onLayout in our ViewGroups, a layout pass never
37 | // happens after a call to requestLayout, so we simulate one here.
38 | post(measureAndLayout);
39 | }
40 |
41 |
42 | private final Runnable measureAndLayout = new Runnable() {
43 | @Override
44 | public void run() {
45 | measure(
46 | MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
47 | MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
48 | layout(getLeft(), getTop(), getRight(), getBottom());
49 | }
50 | };
51 |
52 | @Override
53 | protected void onConfigurationChanged(Configuration newConfig) {
54 | super.onConfigurationChanged(newConfig);
55 |
56 | if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
57 | if (fullScreenOnLandscape) {
58 | this.getPlayer().setFullscreen(true,true);
59 | }
60 | } else if (newConfig.orientation==Configuration.ORIENTATION_PORTRAIT) {
61 | if (exitFullScreenOnPortrait) {
62 | this.getPlayer().setFullscreen(false,false);
63 | }
64 | }
65 | }
66 |
67 | @Override
68 | public boolean onKeyDown(int keyCode, KeyEvent event) {
69 | if (keyCode == KeyEvent.KEYCODE_BACK && this.getPlayer().getFullscreen()) {
70 | this.getPlayer().setFullscreen(false,false);
71 | return true;
72 | }
73 |
74 | return super.onKeyDown(keyCode, event);
75 | }
76 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerAds.java:
--------------------------------------------------------------------------------
1 | package com.jwplayer.rnjwplayer;
2 |
3 | import com.facebook.react.bridge.ReadableArray;
4 | import com.facebook.react.bridge.ReadableMap;
5 | import com.google.ads.interactivemedia.v3.api.FriendlyObstruction;
6 | import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
7 | import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
8 | import com.jwplayer.pub.api.configuration.ads.AdRules;
9 | import com.jwplayer.pub.api.configuration.ads.AdvertisingConfig;
10 | import com.jwplayer.pub.api.configuration.ads.VastAdvertisingConfig;
11 | import com.jwplayer.pub.api.configuration.ads.dai.ImaDaiAdvertisingConfig;
12 | import com.jwplayer.pub.api.configuration.ads.ima.ImaAdvertisingConfig;
13 | import com.jwplayer.pub.api.media.ads.AdBreak;
14 | import com.jwplayer.pub.api.media.ads.dai.ImaDaiSettings;
15 |
16 | import java.util.ArrayList;
17 | import java.util.HashMap;
18 | import java.util.List;
19 | import java.util.Map;
20 | import java.util.Objects;
21 |
22 | public class RNJWPlayerAds {
23 |
24 | // Get advertising config based on the ad client type
25 | public static AdvertisingConfig getAdvertisingConfig(ReadableMap ads) {
26 | if (ads == null) {
27 | return null;
28 | }
29 |
30 | String adClientType = ads.getString("adClient");
31 | switch (adClientType) {
32 | case "ima":
33 | try {
34 | return configureImaAdvertising(ads);
35 | } catch (Exception e) {
36 | throw new RuntimeException(e);
37 | }
38 | case "ima_dai":
39 | try {
40 | return configureImaDaiAdvertising(ads);
41 | } catch (Exception e) {
42 | throw new RuntimeException(e);
43 | }
44 | default: // Defaulting to VAST
45 | return configureVastAdvertising(ads);
46 | }
47 | }
48 |
49 | // Configure IMA Advertising
50 | private static ImaAdvertisingConfig configureImaAdvertising(ReadableMap ads) throws Exception {
51 | if (!BuildConfig.USE_IMA) {
52 | throw new Exception("Error: Google ads services is not installed. Add RNJWPlayerUseGoogleIMA = true to your app/build.gradle ext {}");
53 | }
54 |
55 | ImaAdvertisingConfig.Builder builder = new ImaAdvertisingConfig.Builder();
56 |
57 | List adScheduleList = getAdSchedule(ads);
58 | builder.schedule(adScheduleList);
59 |
60 | if (ads.hasKey("imaSettings")) {
61 | builder.imaSdkSettings(getImaSettings(Objects.requireNonNull(ads.getMap("imaSettings"))));
62 | }
63 |
64 | // companionSlots
65 |
66 | return builder.build();
67 | }
68 |
69 | // Configure IMA DAI Advertising
70 | private static ImaDaiAdvertisingConfig configureImaDaiAdvertising(ReadableMap ads) throws Exception {
71 | if (!BuildConfig.USE_IMA) {
72 | throw new Exception("Error: Google ads services is not installed. Add RNJWPlayerUseGoogleIMA = true to your app/build.gradle ext {}");
73 | }
74 |
75 | ImaDaiAdvertisingConfig.Builder builder = new ImaDaiAdvertisingConfig.Builder();
76 |
77 | if (ads.hasKey("imaSettings")) {
78 | builder.imaSdkSettings(getImaSettings(Objects.requireNonNull(ads.getMap("imaSettings"))));
79 | }
80 |
81 | if (ads.hasKey("imaDaiSettings")) {
82 | builder.imaDaiSettings(getImaDaiSettings(Objects.requireNonNull(ads.getMap("imaDaiSettings"))));
83 | }
84 |
85 | return builder.build();
86 | }
87 |
88 | // You'll need to implement this method based on how you pass ImaDaiSettings from React Native
89 | private static ImaDaiSettings getImaDaiSettings(ReadableMap imaDaiSettingsMap) {
90 | String videoId = imaDaiSettingsMap.hasKey("videoId") ? imaDaiSettingsMap.getString("videoId") : null;
91 | String cmsId = imaDaiSettingsMap.hasKey("cmsId") ? imaDaiSettingsMap.getString("cmsId") : null;
92 | String assetKey = imaDaiSettingsMap.hasKey("assetKey") ? imaDaiSettingsMap.getString("assetKey") : null;
93 | String apiKey = imaDaiSettingsMap.hasKey("apiKey") ? imaDaiSettingsMap.getString("apiKey") : null;
94 |
95 | // Extracting adTagParameters from imaDaiSettingsMap if present
96 | Map adTagParameters = null;
97 | if (imaDaiSettingsMap.hasKey("adTagParameters") && imaDaiSettingsMap.getMap("adTagParameters") != null) {
98 | adTagParameters = new HashMap<>();
99 | ReadableMap adTagParamsMap = imaDaiSettingsMap.getMap("adTagParameters");
100 | for (Map.Entry entry : adTagParamsMap.toHashMap().entrySet()) {
101 | if (entry.getValue() instanceof String) {
102 | adTagParameters.put(entry.getKey(), (String) entry.getValue());
103 | }
104 | }
105 | }
106 |
107 | // Handling streamType
108 | ImaDaiSettings.StreamType streamType = ImaDaiSettings.StreamType.HLS; // Default to HLS
109 | if (imaDaiSettingsMap.hasKey("streamType")) {
110 | String streamTypeStr = imaDaiSettingsMap.getString("streamType");
111 | if ("DASH".equalsIgnoreCase(streamTypeStr)) {
112 | streamType = ImaDaiSettings.StreamType.DASH;
113 | }
114 | }
115 | // Create ImaDaiSettings based on the provided values
116 | ImaDaiSettings imaDaiSettings = (assetKey != null) ?
117 | new ImaDaiSettings(assetKey, streamType, apiKey) :
118 | new ImaDaiSettings(videoId, cmsId, streamType, apiKey);
119 |
120 | if (adTagParameters != null) {
121 | imaDaiSettings.setAdTagParameters(adTagParameters);
122 | }
123 |
124 | return imaDaiSettings;
125 | }
126 |
127 | // Configure VAST Advertising
128 | private static VastAdvertisingConfig configureVastAdvertising(ReadableMap ads) {
129 | VastAdvertisingConfig.Builder builder = new VastAdvertisingConfig.Builder();
130 |
131 | List adScheduleList = getAdSchedule(ads);
132 | builder.schedule(adScheduleList);
133 |
134 | if (ads.hasKey("skipText")) {
135 | builder.skipText(ads.getString("skipText"));
136 | }
137 | if (ads.hasKey("skipMessage")) {
138 | builder.skipMessage(ads.getString("skipMessage"));
139 | }
140 | // ... Add other VAST specific settings from ads ReadableMap
141 |
142 | // Example: Handling VPAID controls
143 | if (ads.hasKey("vpaidControls")) {
144 | builder.vpaidControls(ads.getBoolean("vpaidControls"));
145 | }
146 |
147 | if (ads.hasKey("adRules")) {
148 | AdRules adRules = getAdRules(Objects.requireNonNull(ads.getMap("adRules")));
149 | builder.adRules(adRules);
150 | }
151 |
152 | return builder.build();
153 | }
154 |
155 | private static List getAdSchedule(ReadableMap ads) {
156 | List adScheduleList = new ArrayList<>();
157 | ReadableArray adSchedule = ads.getArray("adSchedule");
158 | for (int i = 0; i < adSchedule.size(); i++) {
159 | ReadableMap adBreakProp = adSchedule.getMap(i);
160 | String offset = adBreakProp.hasKey("offset") ? adBreakProp.getString("offset") : "pre";
161 | if (adBreakProp.hasKey("tag")) {
162 | AdBreak adBreak = new AdBreak.Builder()
163 | .offset(offset)
164 | .tag(adBreakProp.getString("tag"))
165 | .build();
166 | adScheduleList.add(adBreak);
167 | }
168 | }
169 | return adScheduleList;
170 | }
171 |
172 | public static AdRules getAdRules(ReadableMap adRulesMap) {
173 | AdRules.Builder builder = new AdRules.Builder();
174 |
175 | if (adRulesMap.hasKey("startOn")) {
176 | Integer startOn = adRulesMap.getInt("startOn");
177 | builder.startOn(startOn);
178 | }
179 | if (adRulesMap.hasKey("frequency")) {
180 | Integer frequency = adRulesMap.getInt("frequency");
181 | builder.frequency(frequency);
182 | }
183 | if (adRulesMap.hasKey("timeBetweenAds")) {
184 | Integer timeBetweenAds = adRulesMap.getInt("timeBetweenAds");
185 | builder.timeBetweenAds(timeBetweenAds);
186 | }
187 | if (adRulesMap.hasKey("startOnSeek")) {
188 | String startOnSeek = adRulesMap.getString("startOnSeek");
189 | // Mapping the string to the corresponding constant in AdRules
190 | String mappedStartOnSeek = mapStartOnSeek(startOnSeek);
191 | builder.startOnSeek(mappedStartOnSeek);
192 | }
193 |
194 | return builder.build();
195 | }
196 |
197 | private static String mapStartOnSeek(String startOnSeek) {
198 | if ("pre".equals(startOnSeek)) {
199 | return AdRules.RULES_START_ON_SEEK_PRE;
200 | }
201 | // Default to "none" if not "pre"
202 | return AdRules.RULES_START_ON_SEEK_NONE;
203 | }
204 |
205 | // public static List getFriendlyObstructions(ReadableArray obstructionsArray) {
206 | // List obstructions = new ArrayList<>();
207 | // // Example: Parse and create FriendlyObstruction objects from obstructionsArray
208 | // return obstructions;
209 | // }
210 |
211 | public static ImaSdkSettings getImaSettings(ReadableMap imaSettingsMap) {
212 | ImaSdkSettings settings = ImaSdkFactory.getInstance().createImaSdkSettings();
213 |
214 | if (imaSettingsMap.hasKey("maxRedirects")) {
215 | settings.setMaxRedirects(imaSettingsMap.getInt("maxRedirects"));
216 | }
217 | if (imaSettingsMap.hasKey("language")) {
218 | settings.setLanguage(imaSettingsMap.getString("language"));
219 | }
220 | if (imaSettingsMap.hasKey("ppid")) {
221 | settings.setPpid(imaSettingsMap.getString("ppid"));
222 | }
223 | if (imaSettingsMap.hasKey("playerType")) {
224 | settings.setPlayerType(imaSettingsMap.getString("playerType"));
225 | }
226 | if (imaSettingsMap.hasKey("playerVersion")) {
227 | settings.setPlayerVersion(imaSettingsMap.getString("playerVersion"));
228 | }
229 | if (imaSettingsMap.hasKey("sessionId")) {
230 | settings.setSessionId(imaSettingsMap.getString("sessionId"));
231 | }
232 | if (imaSettingsMap.hasKey("debugMode")) {
233 | settings.setDebugMode(imaSettingsMap.getBoolean("debugMode"));
234 | }
235 | // Add other settings as needed
236 |
237 | return settings;
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerPackage.java:
--------------------------------------------------------------------------------
1 |
2 | package com.jwplayer.rnjwplayer;
3 |
4 | import com.facebook.react.ReactPackage;
5 | import com.facebook.react.bridge.JavaScriptModule;
6 | import com.facebook.react.bridge.NativeModule;
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.uimanager.ViewManager;
9 |
10 | import java.util.Arrays;
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | public class RNJWPlayerPackage implements ReactPackage {
15 |
16 | @Override
17 | public List createNativeModules(ReactApplicationContext reactContext) {
18 | return Arrays.asList(new RNJWPlayerModule(reactContext));
19 | }
20 |
21 | // Backwards compatability for RN < 0.47
22 | public List> createJSModules() {
23 | return Collections.emptyList();
24 | }
25 |
26 | @Override
27 | public List createViewManagers(ReactApplicationContext reactContext) {
28 | return Arrays.asList(new RNJWPlayerViewManager(reactContext));
29 | }
30 | }
--------------------------------------------------------------------------------
/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerViewManager.java:
--------------------------------------------------------------------------------
1 | package com.jwplayer.rnjwplayer;
2 |
3 | import com.facebook.react.bridge.ReactApplicationContext;
4 | import com.facebook.react.bridge.ReadableMap;
5 | import com.facebook.react.common.MapBuilder;
6 | import com.facebook.react.uimanager.SimpleViewManager;
7 | import com.facebook.react.uimanager.ThemedReactContext;
8 | import com.facebook.react.uimanager.annotations.ReactProp;
9 |
10 | import java.util.Map;
11 |
12 | import javax.annotation.Nonnull;
13 |
14 | public class RNJWPlayerViewManager extends SimpleViewManager {
15 |
16 | public static final String REACT_CLASS = "RNJWPlayerView";
17 |
18 | private final ReactApplicationContext mAppContext;
19 |
20 | private static final String TAG = "RNJWPlayerViewManager";
21 |
22 | @Override
23 | public String getName() {
24 | return REACT_CLASS;
25 | }
26 |
27 | public RNJWPlayerViewManager(ReactApplicationContext context) {
28 | mAppContext = context;
29 | }
30 |
31 | @Override
32 | public RNJWPlayerView createViewInstance(ThemedReactContext context) {
33 | return new RNJWPlayerView(context, mAppContext);
34 | }
35 |
36 | @ReactProp(name = "config")
37 | public void setConfig(RNJWPlayerView view, ReadableMap prop) {
38 | view.setConfig(prop);
39 | }
40 |
41 | @ReactProp(name = "controls")
42 | public void setControls(RNJWPlayerView view, Boolean controls) {
43 | if (view == null || view.mPlayerView == null) {
44 | return;
45 | }
46 | view.mPlayerView.getPlayer().setControls(controls);
47 | }
48 |
49 | public Map getExportedCustomBubblingEventTypeConstants() {
50 | return MapBuilder.builder()
51 | .put(
52 | "topPlayerError",
53 | MapBuilder.of(
54 | "phasedRegistrationNames",
55 | MapBuilder.of("bubbled", "onPlayerError")))
56 | .put("topSetupPlayerError",
57 | MapBuilder.of(
58 | "phasedRegistrationNames",
59 | MapBuilder.of("bubbled", "onSetupPlayerError")))
60 | .put("topPlayerAdError",
61 | MapBuilder.of(
62 | "phasedRegistrationNames",
63 | MapBuilder.of("bubbled", "onPlayerAdError")))
64 | .put("topPlayerAdWarning",
65 | MapBuilder.of(
66 | "phasedRegistrationNames",
67 | MapBuilder.of("bubbled", "onPlayerAdWarning")))
68 | .put("topAdEvent",
69 | MapBuilder.of(
70 | "phasedRegistrationNames",
71 | MapBuilder.of("bubbled", "onAdEvent")))
72 | .put("topAdTime",
73 | MapBuilder.of(
74 | "phasedRegistrationNames",
75 | MapBuilder.of("bubbled", "onAdTime")))
76 | .put("topTime",
77 | MapBuilder.of(
78 | "phasedRegistrationNames",
79 | MapBuilder.of("bubbled", "onTime")))
80 | .put("topBuffer",
81 | MapBuilder.of(
82 | "phasedRegistrationNames",
83 | MapBuilder.of("bubbled", "onBuffer")))
84 | .put("topFullScreen",
85 | MapBuilder.of(
86 | "phasedRegistrationNames",
87 | MapBuilder.of("bubbled", "onFullScreen")))
88 | .put("topFullScreenExitRequested",
89 | MapBuilder.of(
90 | "phasedRegistrationNames",
91 | MapBuilder.of("bubbled", "onFullScreenExitRequested")))
92 | .put("topFullScreenRequested",
93 | MapBuilder.of(
94 | "phasedRegistrationNames",
95 | MapBuilder.of("bubbled", "onFullScreenRequested")))
96 | .put("topFullScreenExit",
97 | MapBuilder.of(
98 | "phasedRegistrationNames",
99 | MapBuilder.of("bubbled", "onFullScreenExit")))
100 | .put("topPause",
101 | MapBuilder.of(
102 | "phasedRegistrationNames",
103 | MapBuilder.of("bubbled", "onPause")))
104 | .put("topPlay",
105 | MapBuilder.of(
106 | "phasedRegistrationNames",
107 | MapBuilder.of("bubbled", "onPlay")))
108 | .put("topComplete",
109 | MapBuilder.of(
110 | "phasedRegistrationNames",
111 | MapBuilder.of("bubbled", "onComplete")))
112 | .put("topPlaylistComplete",
113 | MapBuilder.of(
114 | "phasedRegistrationNames",
115 | MapBuilder.of("bubbled", "onPlaylistComplete")))
116 | .put("topPlaylistItem",
117 | MapBuilder.of(
118 | "phasedRegistrationNames",
119 | MapBuilder.of("bubbled", "onPlaylistItem")))
120 | .put("topSeek",
121 | MapBuilder.of(
122 | "phasedRegistrationNames",
123 | MapBuilder.of("bubbled", "onSeek")))
124 | .put("topSeeked",
125 | MapBuilder.of(
126 | "phasedRegistrationNames",
127 | MapBuilder.of("bubbled", "onSeeked")))
128 | .put("topRateChanged",
129 | MapBuilder.of(
130 | "phasedRegistrationNames",
131 | MapBuilder.of("bubbled", "onRateChanged")))
132 | .put("topControlBarVisible",
133 | MapBuilder.of(
134 | "phasedRegistrationNames",
135 | MapBuilder.of("bubbled", "onControlBarVisible")))
136 | .put("topOnPlayerReady",
137 | MapBuilder.of(
138 | "phasedRegistrationNames",
139 | MapBuilder.of("bubbled", "onPlayerReady")))
140 | .put("topBeforePlay",
141 | MapBuilder.of(
142 | "phasedRegistrationNames",
143 | MapBuilder.of("bubbled", "onBeforePlay")))
144 | .put("topBeforeComplete",
145 | MapBuilder.of(
146 | "phasedRegistrationNames",
147 | MapBuilder.of("bubbled", "onBeforeComplete")))
148 | .put("topAdPlay",
149 | MapBuilder.of(
150 | "phasedRegistrationNames",
151 | MapBuilder.of("bubbled", "onAdPlay")))
152 | .put("topAdPause",
153 | MapBuilder.of(
154 | "phasedRegistrationNames",
155 | MapBuilder.of("bubbled", "onAdPause")))
156 | .put("topAudioTracks",
157 | MapBuilder.of(
158 | "phasedRegistrationNames",
159 | MapBuilder.of("bubbled", "onAudioTracks")))
160 | .put("topCaptionsChanged",
161 | MapBuilder.of(
162 | "phasedRegistrationNames",
163 | MapBuilder.of("bubbled", "onCaptionsChanged")))
164 | .put("topCaptionsList",
165 | MapBuilder.of(
166 | "phasedRegistrationNames",
167 | MapBuilder.of("bubbled", "onCaptionsList")))
168 | .put("topCasting",
169 | MapBuilder.of(
170 | "phasedRegistrationNames",
171 | MapBuilder.of("bubbled", "onCasting")))
172 | .put("topFirstFrame",
173 | MapBuilder.of(
174 | "phasedRegistrationNames",
175 | MapBuilder.of("bubbled", "onLoaded")))
176 | .put("topBeforeNextPlaylistItem",
177 | MapBuilder.of(
178 | "phasedRegistrationNames",
179 | MapBuilder.of("bubbled", "onBeforeNextPlaylistItem")))
180 | .build();
181 | }
182 |
183 | @Override
184 | public void onDropViewInstance(@Nonnull RNJWPlayerView view) {
185 | view.destroyPlayer();
186 | super.onDropViewInstance(view);
187 | view = null;
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/android/src/main/java/com/jwplayer/rnjwplayer/WidevineCallback.java:
--------------------------------------------------------------------------------
1 | package com.jwplayer.rnjwplayer;
2 |
3 | import android.annotation.TargetApi;
4 | import android.os.Parcel;
5 | import android.text.TextUtils;
6 |
7 | import androidx.media3.exoplayer.drm.ExoMediaDrm;
8 | import com.jwplayer.pub.api.media.drm.MediaDrmCallback;
9 |
10 | import java.io.IOException;
11 | import java.util.UUID;
12 |
13 | @TargetApi(18)
14 | public class WidevineCallback implements MediaDrmCallback {
15 |
16 | private final String defaultUri;
17 |
18 | public WidevineCallback(String drmAuthUrl) {
19 | defaultUri = drmAuthUrl;
20 | }
21 |
22 | protected WidevineCallback(Parcel in) {
23 | defaultUri = in.readString();
24 | }
25 |
26 | public static final Creator CREATOR = new Creator() {
27 | @Override
28 | public WidevineCallback createFromParcel(Parcel in) {
29 | return new WidevineCallback(in);
30 | }
31 |
32 | @Override
33 | public WidevineCallback[] newArray(int size) {
34 | return new WidevineCallback[size];
35 | }
36 | };
37 |
38 | @Override
39 | public byte[] executeProvisionRequest(UUID uuid, ExoMediaDrm.ProvisionRequest request) throws IOException {
40 | String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
41 | return Util.executePost(url, null, null);
42 | }
43 |
44 | @Override
45 | public byte[] executeKeyRequest(UUID uuid, ExoMediaDrm.KeyRequest request) throws IOException {
46 | String url = request.getLicenseServerUrl();
47 | if (TextUtils.isEmpty(url)) {
48 | url = defaultUri;
49 | }
50 | return Util.executePost(url, request.getData(), null);
51 | }
52 |
53 | @Override
54 | public int describeContents() {
55 | return 0;
56 | }
57 |
58 | @Override
59 | public void writeToParcel(Parcel dest, int flags) {
60 | dest.writeString(defaultUri);
61 | }
62 | }
--------------------------------------------------------------------------------
/badges/license.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/badges/version.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/props.md:
--------------------------------------------------------------------------------
1 | # Props
2 |
3 | This wrapper implements the native methods exposed by the [Android](https://sdk.jwplayer.com/android/v4/reference/com/jwplayer/pub/api/JsonHelper.html) and [iOS](https://sdk.jwplayer.com/ios/v4/reference/Classes/JWJSONParser.html) SDK for parsing JSON objects into player configs.
4 |
5 | | Prop | Type | Platform | Description | Default |
6 | | --- | --- | --- | --- | --- |
7 | | `config` | object | A, I | **(REQUIRED)** `JWConfig` object
See: [Config](#config) | `undefined` |
8 | | `controls` | boolean | A, I | Determines if player controls are displayed | `true` |
9 |
10 |
11 |
12 | ## Config
13 | With the exception of `license` and `playlist`, all other fields are optional.
14 |
15 | See [`types`](./types.md) for a definition of config types defined below
16 |
17 | | Field | Type | Platform | Additional Notes |
18 | | --- | --- | --- | --- |
19 | | `license` | string |A, I | **(REQUIRED)** Platform-specific license key
([Android](https://docs.jwplayer.com/players/docs/android-overview#requirements) \| [iOS](https://docs.jwplayer.com/players/docs/ios-overview#requirements))|
20 | | [`playlist`](https://docs.jwplayer.com/players/reference/setup-options#playlist) | JwPlaylistItem[ ] | string | A, I | **(REQUIRED)** |
21 | | [`advertising`](https://docs.jwplayer.com/players/reference/advertising-config-ref) |JwAdvertisingConfig | A, I | |
22 | | `allowCrossProtocolRedirectsSupport` | boolean | A | |
23 | | [`autostart`](https://docs.jwplayer.com/players/reference/setup-options#autostart) | boolean | A, I | |
24 | | [`displaydescription`](https://docs.jwplayer.com/players/reference/setup-options#displaydescription) | boolean| A, I | |
25 | | [`displaytitle`](https://docs.jwplayer.com/players/reference/setup-options#displaytitle) | boolean | A | |
26 | | [`file`](https://docs.jwplayer.com/players/reference/playlists#file) | string | A, I | |
27 | | `forceLegacyConfig` | boolean | A, I | Determines whether to use the legacy configuration settings |
28 | | `logoView` | JwLogoView | A, I | |
29 | | [`mute`](https://docs.jwplayer.com/players/reference/setup-options#mute) | boolean | A, I | |
30 | | [`nextupoffset`](https://docs.jwplayer.com/players/reference/setup-options#nextupoffset) | string | number | A, I | |
31 | | `pid` | string | A, I | Unique identifier of the player |
32 | | [`playbackRateControls`](https://docs.jwplayer.com/players/reference/setup-options#playbackratecontrols) | boolean | A, I | |
33 | | [`playbackRates`](https://docs.jwplayer.com/players/reference/setup-options#playbackrates)|number[ ] | A, I | |
34 | | [`playlistIndex`](https://docs.jwplayer.com/players/reference/setup-options#playlistIndex) | number | A, I | |
35 | | [`preload`](https://docs.jwplayer.com/players/reference/setup-options#preload) | boolean | A, I | |
36 | | [`related`](https://docs.jwplayer.com/players/reference/related-config-ref) | JwRelatedConfig | A, I | |
37 | | [`repeat`](https://docs.jwplayer.com/players/reference/setup-options#repeat) | boolean | A, I | |
38 | | [`sources`](https://docs.jwplayer.com/players/reference/playlists#playlistsources) | JwSource[ ] | A, I | |
39 | | [`stretching`](https://docs.jwplayer.com/players/reference/setup-options#stretching) | JwStretching | A, I | |
40 | | `thumbnailPreview` | JwThumbnailPreview| A, I | |
41 | | `uiConfig` | JwUiConfig | A, I | |
42 | | `useTextureView` | boolean | A | |
43 |
44 |
--------------------------------------------------------------------------------
/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/images/1.png
--------------------------------------------------------------------------------
/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/images/2.png
--------------------------------------------------------------------------------
/images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwplayer/jwplayer-react-native/60d2d5f1e873394d57eee801d178e0d20c4072ae/images/3.png
--------------------------------------------------------------------------------
/ios/RNJWPlayer/RCTConvert+RNJWPlayer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RNJWPlayerViewController.m
3 | // RNJWPlayer
4 | //
5 | // Created by Chaim Paneth on 3/30/22.
6 | //
7 |
8 | import Foundation
9 | import React
10 | import JWPlayerKit
11 |
12 | extension RCTConvert {
13 |
14 | static func JWAdClient(_ value: String) -> JWAdClient {
15 | switch value {
16 | case "vast":
17 | return .JWPlayer
18 | case "ima":
19 | return .GoogleIMA
20 | case "ima_dai":
21 | return .GoogleIMADAI
22 | default:
23 | return .unknown
24 | }
25 | }
26 |
27 | static func JWInterfaceBehavior(_ value: String) -> JWInterfaceBehavior {
28 | switch value {
29 | case "normal":
30 | return .normal
31 | case "hidden":
32 | return .hidden
33 | case "onscreen":
34 | return .alwaysOnScreen
35 | default:
36 | return .normal
37 | }
38 | }
39 |
40 | static func JWCaptionEdgeStyle(_ value: String) -> JWCaptionEdgeStyle {
41 | switch value {
42 | case "none":
43 | return .none
44 | case "dropshadow":
45 | return .dropshadow
46 | case "raised":
47 | return .raised
48 | case "depressed":
49 | return .depressed
50 | case "uniform":
51 | return .uniform
52 | default:
53 | return .undefined
54 | }
55 | }
56 |
57 | static func JWPreload(_ value: String) -> JWPreload {
58 | switch value {
59 | case "auto":
60 | return .auto
61 | case "none":
62 | return .none
63 | default:
64 | return .none
65 | }
66 | }
67 |
68 | static func JWRelatedOnClick(_ value: String) -> JWRelatedOnClick {
69 | switch value {
70 | case "play":
71 | return .play
72 | case "link":
73 | return .link
74 | default:
75 | return .play
76 | }
77 | }
78 |
79 | static func JWRelatedOnComplete(_ value: String) -> JWRelatedOnComplete {
80 | switch value {
81 | case "show":
82 | return .show
83 | case "hide":
84 | return .hide
85 | case "autoplay":
86 | return .autoplay
87 | default:
88 | return .show
89 | }
90 | }
91 |
92 | static func JWControlType(_ value: String) -> JWControlType {
93 | switch value {
94 | case "forward":
95 | return .fastForwardButton
96 | case "rewind":
97 | return .rewindButton
98 | case "pip":
99 | return .pictureInPictureButton
100 | case "airplay":
101 | return .airplayButton
102 | case "chromecast":
103 | return .chromecastButton
104 | case "next":
105 | return .nextButton
106 | case "previous":
107 | return .previousButton
108 | case "settings":
109 | return .settingsButton
110 | case "languages":
111 | return .languagesButton
112 | case "fullscreen":
113 | return .fullscreenButton
114 | default:
115 | return .fullscreenButton
116 | }
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/ios/RNJWPlayer/RNJWPlayer-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #if __has_include("React/RCTViewManager.h")
2 | #import "React/RCTViewManager.h"
3 | #else
4 | #import "RCTViewManager.h"
5 | #endif
6 |
--------------------------------------------------------------------------------
/ios/RNJWPlayer/RNJWPlayerAds.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RNJWPlayerAds.swift
3 | // RNJWPlayer
4 | //
5 | // Created by Chaim Paneth on 24/12/2023.
6 | //
7 |
8 | import Foundation
9 | import JWPlayerKit
10 |
11 | class RNJWPlayerAds {
12 |
13 | // Convert configureVASTWithAds function
14 | static func configureVAST(with ads: [String: Any]) -> JWAdvertisingConfig? {
15 | let adConfigBuilder = JWAdsAdvertisingConfigBuilder()
16 |
17 | // Configure VAST specific settings here
18 | // Example: setting ad schedule, tag, etc.
19 |
20 | if let scheduleArray = getAdSchedule(from: ads), !scheduleArray.isEmpty {
21 | adConfigBuilder.schedule(scheduleArray)
22 | }
23 |
24 | if let tag = ads["tag"] as? String, let encodedString = tag.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed), let tagUrl = URL(string: encodedString) {
25 | adConfigBuilder.tag(tagUrl)
26 | }
27 |
28 | if let adVmap = ads["adVmap"] as? String, let encodedString = adVmap.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed), let adVmapUrl = URL(string: encodedString) {
29 | adConfigBuilder.vmapURL(adVmapUrl)
30 | }
31 |
32 | if let openBrowserOnAdClick = ads["openBrowserOnAdClick"] as? Bool {
33 | adConfigBuilder.openBrowserOnAdClick(openBrowserOnAdClick)
34 | }
35 |
36 | if let adRulesDict = ads["adRules"] as? [String: Any], let adRules = getAdRules(from: adRulesDict) {
37 | adConfigBuilder.adRules(adRules)
38 | }
39 |
40 | if let adSettingsDict = ads["adSettings"] as? [String: Any] {
41 | if let adSettings = getAdSettings(from: adSettingsDict) {
42 | adConfigBuilder.adSettings(adSettings)
43 | }
44 | }
45 |
46 | return try? adConfigBuilder.build()
47 | }
48 |
49 | // Convert configureIMAWithAds function
50 | static func configureIMA(with ads: [String: Any]) -> JWAdvertisingConfig? {
51 | // Ensure Google IMA SDK is available
52 | #if !USE_GOOGLE_IMA
53 | assertionFailure("Error: GoogleAds-IMA-iOS-SDK is not installed. Add $RNJWPlayerUseGoogleIMA = true; to your podfile")
54 | #endif
55 |
56 | let adConfigBuilder = JWImaAdvertisingConfigBuilder()
57 |
58 | // Configure Google IMA specific settings here
59 | // Example: setting ad schedule, tag, IMA settings, etc.
60 |
61 | if let scheduleArray = getAdSchedule(from: ads), !scheduleArray.isEmpty {
62 | adConfigBuilder.schedule(scheduleArray)
63 | }
64 |
65 | if let tag = ads["tag"] as? String, let encodedString = tag.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed), let tagUrl = URL(string: encodedString) {
66 | adConfigBuilder.tag(tagUrl)
67 | }
68 |
69 | if let adVmap = ads["adVmap"] as? String, let encodedString = adVmap.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed), let adVmapUrl = URL(string: encodedString) {
70 | adConfigBuilder.vmapURL(adVmapUrl)
71 | }
72 |
73 | if let adRulesDict = ads["adRules"] as? [String: Any], let adRules = getAdRules(from: adRulesDict) {
74 | adConfigBuilder.adRules(adRules)
75 | }
76 |
77 | if let imaSettingsDict = ads["imaSettings"] as? [String: Any] {
78 | if let imaSettings = getIMASettings(from: imaSettingsDict) {
79 | adConfigBuilder.imaSettings(imaSettings)
80 | }
81 | }
82 |
83 | return try? adConfigBuilder.build()
84 | }
85 |
86 | // Convert configureIMADAIWithAds function
87 | static func configureIMADAI(with ads: [String: Any]) -> JWAdvertisingConfig? {
88 | // Ensure Google IMA SDK is available
89 | #if !USE_GOOGLE_IMA
90 | assertionFailure("Error: GoogleAds-IMA-iOS-SDK is not installed. Add $RNJWPlayerUseGoogleIMA = true; to your podfile")
91 | #endif
92 |
93 | let adConfigBuilder = JWImaDaiAdvertisingConfigBuilder()
94 |
95 | // Configure Google IMA DAI specific settings here
96 | // Example: setting stream configuration, friendly obstructions, etc.
97 |
98 | if let imaSettingsDict = ads["imaSettings"] as? [String: Any] {
99 | if let imaSettings = getIMASettings(from: imaSettingsDict) {
100 | adConfigBuilder.imaSettings(imaSettings)
101 | }
102 | }
103 |
104 | if let googleDAIStreamDict = ads["googleDAIStream"] as? [String: Any], let googleDAIStream = getGoogleDAIStream(from: googleDAIStreamDict) {
105 | adConfigBuilder.googleDAIStream(googleDAIStream)
106 | }
107 |
108 | return try? adConfigBuilder.build()
109 | }
110 |
111 | // Convert getAdSchedule function
112 | static func getAdSchedule(from ads: [String: Any]) -> [JWAdBreak]? {
113 | guard let schedule = ads["adSchedule"] as? [[String: Any]], !schedule.isEmpty else { return nil }
114 |
115 | var scheduleArray: [JWAdBreak] = []
116 |
117 | for item in schedule {
118 | if let offsetString = item["offset"] as? String, let tag = item["tag"] as? String, let encodedString = tag.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed), let tagUrl = URL(string: encodedString) {
119 |
120 | let adBreakBuilder = JWAdBreakBuilder()
121 | if let offset = JWAdOffset.from(string: offsetString) {
122 | adBreakBuilder.offset(offset)
123 | adBreakBuilder.tags([tagUrl])
124 |
125 | if let adBreak = try? adBreakBuilder.build() {
126 | scheduleArray.append(adBreak)
127 | }
128 | }
129 | }
130 | }
131 |
132 | return scheduleArray.isEmpty ? nil : scheduleArray
133 | }
134 |
135 | // Convert getAdRules function
136 | static func getAdRules(from adRulesDict: [String: Any]) -> JWAdRules? {
137 | let builder = JWAdRulesBuilder()
138 |
139 | if let startOn = adRulesDict["startOn"] as? UInt, let frequency = adRulesDict["frequency"] as? UInt, let timeBetweenAds = adRulesDict["timeBetweenAds"] as? UInt {
140 | let startOnSeek = mapStringToJWAdShownOnSeek(adRulesDict["startOnSeek"] as? String)
141 | builder.jwRules(startOn: startOn, frequency: frequency, timeBetweenAds: timeBetweenAds, startOnSeek: startOnSeek)
142 | }
143 |
144 | return try? builder.build()
145 | }
146 |
147 | // Convert mapStringToJWAdShownOnSeek function
148 | static func mapStringToJWAdShownOnSeek(_ seekString: String?) -> JWAdShownOnSeek {
149 | guard let seekString = seekString else { return .none }
150 | switch seekString {
151 | case "pre":
152 | return .pre
153 | default:
154 | return .none
155 | }
156 | }
157 |
158 | // Convert getAdSettings function
159 | static func getAdSettings(from settingsDict: [String: Any]) -> JWAdSettings? {
160 | let builder = JWAdSettingsBuilder()
161 |
162 | if let allowsBackgroundPlayback = settingsDict["allowsBackgroundPlayback"] as? Bool {
163 | builder.allowsBackgroundPlayback(allowsBackgroundPlayback)
164 | }
165 |
166 | // Add other settings as needed
167 |
168 | return builder.build()
169 | }
170 |
171 | // Convert getIMASettings function
172 | static func getIMASettings(from imaSettingsDict: [String: Any]) -> JWImaSettings? {
173 | let builder = JWImaSettingsBuilder()
174 | if let locale = imaSettingsDict["locale"] as? String {
175 | builder.locale(locale)
176 | }
177 | if let ppid = imaSettingsDict["ppid"] as? String {
178 | builder.ppid(ppid)
179 | }
180 | if let maxRedirects = imaSettingsDict["maxRedirects"] as? UInt {
181 | builder.maxRedirects(maxRedirects)
182 | }
183 | if let sessionID = imaSettingsDict["sessionID"] as? String {
184 | builder.sessionID(sessionID)
185 | }
186 | if let debugMode = imaSettingsDict["debugMode"] as? Bool {
187 | builder.debugMode(debugMode)
188 | }
189 | return builder.build()
190 | }
191 |
192 | // Convert getGoogleDAIStream function
193 | static func getGoogleDAIStream(from googleDAIStreamDict: [String: Any]) -> JWGoogleDAIStream? {
194 | let builder = JWGoogleDAIStreamBuilder()
195 | if let videoID = googleDAIStreamDict["videoID"] as? String, let cmsID = googleDAIStreamDict["cmsID"] as? String {
196 | builder.vodStreamInfo(videoID: videoID, cmsID: cmsID)
197 | } else if let assetKey = googleDAIStreamDict["assetKey"] as? String {
198 | builder.liveStreamInfo(assetKey: assetKey)
199 | }
200 | if let apiKey = googleDAIStreamDict["apiKey"] as? String {
201 | builder.apiKey(apiKey)
202 | }
203 | if let adTagParameters = googleDAIStreamDict["adTagParameters"] as? [String: Any] {
204 | let stringParams = adTagParameters.compactMapValues { $0 as? String }
205 | builder.adTagParameters(stringParams)
206 | }
207 | return try? builder.build()
208 | }
209 |
210 | // Placeholder for findViewWithId function - Needs implementation
211 | // static func findView(withId viewId: String) -> UIView? {
212 | // // Implementation needed to find and return the view with the given id
213 | // return nil
214 | // }
215 | //
216 | // static func createFriendlyObstructions(fromArray obstructionsArray: [[String: Any]]) -> [JWFriendlyObstruction] {
217 | // var obstructions: [JWFriendlyObstruction] = []
218 | //
219 | // for obstructionDict in obstructionsArray {
220 | // if let viewId = obstructionDict["viewId"] as? String,
221 | // let view = findView(withId: viewId),
222 | // let purposeString = obstructionDict["purpose"] as? String,
223 | // let reason = obstructionDict["reason"] as? String {
224 | // let purpose = mapStringToJWFriendlyObstructionPurpose(purposeString)
225 | // let obstruction = JWFriendlyObstruction(view: view, purpose: purpose, reason: reason)
226 | // obstructions.append(obstruction)
227 | // }
228 | // }
229 | //
230 | // return obstructions
231 | // }
232 | //
233 | // static func mapStringToJWFriendlyObstructionPurpose(_ purposeString: String) -> JWFriendlyObstructionPurpose {
234 | // switch purposeString {
235 | // case "mediaControls":
236 | // return .mediaControls
237 | // case "closeAd":
238 | // return .closeAd
239 | // case "notVisible":
240 | // return .notVisible
241 | // default:
242 | // return .other
243 | // }
244 | // }
245 | //
246 | // static func createCompanionAdSlot(fromDictionary companionAdSlotDict: [String: Any]) -> JWCompanionAdSlot? {
247 | // guard let viewId = companionAdSlotDict["viewId"] as? String,
248 | // let view = findView(withId: viewId),
249 | // let sizeDict = companionAdSlotDict["size"] as? [String: Float],
250 | // let width = sizeDict["width"],
251 | // let height = sizeDict["height"] else {
252 | // return nil
253 | // }
254 | //
255 | // let size = CGSize(width: CGFloat(width), height: CGFloat(height))
256 | // let slot = JWCompanionAdSlot(view: view, size: size)
257 | // return slot
258 | // }
259 | }
260 |
261 |
--------------------------------------------------------------------------------
/ios/RNJWPlayer/RNJWPlayerModels.swift:
--------------------------------------------------------------------------------
1 | // This file was generated from JSON Schema using quicktype, do not modify it directly.
2 | // To parse the JSON, add this file to your project and do:
3 | //
4 | // let jWConfig = try? JSONDecoder().decode(JWConfig.self, from: jsonData)
5 |
6 | import Foundation
7 |
8 | // MARK: - JWConfig
9 | struct JWConfig: Codable {
10 | let config: Config
11 | }
12 |
13 | // MARK: - Config
14 | struct Config: Codable {
15 | let license: String
16 | let advertising: Advertising
17 | let autostart, controls, configRepeat: Bool
18 | let nextUpStyle: NextUpStyle
19 | let styling: Styling
20 | let backgroundAudioEnabled: Bool
21 | let category: String
22 | let categoryOptions: [String]
23 | let mode: String
24 | let fullScreenOnLandscape, landscapeOnFullScreen, portraitOnExitFullScreen, exitFullScreenOnPortrait: Bool
25 | let playlist: [Playlist]
26 | let stretching: String
27 | let related: Related
28 | let preload, interfaceBehavior: String
29 | let interfaceFadeDelay: Int
30 | let hideUIGroups: [String]
31 | let processSpcURL, fairplayCERTURL, contentUUID: String
32 | let viewOnly, enableLockScreenControls, pipEnabled: Bool
33 |
34 | enum CodingKeys: String, CodingKey {
35 | case license, advertising, autostart, controls
36 | case configRepeat = "repeat"
37 | case nextUpStyle, styling, backgroundAudioEnabled, category, categoryOptions, mode, fullScreenOnLandscape, landscapeOnFullScreen, portraitOnExitFullScreen, exitFullScreenOnPortrait, playlist, stretching, related, preload, interfaceBehavior, interfaceFadeDelay, hideUIGroups
38 | case processSpcURL = "processSpcUrl"
39 | case fairplayCERTURL = "fairplayCertUrl"
40 | case contentUUID, viewOnly, enableLockScreenControls, pipEnabled
41 | }
42 | }
43 |
44 | // MARK: - Advertising
45 | struct Advertising: Codable {
46 | let adSchedule: [AdSchedule]
47 | let adVmap, tag: String
48 | let openBrowserOnAdClick: Bool
49 | let adClient: String
50 | }
51 |
52 | // MARK: - AdSchedule
53 | struct AdSchedule: Codable {
54 | let tag, offset: String
55 | }
56 |
57 | // MARK: - NextUpStyle
58 | struct NextUpStyle: Codable {
59 | let offsetSeconds, offsetPercentage: Int
60 | }
61 |
62 | // MARK: - Playlist
63 | struct Playlist: Codable {
64 | let file: String
65 | let sources: [Source]
66 | let image, title, description, mediaID: String
67 | let adSchedule: [AdSchedule]
68 | let adVmap: String
69 | let tracks: [Source]
70 | let recommendations, startTime: String
71 | let autostart: Bool
72 |
73 | enum CodingKeys: String, CodingKey {
74 | case file, sources, image, title, description
75 | case mediaID = "mediaId"
76 | case adSchedule, adVmap, tracks, recommendations, startTime, autostart
77 | }
78 | }
79 |
80 | // MARK: - Source
81 | struct Source: Codable {
82 | let file, label: String
83 | let sourceDefault: Bool
84 |
85 | enum CodingKeys: String, CodingKey {
86 | case file, label
87 | case sourceDefault = "default"
88 | }
89 | }
90 |
91 | // MARK: - Related
92 | struct Related: Codable {
93 | let onClick, onComplete, heading, url: String
94 | let autoplayMessage: String
95 | let autoplayTimer: Int
96 | }
97 |
98 | // MARK: - Styling
99 | struct Styling: Codable {
100 | let colors: Colors
101 | let font: Font
102 | let displayTitle, displayDescription: Bool
103 | let captionsStyle: CaptionsStyle
104 | let menuStyle: MenuStyle
105 | }
106 |
107 | // MARK: - CaptionsStyle
108 | struct CaptionsStyle: Codable {
109 | let font: Font
110 | let fontColor, backgroundColor, highlightColor, edgeStyle: String
111 | }
112 |
113 | // MARK: - Font
114 | struct Font: Codable {
115 | let name: String
116 | let size: Int
117 | }
118 |
119 | // MARK: - Colors
120 | struct Colors: Codable {
121 | let buttons, backgroundColor, fontColor: String
122 | let timeslider: Timeslider
123 | }
124 |
125 | // MARK: - Timeslider
126 | struct Timeslider: Codable {
127 | let progress, rail, thumb: String
128 | }
129 |
130 | // MARK: - MenuStyle
131 | struct MenuStyle: Codable {
132 | let font: Font
133 | let fontColor, backgroundColor: String
134 | }
135 |
136 | // MARK: - Extensions
137 | extension Decodable {
138 | init(_ dict: [Key: Value]) throws where Key: Codable, Value: Codable {
139 | let data = try JSONEncoder().encode(dict)
140 | self = try JSONDecoder().decode(Self.self, from: data)
141 | }
142 | }
143 |
144 | extension Decodable {
145 | init(_ dict: [Key: Any]) throws {
146 | let data = try JSONSerialization.data(withJSONObject: dict, options: [])
147 | self = try JSONDecoder().decode(Self.self, from: data)
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/ios/RNJWPlayer/RNJWPlayerViewManager.m:
--------------------------------------------------------------------------------
1 | #if __has_include("React/RCTViewManager.h")
2 | #import "React/RCTViewManager.h"
3 | #else
4 | #import "RCTViewManager.h"
5 | #endif
6 |
7 | #import
8 |
9 | #import "RCTUIManager.h"
10 |
11 | @interface RCT_EXTERN_MODULE(RNJWPlayerViewManager, RCTViewManager)
12 |
13 | /* player state events */
14 | RCT_EXPORT_VIEW_PROPERTY(onTime, RCTDirectEventBlock);
15 | RCT_EXPORT_VIEW_PROPERTY(onLoaded, RCTDirectEventBlock);
16 | RCT_EXPORT_VIEW_PROPERTY(onSeek, RCTDirectEventBlock);
17 | RCT_EXPORT_VIEW_PROPERTY(onSeeked, RCTDirectEventBlock);
18 | RCT_EXPORT_VIEW_PROPERTY(onRateChanged, RCTDirectEventBlock);
19 | RCT_EXPORT_VIEW_PROPERTY(onPlaylist, RCTDirectEventBlock);
20 | RCT_EXPORT_VIEW_PROPERTY(onPlaylistComplete, RCTDirectEventBlock);
21 | RCT_EXPORT_VIEW_PROPERTY(onBeforeComplete, RCTDirectEventBlock);
22 | RCT_EXPORT_VIEW_PROPERTY(onComplete, RCTDirectEventBlock);
23 | RCT_EXPORT_VIEW_PROPERTY(onVisible, RCTDirectEventBlock);
24 | RCT_EXPORT_VIEW_PROPERTY(onBeforePlay, RCTDirectEventBlock);
25 | RCT_EXPORT_VIEW_PROPERTY(onAttemptPlay, RCTDirectEventBlock);
26 | RCT_EXPORT_VIEW_PROPERTY(onPlay, RCTDirectEventBlock);
27 | RCT_EXPORT_VIEW_PROPERTY(onPause, RCTDirectEventBlock);
28 | RCT_EXPORT_VIEW_PROPERTY(onBuffer, RCTDirectEventBlock);
29 | RCT_EXPORT_VIEW_PROPERTY(onUpdateBuffer, RCTDirectEventBlock);
30 | RCT_EXPORT_VIEW_PROPERTY(onIdle, RCTDirectEventBlock);
31 | RCT_EXPORT_VIEW_PROPERTY(onPlaylistItem, RCTDirectEventBlock);
32 |
33 | /* av events */
34 | RCT_EXPORT_VIEW_PROPERTY(onAudioTracks, RCTDirectEventBlock);
35 | RCT_EXPORT_VIEW_PROPERTY(onCaptionsChanged, RCTDirectEventBlock);
36 | RCT_EXPORT_VIEW_PROPERTY(onCaptionsList, RCTDirectEventBlock);
37 |
38 | /* player events */
39 | RCT_EXPORT_VIEW_PROPERTY(onPlayerReady, RCTDirectEventBlock);
40 | RCT_EXPORT_VIEW_PROPERTY(onSetupPlayerError, RCTDirectEventBlock);
41 | RCT_EXPORT_VIEW_PROPERTY(onPlayerError, RCTDirectEventBlock);
42 | RCT_EXPORT_VIEW_PROPERTY(onPlayerWarning, RCTDirectEventBlock);
43 | RCT_EXPORT_VIEW_PROPERTY(onBeforeNextPlaylistItem, RCTDirectEventBlock);
44 |
45 | /* ad events */
46 | RCT_EXPORT_VIEW_PROPERTY(onPlayerAdWarning, RCTDirectEventBlock);
47 | RCT_EXPORT_VIEW_PROPERTY(onPlayerAdError, RCTDirectEventBlock);
48 | RCT_EXPORT_VIEW_PROPERTY(onAdEvent, RCTDirectEventBlock);
49 | RCT_EXPORT_VIEW_PROPERTY(onAdTime, RCTDirectEventBlock);
50 |
51 | /* jwplayer view controller events */
52 | RCT_EXPORT_VIEW_PROPERTY(onControlBarVisible, RCTDirectEventBlock);
53 | RCT_EXPORT_VIEW_PROPERTY(onScreenTapped, RCTDirectEventBlock);
54 | RCT_EXPORT_VIEW_PROPERTY(onFullScreen, RCTDirectEventBlock);
55 | RCT_EXPORT_VIEW_PROPERTY(onFullScreenRequested, RCTDirectEventBlock);
56 | RCT_EXPORT_VIEW_PROPERTY(onFullScreenExit, RCTDirectEventBlock);
57 | RCT_EXPORT_VIEW_PROPERTY(onFullScreenExitRequested, RCTDirectEventBlock);
58 |
59 | /* jwplayer view events */
60 | RCT_EXPORT_VIEW_PROPERTY(onPlayerSizeChange, RCTDirectEventBlock);
61 |
62 | /* casting events */
63 | RCT_EXPORT_VIEW_PROPERTY(onCastingDevicesAvailable, RCTDirectEventBlock);
64 | RCT_EXPORT_VIEW_PROPERTY(onConnectedToCastingDevice, RCTDirectEventBlock);
65 | RCT_EXPORT_VIEW_PROPERTY(onDisconnectedFromCastingDevice, RCTDirectEventBlock);
66 | RCT_EXPORT_VIEW_PROPERTY(onConnectionTemporarilySuspended, RCTDirectEventBlock);
67 | RCT_EXPORT_VIEW_PROPERTY(onConnectionRecovered, RCTDirectEventBlock);
68 | RCT_EXPORT_VIEW_PROPERTY(onConnectionFailed, RCTDirectEventBlock);
69 | RCT_EXPORT_VIEW_PROPERTY(onCasting, RCTDirectEventBlock);
70 | RCT_EXPORT_VIEW_PROPERTY(onCastingEnded, RCTDirectEventBlock);
71 | RCT_EXPORT_VIEW_PROPERTY(onCastingFailed, RCTDirectEventBlock);
72 |
73 | /* props */
74 | RCT_EXPORT_VIEW_PROPERTY(config, NSDictionary);
75 | RCT_EXPORT_VIEW_PROPERTY(controls, BOOL);
76 |
77 | RCT_EXTERN_METHOD(state: (nonnull NSNumber*) reactTag: (RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject)
78 |
79 | RCT_EXTERN_METHOD(pause: (nonnull NSNumber*)reactTag)
80 |
81 | RCT_EXTERN_METHOD(play: (nonnull NSNumber *)reactTag)
82 |
83 | RCT_EXTERN_METHOD(stop: (nonnull NSNumber *)reactTag)
84 |
85 | RCT_EXTERN_METHOD(position: (nonnull NSNumber *)reactTag: (RCTPromiseResolveBlock)resolve: (RCTPromiseRejectBlock)reject)
86 |
87 | RCT_EXTERN_METHOD(toggleSpeed: (nonnull NSNumber*)reactTag)
88 |
89 | RCT_EXTERN_METHOD(setSpeed: (nonnull NSNumber*)reactTag: (double)speed)
90 |
91 | RCT_EXTERN_METHOD(setPlaylistIndex: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index)
92 |
93 | RCT_EXTERN_METHOD(seekTo: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)time)
94 |
95 | RCT_EXTERN_METHOD(setVolume: (nonnull NSNumber *)reactTag :(nonnull NSNumber *)volume)
96 |
97 | RCT_EXTERN_METHOD(togglePIP: (nonnull NSNumber *)reactTag)
98 |
99 | RCT_EXTERN_METHOD(resolveNextPlaylistItem: (nonnull NSNumber *)reactTag :(nonnull NSDictionary *)playlistItem)
100 |
101 | #if USE_GOOGLE_CAST
102 |
103 | RCT_EXTERN_METHOD(setUpCastController: (nonnull NSNumber *)reactTag)
104 |
105 | RCT_EXTERN_METHOD(presentCastDialog: (nonnull NSNumber *)reactTag)
106 |
107 | RCT_EXTERN_METHOD(connectedDevice: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject)
108 |
109 | RCT_EXTERN_METHOD(availableDevices: (nonnull NSNumber *)reactTag: (RCTPromiseResolveBlock)resolve: (RCTPromiseRejectBlock)reject)
110 |
111 | RCT_EXTERN_METHOD(castState: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject)
112 |
113 | #endif
114 |
115 | RCT_EXTERN_METHOD(getAudioTracks: (nonnull NSNumber *)reactTag: (RCTPromiseResolveBlock)resolve: (RCTPromiseRejectBlock)reject)
116 |
117 | RCT_EXTERN_METHOD(getCurrentAudioTrack: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject)
118 |
119 | RCT_EXTERN_METHOD(setCurrentAudioTrack: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index)
120 |
121 | RCT_EXTERN_METHOD(setControls: (nonnull NSNumber *)reactTag: (BOOL)show)
122 |
123 | RCT_EXTERN_METHOD(setVisibility: (nonnull NSNumber *)reactTag: (BOOL)visibilty: (nonnull NSArray *)controls)
124 |
125 | RCT_EXTERN_METHOD(setLockScreenControls: (nonnull NSNumber *)reactTag: (BOOL)show)
126 |
127 | RCT_EXTERN_METHOD(setCurrentCaptions: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index)
128 |
129 | RCT_EXTERN_METHOD(getCurrentCaptions: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject)
130 |
131 | RCT_EXTERN_METHOD(setLicenseKey: (nonnull NSNumber *)reactTag: (nonnull NSString *)license)
132 |
133 | RCT_EXTERN_METHOD(quite)
134 |
135 | RCT_EXTERN_METHOD(reset)
136 |
137 | RCT_EXTERN_METHOD(loadPlaylist: (nonnull NSNumber *)reactTag: (nonnull NSArray *)playlist)
138 |
139 | RCT_EXTERN_METHOD(loadPlaylistWithUrl: (nonnull NSNumber *)reactTag: (nonnull NSString *)playlist)
140 |
141 | RCT_EXTERN_METHOD(setFullscreen: (nonnull NSNumber *)reactTag: (BOOL)fullscreen)
142 |
143 | @end
144 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@jwplayer/jwplayer-react-native",
3 | "version": "1.1.1",
4 | "description": "React-native Android/iOS plugin for JWPlayer SDK (https://www.jwplayer.com/)",
5 | "main": "index.js",
6 | "types": "./index.d.ts",
7 | "scripts": {
8 | "lint": "eslint index.js example/index.js example/src",
9 | "badges:license": "badge license MIT :green > ./badges/license.svg",
10 | "badges:version": "badge version $npm_package_version :blue > ./badges/version.svg"
11 | },
12 | "pre-commit": [
13 | "lint"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/jwplayer/jwplayer-react-native"
18 | },
19 | "keywords": [
20 | "react",
21 | "react-native",
22 | "jwplayer",
23 | "media",
24 | "player",
25 | "mediaplayer",
26 | "media-player",
27 | "jw",
28 | "android",
29 | "ios",
30 | "audio",
31 | "video",
32 | "sdk"
33 | ],
34 | "author": "JWPLAYER",
35 | "license": "MIT",
36 | "homepage": "https://github.com/jwplayer/jwplayer-react-native#readme",
37 | "devDependencies": {
38 | "badge-maker": "^3.3.1",
39 | "react": ">= 18.2.0",
40 | "react-native": ">= 0.72.5"
41 | },
42 | "dependencies": {
43 | "lodash.isequalwith": "^4.4.0"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------