├── .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 | license: MITlicenseMIT 2 | -------------------------------------------------------------------------------- /badges/version.svg: -------------------------------------------------------------------------------- 1 | version: 1.1.1version1.1.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 | --------------------------------------------------------------------------------