├── .eslintrc ├── .gitignore ├── .npmignore ├── Example ├── .babelrc ├── .buckconfig ├── .editorconfig ├── .eslintrc.js ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .npmignore ├── .prettierrc.js ├── .watchmanconfig ├── Gemfile ├── Gemfile.lock ├── __tests__ │ └── App-test.js ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── rnjwplayer │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── rnjwplayer │ │ │ │ ├── CastOptionsProvider.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── 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.js │ │ ├── components │ │ │ ├── AnimatedPlayer │ │ │ │ ├── index.js │ │ │ │ └── player.style.js │ │ │ ├── Player.js │ │ │ └── PlayerContainer.js │ │ └── screens │ │ │ ├── DRMExample.js │ │ │ ├── Home.js │ │ │ ├── ListExample.js │ │ │ ├── LocalFileExample.js │ │ │ ├── SingleExample.js │ │ │ ├── SourcesExample.js │ │ │ └── YoutubeExample.js │ └── ui │ │ └── styles │ │ └── global.style.js ├── babel.config.js ├── index.js ├── ios │ ├── .xcode.env │ ├── Podfile │ ├── Podfile.lock │ ├── RNJWPlayer.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── RNJWPlayer.xcscheme │ ├── RNJWPlayer.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── RNJWPlayer │ │ ├── AppDelegate.h │ │ ├── AppDelegate.mm │ │ ├── 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 │ │ └── main.m │ ├── RNJWPlayerTests │ │ ├── Info.plist │ │ └── RNJWPlayerTests.m │ └── local_file.mp4 ├── metro.config.js ├── package-lock.json ├── package.json └── yarn.lock ├── LICENSE ├── Pre.0.2.0_README.md ├── README.md ├── RNJWPlayer.podspec ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── appgoalz │ └── rnjwplayer │ ├── CastOptionsProvider.java │ ├── RNJWPlayer.java │ ├── RNJWPlayerAds.java │ ├── RNJWPlayerModule.java │ ├── RNJWPlayerPackage.java │ ├── RNJWPlayerView.java │ ├── RNJWPlayerViewManager.java │ ├── Util.java │ └── WidevineCallback.java ├── 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 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /Example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["module:react-native-dotenv"] 4 | ] 5 | } -------------------------------------------------------------------------------- /Example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /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-community', 4 | }; 5 | -------------------------------------------------------------------------------- /Example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore polyfills 9 | node_modules/react-native/Libraries/polyfills/.* 10 | 11 | ; Flow doesn't support platforms 12 | .*/Libraries/Utilities/LoadingView.js 13 | 14 | [untyped] 15 | .*/node_modules/@react-native-community/cli/.*/.* 16 | 17 | [include] 18 | 19 | [libs] 20 | node_modules/react-native/interface.js 21 | node_modules/react-native/flow/ 22 | 23 | [options] 24 | emoji=true 25 | 26 | esproposal.optional_chaining=enable 27 | esproposal.nullish_coalescing=enable 28 | 29 | exact_by_default=true 30 | 31 | module.file_ext=.js 32 | module.file_ext=.json 33 | module.file_ext=.ios.js 34 | 35 | munge_underscores=true 36 | 37 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 38 | module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub' 39 | 40 | suppress_type=$FlowIssue 41 | suppress_type=$FlowFixMe 42 | suppress_type=$FlowFixMeProps 43 | suppress_type=$FlowFixMeState 44 | 45 | [lints] 46 | sketchy-null-number=warn 47 | sketchy-null-mixed=warn 48 | sketchy-number=warn 49 | untyped-type-import=warn 50 | nonstrict-import=warn 51 | deprecated-type=warn 52 | unsafe-getters-setters=warn 53 | unnecessary-invariant=warn 54 | 55 | [strict] 56 | deprecated-type 57 | nonstrict-import 58 | sketchy-null 59 | unclear-type 60 | unsafe-getters-setters 61 | untyped-import 62 | untyped-type-import 63 | 64 | [version] 65 | ^0.182.0 66 | -------------------------------------------------------------------------------- /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 | ios/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | 35 | # node.js 36 | # 37 | node_modules/ 38 | npm-debug.log 39 | yarn-error.log 40 | 41 | # BUCK 42 | buck-out/ 43 | \.buckd/ 44 | *.keystore 45 | !debug.keystore 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | */fastlane/report.xml 55 | */fastlane/Preview.html 56 | */fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # CocoaPods 63 | /ios/Pods/ 64 | 65 | # Environment 66 | .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 | gem 'cocoapods', '>= 1.11.3' 7 | gem 'activesupport', '>= 6.1.7.3', '< 7.1.0' -------------------------------------------------------------------------------- /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.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import App from '../App'; 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer'; 11 | 12 | it('renders correctly', () => { 13 | renderer.create(); 14 | }); 15 | -------------------------------------------------------------------------------- /Example/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.rnjwplayer", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.rnjwplayer", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /Example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | /** 3 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 4 | * and bundleReleaseJsAndAssets). 5 | * These basically call `react-native bundle` with the correct arguments during the Android build 6 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 7 | * bundle directly from the development server. Below you can see all the possible configurations 8 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 9 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 10 | * 11 | * project.ext.react = [ 12 | * // the name of the generated asset file containing your JS bundle 13 | * bundleAssetName: "index.android.bundle", 14 | * 15 | * // the entry file for bundle generation. If none specified and 16 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is 17 | * // default. Can be overridden with ENTRY_FILE environment variable. 18 | * entryFile: "index.android.js", 19 | * 20 | * // https://reactnative.dev/docs/performance#enable-the-ram-format 21 | * bundleCommand: "ram-bundle", 22 | * 23 | * // whether to bundle JS and assets in debug mode 24 | * bundleInDebug: false, 25 | * 26 | * // whether to bundle JS and assets in release mode 27 | * bundleInRelease: true, 28 | * 29 | * // whether to bundle JS and assets in another build variant (if configured). 30 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 31 | * // The configuration property can be in the following formats 32 | * // 'bundleIn${productFlavor}${buildType}' 33 | * // 'bundleIn${buildType}' 34 | * // bundleInFreeDebug: true, 35 | * // bundleInPaidRelease: true, 36 | * // bundleInBeta: true, 37 | * 38 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 39 | * // for example: to disable dev mode in the staging build type (if configured) 40 | * devDisabledInStaging: true, 41 | * // The configuration property can be in the following formats 42 | * // 'devDisabledIn${productFlavor}${buildType}' 43 | * // 'devDisabledIn${buildType}' 44 | * 45 | * // the root of your project, i.e. where "package.json" lives 46 | * root: "../../", 47 | * 48 | * // where to put the JS bundle asset in debug mode 49 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 50 | * 51 | * // where to put the JS bundle asset in release mode 52 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 53 | * 54 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 55 | * // require('./image.png')), in debug mode 56 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 57 | * 58 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 59 | * // require('./image.png')), in release mode 60 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 61 | * 62 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 63 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 64 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 65 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 66 | * // for example, you might want to remove it from here. 67 | * inputExcludes: ["android/**", "ios/**"], 68 | * 69 | * // override which node gets called and with what additional arguments 70 | * nodeExecutableAndArgs: ["node"], 71 | * 72 | * // supply additional arguments to the packager 73 | * extraPackagerArgs: [] 74 | * ] 75 | */ 76 | 77 | project.ext.react = [ 78 | enableHermes: false, // clean and rebuild if changing 79 | ] 80 | 81 | apply from: "../../node_modules/react-native/react.gradle" 82 | 83 | /** 84 | * Set this to true to create two separate APKs instead of one: 85 | * - An APK that only works on ARM devices 86 | * - An APK that only works on x86 devices 87 | * The advantage is the size of the APK is reduced by about 4MB. 88 | * Upload all the APKs to the Play Store and people will download 89 | * the correct one based on the CPU architecture of their device. 90 | */ 91 | def enableSeparateBuildPerCPUArchitecture = false 92 | 93 | /** 94 | * Run Proguard to shrink the Java bytecode in release builds. 95 | */ 96 | def enableProguardInReleaseBuilds = false 97 | 98 | /** 99 | * The preferred build flavor of JavaScriptCore. 100 | * 101 | * For example, to use the international variant, you can use: 102 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 103 | * 104 | * The international variant includes ICU i18n library and necessary data 105 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 106 | * give correct results when using with locales other than en-US. Note that 107 | * this variant is about 6MiB larger per architecture than default. 108 | */ 109 | def jscFlavor = 'org.webkit:android-jsc:+' 110 | 111 | /** 112 | * Whether to enable the Hermes VM. 113 | * 114 | * This should be set on project.ext.react and mirrored here. If it is not set 115 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode 116 | * and the benefits of using Hermes will therefore be sharply reduced. 117 | */ 118 | def enableHermes = project.ext.react.get("enableHermes", false); 119 | 120 | android { 121 | ndkVersion rootProject.ext.ndkVersion 122 | 123 | compileSdkVersion rootProject.ext.compileSdkVersion 124 | 125 | compileOptions { 126 | sourceCompatibility JavaVersion.VERSION_1_8 127 | targetCompatibility JavaVersion.VERSION_1_8 128 | } 129 | 130 | defaultConfig { 131 | applicationId "com.rnjwplayer" 132 | minSdkVersion rootProject.ext.minSdkVersion 133 | targetSdkVersion rootProject.ext.targetSdkVersion 134 | versionCode 1 135 | versionName "1.0" 136 | multiDexEnabled true 137 | } 138 | splits { 139 | abi { 140 | reset() 141 | enable enableSeparateBuildPerCPUArchitecture 142 | universalApk false // If true, also generate a universal APK 143 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" 144 | } 145 | } 146 | signingConfigs { 147 | debug { 148 | storeFile file('debug.keystore') 149 | storePassword 'android' 150 | keyAlias 'androiddebugkey' 151 | keyPassword 'android' 152 | } 153 | } 154 | buildTypes { 155 | debug { 156 | signingConfig signingConfigs.debug 157 | } 158 | release { 159 | // Caution! In production, you need to generate your own keystore file. 160 | // see https://reactnative.dev/docs/signed-apk-android. 161 | signingConfig signingConfigs.debug 162 | minifyEnabled enableProguardInReleaseBuilds 163 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 164 | } 165 | } 166 | 167 | // applicationVariants are e.g. debug, release 168 | applicationVariants.all { variant -> 169 | variant.outputs.each { output -> 170 | // For each separate APK per architecture, set a unique version code as described here: 171 | // https://developer.android.com/studio/build/configure-apk-splits.html 172 | // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. 173 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] 174 | def abi = output.getFilter(com.android.build.OutputFile.ABI) 175 | if (abi != null) { // null for the universal-debug, universal-release variants 176 | output.versionCodeOverride = 177 | defaultConfig.versionCode * 1000 + versionCodes.get(abi) 178 | } 179 | 180 | } 181 | } 182 | } 183 | 184 | dependencies { 185 | implementation fileTree(dir: "libs", include: ["*.jar"]) 186 | //noinspection GradleDynamicVersion 187 | implementation "com.facebook.react:react-native:+" // From node_modules 188 | 189 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" 190 | 191 | implementation "androidx.core:core-splashscreen:1.0.0-beta01" 192 | implementation 'com.google.android.gms:play-services-cast-framework:21.3.0' 193 | 194 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { 195 | exclude group:'com.facebook.fbjni' 196 | } 197 | 198 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { 199 | exclude group:'com.facebook.flipper' 200 | exclude group:'com.squareup.okhttp3', module:'okhttp' 201 | } 202 | 203 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { 204 | exclude group:'com.facebook.flipper' 205 | } 206 | 207 | if (enableHermes) { 208 | def hermesPath = "../../node_modules/hermes-engine/android/"; 209 | debugImplementation files(hermesPath + "hermes-debug.aar") 210 | releaseImplementation files(hermesPath + "hermes-release.aar") 211 | } else { 212 | implementation jscFlavor 213 | } 214 | } 215 | 216 | // Run this once to be able to run the application with BUCK 217 | // puts all compile dependencies into folder libs for BUCK to use 218 | task copyDownloadableDepsToLibs(type: Copy) { 219 | from configurations.implementation 220 | into 'libs' 221 | } 222 | 223 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 224 | apply from: "../../node_modules/react-native-vector-icons/fonts.gradle" 225 | -------------------------------------------------------------------------------- /Example/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /Example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Example/android/app/src/debug/java/com/rnjwplayer/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.rnjwplayer; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin; 21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | public class ReactNativeFlipper { 28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 29 | if (FlipperUtils.shouldEnableFlipper(context)) { 30 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 31 | 32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 33 | client.addPlugin(new ReactFlipperPlugin()); 34 | client.addPlugin(new DatabasesFlipperPlugin(context)); 35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 36 | client.addPlugin(CrashReporterPlugin.getInstance()); 37 | 38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 39 | NetworkingModule.setCustomClientBuilder( 40 | new NetworkingModule.CustomClientBuilder() { 41 | @Override 42 | public void apply(OkHttpClient.Builder builder) { 43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 44 | } 45 | }); 46 | client.addPlugin(networkFlipperPlugin); 47 | client.start(); 48 | 49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 50 | // Hence we run if after all native modules have been initialized 51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 52 | if (reactContext == null) { 53 | reactInstanceManager.addReactInstanceEventListener( 54 | new ReactInstanceManager.ReactInstanceEventListener() { 55 | @Override 56 | public void onReactContextInitialized(ReactContext reactContext) { 57 | reactInstanceManager.removeReactInstanceEventListener(this); 58 | reactContext.runOnNativeModulesQueueThread( 59 | new Runnable() { 60 | @Override 61 | public void run() { 62 | client.addPlugin(new FrescoFlipperPlugin()); 63 | } 64 | }); 65 | } 66 | }); 67 | } else { 68 | client.addPlugin(new FrescoFlipperPlugin()); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /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.java: -------------------------------------------------------------------------------- 1 | package com.rnjwplayer; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.os.Bundle; 6 | 7 | import com.facebook.react.ReactActivity; 8 | import com.facebook.react.ReactActivityDelegate; 9 | import com.zoontek.rnbootsplash.RNBootSplash; 10 | 11 | public class MainActivity extends ReactActivity { 12 | 13 | /** 14 | * Returns the name of the main component registered from JavaScript. This is used to schedule 15 | * rendering of the component. 16 | */ 17 | @Override 18 | protected String getMainComponentName() { 19 | return "RNJWPlayer"; 20 | } 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(null); 25 | } 26 | 27 | @Override 28 | protected ReactActivityDelegate createReactActivityDelegate() { 29 | return new MainActivityDelegate(this, getMainComponentName()); 30 | } 31 | 32 | 33 | public static class MainActivityDelegate extends ReactActivityDelegate { 34 | public MainActivityDelegate(ReactActivity activity, String mainComponentName) { 35 | super(activity, mainComponentName); 36 | } 37 | 38 | @Override 39 | protected void loadApp(String appKey) { 40 | RNBootSplash.init(getPlainActivity()); 41 | super.loadApp(appKey); 42 | } 43 | } 44 | 45 | @Override 46 | public void onConfigurationChanged(Configuration newConfig) { 47 | super.onConfigurationChanged(newConfig); 48 | Intent intent = new Intent("onConfigurationChanged"); 49 | intent.putExtra("newConfig", newConfig); 50 | this.sendBroadcast(intent); 51 | } 52 | 53 | @Override 54 | public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) { 55 | super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); 56 | 57 | Intent intent = new Intent("onPictureInPictureModeChanged"); 58 | intent.putExtra("isInPictureInPictureMode", isInPictureInPictureMode); 59 | intent.putExtra("newConfig", newConfig); 60 | this.sendBroadcast(intent); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Example/android/app/src/main/java/com/rnjwplayer/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.rnjwplayer; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.facebook.react.PackageList; 6 | import com.facebook.react.ReactApplication; 7 | import com.facebook.react.ReactInstanceManager; 8 | import com.facebook.react.ReactNativeHost; 9 | import com.facebook.react.ReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.util.List; 13 | 14 | public class MainApplication extends Application implements ReactApplication { 15 | 16 | private final ReactNativeHost mReactNativeHost = 17 | new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | @SuppressWarnings("UnnecessaryLocalVariable") 26 | List packages = new PackageList(this).getPackages(); 27 | // Packages that cannot be autolinked yet can be added manually here, for example: 28 | // packages.add(new MyReactNativePackage()); 29 | return packages; 30 | } 31 | 32 | @Override 33 | protected String getJSMainModuleName() { 34 | return "index"; 35 | } 36 | }; 37 | 38 | @Override 39 | public ReactNativeHost getReactNativeHost() { 40 | return mReactNativeHost; 41 | } 42 | 43 | @Override 44 | public void onCreate() { 45 | super.onCreate(); 46 | SoLoader.init(this, /* native exopackage */ false); 47 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 48 | } 49 | 50 | /** 51 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like 52 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 53 | * 54 | * @param context 55 | * @param reactInstanceManager 56 | */ 57 | private static void initializeFlipper( 58 | Context context, ReactInstanceManager reactInstanceManager) { 59 | if (BuildConfig.DEBUG) { 60 | try { 61 | /* 62 | We use reflection here to pick up the class that initializes Flipper, 63 | since Flipper library is not available in release mode 64 | */ 65 | Class aClass = Class.forName("com.rnjwplayer.ReactNativeFlipper"); 66 | aClass 67 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 68 | .invoke(null, context, reactInstanceManager); 69 | } catch (ClassNotFoundException e) { 70 | e.printStackTrace(); 71 | } catch (NoSuchMethodException e) { 72 | e.printStackTrace(); 73 | } catch (IllegalAccessException e) { 74 | e.printStackTrace(); 75 | } catch (InvocationTargetException e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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 | -------------------------------------------------------------------------------- /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 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "34.0.0" 6 | minSdkVersion = 24 7 | compileSdkVersion = 34 8 | targetSdkVersion = 34 9 | ndkVersion = "20.1.5948944" 10 | kotlinVersion = "1.6.21" 11 | } 12 | repositories { 13 | google() 14 | jcenter() 15 | } 16 | dependencies { 17 | classpath('com.android.tools.build:gradle:7.2.2') 18 | classpath("com.facebook.react:react-native-gradle-plugin") 19 | classpath("de.undercouch:gradle-download-task:5.0.1") 20 | // NOTE: Do not place your application dependencies here; they belong 21 | // in the individual module build.gradle files 22 | } 23 | } 24 | 25 | allprojects { 26 | repositories { 27 | mavenLocal() 28 | maven { 29 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 30 | url("$rootDir/../node_modules/react-native/android") 31 | } 32 | maven { 33 | // Android JSC is installed from npm 34 | url("$rootDir/../node_modules/jsc-android/dist") 35 | } 36 | 37 | google() 38 | jcenter() 39 | maven { url 'https://www.jitpack.io' } 40 | maven { 41 | url 'https://mvn.jwplayer.com/content/repositories/releases/' 42 | } 43 | } 44 | 45 | def REACT_NATIVE_VERSION = new File(['node', '--print',"JSON.parse(require('fs').readFileSync(require.resolve('react-native/package.json'), 'utf-8')).version"].execute(null, rootDir).text.trim()) 46 | 47 | configurations.all { 48 | resolutionStrategy { 49 | force "com.facebook.react:react-native:" + REACT_NATIVE_VERSION 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /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 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.125.0 29 | 30 | # Use this property to specify which architecture you want to build. 31 | # You can also override it from the CLI using 32 | # ./gradlew -PreactNativeArchitectures=x86_64 33 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 34 | 35 | # Use this property to enable support to the new architecture. 36 | # This will allow you to use TurboModules and the Fabric render in 37 | # your application. You should enable this flag either if you want 38 | # to write custom TurboModules/Fabric components OR use libraries that 39 | # are providing them. 40 | newArchEnabled=false -------------------------------------------------------------------------------- /Example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/Example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 17 02:37:01 EEST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /Example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or 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 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /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 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /Example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'RNJWPlayer' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | includeBuild('../node_modules/react-native-gradle-plugin') 5 | 6 | if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") { 7 | include(":ReactAndroid") 8 | project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid') 9 | include(":ReactAndroid:hermes-engine") 10 | project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine') 11 | } -------------------------------------------------------------------------------- /Example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RNJWPlayer", 3 | "displayName": "RNJWPlayer" 4 | } -------------------------------------------------------------------------------- /Example/app/jsx/App.js: -------------------------------------------------------------------------------- 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 DRMExample from './screens/DRMExample'; 13 | import LocalFileExample from './screens/LocalFileExample'; 14 | import SourcesExample from './screens/SourcesExample'; 15 | import YoutubeExample from './screens/YoutubeExample'; 16 | 17 | const Stack = createNativeStackNavigator(); 18 | 19 | export default class App extends Component { 20 | render() { 21 | return ( 22 | RNBootSplash.hide({fade: true})}> 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /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 'react-native-jw-media-player'; 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.')} 33 | /> 34 | ); 35 | }); 36 | -------------------------------------------------------------------------------- /Example/app/jsx/components/PlayerContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {View, Text} from 'react-native'; 3 | 4 | /* styles */ 5 | import {globalStyles} from '../../ui/styles/global.style'; 6 | 7 | export default ({children, text}) => { 8 | return ( 9 | 10 | 11 | {children} 12 | 13 | {text} 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /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(false); 10 | 11 | const renderIOSPlayer = () => { 12 | const EZDRMLicenseAPIEndpoint = 'https://fps.ezdrm.com/api/licenses'; 13 | const EZDRMCertificateEndpoint = 14 | 'https://fps.ezdrm.com/demo/video/eleisure.cer'; 15 | const EZDRMVideoEndpoint = 'https://fps.ezdrm.com/demo/video/ezdrm.m3u8'; 16 | 17 | return ( 18 | 35 | ); 36 | }; 37 | 38 | const renderAndroidPlayer = () => { 39 | const AuthUrl = 'https://cwip-shaka-proxy.appspot.com/no_auth'; 40 | const StreamUrl = 41 | 'https://storage.googleapis.com/shaka-demo-assets/sintel-widevine/dash.mpd'; 42 | 43 | return ( 44 | 61 | ); 62 | }; 63 | 64 | useEffect(() => { 65 | getIsEmulator(); 66 | }, []); 67 | 68 | const getIsEmulator = async () => { 69 | const emulator = await DeviceInfo.isEmulator(); 70 | setIsEmulator(emulator); 71 | }; 72 | 73 | return ( 74 | 78 | {"DRM Doesn't work in the simulator. Check out "} 79 | 82 | Linking.openURL( 83 | 'https://reactnative.dev/docs/running-on-device', 84 | ) 85 | }> 86 | this 87 | 88 | {' link to run on a real device.'} 89 | 90 | ) : Platform.OS === 'ios' ? ( 91 | renderIOSPlayer() 92 | ) : ( 93 | renderAndroidPlayer() 94 | ) 95 | } 96 | text="Welcome to react-native-jw-media-player" 97 | /> 98 | ); 99 | }; 100 | 101 | const styles = StyleSheet.create({ 102 | text: { 103 | fontSize: 18, 104 | margin: 40, 105 | }, 106 | errorText: { 107 | textAlign: 'center', 108 | color: 'white', 109 | padding: 20, 110 | fontSize: 17, 111 | }, 112 | }); 113 | -------------------------------------------------------------------------------- /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', '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} from 'react-native'; 3 | import Player from '../components/Player'; 4 | import PlayerContainer from '../components/PlayerContainer'; 5 | 6 | /* utils */ 7 | import RNFS from '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 | const data = await RNFS.readDir(RNFS.MainBundlePath); 19 | const local = data?.find(file => file.name === 'local_file.mp4')?.path; 20 | setLocalFile('file://' + local); 21 | }; 22 | 23 | const renderPlayer = () => { 24 | return localFile ? ( 25 | 40 | ) : ( 41 | Failed to load local file. 42 | ); 43 | }; 44 | 45 | return ( 46 | 50 | ); 51 | }; 52 | 53 | const styles = StyleSheet.create({ 54 | errorText: { 55 | textAlign: 'center', 56 | color: 'white', 57 | padding: 20, 58 | fontSize: 17, 59 | }, 60 | }); 61 | -------------------------------------------------------------------------------- /Example/app/jsx/screens/SingleExample.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 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 | const renderPlayer = () => { 24 | return ( 25 | 44 | ); 45 | }; 46 | 47 | return ( 48 | 52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /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:metro-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/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '14.0' 5 | install! 'cocoapods', :deterministic_uuids => false 6 | 7 | $RNJWPlayerUseGoogleIMA = true 8 | $RNJWPlayerUseGoogleCast = true 9 | 10 | target 'RNJWPlayer' do 11 | config = use_native_modules! 12 | 13 | # Flags change depending on the env values. 14 | flags = get_default_flags() 15 | 16 | use_react_native!( 17 | :path => config[:reactNativePath], 18 | # Hermes is now enabled by default. Disable by setting this flag to false. 19 | # Upcoming versions of React Native may rely on get_default_flags(), but 20 | # we make it explicit here to aid in the React Native upgrade process. 21 | :hermes_enabled => true, 22 | :fabric_enabled => flags[:fabric_enabled], 23 | # Enables Flipper. 24 | # 25 | # Note that if you have use_frameworks! enabled, Flipper will not work and 26 | # you should disable the next line. 27 | :flipper_configuration => FlipperConfiguration.enabled, 28 | # An absolute path to your application root. 29 | :app_path => "#{Pod::Config.instance.installation_root}/.." 30 | ) 31 | 32 | target 'RNJWPlayerTests' do 33 | inherit! :complete 34 | # Pods for testing 35 | end 36 | 37 | post_install do |installer| 38 | react_native_post_install( 39 | installer, 40 | # Set `mac_catalyst_enabled` to `true` in order to apply patches 41 | # necessary for Mac Catalyst builds 42 | :mac_catalyst_enabled => false 43 | ) 44 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 45 | end 46 | 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/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /Example/ios/RNJWPlayer/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #import 8 | 9 | #import 10 | 11 | #import "Orientation.h" 12 | #import "RNFSManager.h" 13 | #import "RNBootSplash.h" 14 | 15 | #if USE_GOOGLE_CAST 16 | #import 17 | 18 | static NSString *const kReceiverAppID = @"RNJWPlayer"; 19 | #endif 20 | 21 | #if RCT_NEW_ARCH_ENABLED 22 | #import 23 | #import 24 | #import 25 | #import 26 | #import 27 | #import 28 | 29 | #import 30 | 31 | static NSString *const kRNConcurrentRoot = @"concurrentRoot"; 32 | 33 | @interface AppDelegate () { 34 | RCTTurboModuleManager *_turboModuleManager; 35 | RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; 36 | std::shared_ptr _reactNativeConfig; 37 | facebook::react::ContextContainer::Shared _contextContainer; 38 | } 39 | @end 40 | #endif 41 | 42 | @implementation AppDelegate 43 | 44 | -(void)initApis:(UIApplication *)application launchOptions:(NSDictionary *)launchOptions 45 | { 46 | #if USE_GOOGLE_CAST 47 | GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc] 48 | initWithApplicationID:kReceiverAppID]; 49 | GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria:criteria]; 50 | [GCKCastContext setSharedInstanceWithOptions:options]; 51 | #endif 52 | } 53 | 54 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 55 | { 56 | [self initApis:application launchOptions:launchOptions]; 57 | 58 | RCTAppSetupPrepareApp(application); 59 | 60 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 61 | 62 | #if RCT_NEW_ARCH_ENABLED 63 | _contextContainer = std::make_shared(); 64 | _reactNativeConfig = std::make_shared(); 65 | _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); 66 | _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; 67 | bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; 68 | #endif 69 | 70 | NSDictionary *initProps = [self prepareInitialProps]; 71 | UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"RNJWPlayer", initProps); 72 | 73 | if (@available(iOS 13.0, *)) { 74 | rootView.backgroundColor = [UIColor systemBackgroundColor]; 75 | } else { 76 | rootView.backgroundColor = [UIColor whiteColor]; 77 | } 78 | 79 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 80 | UIViewController *rootViewController = [UIViewController new]; 81 | rootViewController.view = rootView; 82 | self.window.rootViewController = rootViewController; 83 | [self.window makeKeyAndVisible]; 84 | 85 | [RNBootSplash initWithStoryboard:@"LaunchScreen" rootView:rootView]; 86 | 87 | return YES; 88 | } 89 | 90 | // This method controls whether the `concurrentRoot`feature of React18 is turned on or off. 91 | /// 92 | /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html 93 | /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). 94 | /// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`. 95 | - (BOOL)concurrentRootEnabled 96 | { 97 | // Switch this bool to turn on and off the concurrent root 98 | return true; 99 | } 100 | - (NSDictionary *)prepareInitialProps 101 | { 102 | NSMutableDictionary *initProps = [NSMutableDictionary new]; 103 | #ifdef RCT_NEW_ARCH_ENABLED 104 | initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); 105 | #endif 106 | return initProps; 107 | } 108 | 109 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 110 | { 111 | #if DEBUG 112 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 113 | #else 114 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 115 | #endif 116 | } 117 | 118 | #if RCT_NEW_ARCH_ENABLED 119 | 120 | #pragma mark - RCTCxxBridgeDelegate 121 | 122 | - (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge 123 | { 124 | _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge 125 | delegate:self 126 | jsInvoker:bridge.jsCallInvoker]; 127 | return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); 128 | } 129 | 130 | #pragma mark RCTTurboModuleManagerDelegate 131 | 132 | - (Class)getModuleClassFromName:(const char *)name 133 | { 134 | return RCTCoreModulesClassProvider(name); 135 | } 136 | 137 | - (std::shared_ptr)getTurboModule:(const std::string &)name 138 | jsInvoker:(std::shared_ptr)jsInvoker 139 | { 140 | return nullptr; 141 | } 142 | 143 | - (std::shared_ptr)getTurboModule:(const std::string &)name 144 | initParams: 145 | (const facebook::react::ObjCTurboModule::InitParams &)params 146 | { 147 | return nullptr; 148 | } 149 | 150 | - (id)getModuleInstanceFromClass:(Class)moduleClass 151 | { 152 | return RCTAppSetupDefaultModuleFromClass(moduleClass); 153 | } 154 | 155 | #endif 156 | 157 | - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url 158 | options:(NSDictionary *)options 159 | { 160 | return [RCTLinkingManager application:app openURL:url options:options]; 161 | } 162 | 163 | - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity 164 | restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler 165 | { 166 | return [RCTLinkingManager application:application 167 | continueUserActivity:userActivity 168 | restorationHandler:restorationHandler]; 169 | } 170 | 171 | - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler { 172 | [RNFSManager setCompletionHandlerForIdentifier:identifier completionHandler:completionHandler]; 173 | } 174 | 175 | - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { 176 | return [Orientation getOrientation]; 177 | } 178 | 179 | @end 180 | -------------------------------------------------------------------------------- /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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/Example/ios/RNJWPlayer/Images.xcassets/jwlogo.imageset/jwlogo-sketch.png -------------------------------------------------------------------------------- /Example/ios/RNJWPlayer/Images.xcassets/jwlogo.imageset/jwlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/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 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSBluetoothAlwaysUsageDescription 41 | ${PRODUCT_NAME} uses bluetooth to discover Cast-enabled devices around you. 42 | NSBonjourServices 43 | 44 | _googlecast._tcp 45 | _RNJWPlayer._googlecast._tcp 46 | 47 | NSLocalNetworkUsageDescription 48 | ${PRODUCT_NAME} uses the local network to discover Cast-enabled devices on your WiFi 49 | network. 50 | NSLocationWhenInUseUsageDescription 51 | 52 | UIAppFonts 53 | 54 | AntDesign.ttf 55 | Entypo.ttf 56 | EvilIcons.ttf 57 | Feather.ttf 58 | FontAwesome.ttf 59 | FontAwesome5_Brands.ttf 60 | FontAwesome5_Regular.ttf 61 | FontAwesome5_Solid.ttf 62 | Foundation.ttf 63 | Ionicons.ttf 64 | MaterialIcons.ttf 65 | MaterialCommunityIcons.ttf 66 | SimpleLineIcons.ttf 67 | Octicons.ttf 68 | Zocial.ttf 69 | Fontisto.ttf 70 | 71 | UIBackgroundModes 72 | 73 | audio 74 | 75 | UILaunchStoryboardName 76 | LaunchScreen 77 | UIRequiredDeviceCapabilities 78 | 79 | armv7 80 | 81 | UISupportedInterfaceOrientations 82 | 83 | UIInterfaceOrientationPortrait 84 | UIInterfaceOrientationLandscapeLeft 85 | UIInterfaceOrientationLandscapeRight 86 | 87 | UIViewControllerBasedStatusBarAppearance 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /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/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Example/ios/RNJWPlayerTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/ios/RNJWPlayerTests/RNJWPlayerTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface RNJWPlayerTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation RNJWPlayerTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 38 | if (level >= RCTLogLevelError) { 39 | redboxError = message; 40 | } 41 | }); 42 | #endif 43 | 44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | 48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 50 | return YES; 51 | } 52 | return NO; 53 | }]; 54 | } 55 | 56 | #ifdef DEBUG 57 | RCTSetLogFunction(RCTDefaultLogFunction); 58 | #endif 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Example/ios/local_file.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/Example/ios/local_file.mp4 -------------------------------------------------------------------------------- /Example/metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: true, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /Example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RNJWPlayer", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "test": "jest", 10 | "lint": "eslint ." 11 | }, 12 | "dependencies": { 13 | "@react-navigation/native": "6.1.7", 14 | "@react-navigation/native-stack": "6.9.13", 15 | "react": "18.1.0", 16 | "react-native": "0.70.14", 17 | "react-native-bootsplash": "4.1.5", 18 | "react-native-device-info": "9.0.2", 19 | "react-native-fs": "2.16.6", 20 | "react-native-gesture-handler": "2.12.1", 21 | "react-native-jw-media-player": "0.2.45", 22 | "react-native-orientation-locker": "1.5.0", 23 | "react-native-safe-area-context": "4.7.2", 24 | "react-native-screens": "3.25.0", 25 | "react-native-vector-icons": "10.0.0", 26 | "rn-iphone-helper": "2.0.0" 27 | }, 28 | "devDependencies": { 29 | "@babel/core": "^7.14.8", 30 | "@babel/runtime": "^7.14.8", 31 | "@react-native-community/eslint-config": "^3.0.0", 32 | "babel-jest": "^27.0.6", 33 | "eslint": "^7.31.0", 34 | "jest": "^27.0.6", 35 | "metro-react-native-babel-preset": "^0.72.4", 36 | "react-native-dotenv": "3.3.1", 37 | "react-test-renderer": "18.1.0" 38 | }, 39 | "jest": { 40 | "preset": "react-native" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 chaimPaneth 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/chaimPaneth/react-native-jw-media-player.git", :tag => "v#{s.version}" } 14 | s.source_files = "ios/RNJWPlayer/*.{h,m,swift}" 15 | s.dependency 'JWPlayerKit', '~> 4.17.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' 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.19.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 useIMA = safeExtGet("RNJWPlayerUseGoogleIMA", "")?.toBoolean() ?: false 19 | def useCast = safeExtGet("RNJWPlayerUseGoogleCast", "")?.toBoolean() ?: false 20 | 21 | android { 22 | compileSdkVersion safeExtGet('compileSdkVersion', 28) 23 | buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3') 24 | 25 | defaultConfig { 26 | minSdkVersion safeExtGet('minSdkVersion', 16) 27 | targetSdkVersion safeExtGet('targetSdkVersion', 28) 28 | versionCode 1 29 | versionName "1.0" 30 | ndk { 31 | abiFilters "armeabi-v7a", "x86" 32 | } 33 | } 34 | 35 | buildTypes { 36 | debug { 37 | // Set the build config fields for the debug build type 38 | buildConfigField "boolean", "USE_IMA", useIMA.toString() 39 | buildConfigField "boolean", "USE_CAST", useCast.toString() 40 | // ... other debug configurations 41 | } 42 | release { 43 | // Set the build config fields for the release build type 44 | buildConfigField "boolean", "USE_IMA", useIMA.toString() 45 | buildConfigField "boolean", "USE_CAST", useCast.toString() 46 | // ... other release configurations 47 | } 48 | } 49 | lintOptions { 50 | warning 'InvalidPackage' 51 | } 52 | } 53 | 54 | allprojects { 55 | repositories { 56 | mavenLocal() 57 | mavenCentral() 58 | google() 59 | maven { 60 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 61 | url "$rootDir/../node_modules/react-native/android" 62 | } 63 | maven{ 64 | url 'https://mvn.jwplayer.com/content/repositories/releases/' 65 | } 66 | } 67 | } 68 | 69 | def jwPlayerVersion = "4.16.2" 70 | def exoplayerVersion = "1.3.0" 71 | 72 | dependencies { 73 | implementation 'com.facebook.react:react-native:+' 74 | implementation 'com.google.code.gson:gson:2.10' 75 | 76 | // JWPlayer SDK 77 | implementation "com.jwplayer:jwplayer-core:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}" 78 | implementation "com.jwplayer:jwplayer-common:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}" 79 | implementation "com.jwplayer:jwplayer-ima:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}" 80 | implementation "com.jwplayer:jwplayer-chromecast:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}" 81 | 82 | // Ad dependencies 83 | if (useIMA) { 84 | implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.29.0' 85 | implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1' 86 | } 87 | 88 | // Cast dependencies 89 | if (useCast) { 90 | implementation 'com.google.android.gms:play-services-cast-framework:21.3.0' 91 | } 92 | 93 | // ExoPlayer dependencies 94 | 95 | implementation "androidx.media3:media3-common:$exoplayerVersion" 96 | implementation "androidx.media3:media3-extractor:$exoplayerVersion" 97 | implementation "androidx.media3:media3-exoplayer:$exoplayerVersion" 98 | implementation "androidx.media3:media3-exoplayer-dash:$exoplayerVersion" 99 | implementation "androidx.media3:media3-exoplayer-hls:$exoplayerVersion" 100 | implementation "androidx.media3:media3-exoplayer-smoothstreaming:$exoplayerVersion" 101 | implementation "androidx.media3:media3-ui:$exoplayerVersion" 102 | 103 | // JWP Native UI dependencies 104 | implementation 'com.squareup.picasso:picasso:2.71828' 105 | implementation 'androidx.viewpager2:viewpager2:1.0.0' 106 | implementation 'com.android.volley:volley:1.2.1' 107 | implementation 'androidx.recyclerview:recyclerview:1.3.2' 108 | implementation 'androidx.appcompat:appcompat:1.6.1' 109 | implementation 'com.google.android.material:material:1.11.0' 110 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 111 | } 112 | 113 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /android/src/main/java/com/appgoalz/rnjwplayer/CastOptionsProvider.java: -------------------------------------------------------------------------------- 1 | package com.appgoalz.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/appgoalz/rnjwplayer/RNJWPlayer.java: -------------------------------------------------------------------------------- 1 | package com.appgoalz.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/appgoalz/rnjwplayer/RNJWPlayerAds.java: -------------------------------------------------------------------------------- 1 | package com.appgoalz.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/appgoalz/rnjwplayer/RNJWPlayerPackage.java: -------------------------------------------------------------------------------- 1 | 2 | package com.appgoalz.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/appgoalz/rnjwplayer/RNJWPlayerViewManager.java: -------------------------------------------------------------------------------- 1 | 2 | package com.appgoalz.rnjwplayer; 3 | 4 | import com.facebook.react.bridge.ReactApplicationContext; 5 | import com.facebook.react.bridge.ReadableMap; 6 | import com.facebook.react.common.MapBuilder; 7 | import com.facebook.react.uimanager.SimpleViewManager; 8 | import com.facebook.react.uimanager.ThemedReactContext; 9 | import com.facebook.react.uimanager.annotations.ReactProp; 10 | 11 | import java.util.Map; 12 | 13 | import javax.annotation.Nonnull; 14 | 15 | public class RNJWPlayerViewManager extends SimpleViewManager { 16 | 17 | public static final String REACT_CLASS = "RNJWPlayerView"; 18 | 19 | private final ReactApplicationContext mAppContext; 20 | 21 | private static final String TAG = "RNJWPlayerViewManager"; 22 | 23 | @Override 24 | public String getName() { 25 | return REACT_CLASS; 26 | } 27 | 28 | public RNJWPlayerViewManager(ReactApplicationContext context) { 29 | mAppContext = context; 30 | } 31 | 32 | @Override 33 | public RNJWPlayerView createViewInstance(ThemedReactContext context) { 34 | return new RNJWPlayerView(context, mAppContext); 35 | } 36 | 37 | @ReactProp(name = "config") 38 | public void setConfig(RNJWPlayerView view, ReadableMap prop) { 39 | view.setConfig(prop); 40 | } 41 | 42 | @ReactProp(name = "controls") 43 | public void setControls(RNJWPlayerView view, Boolean controls) { 44 | view.mPlayerView.getPlayer().setControls(controls); 45 | } 46 | 47 | public Map getExportedCustomBubblingEventTypeConstants() { 48 | return MapBuilder.builder() 49 | .put( 50 | "topPlayerError", 51 | MapBuilder.of( 52 | "phasedRegistrationNames", 53 | MapBuilder.of("bubbled", "onPlayerError"))) 54 | .put("topSetupPlayerError", 55 | MapBuilder.of( 56 | "phasedRegistrationNames", 57 | MapBuilder.of("bubbled", "onSetupPlayerError"))) 58 | .put("topPlayerAdError", 59 | MapBuilder.of( 60 | "phasedRegistrationNames", 61 | MapBuilder.of("bubbled", "onPlayerAdError"))) 62 | .put("topPlayerAdWarning", 63 | MapBuilder.of( 64 | "phasedRegistrationNames", 65 | MapBuilder.of("bubbled", "onPlayerAdWarning"))) 66 | .put("topAdEvent", 67 | MapBuilder.of( 68 | "phasedRegistrationNames", 69 | MapBuilder.of("bubbled", "onAdEvent"))) 70 | .put("topAdTime", 71 | MapBuilder.of( 72 | "phasedRegistrationNames", 73 | MapBuilder.of("bubbled", "onAdTime"))) 74 | .put("topTime", 75 | MapBuilder.of( 76 | "phasedRegistrationNames", 77 | MapBuilder.of("bubbled", "onTime"))) 78 | .put("topBuffer", 79 | MapBuilder.of( 80 | "phasedRegistrationNames", 81 | MapBuilder.of("bubbled", "onBuffer"))) 82 | .put("topFullScreen", 83 | MapBuilder.of( 84 | "phasedRegistrationNames", 85 | MapBuilder.of("bubbled", "onFullScreen"))) 86 | .put("topFullScreenExitRequested", 87 | MapBuilder.of( 88 | "phasedRegistrationNames", 89 | MapBuilder.of("bubbled", "onFullScreenExitRequested"))) 90 | .put("topFullScreenRequested", 91 | MapBuilder.of( 92 | "phasedRegistrationNames", 93 | MapBuilder.of("bubbled", "onFullScreenRequested"))) 94 | .put("topFullScreenExit", 95 | MapBuilder.of( 96 | "phasedRegistrationNames", 97 | MapBuilder.of("bubbled", "onFullScreenExit"))) 98 | .put("topPause", 99 | MapBuilder.of( 100 | "phasedRegistrationNames", 101 | MapBuilder.of("bubbled", "onPause"))) 102 | .put("topPlay", 103 | MapBuilder.of( 104 | "phasedRegistrationNames", 105 | MapBuilder.of("bubbled", "onPlay"))) 106 | .put("topComplete", 107 | MapBuilder.of( 108 | "phasedRegistrationNames", 109 | MapBuilder.of("bubbled", "onComplete"))) 110 | .put("topPlaylistComplete", 111 | MapBuilder.of( 112 | "phasedRegistrationNames", 113 | MapBuilder.of("bubbled", "onPlaylistComplete"))) 114 | .put("topPlaylistItem", 115 | MapBuilder.of( 116 | "phasedRegistrationNames", 117 | MapBuilder.of("bubbled", "onPlaylistItem"))) 118 | .put("topSeek", 119 | MapBuilder.of( 120 | "phasedRegistrationNames", 121 | MapBuilder.of("bubbled", "onSeek"))) 122 | .put("topSeeked", 123 | MapBuilder.of( 124 | "phasedRegistrationNames", 125 | MapBuilder.of("bubbled", "onSeeked"))) 126 | .put("topRateChanged", 127 | MapBuilder.of( 128 | "phasedRegistrationNames", 129 | MapBuilder.of("bubbled", "onRateChanged"))) 130 | .put("topControlBarVisible", 131 | MapBuilder.of( 132 | "phasedRegistrationNames", 133 | MapBuilder.of("bubbled", "onControlBarVisible"))) 134 | .put("topOnPlayerReady", 135 | MapBuilder.of( 136 | "phasedRegistrationNames", 137 | MapBuilder.of("bubbled", "onPlayerReady"))) 138 | .put("topBeforePlay", 139 | MapBuilder.of( 140 | "phasedRegistrationNames", 141 | MapBuilder.of("bubbled", "onBeforePlay"))) 142 | .put("topBeforeComplete", 143 | MapBuilder.of( 144 | "phasedRegistrationNames", 145 | MapBuilder.of("bubbled", "onBeforeComplete"))) 146 | .put("topAdPlay", 147 | MapBuilder.of( 148 | "phasedRegistrationNames", 149 | MapBuilder.of("bubbled", "onAdPlay"))) 150 | .put("topAdPause", 151 | MapBuilder.of( 152 | "phasedRegistrationNames", 153 | MapBuilder.of("bubbled", "onAdPause"))) 154 | .put("topAudioTracks", 155 | MapBuilder.of( 156 | "phasedRegistrationNames", 157 | MapBuilder.of("bubbled", "onAudioTracks"))) 158 | .put("topCasting", 159 | MapBuilder.of( 160 | "phasedRegistrationNames", 161 | MapBuilder.of("bubbled", "onCasting"))) 162 | .build(); 163 | } 164 | 165 | @Override 166 | public void onDropViewInstance(@Nonnull RNJWPlayerView view) { 167 | view.destroyPlayer(); 168 | super.onDropViewInstance(view); 169 | view = null; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /android/src/main/java/com/appgoalz/rnjwplayer/Util.java: -------------------------------------------------------------------------------- 1 | package com.appgoalz.rnjwplayer; 2 | 3 | import static androidx.media3.common.util.Util.toByteArray; 4 | 5 | import android.util.Patterns; 6 | import android.webkit.URLUtil; 7 | 8 | import com.facebook.react.bridge.ReadableArray; 9 | import com.facebook.react.bridge.ReadableMap; 10 | import com.jwplayer.pub.api.media.ads.AdBreak; 11 | import com.jwplayer.pub.api.media.captions.Caption; 12 | import com.jwplayer.pub.api.media.captions.CaptionType; 13 | import com.jwplayer.pub.api.media.playlists.MediaSource; 14 | import com.jwplayer.pub.api.media.playlists.PlaylistItem; 15 | 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.io.OutputStream; 19 | import java.net.HttpURLConnection; 20 | import java.net.URL; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | public class Util { 26 | 27 | public static byte[] executePost(String url, byte[] data, Map requestProperties) 28 | throws IOException { 29 | HttpURLConnection urlConnection = null; 30 | try { 31 | urlConnection = (HttpURLConnection) new URL(url).openConnection(); 32 | urlConnection.setRequestMethod("POST"); 33 | urlConnection.setDoOutput(data != null); 34 | urlConnection.setDoInput(true); 35 | if (requestProperties != null) { 36 | for (Map.Entry requestProperty : requestProperties.entrySet()) { 37 | urlConnection.setRequestProperty(requestProperty.getKey(), 38 | requestProperty.getValue()); 39 | } 40 | } 41 | // Write the request body, if there is one. 42 | if (data != null) { 43 | OutputStream out = urlConnection.getOutputStream(); 44 | try { 45 | out.write(data); 46 | } finally { 47 | out.close(); 48 | } 49 | } 50 | // Read and return the response body. 51 | InputStream inputStream = urlConnection.getInputStream(); 52 | try { 53 | return toByteArray(inputStream); 54 | } finally { 55 | inputStream.close(); 56 | } 57 | } finally { 58 | if (urlConnection != null) { 59 | urlConnection.disconnect(); 60 | } 61 | } 62 | } 63 | 64 | public static boolean isValidURL(String url){ 65 | return URLUtil.isValidUrl(url) && Patterns.WEB_URL.matcher(url).matches(); 66 | } 67 | 68 | public static List createPlaylist(ReadableArray playlistItems) { 69 | List playlist = new ArrayList<>(); 70 | if (playlistItems == null || playlistItems.size() <= 0) 71 | return playlist; 72 | 73 | int j = 0; 74 | while (playlistItems.size() > j) { 75 | ReadableMap playlistItem = playlistItems.getMap(j); 76 | 77 | PlaylistItem newPlayListItem = getPlaylistItem((playlistItem)); 78 | playlist.add(newPlayListItem); 79 | j++; 80 | } 81 | return playlist; 82 | } 83 | 84 | public static PlaylistItem getPlaylistItem (ReadableMap playlistItem) { 85 | PlaylistItem.Builder itemBuilder = new PlaylistItem.Builder(); 86 | 87 | if (playlistItem.hasKey("file")) { 88 | String file = playlistItem.getString("file"); 89 | itemBuilder.file(file); 90 | } 91 | 92 | if (playlistItem.hasKey("sources")) { 93 | ArrayList sources = new ArrayList<>(); 94 | ReadableArray sourcesArray = playlistItem.getArray("sources"); 95 | if (sourcesArray != null) { 96 | for (int i = 0; i < sourcesArray.size(); i++) { 97 | ReadableMap sourceProp = sourcesArray.getMap(i); 98 | if (sourceProp.hasKey("file")) { 99 | String file = sourceProp.getString("file"); 100 | String label = sourceProp.getString("label"); 101 | boolean isDefault = sourceProp.getBoolean("default"); 102 | MediaSource source = new MediaSource.Builder().file(file).label(label).isDefault(isDefault).build(); 103 | sources.add(source); 104 | } 105 | } 106 | } 107 | 108 | itemBuilder.sources(sources); 109 | } 110 | 111 | if (playlistItem.hasKey("title")) { 112 | String title = playlistItem.getString("title"); 113 | itemBuilder.title(title); 114 | } 115 | 116 | if (playlistItem.hasKey("description")) { 117 | String desc = playlistItem.getString("description"); 118 | itemBuilder.description(desc); 119 | } 120 | 121 | if (playlistItem.hasKey("image")) { 122 | String image = playlistItem.getString("image"); 123 | itemBuilder.image(image); 124 | } 125 | 126 | if (playlistItem.hasKey("mediaId")) { 127 | String mediaId = playlistItem.getString("mediaId"); 128 | itemBuilder.mediaId(mediaId); 129 | } 130 | 131 | if (playlistItem.hasKey("startTime")) { 132 | double startTime = playlistItem.getDouble("startTime"); 133 | itemBuilder.startTime(startTime); 134 | } 135 | 136 | if (playlistItem.hasKey("duration")) { 137 | int duration = playlistItem.getInt("duration"); 138 | itemBuilder.duration(duration); 139 | } 140 | 141 | if (playlistItem.hasKey("tracks")) { 142 | ArrayList tracks = new ArrayList<>(); 143 | ReadableArray track = playlistItem.getArray("tracks"); 144 | if (track != null) { 145 | for (int i = 0; i < track.size(); i++) { 146 | ReadableMap trackProp = track.getMap(i); 147 | if (trackProp.hasKey("file")) { 148 | String file = trackProp.getString("file"); 149 | String label = trackProp.getString("label"); 150 | boolean isDefault = trackProp.getBoolean("default"); 151 | Caption caption = new Caption.Builder().file(file).label(label).kind(CaptionType.CAPTIONS).isDefault(isDefault).build(); 152 | tracks.add(caption); 153 | } 154 | } 155 | } 156 | 157 | itemBuilder.tracks(tracks); 158 | } 159 | 160 | if (playlistItem.hasKey("authUrl")) { 161 | itemBuilder.mediaDrmCallback(new WidevineCallback(playlistItem.getString("authUrl"))); 162 | } 163 | 164 | if (playlistItem.hasKey("adSchedule")) { 165 | ArrayList adSchedule = new ArrayList<>(); 166 | ReadableArray ad = playlistItem.getArray("adSchedule"); 167 | 168 | for (int i = 0; i < ad.size(); i++) { 169 | ReadableMap adBreakProp = ad.getMap(i); 170 | String offset = adBreakProp.hasKey("offset") ? adBreakProp.getString("offset") : "pre"; 171 | if (adBreakProp.hasKey("tag")) { 172 | AdBreak adBreak = new AdBreak.Builder().offset(offset).tag(adBreakProp.getString("tag")).build(); 173 | adSchedule.add(adBreak); 174 | } 175 | } 176 | 177 | itemBuilder.adSchedule(adSchedule); 178 | } 179 | 180 | String recommendations; 181 | if (playlistItem.hasKey("recommendations")) { 182 | recommendations = playlistItem.getString("recommendations"); 183 | itemBuilder.recommendations(recommendations); 184 | } 185 | 186 | return itemBuilder.build(); 187 | } 188 | 189 | public enum AdEventType { 190 | JWAdEventTypeAdBreakEnd(0), 191 | JWAdEventTypeAdBreakStart(1), 192 | JWAdEventTypeClicked(2), 193 | JWAdEventTypeComplete(3), 194 | JWAdEventTypeImpression(4), 195 | JWAdEventTypeMeta(5), 196 | JWAdEventTypePause(6), 197 | JWAdEventTypePlay(7), 198 | JWAdEventTypeRequest(8), 199 | JWAdEventTypeSchedule(9), 200 | JWAdEventTypeSkipped(10), 201 | JWAdEventTypeStarted(11), 202 | JWAdEventTypeCompanion(12); 203 | 204 | private final int value; 205 | 206 | AdEventType(int value) { 207 | this.value = value; 208 | } 209 | 210 | public int getValue() { 211 | return value; 212 | } 213 | } 214 | 215 | // Method to get the event type value 216 | public static int getEventTypeValue(AdEventType eventType) { 217 | return eventType.getValue(); 218 | } 219 | } -------------------------------------------------------------------------------- /android/src/main/java/com/appgoalz/rnjwplayer/WidevineCallback.java: -------------------------------------------------------------------------------- 1 | package com.appgoalz.rnjwplayer; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Parcel; 5 | import android.text.TextUtils; 6 | 7 | import androidx.annotation.OptIn; 8 | import androidx.media3.common.util.UnstableApi; 9 | import androidx.media3.exoplayer.drm.ExoMediaDrm; 10 | 11 | import com.jwplayer.pub.api.media.drm.MediaDrmCallback; 12 | 13 | import java.io.IOException; 14 | import java.util.UUID; 15 | 16 | public class WidevineCallback implements MediaDrmCallback { 17 | 18 | private final String defaultUri; 19 | 20 | public WidevineCallback(String drmAuthUrl) { 21 | defaultUri = drmAuthUrl; 22 | } 23 | 24 | protected WidevineCallback(Parcel in) { 25 | defaultUri = in.readString(); 26 | } 27 | 28 | public static final Creator CREATOR = new Creator() { 29 | @Override 30 | public WidevineCallback createFromParcel(Parcel in) { 31 | return new WidevineCallback(in); 32 | } 33 | 34 | @Override 35 | public WidevineCallback[] newArray(int size) { 36 | return new WidevineCallback[size]; 37 | } 38 | }; 39 | 40 | @OptIn(markerClass = UnstableApi.class) @Override 41 | public byte[] executeProvisionRequest(UUID uuid, ExoMediaDrm.ProvisionRequest request) throws IOException { 42 | String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); 43 | return Util.executePost(url, null, null); 44 | } 45 | 46 | @OptIn(markerClass = UnstableApi.class) @Override 47 | public byte[] executeKeyRequest(UUID uuid, ExoMediaDrm.KeyRequest request) throws IOException { 48 | String url = request.getLicenseServerUrl(); 49 | if (TextUtils.isEmpty(url)) { 50 | url = defaultUri; 51 | } 52 | return Util.executePost(url, request.getData(), null); 53 | } 54 | 55 | @Override 56 | public int describeContents() { 57 | return 0; 58 | } 59 | 60 | @Override 61 | public void writeToParcel(Parcel dest, int flags) { 62 | dest.writeString(defaultUri); 63 | } 64 | } -------------------------------------------------------------------------------- /images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/images/1.png -------------------------------------------------------------------------------- /images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/images/2.png -------------------------------------------------------------------------------- /images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaimPaneth/react-native-jw-media-player/6856ee8b83f64af8c70a9586369ae4d02d7b2b90/images/3.png -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "react-native-jw-media-player" { 2 | import React from "react"; 3 | import { ViewStyle } from "react-native"; 4 | 5 | interface AudioTrack { 6 | autoSelect: boolean; 7 | defaultTrack: boolean; 8 | groupId: string; 9 | language: string; 10 | name: string; 11 | } 12 | 13 | export interface QualityLevel { 14 | playlistPosition: number; 15 | bitRate: number; 16 | label: string; 17 | height: number; 18 | width: number; 19 | index: number; 20 | } 21 | 22 | interface CastingDevice { 23 | name?: string; 24 | identifier?: string; 25 | } 26 | interface Source { 27 | file: string; 28 | label: string; 29 | default?: boolean; 30 | } 31 | interface Track { 32 | file: string; 33 | label: string; 34 | default?: boolean; 35 | } 36 | interface JWAdSettings { 37 | allowsBackgroundPlayback?: boolean; 38 | // Add other ad settings properties as needed 39 | } 40 | interface IMASettings { 41 | locale?: string; 42 | ppid?: string; 43 | maxRedirects?: number; 44 | sessionID?: string; 45 | debugMode?: boolean; 46 | } 47 | interface AdSchedule { 48 | tag: string; 49 | offset: string; 50 | } 51 | // interface CompanionAdSlot { 52 | // viewId: string; // Reference to a UIView in the application 53 | // size?: { width: number; height: number }; 54 | // } 55 | interface GoogleDAIStream { 56 | videoID?: string; 57 | cmsID?: string; 58 | assetKey?: string; 59 | apiKey?: string; 60 | adTagParameters?: { [key: string]: string }; 61 | } 62 | interface AdRule { 63 | startOn: number; 64 | frequency: number; 65 | timeBetweenAds: number; 66 | startOnSeek: 'none' | 'pre'; // Mapped from JWAdShownOnSeek 67 | } 68 | // interface FriendlyObstruction { 69 | // viewId: string; 70 | // purpose: 'mediaControls' | 'closeAd' | 'notVisible' | 'other'; // Mapped from JWFriendlyObstructionPurpose 71 | // reason?: string; 72 | // } 73 | type ClientTypes = "vast" | "ima" | "ima_dai"; 74 | interface VASTAdvertising { 75 | adSchedule?: AdSchedule[]; 76 | adVmap?: string; 77 | tag?: string; // Vast xml url 78 | openBrowserOnAdClick?: boolean; 79 | adClient: "vast"; 80 | adRules?: AdRule; 81 | adSettings?: JWAdSettings; 82 | } 83 | interface IMAAdvertising { 84 | adSchedule?: AdSchedule[]; 85 | adVmap?: string; 86 | tag?: string; // Vast xml url 87 | adClient: "ima"; 88 | adRules?: AdRule; 89 | imaSettings?: IMASettings; 90 | // companionAdSlots?: CompanionAdSlot[]; 91 | // friendlyObstructions?: FriendlyObstruction[]; 92 | } 93 | interface IMA_DAIAdvertising { 94 | adClient: "ima_dai"; 95 | imaSettings?: IMASettings; 96 | // friendlyObstructions?: FriendlyObstruction[]; 97 | googleDAIStream?: GoogleDAIStream; 98 | } 99 | type Advertising = VASTAdvertising | IMAAdvertising | IMA_DAIAdvertising; 100 | interface PlaylistItem { 101 | file: string; 102 | sources?: Source[]; 103 | image?: string; 104 | title?: string; 105 | description?: string; 106 | mediaId?: string; 107 | adSchedule?: AdSchedule[]; 108 | adVmap?: string; 109 | tracks?: Track[]; 110 | recommendations?: string; 111 | startTime?: number; 112 | autostart?: boolean; 113 | } 114 | type RelatedOnClicks = "play" | "link"; 115 | type RelatedOnCompletes = "show" | "hide" | "autoplay"; 116 | interface Related { 117 | onClick?: RelatedOnClicks; 118 | onComplete?: RelatedOnCompletes; 119 | heading?: string; 120 | url?: string; 121 | autoplayMessage?: string; 122 | autoplayTimer?: number; 123 | } 124 | interface Font { 125 | name?: string; 126 | size?: number; 127 | } 128 | type EdgeStyles = "none" | "dropshadow" | "raised" | "depressed" | "uniform"; 129 | interface Styling { 130 | colors?: { 131 | buttons?: string; 132 | backgroundColor?: string; 133 | fontColor?: string; 134 | timeslider?: { progress?: string; rail?: string; thumb?: string }; 135 | }; 136 | font?: Font; 137 | displayTitle?: boolean; 138 | displayDescription?: boolean; 139 | captionsStyle?: { 140 | font?: Font; 141 | fontColor?: string; 142 | backgroundColor?: string; 143 | highlightColor?: string; 144 | edgeStyle?: EdgeStyles; 145 | }; 146 | menuStyle: { 147 | font?: Font; 148 | fontColor?: string; 149 | backgroundColor?: string; 150 | }; 151 | } 152 | type Preloads = "auto" | "none"; 153 | type InterfaceBehaviors = "normal" | "hidden" | "onscreen"; 154 | type UIGroups = 155 | | "overlay" 156 | | "control_bar" 157 | | "center_controls" 158 | | "next_up" 159 | | "error" 160 | | "playlist" 161 | | "controls_container" 162 | | "settings_menu" 163 | | "quality_submenu" 164 | | "captions_submenu" 165 | | "playback_submenu" 166 | | "audiotracks_submenu" 167 | | "casting_menu"; 168 | type AudioSessionCategory = 169 | | "Ambient" 170 | | "SoloAmbient" 171 | | "Playback" 172 | | "Record" 173 | | "PlayAndRecord" 174 | | "MultiRoute"; 175 | type AudioSessionCategoryOptions = 176 | | "MixWithOthers" 177 | | "DuckOthers" 178 | | "AllowBluetooth" 179 | | "DefaultToSpeaker" 180 | | "InterruptSpokenAudioAndMix" 181 | | "AllowBluetoothA2DP" 182 | | "AllowAirPlay" 183 | | "OverrideMutedMicrophone"; 184 | type AudioSessionMode = 185 | | "Default" 186 | | "VoiceChat" 187 | | "VideoChat" 188 | | "GameChat" 189 | | "VideoRecording" 190 | | "Measurement" 191 | | "MoviePlayback" 192 | | "SpokenAudio" 193 | | "VoicePrompt"; 194 | type JWControlType = 195 | | "forward" 196 | | "rewind" 197 | | "pip" 198 | | "airplay" 199 | | "chromecast" 200 | | "next" 201 | | "previous" 202 | | "settings" 203 | | "languages" 204 | | "fullscreen"; 205 | interface Config { 206 | license: string; 207 | advertising?: Advertising; 208 | autostart?: boolean; 209 | controls?: boolean; 210 | repeat?: boolean; 211 | nextUpStyle?: { offsetSeconds: number; offsetPercentage: number }; 212 | styling?: Styling; 213 | backgroundAudioEnabled?: boolean; 214 | category?: AudioSessionCategory; 215 | categoryOptions?: Array; 216 | mode?: AudioSessionMode; 217 | fullScreenOnLandscape?: boolean; 218 | landscapeOnFullScreen?: boolean; 219 | portraitOnExitFullScreen?: boolean; 220 | exitFullScreenOnPortrait?: boolean; 221 | playlist?: PlaylistItem[]; 222 | stretching?: string; 223 | related?: Related; 224 | preload?: Preloads; 225 | interfaceBehavior?: InterfaceBehaviors; 226 | interfaceFadeDelay?: number; 227 | hideUIGroups?: UIGroups[]; 228 | processSpcUrl?: string; 229 | fairplayCertUrl?: string; 230 | contentUUID?: string; 231 | viewOnly?: boolean; 232 | enableLockScreenControls: boolean; 233 | pipEnabled: boolean; 234 | } 235 | interface BaseEvent { 236 | nativeEvent: T; 237 | } 238 | interface SeekEventProps { 239 | position: number; 240 | offset: number; 241 | } 242 | interface SeekedEventProps { 243 | position: number; 244 | } 245 | interface RateChangedEventProps { 246 | rate: number; 247 | at: number; 248 | } 249 | interface TimeEventProps { 250 | position: number; 251 | duration: number; 252 | } 253 | interface ControlBarVisibleEventProps { 254 | visible: boolean; 255 | } 256 | interface PlaylistEventProps { 257 | playlist: PlaylistItem[] 258 | } 259 | interface PlaylistItemEventProps { 260 | playlistItem: PlaylistItem 261 | } 262 | interface PlayerErrorEventProps { 263 | code: string; 264 | error: string; 265 | } 266 | interface PlayerWarningEventProps { 267 | code: string; 268 | warning: string; 269 | } 270 | interface AdEventProps { 271 | client?: string; 272 | reason?: string; 273 | type: number; 274 | } 275 | type NativeError = (event: BaseEvent) => void; 276 | type NativeWarning = (event: BaseEvent) => void; 277 | interface PropsType { 278 | config: Config; 279 | style?: ViewStyle; 280 | controls?: boolean; 281 | onPlayerReady?: () => void; 282 | onPlaylist?: (event: BaseEvent) => void; 283 | onBeforePlay?: () => void; 284 | onBeforeComplete?: () => void; 285 | onComplete?: () => void; 286 | onPlay?: () => void; 287 | onPause?: () => void; 288 | onSeek?: (event: BaseEvent) => void; 289 | onSeeked?: (event?: BaseEvent) => void; 290 | onRateChanged?: (event?: BaseEvent) => void; 291 | onSetupPlayerError?: NativeError; 292 | onPlayerError?: NativeError; 293 | onPlayerWarning?: NativeWarning; 294 | onPlayerAdError?: NativeError; 295 | onPlayerAdWarning?: NativeWarning; 296 | onAdEvent?: (event: BaseEvent) => void; 297 | onAdTime?: (event: BaseEvent) => void; 298 | onBuffer?: () => void; 299 | onTime?: (event: BaseEvent) => void; 300 | onFullScreenRequested?: () => void; 301 | onFullScreen?: () => void; 302 | onFullScreenExitRequested?: () => void; 303 | onFullScreenExit?: () => void; 304 | onControlBarVisible?: (event: BaseEvent) => void; 305 | onPlaylistComplete?: () => void; 306 | onPlaylistItem?: (event: BaseEvent) => void; 307 | onAudioTracks?: () => void; 308 | shouldComponentUpdate?: (nextProps: any, nextState: any) => boolean; 309 | } 310 | 311 | export default class JWPlayer extends React.Component { 312 | pause(): void; 313 | play(): void; 314 | stop(): void; 315 | toggleSpeed(): void; 316 | setSpeed(speed: number): void; 317 | setCurrentQuality(index: number): void; 318 | currentQuality(): number; 319 | getQualityLevels(): Promise; 320 | setVolume(volume: number): void; 321 | setPlaylistIndex(index: number): void; 322 | setControls(show: boolean): void; 323 | setLockScreenControls(show: boolean): void; 324 | seekTo(time: number): void; 325 | loadPlaylist(playlistItems: PlaylistItem[]): void; 326 | setFullscreen(fullScreen: boolean): void; 327 | position(): Promise; 328 | setUpCastController(): void; 329 | presentCastDialog(): void; 330 | connectedDevice(): Promise; 331 | availableDevices(): Promise; 332 | castState(): Promise; 333 | playerState(): Promise; 334 | getAudioTracks(): Promise; 335 | getCurrentAudioTrack(): Promise; 336 | setCurrentAudioTrack(index: number): void; 337 | setCurrentCaptions(index: number): void; 338 | setVisibility(visibility: boolean, controls: JWControlType[]): void; 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /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 | 36 | /* player events */ 37 | RCT_EXPORT_VIEW_PROPERTY(onPlayerReady, RCTDirectEventBlock); 38 | RCT_EXPORT_VIEW_PROPERTY(onSetupPlayerError, RCTDirectEventBlock); 39 | RCT_EXPORT_VIEW_PROPERTY(onPlayerError, RCTDirectEventBlock); 40 | RCT_EXPORT_VIEW_PROPERTY(onPlayerWarning, RCTDirectEventBlock); 41 | 42 | /* ad events */ 43 | RCT_EXPORT_VIEW_PROPERTY(onPlayerAdWarning, RCTDirectEventBlock); 44 | RCT_EXPORT_VIEW_PROPERTY(onPlayerAdError, RCTDirectEventBlock); 45 | RCT_EXPORT_VIEW_PROPERTY(onAdEvent, RCTDirectEventBlock); 46 | RCT_EXPORT_VIEW_PROPERTY(onAdTime, RCTDirectEventBlock); 47 | 48 | /* jwplayer view controller events */ 49 | RCT_EXPORT_VIEW_PROPERTY(onControlBarVisible, RCTDirectEventBlock); 50 | RCT_EXPORT_VIEW_PROPERTY(onScreenTapped, RCTDirectEventBlock); 51 | RCT_EXPORT_VIEW_PROPERTY(onFullScreen, RCTDirectEventBlock); 52 | RCT_EXPORT_VIEW_PROPERTY(onFullScreenRequested, RCTDirectEventBlock); 53 | RCT_EXPORT_VIEW_PROPERTY(onFullScreenExit, RCTDirectEventBlock); 54 | RCT_EXPORT_VIEW_PROPERTY(onFullScreenExitRequested, RCTDirectEventBlock); 55 | 56 | /* jwplayer view events */ 57 | RCT_EXPORT_VIEW_PROPERTY(onPlayerSizeChange, RCTDirectEventBlock); 58 | 59 | /* casting events */ 60 | RCT_EXPORT_VIEW_PROPERTY(onCastingDevicesAvailable, RCTDirectEventBlock); 61 | RCT_EXPORT_VIEW_PROPERTY(onConnectedToCastingDevice, RCTDirectEventBlock); 62 | RCT_EXPORT_VIEW_PROPERTY(onDisconnectedFromCastingDevice, RCTDirectEventBlock); 63 | RCT_EXPORT_VIEW_PROPERTY(onConnectionTemporarilySuspended, RCTDirectEventBlock); 64 | RCT_EXPORT_VIEW_PROPERTY(onConnectionRecovered, RCTDirectEventBlock); 65 | RCT_EXPORT_VIEW_PROPERTY(onConnectionFailed, RCTDirectEventBlock); 66 | RCT_EXPORT_VIEW_PROPERTY(onCasting, RCTDirectEventBlock); 67 | RCT_EXPORT_VIEW_PROPERTY(onCastingEnded, RCTDirectEventBlock); 68 | RCT_EXPORT_VIEW_PROPERTY(onCastingFailed, RCTDirectEventBlock); 69 | 70 | /* props */ 71 | RCT_EXPORT_VIEW_PROPERTY(config, NSDictionary); 72 | RCT_EXPORT_VIEW_PROPERTY(controls, BOOL); 73 | 74 | RCT_EXTERN_METHOD(state: (nonnull NSNumber*) reactTag: (RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject) 75 | 76 | RCT_EXTERN_METHOD(pause: (nonnull NSNumber*)reactTag) 77 | 78 | RCT_EXTERN_METHOD(play: (nonnull NSNumber *)reactTag) 79 | 80 | RCT_EXTERN_METHOD(stop: (nonnull NSNumber *)reactTag) 81 | 82 | RCT_EXTERN_METHOD(position: (nonnull NSNumber *)reactTag: (RCTPromiseResolveBlock)resolve: (RCTPromiseRejectBlock)reject) 83 | 84 | RCT_EXTERN_METHOD(toggleSpeed: (nonnull NSNumber*)reactTag) 85 | 86 | RCT_EXTERN_METHOD(setSpeed: (nonnull NSNumber*)reactTag: (double)speed) 87 | 88 | RCT_EXTERN_METHOD(setPlaylistIndex: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index) 89 | 90 | RCT_EXTERN_METHOD(seekTo: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)time) 91 | 92 | RCT_EXTERN_METHOD(setVolume: (nonnull NSNumber *)reactTag :(nonnull NSNumber *)volume) 93 | 94 | RCT_EXTERN_METHOD(togglePIP: (nonnull NSNumber *)reactTag) 95 | 96 | RCT_EXTERN_METHOD(setUpCastController: (nonnull NSNumber *)reactTag) 97 | 98 | RCT_EXTERN_METHOD(presentCastDialog: (nonnull NSNumber *)reactTag) 99 | 100 | RCT_EXTERN_METHOD(connectedDevice: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject) 101 | 102 | RCT_EXTERN_METHOD(availableDevices: (nonnull NSNumber *)reactTag: (RCTPromiseResolveBlock)resolve: (RCTPromiseRejectBlock)reject) 103 | 104 | RCT_EXTERN_METHOD(castState: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject) 105 | 106 | RCT_EXTERN_METHOD(getAudioTracks: (nonnull NSNumber *)reactTag: (RCTPromiseResolveBlock)resolve: (RCTPromiseRejectBlock)reject) 107 | 108 | RCT_EXTERN_METHOD(getCurrentAudioTrack: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject) 109 | 110 | RCT_EXTERN_METHOD(setCurrentAudioTrack: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index) 111 | 112 | RCT_EXTERN_METHOD(setControls: (nonnull NSNumber *)reactTag: (BOOL)show) 113 | 114 | RCT_EXTERN_METHOD(setVisibility: (nonnull NSNumber *)reactTag: (BOOL)visibilty: (nonnull NSArray *)controls) 115 | 116 | RCT_EXTERN_METHOD(setLockScreenControls: (nonnull NSNumber *)reactTag: (BOOL)show) 117 | 118 | RCT_EXTERN_METHOD(setCurrentCaptions: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index) 119 | 120 | RCT_EXTERN_METHOD(setCurrentCaptions: (nonnull NSNumber *)reactTag: (nonnull NSNumber *)index) 121 | 122 | RCT_EXTERN_METHOD(setLicenseKey: (nonnull NSNumber *)reactTag: (nonnull NSString *)license) 123 | 124 | RCT_EXTERN_METHOD(quite) 125 | 126 | RCT_EXTERN_METHOD(reset) 127 | 128 | RCT_EXTERN_METHOD(loadPlaylist: (nonnull NSNumber *)reactTag: (nonnull NSArray *)playlist) 129 | 130 | RCT_EXTERN_METHOD(setFullscreen: (nonnull NSNumber *)reactTag: (BOOL)fullscreen) 131 | 132 | @end 133 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-jw-media-player", 3 | "version": "0.2.46", 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 | }, 10 | "pre-commit": [ 11 | "lint" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/chaimPaneth/react-native-jw-media-player.git" 16 | }, 17 | "keywords": [ 18 | "react", 19 | "react-native", 20 | "jwplayer", 21 | "media", 22 | "player", 23 | "mediaplayer", 24 | "media-player", 25 | "jw", 26 | "android", 27 | "ios", 28 | "audio", 29 | "video", 30 | "sdk" 31 | ], 32 | "author": "chaimPaneth (chaimpaneth.com)", 33 | "license": "MIT", 34 | "homepage": "https://github.com/chaimPaneth/react-native-jw-media-player#readme", 35 | "devDependencies": { 36 | "react": ">= 18.2.0", 37 | "react-native": ">= 0.72.5", 38 | "lodash": ">= 4.17.21" 39 | } 40 | } 41 | --------------------------------------------------------------------------------