├── example ├── .watchmanconfig ├── .gitattributes ├── app.json ├── .prettierrc.js ├── .eslintrc.js ├── babel.config.js ├── react-native-media-controls │ ├── @types │ │ └── react-native-slider.d.ts │ ├── assets │ │ ├── ic_pause@2x.png │ │ ├── ic_pause@3x.png │ │ ├── ic_play@2x.png │ │ ├── ic_play@3x.png │ │ ├── ic_replay@2x.png │ │ ├── ic_replay@3x.png │ │ ├── ic_fullscreen@2x.png │ │ └── ic_fullscreen@3x.png │ ├── constants │ │ └── playerStates.d.ts │ ├── index.d.ts │ ├── Toolbar.d.ts │ ├── utils.d.ts │ ├── Controls.d.ts │ ├── Slider.d.ts │ ├── MediaControls.d.ts │ ├── MediaControls.style.d.ts │ ├── react-native-media-controls.cjs.production.min.js │ ├── react-native-media-controls.esm.js │ ├── react-native-media-controls.cjs.development.js │ ├── react-native-media-controls.cjs.production.min.js.map │ ├── react-native-media-controls.esm.js.map │ └── react-native-media-controls.cjs.development.js.map ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ └── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── java │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ │ └── MainApplication.java │ │ │ │ └── AndroidManifest.xml │ │ │ └── debug │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ReactNativeFlipper.java │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ ├── build_defs.bzl │ │ ├── _BUCK │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── build.gradle │ ├── gradle.properties │ └── gradlew ├── ios │ ├── example │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── Info.plist │ │ ├── AppDelegate.m │ │ └── Base.lproj │ │ │ └── LaunchScreen.xib │ ├── example.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m │ ├── example-tvOSTests │ │ └── Info.plist │ ├── example-tvOS │ │ └── Info.plist │ ├── example.xcodeproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── example.xcscheme │ │ │ └── example-tvOS.xcscheme │ ├── Podfile │ └── Podfile.lock ├── .buckconfig ├── index.js ├── __tests__ │ └── App-test.js ├── metro.config.js ├── package.json ├── .gitignore ├── .flowconfig └── App.js ├── .npmignore ├── src ├── @types │ └── react-native-slider.ts ├── assets │ ├── ic_pause@2x.png │ ├── ic_pause@3x.png │ ├── ic_play@2x.png │ ├── ic_play@3x.png │ ├── ic_replay@2x.png │ ├── ic_replay@3x.png │ ├── ic_fullscreen@2x.png │ └── ic_fullscreen@3x.png ├── constants │ └── playerStates.ts ├── index.tsx ├── Toolbar.tsx ├── utils.ts ├── Controls.tsx ├── MediaControls.style.ts ├── Slider.tsx └── MediaControls.tsx ├── .prettierrc.js ├── tsconfig.json ├── tsdx.config.js ├── .gitignore ├── .github └── workflows │ └── main.yml ├── LICENSE ├── package.json └── README.md /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example/ 2 | src/ 3 | .*rc 4 | *.iml 5 | -------------------------------------------------------------------------------- /src/@types/react-native-slider.ts: -------------------------------------------------------------------------------- 1 | declare module "react-native-slider"; 2 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "displayName": "example" 4 | } -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: "all", 3 | printWidth: 80, 4 | }; 5 | -------------------------------------------------------------------------------- /example/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: 'all', 3 | printWidth: 80, 4 | }; 5 | -------------------------------------------------------------------------------- /example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native-community', 4 | }; 5 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/react-native-media-controls/@types/react-native-slider.d.ts: -------------------------------------------------------------------------------- 1 | declare module "react-native-slider"; 2 | -------------------------------------------------------------------------------- /src/assets/ic_pause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/src/assets/ic_pause@2x.png -------------------------------------------------------------------------------- /src/assets/ic_pause@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/src/assets/ic_pause@3x.png -------------------------------------------------------------------------------- /src/assets/ic_play@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/src/assets/ic_play@2x.png -------------------------------------------------------------------------------- /src/assets/ic_play@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/src/assets/ic_play@3x.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | example 3 | 4 | -------------------------------------------------------------------------------- /src/assets/ic_replay@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/src/assets/ic_replay@2x.png -------------------------------------------------------------------------------- /src/assets/ic_replay@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/src/assets/ic_replay@3x.png -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/ic_fullscreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/src/assets/ic_fullscreen@2x.png -------------------------------------------------------------------------------- /src/assets/ic_fullscreen@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/src/assets/ic_fullscreen@3x.png -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/debug.keystore -------------------------------------------------------------------------------- /src/constants/playerStates.ts: -------------------------------------------------------------------------------- 1 | enum PLAYER_STATES { 2 | PLAYING = 0, 3 | PAUSED = 1, 4 | ENDED = 2, 5 | } 6 | 7 | export { PLAYER_STATES }; 8 | -------------------------------------------------------------------------------- /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/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/react-native-media-controls/assets/ic_pause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/react-native-media-controls/assets/ic_pause@2x.png -------------------------------------------------------------------------------- /example/react-native-media-controls/assets/ic_pause@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/react-native-media-controls/assets/ic_pause@3x.png -------------------------------------------------------------------------------- /example/react-native-media-controls/assets/ic_play@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/react-native-media-controls/assets/ic_play@2x.png -------------------------------------------------------------------------------- /example/react-native-media-controls/assets/ic_play@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/react-native-media-controls/assets/ic_play@3x.png -------------------------------------------------------------------------------- /example/react-native-media-controls/assets/ic_replay@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/react-native-media-controls/assets/ic_replay@2x.png -------------------------------------------------------------------------------- /example/react-native-media-controls/assets/ic_replay@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/react-native-media-controls/assets/ic_replay@3x.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/react-native-media-controls/constants/playerStates.d.ts: -------------------------------------------------------------------------------- 1 | declare enum PLAYER_STATES { 2 | PLAYING = 0, 3 | PAUSED = 1, 4 | ENDED = 2 5 | } 6 | export { PLAYER_STATES }; 7 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import MediaControls from "./MediaControls"; 2 | import { PLAYER_STATES } from "./constants/playerStates"; 3 | 4 | export { PLAYER_STATES }; 5 | 6 | export default MediaControls; 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/react-native-media-controls/assets/ic_fullscreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/react-native-media-controls/assets/ic_fullscreen@2x.png -------------------------------------------------------------------------------- /example/react-native-media-controls/assets/ic_fullscreen@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/react-native-media-controls/assets/ic_fullscreen@3x.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charliesbot/react-native-media-controls/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src/Toolbar.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | type Props = { 4 | children: React.ReactNode; 5 | }; 6 | 7 | const Toolbar = ({ children }: Props) => <>{children}; 8 | 9 | export { Toolbar }; 10 | -------------------------------------------------------------------------------- /example/react-native-media-controls/index.d.ts: -------------------------------------------------------------------------------- 1 | import MediaControls from "./MediaControls"; 2 | import { PLAYER_STATES } from "./constants/playerStates"; 3 | export { PLAYER_STATES }; 4 | export default MediaControls; 5 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /example/react-native-media-controls/Toolbar.d.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | declare type Props = { 3 | children: React.ReactNode; 4 | }; 5 | declare const Toolbar: ({ children }: Props) => JSX.Element; 6 | export { Toolbar }; 7 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /example/ios/example/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/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/react-native-media-controls/utils.d.ts: -------------------------------------------------------------------------------- 1 | import { PLAYER_STATES } from "./constants/playerStates"; 2 | export declare const humanizeVideoDuration: (seconds: number) => string; 3 | export declare const noop: () => void; 4 | export declare const getPlayerStateIcon: (playerState: PLAYER_STATES) => any; 5 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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/react-native-media-controls/Controls.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { Props } from "./MediaControls"; 3 | declare type ControlsProps = Pick & { 4 | onPause: () => void; 5 | }; 6 | declare const Controls: (props: ControlsProps) => JSX.Element; 7 | export { Controls }; 8 | -------------------------------------------------------------------------------- /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: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types"], 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "lib": ["dom", "esnext"], 6 | "importHelpers": true, 7 | "declaration": true, 8 | "sourceMap": true, 9 | "rootDir": "./src", 10 | "strict": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "noImplicitReturns": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "moduleResolution": "node", 16 | "baseUrl": "./", 17 | "paths": { 18 | "*": ["src/*", "node_modules/*"] 19 | }, 20 | "jsx": "react", 21 | "esModuleInterop": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/react-native-media-controls/Slider.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { ViewStyle } from "react-native"; 3 | import { Props as MediaControlsProps } from "./MediaControls"; 4 | export declare type CustomSliderStyle = { 5 | containerStyle: ViewStyle; 6 | trackStyle: ViewStyle; 7 | thumbStyle: ViewStyle; 8 | }; 9 | declare type Props = Pick & { 10 | onPause: () => void; 11 | customSliderStyle?: CustomSliderStyle; 12 | }; 13 | declare const Slider: (props: Props) => JSX.Element; 14 | export { Slider }; 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { PLAYER_STATES } from "./constants/playerStates"; 2 | 3 | export const humanizeVideoDuration = (seconds: number) => { 4 | const [begin, end] = seconds >= 3600 ? [11, 8] : [14, 5]; 5 | const date = new Date(0); 6 | 7 | date.setSeconds(seconds); 8 | return date.toISOString().substr(begin, end); 9 | }; 10 | 11 | export const noop = () => {}; 12 | 13 | export const getPlayerStateIcon = (playerState: PLAYER_STATES) => { 14 | switch (playerState) { 15 | case PLAYER_STATES.PAUSED: 16 | return require("./assets/ic_play.png"); 17 | case PLAYER_STATES.PLAYING: 18 | return require("./assets/ic_pause.png"); 19 | case PLAYER_STATES.ENDED: 20 | return require("./assets/ic_replay.png"); 21 | default: 22 | return null; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /tsdx.config.js: -------------------------------------------------------------------------------- 1 | const copy = require("rollup-plugin-copy-assets"); 2 | 3 | module.exports = { 4 | rollup(config, options) { 5 | const { localDev, name } = options; 6 | const outputDirectory = (() => { 7 | if (localDev) { 8 | return `example/${name}`; 9 | } 10 | 11 | return `dist/`; 12 | })(); 13 | 14 | const external = config.external; 15 | config.external = id => external(id) || id.endsWith(".png"); 16 | 17 | config.plugins = [ 18 | ...config.plugins, 19 | copy({ 20 | assets: ["src/assets"], 21 | dest: `${outputDirectory}/assets`, 22 | }), 23 | ]; 24 | 25 | if (localDev) { 26 | config.output.file = config.output.file.replace("dist", outputDirectory); 27 | } 28 | 29 | return config; 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | .rts2_cache_cjs 6 | .rts2_cache_esm 7 | .rts2_cache_umd 8 | .rts2_cache_system 9 | dist 10 | 11 | # Xcode 12 | build/ 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata 22 | *.xccheckout 23 | *.moved-aside 24 | DerivedData 25 | *.hmap 26 | *.ipa 27 | *.xcuserstate 28 | project.xcworkspace 29 | 30 | # Android/IJ 31 | # 32 | .idea 33 | .gradle 34 | local.properties 35 | *.iml 36 | example/android/build 37 | 38 | # node.js 39 | node_modules/ 40 | example/node_modules/ 41 | example/npm-debug.log 42 | 43 | # BUCK 44 | buck-out/ 45 | \.buckd/ 46 | example/android/app/libs 47 | example/android/keystores/debug.keystore 48 | 49 | lib/ 50 | 51 | yarn-error.log 52 | -------------------------------------------------------------------------------- /example/ios/exampleTests/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/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 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": "16.13.1", 14 | "react-native": "0.63.2", 15 | "react-native-slider": "^0.11.0", 16 | "react-native-video": "^4.4.5" 17 | }, 18 | "devDependencies": { 19 | "@babel/core": "^7.8.4", 20 | "@babel/runtime": "^7.8.4", 21 | "@react-native-community/eslint-config": "^1.1.0", 22 | "babel-jest": "^25.1.0", 23 | "eslint": "^6.5.1", 24 | "jest": "^25.1.0", 25 | "metro-react-native-babel-preset": "^0.59.0", 26 | "react-test-renderer": "16.13.1" 27 | }, 28 | "jest": { 29 | "preset": "react-native" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/ios/example-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 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 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | 7 | steps: 8 | - name: Begin CI... 9 | uses: actions/checkout@v2 10 | 11 | - name: Use Node 12 12 | uses: actions/setup-node@v1 13 | with: 14 | node-version: 12.x 15 | 16 | - name: Use cached node_modules 17 | uses: actions/cache@v1 18 | with: 19 | path: node_modules 20 | key: nodeModules-${{ hashFiles('**/yarn.lock') }} 21 | restore-keys: | 22 | nodeModules- 23 | 24 | - name: Install dependencies 25 | run: yarn install --frozen-lockfile 26 | env: 27 | CI: true 28 | 29 | - name: Lint 30 | run: yarn lint 31 | env: 32 | CI: true 33 | 34 | - name: Build 35 | run: yarn build 36 | env: 37 | CI: true 38 | -------------------------------------------------------------------------------- /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 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Charlie L 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. -------------------------------------------------------------------------------- /example/react-native-media-controls/MediaControls.d.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { GestureResponderEvent, ViewStyle } from "react-native"; 3 | import { PLAYER_STATES } from "./constants/playerStates"; 4 | import { CustomSliderStyle } from "./Slider"; 5 | export declare type Props = { 6 | children: React.ReactNode; 7 | containerStyle: ViewStyle; 8 | duration: number; 9 | fadeOutDelay?: number; 10 | isFullScreen: boolean; 11 | isLoading: boolean; 12 | mainColor: string; 13 | onFullScreen?: (event: GestureResponderEvent) => void; 14 | onPaused: (playerState: PLAYER_STATES) => void; 15 | onReplay: () => void; 16 | onSeek: (value: number) => void; 17 | onSeeking: (value: number) => void; 18 | playerState: PLAYER_STATES; 19 | progress: number; 20 | showOnStart?: boolean; 21 | sliderStyle: CustomSliderStyle; 22 | toolbarStyle: ViewStyle; 23 | }; 24 | declare const MediaControls: { 25 | (props: Props): JSX.Element; 26 | Toolbar: ({ children }: { 27 | children: React.ReactNode; 28 | }) => JSX.Element; 29 | }; 30 | export default MediaControls; 31 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /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 = "29.0.2" 6 | minSdkVersion = 16 7 | compileSdkVersion = 29 8 | targetSdkVersion = 29 9 | } 10 | repositories { 11 | google() 12 | jcenter() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:3.5.3") 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | mavenLocal() 24 | maven { 25 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 26 | url("$rootDir/../node_modules/react-native/android") 27 | } 28 | maven { 29 | // Android JSC is installed from npm 30 | url("$rootDir/../node_modules/jsc-android/dist") 31 | } 32 | 33 | google() 34 | jcenter() 35 | maven { url 'https://www.jitpack.io' } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.3.0", 3 | "name": "react-native-media-controls", 4 | "author": { 5 | "name": "Charlie L", 6 | "email": "charliesbot@gmail.com" 7 | }, 8 | "license": "MIT", 9 | "keywords": [ 10 | "react-native", 11 | "ios", 12 | "android", 13 | "video", 14 | "audio", 15 | "media", 16 | "control" 17 | ], 18 | "main": "dist/index.js", 19 | "module": "dist/react-native-media-controls.esm.js", 20 | "typings": "dist/index.d.ts", 21 | "files": [ 22 | "dist", 23 | "src" 24 | ], 25 | "engines": { 26 | "node": ">=10" 27 | }, 28 | "scripts": { 29 | "start": "tsdx watch", 30 | "dev": "tsdx watch --localDev", 31 | "build": "tsdx build", 32 | "lint": "tsdx lint", 33 | "prepare": "tsdx build" 34 | }, 35 | "peerDependencies": { 36 | "react": ">=16", 37 | "react-native": ">=0.60" 38 | }, 39 | "husky": { 40 | "hooks": { 41 | "pre-commit": "tsdx lint" 42 | } 43 | }, 44 | "devDependencies": { 45 | "@types/react": "^16.9.34", 46 | "@types/react-native": "^0.62.5", 47 | "husky": "^4.2.5", 48 | "react": "^16.13.1", 49 | "react-native-slider": "^0.11.0", 50 | "rollup-plugin-copy-assets": "^2.0.3", 51 | "tsdx": "^0.13.2", 52 | "tslib": "^1.11.1", 53 | "typescript": "^3.8.3" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Controls.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { TouchableOpacity, View, ActivityIndicator, Image } from "react-native"; 3 | import styles from "./MediaControls.style"; 4 | import { getPlayerStateIcon } from "./utils"; 5 | import { Props } from "./MediaControls"; 6 | import { PLAYER_STATES } from "./constants/playerStates"; 7 | 8 | type ControlsProps = Pick< 9 | Props, 10 | "isLoading" | "mainColor" | "playerState" | "onReplay" 11 | > & { 12 | onPause: () => void; 13 | }; 14 | 15 | const Controls = (props: ControlsProps) => { 16 | const { isLoading, mainColor, playerState, onReplay, onPause } = props; 17 | const icon = getPlayerStateIcon(playerState); 18 | const pressAction = playerState === PLAYER_STATES.ENDED ? onReplay : onPause; 19 | 20 | const content = isLoading ? ( 21 | 22 | ) : ( 23 | 29 | 30 | 31 | ); 32 | 33 | return {content}; 34 | }; 35 | 36 | export { Controls }; 37 | -------------------------------------------------------------------------------- /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: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 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.37.0 29 | -------------------------------------------------------------------------------- /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.example", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.example", 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/ios/example-tvOS/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSExceptionDomains 28 | 29 | localhost 30 | 31 | NSExceptionAllowsInsecureHTTPLoads 32 | 33 | 34 | 35 | 36 | NSLocationWhenInUseUsageDescription 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | example 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 | NSLocationWhenInUseUsageDescription 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UIViewControllerBasedStatusBarAppearance 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /example/ios/exampleTests/exampleTests.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 exampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation exampleTests 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/.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 | ; These should not be required directly 12 | ; require from fbjs/lib instead: require('fbjs/lib/warning') 13 | node_modules/warning/.* 14 | 15 | ; Flow doesn't support platforms 16 | .*/Libraries/Utilities/LoadingView.js 17 | 18 | [untyped] 19 | .*/node_modules/@react-native-community/cli/.*/.* 20 | 21 | [include] 22 | 23 | [libs] 24 | node_modules/react-native/interface.js 25 | node_modules/react-native/flow/ 26 | 27 | [options] 28 | emoji=true 29 | 30 | esproposal.optional_chaining=enable 31 | esproposal.nullish_coalescing=enable 32 | 33 | module.file_ext=.js 34 | module.file_ext=.json 35 | module.file_ext=.ios.js 36 | 37 | munge_underscores=true 38 | 39 | module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1' 40 | 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' 41 | 42 | suppress_type=$FlowIssue 43 | suppress_type=$FlowFixMe 44 | suppress_type=$FlowFixMeProps 45 | suppress_type=$FlowFixMeState 46 | 47 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\) 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+ 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 50 | 51 | [lints] 52 | sketchy-null-number=warn 53 | sketchy-null-mixed=warn 54 | sketchy-number=warn 55 | untyped-type-import=warn 56 | nonstrict-import=warn 57 | deprecated-type=warn 58 | unsafe-getters-setters=warn 59 | unnecessary-invariant=warn 60 | signature-verification-failure=warn 61 | deprecated-utility=error 62 | 63 | [strict] 64 | deprecated-type 65 | nonstrict-import 66 | sketchy-null 67 | unclear-type 68 | unsafe-getters-setters 69 | untyped-import 70 | untyped-type-import 71 | 72 | [version] 73 | ^0.122.0 74 | -------------------------------------------------------------------------------- /src/MediaControls.style.ts: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from "react-native"; 2 | 3 | const containerBackgroundColor = "rgba(45, 59, 62, 0.4)"; 4 | const playButtonBorderColor = "rgba(255,255,255,0.5)"; 5 | const white = "#fff"; 6 | 7 | export default StyleSheet.create({ 8 | container: { 9 | alignItems: "center", 10 | backgroundColor: containerBackgroundColor, 11 | bottom: 0, 12 | flex: 1, 13 | flexDirection: "column", 14 | justifyContent: "space-between", 15 | left: 0, 16 | paddingHorizontal: 20, 17 | paddingVertical: 13, 18 | position: "absolute", 19 | right: 0, 20 | top: 0, 21 | }, 22 | controlsRow: { 23 | alignItems: "center", 24 | alignSelf: "stretch", 25 | flex: 1, 26 | justifyContent: "center", 27 | }, 28 | fullScreenContainer: { 29 | alignItems: "center", 30 | alignSelf: "stretch", 31 | justifyContent: "center", 32 | paddingLeft: 20, 33 | }, 34 | playButton: { 35 | alignItems: "center", 36 | borderColor: playButtonBorderColor, 37 | borderRadius: 3, 38 | borderWidth: 1.5, 39 | height: 50, 40 | justifyContent: "center", 41 | width: 50, 42 | }, 43 | playIcon: { 44 | height: 22, 45 | resizeMode: "contain", 46 | width: 22, 47 | }, 48 | progressColumnContainer: { 49 | flex: 1, 50 | }, 51 | progressContainer: { 52 | flexDirection: "row", 53 | justifyContent: "flex-end", 54 | marginBottom: -25, 55 | }, 56 | progressSlider: { 57 | alignSelf: "stretch", 58 | }, 59 | replayIcon: { 60 | height: 20, 61 | resizeMode: "stretch", 62 | width: 25, 63 | }, 64 | thumb: { 65 | backgroundColor: white, 66 | borderRadius: 50, 67 | borderWidth: 3, 68 | height: 20, 69 | width: 20, 70 | }, 71 | timeRow: { 72 | alignSelf: "stretch", 73 | }, 74 | timerLabel: { 75 | color: white, 76 | fontSize: 12, 77 | }, 78 | timerLabelsContainer: { 79 | alignSelf: "stretch", 80 | flexDirection: "row", 81 | justifyContent: "space-between", 82 | marginBottom: -7, 83 | }, 84 | toolbar: { 85 | flexDirection: "row", 86 | flex: 1, 87 | justifyContent: "flex-end", 88 | }, 89 | toolbarRow: { 90 | alignItems: "flex-start", 91 | flexDirection: "row", 92 | justifyContent: "flex-start", 93 | }, 94 | track: { 95 | borderRadius: 1, 96 | height: 5, 97 | }, 98 | }); 99 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #if DEBUG 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | static void InitializeFlipper(UIApplication *application) { 16 | FlipperClient *client = [FlipperClient sharedClient]; 17 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 18 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 19 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 20 | [client addPlugin:[FlipperKitReactPlugin new]]; 21 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 22 | [client start]; 23 | } 24 | #endif 25 | 26 | @implementation AppDelegate 27 | 28 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 29 | { 30 | #if DEBUG 31 | InitializeFlipper(application); 32 | #endif 33 | 34 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 35 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 36 | moduleName:@"example" 37 | initialProperties:nil]; 38 | 39 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 40 | 41 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 42 | UIViewController *rootViewController = [UIViewController new]; 43 | rootViewController.view = rootView; 44 | self.window.rootViewController = rootViewController; 45 | [self.window makeKeyAndVisible]; 46 | return YES; 47 | } 48 | 49 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 50 | { 51 | #if DEBUG 52 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 53 | #else 54 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 55 | #endif 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /example/react-native-media-controls/MediaControls.style.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: { 2 | container: { 3 | alignItems: "center"; 4 | backgroundColor: string; 5 | bottom: number; 6 | flex: number; 7 | flexDirection: "column"; 8 | justifyContent: "space-between"; 9 | left: number; 10 | paddingHorizontal: number; 11 | paddingVertical: number; 12 | position: "absolute"; 13 | right: number; 14 | top: number; 15 | }; 16 | controlsRow: { 17 | alignItems: "center"; 18 | alignSelf: "stretch"; 19 | flex: number; 20 | justifyContent: "center"; 21 | }; 22 | fullScreenContainer: { 23 | alignItems: "center"; 24 | alignSelf: "stretch"; 25 | justifyContent: "center"; 26 | paddingLeft: number; 27 | }; 28 | playButton: { 29 | alignItems: "center"; 30 | borderColor: string; 31 | borderRadius: number; 32 | borderWidth: number; 33 | height: number; 34 | justifyContent: "center"; 35 | width: number; 36 | }; 37 | playIcon: { 38 | height: number; 39 | resizeMode: "contain"; 40 | width: number; 41 | }; 42 | progressColumnContainer: { 43 | flex: number; 44 | }; 45 | progressContainer: { 46 | flexDirection: "row"; 47 | justifyContent: "flex-end"; 48 | marginBottom: number; 49 | }; 50 | progressSlider: { 51 | alignSelf: "stretch"; 52 | }; 53 | replayIcon: { 54 | height: number; 55 | resizeMode: "stretch"; 56 | width: number; 57 | }; 58 | thumb: { 59 | backgroundColor: string; 60 | borderRadius: number; 61 | borderWidth: number; 62 | height: number; 63 | width: number; 64 | }; 65 | timeRow: { 66 | alignSelf: "stretch"; 67 | }; 68 | timerLabel: { 69 | color: string; 70 | fontSize: number; 71 | }; 72 | timerLabelsContainer: { 73 | alignSelf: "stretch"; 74 | flexDirection: "row"; 75 | justifyContent: "space-between"; 76 | marginBottom: number; 77 | }; 78 | toolbar: { 79 | flexDirection: "row"; 80 | flex: number; 81 | justifyContent: "flex-end"; 82 | }; 83 | toolbarRow: { 84 | alignItems: "flex-start"; 85 | flexDirection: "row"; 86 | justifyContent: "flex-start"; 87 | }; 88 | track: { 89 | borderRadius: number; 90 | height: number; 91 | }; 92 | }; 93 | export default _default; 94 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 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.example.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 | -------------------------------------------------------------------------------- /src/Slider.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { TouchableOpacity, View, Text, Image, ViewStyle } from "react-native"; 3 | import RNSlider from "react-native-slider"; 4 | import styles from "./MediaControls.style"; 5 | import { humanizeVideoDuration } from "./utils"; 6 | import { Props as MediaControlsProps } from "./MediaControls"; 7 | import { PLAYER_STATES } from "./constants/playerStates"; 8 | 9 | export type CustomSliderStyle = { 10 | containerStyle: ViewStyle; 11 | trackStyle: ViewStyle; 12 | thumbStyle: ViewStyle; 13 | }; 14 | 15 | type Props = Pick< 16 | MediaControlsProps, 17 | | "progress" 18 | | "duration" 19 | | "mainColor" 20 | | "onFullScreen" 21 | | "playerState" 22 | | "onSeek" 23 | | "onSeeking" 24 | > & { 25 | onPause: () => void; 26 | customSliderStyle?: CustomSliderStyle; 27 | }; 28 | 29 | const fullScreenImage = require("./assets/ic_fullscreen.png"); 30 | 31 | const Slider = (props: Props) => { 32 | const { 33 | customSliderStyle, 34 | duration, 35 | mainColor, 36 | onFullScreen, 37 | onPause, 38 | progress, 39 | } = props; 40 | 41 | const containerStyle = customSliderStyle?.containerStyle || {}; 42 | const customTrackStyle = customSliderStyle?.trackStyle || {}; 43 | const customThumbStyle = customSliderStyle?.thumbStyle || {}; 44 | 45 | const dragging = (value: number) => { 46 | const { onSeeking, playerState } = props; 47 | onSeeking(value); 48 | 49 | if (playerState === PLAYER_STATES.PAUSED) { 50 | return; 51 | } 52 | 53 | onPause(); 54 | }; 55 | 56 | const seekVideo = (value: number) => { 57 | props.onSeek(value); 58 | onPause(); 59 | }; 60 | 61 | return ( 62 | 65 | 66 | 67 | 68 | {humanizeVideoDuration(progress)} 69 | 70 | 71 | {humanizeVideoDuration(duration)} 72 | 73 | 74 | 88 | 89 | {Boolean(onFullScreen) && ( 90 | 94 | 95 | 96 | )} 97 | 98 | ); 99 | }; 100 | 101 | export { Slider }; 102 | -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | /** @type {import("./react-native-media-controls/index")} */ 3 | 4 | import React, { useState, useRef } from "react"; 5 | import { StyleSheet, Text, View } from "react-native"; 6 | import Video from "react-native-video"; 7 | import MediaControls, { 8 | PLAYER_STATES, 9 | } from "./react-native-media-controls/react-native-media-controls.esm"; 10 | 11 | const noop = () => {}; 12 | 13 | const App = () => { 14 | const videoPlayer = useRef(null); 15 | const [currentTime, setCurrentTime] = useState(0); 16 | const [duration, setDuration] = useState(0); 17 | const [isFullScreen, setIsFullScreen] = useState(false); 18 | const [isLoading, setIsLoading] = useState(true); 19 | const [paused, setPaused] = useState(false); 20 | const [playerState, setPlayerState] = useState(PLAYER_STATES.PLAYING); 21 | 22 | const onSeek = (seek) => { 23 | videoPlayer?.current.seek(seek); 24 | }; 25 | 26 | const onPaused = (playerState) => { 27 | setPaused(!paused); 28 | setPlayerState(playerState); 29 | }; 30 | 31 | const onReplay = () => { 32 | setPlayerState(PLAYER_STATES.PLAYING); 33 | videoPlayer?.current.seek(0); 34 | }; 35 | 36 | const onProgress = (data) => { 37 | // Video Player will continue progress even if the video already ended 38 | if (!isLoading) { 39 | setCurrentTime(data.currentTime); 40 | } 41 | }; 42 | 43 | const onLoad = (data) => { 44 | setDuration(data.duration); 45 | setIsLoading(false); 46 | }; 47 | 48 | const onLoadStart = () => setIsLoading(true); 49 | 50 | const onEnd = () => { 51 | // Uncomment this line if you choose repeat=false in the video player 52 | // setPlayerState(PLAYER_STATES.ENDED); 53 | }; 54 | 55 | const onSeeking = (currentTime) => setCurrentTime(currentTime); 56 | 57 | return ( 58 | 59 | 95 | ); 96 | }; 97 | 98 | const styles = StyleSheet.create({ 99 | container: { 100 | flex: 1, 101 | }, 102 | toolbar: { 103 | marginTop: 30, 104 | backgroundColor: "white", 105 | padding: 10, 106 | borderRadius: 5, 107 | }, 108 | mediaPlayer: { 109 | position: "absolute", 110 | top: 0, 111 | left: 0, 112 | bottom: 0, 113 | right: 0, 114 | backgroundColor: "black", 115 | }, 116 | }); 117 | 118 | export default App; 119 | -------------------------------------------------------------------------------- /example/android/app/src/debug/java/com/example/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.example; 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/ios/example.xcodeproj/xcshareddata/xcschemes/example.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/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.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/example/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-media-controls 2 | A sweet UI component to manipulate your media. Strongly typed using Typescript. 3 | 4 | ![mediac](https://user-images.githubusercontent.com/10927770/80893585-89967000-8c88-11ea-83af-2a028115ee12.gif) 5 | 6 | ## Installation 7 | ```bash 8 | // install package 9 | yarn add react-native-media-controls 10 | // install react-native-slider, as this is a dependency of this library 11 | yarn add react-native-slider 12 | ``` 13 | 14 | ## Usage 15 | ```js 16 | // Require the module 17 | import MediaControls, { PLAYER_STATES } from 'react-native-media-controls'; 18 | 19 | const App = () => { 20 | const [currentTime, setCurrentTime] = useState(0); 21 | const [duration, setDuration] = useState(0); 22 | const [isFullScreen, setIsFullScreen] = useState(false); 23 | const [isLoading, setIsLoading] = useState(true); 24 | const [paused, setPaused] = useState(false); 25 | const [playerState, setPlayerState] = useState(PLAYER_STATES.PLAYING); 26 | 27 | // ... ... ... 28 | // ... ... ... 29 | 30 | return ( 31 | 32 | 55 | ); 56 | }; 57 | 58 | ``` 59 | ## Props 60 | | Prop | Type | Optional | Default | Description | 61 | |--------------|----------|----------|------------------------|----------------------------------------------------------------------| 62 | | mainColor | string | Yes | rgba(12, 83, 175, 0.9) | Change custom color to the media controls | 63 | | isLoading | boolean | Yes | false | When is loading | 64 | | isFullScreen | boolean | Yes | false | To change icon state of fullscreen | 65 | | fadeOutDelay | number | Yes | 5000 | Allows to customize the delay between fade in and fade out transition| 66 | | progress | number | No | | Current time of the media player | 67 | | duration | number | No | | Total duration of the media | 68 | | playerState | number | No | | Could be PLAYING, PAUSED or ENDED (take a look at constants section) | 69 | | onFullScreen | function | Yes | | Triggered when the fullscreen button is pressed. If not provided, the fullscreen icon is not displayed | 70 | | onPaused | function | No | | Triggered when the play/pause button is pressed. It returns the new toggled value (PLAYING or PAUSED) | 71 | | onReplay | function | Yes | | Triggered when the replay button is pressed | 72 | | onSeek | function | No | | Triggered when the user released the slider | 73 | | onSeeking | function | Yes | | Triggered when the user is interacting with the slider | 74 | | showOnStart | boolean | Yes | true | controls the visibility of the controls during the initial render | 75 | 76 | ## Constants 77 | ```js 78 | PLAYING, 79 | PAUSED, 80 | ENDED, 81 | ``` 82 | ## Example 83 | Refer to the example folder to find an implementation of this project 84 | -------------------------------------------------------------------------------- /example/react-native-media-controls/react-native-media-controls.cjs.production.min.js: -------------------------------------------------------------------------------- 1 | "use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0});var t,n=require("react"),r=e(n),o=require("react-native"),i=e(require("react-native-slider")),a=o.StyleSheet.create({container:{alignItems:"center",backgroundColor:"rgba(45, 59, 62, 0.4)",bottom:0,flex:1,flexDirection:"column",justifyContent:"space-between",left:0,paddingHorizontal:20,paddingVertical:13,position:"absolute",right:0,top:0},controlsRow:{alignItems:"center",alignSelf:"stretch",flex:1,justifyContent:"center"},fullScreenContainer:{alignItems:"center",alignSelf:"stretch",justifyContent:"center",paddingLeft:20},playButton:{alignItems:"center",borderColor:"rgba(255,255,255,0.5)",borderRadius:3,borderWidth:1.5,height:50,justifyContent:"center",width:50},playIcon:{height:22,resizeMode:"contain",width:22},progressColumnContainer:{flex:1},progressContainer:{flexDirection:"row",justifyContent:"flex-end",marginBottom:-25},progressSlider:{alignSelf:"stretch"},replayIcon:{height:20,resizeMode:"stretch",width:25},thumb:{backgroundColor:"#fff",borderRadius:50,borderWidth:3,height:20,width:20},timeRow:{alignSelf:"stretch"},timerLabel:{color:"#fff",fontSize:12},timerLabelsContainer:{alignSelf:"stretch",flexDirection:"row",justifyContent:"space-between",marginBottom:-7},toolbar:{flexDirection:"row",flex:1,justifyContent:"flex-end"},toolbarRow:{alignItems:"flex-start",flexDirection:"row",justifyContent:"flex-start"},track:{borderRadius:1,height:5}});(t=exports.PLAYER_STATES||(exports.PLAYER_STATES={}))[t.PLAYING=0]="PLAYING",t[t.PAUSED=1]="PAUSED",t[t.ENDED=2]="ENDED";var l=function(e){var t=e>=3600?[11,8]:[14,5],n=t[0],r=t[1],o=new Date(0);return o.setSeconds(e),o.toISOString().substr(n,r)},s=function(e){var t=e.isLoading,n=e.mainColor,i=e.playerState,l=e.onReplay,s=e.onPause,c=function(e){switch(e){case exports.PLAYER_STATES.PAUSED:return require("./assets/ic_play.png");case exports.PLAYER_STATES.PLAYING:return require("./assets/ic_pause.png");case exports.PLAYER_STATES.ENDED:return require("./assets/ic_replay.png");default:return null}}(i),u=i===exports.PLAYER_STATES.ENDED?l:s,d=t?r.createElement(o.ActivityIndicator,{size:"large",color:"#FFF"}):r.createElement(o.TouchableOpacity,{style:[a.playButton,{backgroundColor:n}],onPress:u,accessibilityLabel:exports.PLAYER_STATES.PAUSED?"Tap to Play":"Tap to Pause",accessibilityHint:"Plays and Pauses the Video"},r.createElement(o.Image,{source:c,style:a.playIcon}));return r.createElement(o.View,{style:[a.controlsRow]},d)},c=require("./assets/ic_fullscreen.png"),u=function(e){var t=e.customSliderStyle,n=e.duration,s=e.mainColor,u=e.onFullScreen,d=e.onPause,S=e.progress,f=(null==t?void 0:t.trackStyle)||{},m=(null==t?void 0:t.thumbStyle)||{};return r.createElement(o.View,{style:[a.controlsRow,a.progressContainer,(null==t?void 0:t.containerStyle)||{}]},r.createElement(o.View,{style:a.progressColumnContainer},r.createElement(o.View,{style:[a.timerLabelsContainer]},r.createElement(o.Text,{style:a.timerLabel},l(S)),r.createElement(o.Text,{style:a.timerLabel},l(n))),r.createElement(i,{style:[a.progressSlider],onValueChange:function(t){var n=e.playerState;(0,e.onSeeking)(t),n!==exports.PLAYER_STATES.PAUSED&&d()},onSlidingComplete:function(t){e.onSeek(t),d()},maximumValue:Math.floor(n),value:Math.floor(S),trackStyle:[a.track,f],thumbStyle:[a.thumb,m,{borderColor:s}],minimumTrackTintColor:s})),Boolean(u)&&r.createElement(o.TouchableOpacity,{style:a.fullScreenContainer,onPress:u},r.createElement(o.Image,{source:c})))},d=function(e){var t=e.children,i=e.containerStyle,l=void 0===i?{}:i,c=e.duration,d=e.fadeOutDelay,S=void 0===d?5e3:d,f=e.isLoading,m=void 0!==f&&f,y=e.mainColor,p=void 0===y?"rgba(12, 83, 175, 0.9)":y,g=e.onFullScreen,E=e.onReplay,b=e.onSeek,h=e.onSeeking,A=e.playerState,P=e.progress,v=e.showOnStart,C=e.sliderStyle,T=e.toolbarStyle,w=void 0===T?{}:T,x=void 0===v||v?{initialOpacity:1,initialIsVisible:!0}:{initialOpacity:0,initialIsVisible:!1},L=x.initialIsVisible,D=n.useState(new o.Animated.Value(x.initialOpacity))[0],R=n.useState(L),I=R[0],V=R[1];n.useEffect((function(){k(S)}),[]);var k=function(e){void 0===e&&(e=0),o.Animated.timing(D,{toValue:0,duration:300,delay:e,useNativeDriver:!1}).start((function(e){e.finished&&V(!1)}))},_=function(){var t=e.playerState,n=e.onPaused,r=exports.PLAYER_STATES.PLAYING,o=exports.PLAYER_STATES.PAUSED;switch(t){case r:D.stopAnimation((function(){return V(!0)}));break;case o:k(S)}return n(t===r?o:r)};return r.createElement(o.TouchableWithoutFeedback,{accessible:!1,onPress:function(){D.stopAnimation((function(e){return V(!!e),e?k():(void 0===t&&(t=!0),V(!0),void o.Animated.timing(D,{toValue:1,duration:300,delay:0,useNativeDriver:!1}).start((function(){t&&k(S)})));var t}))}},r.createElement(o.Animated.View,{style:[a.container,{opacity:D}]},I&&r.createElement(o.View,{style:[a.container,l]},r.createElement(o.View,{style:[a.controlsRow,a.toolbarRow,w]},t),r.createElement(s,{onPause:_,onReplay:function(){k(S),E()},isLoading:m,mainColor:p,playerState:A}),r.createElement(u,{progress:P,duration:c,mainColor:p,onFullScreen:g,playerState:A,onSeek:b,onSeeking:h,onPause:_,customSliderStyle:C}))))};d.Toolbar=function(e){return r.createElement(r.Fragment,null,e.children)},exports.default=d; 2 | //# sourceMappingURL=react-native-media-controls.cjs.production.min.js.map 3 | -------------------------------------------------------------------------------- /src/MediaControls.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { 3 | View, 4 | Animated, 5 | TouchableWithoutFeedback, 6 | GestureResponderEvent, 7 | ViewStyle, 8 | } from "react-native"; 9 | import styles from "./MediaControls.style"; 10 | import { PLAYER_STATES } from "./constants/playerStates"; 11 | import { Controls } from "./Controls"; 12 | import { Slider, CustomSliderStyle } from "./Slider"; 13 | import { Toolbar } from "./Toolbar"; 14 | 15 | export type Props = { 16 | children: React.ReactNode; 17 | containerStyle: ViewStyle; 18 | duration: number; 19 | fadeOutDelay?: number; 20 | isFullScreen: boolean; 21 | isLoading: boolean; 22 | mainColor: string; 23 | onFullScreen?: (event: GestureResponderEvent) => void; 24 | onPaused: (playerState: PLAYER_STATES) => void; 25 | onReplay: () => void; 26 | onSeek: (value: number) => void; 27 | onSeeking: (value: number) => void; 28 | playerState: PLAYER_STATES; 29 | progress: number; 30 | showOnStart?: boolean; 31 | sliderStyle?: CustomSliderStyle; 32 | toolbarStyle?: ViewStyle; 33 | }; 34 | 35 | const MediaControls = (props: Props) => { 36 | const { 37 | children, 38 | containerStyle: customContainerStyle = {}, 39 | duration, 40 | fadeOutDelay = 5000, 41 | isLoading = false, 42 | mainColor = "rgba(12, 83, 175, 0.9)", 43 | onFullScreen, 44 | onReplay: onReplayCallback, 45 | onSeek, 46 | onSeeking, 47 | playerState, 48 | progress, 49 | showOnStart = true, 50 | sliderStyle, // defaults are applied in Slider.tsx 51 | toolbarStyle: customToolbarStyle = {}, 52 | } = props; 53 | const { initialOpacity, initialIsVisible } = (() => { 54 | if (showOnStart) { 55 | return { 56 | initialOpacity: 1, 57 | initialIsVisible: true, 58 | }; 59 | } 60 | 61 | return { 62 | initialOpacity: 0, 63 | initialIsVisible: false, 64 | }; 65 | })(); 66 | 67 | const [opacity] = useState(new Animated.Value(initialOpacity)); 68 | const [isVisible, setIsVisible] = useState(initialIsVisible); 69 | 70 | useEffect(() => { 71 | fadeOutControls(fadeOutDelay); 72 | }, []); 73 | 74 | const fadeOutControls = (delay = 0) => { 75 | Animated.timing(opacity, { 76 | toValue: 0, 77 | duration: 300, 78 | delay, 79 | useNativeDriver: false, 80 | }).start(result => { 81 | /* I noticed that the callback is called twice, when it is invoked and when it completely finished 82 | This prevents some flickering */ 83 | if (result.finished) { 84 | setIsVisible(false); 85 | } 86 | }); 87 | }; 88 | 89 | const fadeInControls = (loop = true) => { 90 | setIsVisible(true); 91 | Animated.timing(opacity, { 92 | toValue: 1, 93 | duration: 300, 94 | delay: 0, 95 | useNativeDriver: false, 96 | }).start(() => { 97 | if (loop) { 98 | fadeOutControls(fadeOutDelay); 99 | } 100 | }); 101 | }; 102 | 103 | const onReplay = () => { 104 | fadeOutControls(fadeOutDelay); 105 | onReplayCallback(); 106 | }; 107 | 108 | const cancelAnimation = () => opacity.stopAnimation(() => setIsVisible(true)); 109 | 110 | const onPause = () => { 111 | const { playerState, onPaused } = props; 112 | const { PLAYING, PAUSED, ENDED } = PLAYER_STATES; 113 | switch (playerState) { 114 | case PLAYING: { 115 | cancelAnimation(); 116 | break; 117 | } 118 | case PAUSED: { 119 | fadeOutControls(fadeOutDelay); 120 | break; 121 | } 122 | case ENDED: 123 | break; 124 | } 125 | 126 | const newPlayerState = playerState === PLAYING ? PAUSED : PLAYING; 127 | return onPaused(newPlayerState); 128 | }; 129 | 130 | const toggleControls = () => { 131 | // value is the last value of the animation when stop animation was called. 132 | // As this is an opacity effect, I (Charlie) used the value (0 or 1) as a boolean 133 | opacity.stopAnimation((value: number) => { 134 | setIsVisible(!!value); 135 | return value ? fadeOutControls() : fadeInControls(); 136 | }); 137 | }; 138 | 139 | return ( 140 | 141 | 144 | {isVisible && ( 145 | 146 | 153 | {children} 154 | 155 | 162 | 173 | 174 | )} 175 | 176 | 177 | ); 178 | }; 179 | 180 | MediaControls.Toolbar = Toolbar; 181 | 182 | export default MediaControls; 183 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | def add_flipper_pods!(versions = {}) 5 | versions['Flipper'] ||= '~> 0.33.1' 6 | versions['DoubleConversion'] ||= '1.1.7' 7 | versions['Flipper-Folly'] ||= '~> 2.1' 8 | versions['Flipper-Glog'] ||= '0.3.6' 9 | versions['Flipper-PeerTalk'] ||= '~> 0.0.4' 10 | versions['Flipper-RSocket'] ||= '~> 1.0' 11 | 12 | pod 'FlipperKit', versions['Flipper'], :configuration => 'Debug' 13 | pod 'FlipperKit/FlipperKitLayoutPlugin', versions['Flipper'], :configuration => 'Debug' 14 | pod 'FlipperKit/SKIOSNetworkPlugin', versions['Flipper'], :configuration => 'Debug' 15 | pod 'FlipperKit/FlipperKitUserDefaultsPlugin', versions['Flipper'], :configuration => 'Debug' 16 | pod 'FlipperKit/FlipperKitReactPlugin', versions['Flipper'], :configuration => 'Debug' 17 | 18 | # List all transitive dependencies for FlipperKit pods 19 | # to avoid them being linked in Release builds 20 | pod 'Flipper', versions['Flipper'], :configuration => 'Debug' 21 | pod 'Flipper-DoubleConversion', versions['DoubleConversion'], :configuration => 'Debug' 22 | pod 'Flipper-Folly', versions['Flipper-Folly'], :configuration => 'Debug' 23 | pod 'Flipper-Glog', versions['Flipper-Glog'], :configuration => 'Debug' 24 | pod 'Flipper-PeerTalk', versions['Flipper-PeerTalk'], :configuration => 'Debug' 25 | pod 'Flipper-RSocket', versions['Flipper-RSocket'], :configuration => 'Debug' 26 | pod 'FlipperKit/Core', versions['Flipper'], :configuration => 'Debug' 27 | pod 'FlipperKit/CppBridge', versions['Flipper'], :configuration => 'Debug' 28 | pod 'FlipperKit/FBCxxFollyDynamicConvert', versions['Flipper'], :configuration => 'Debug' 29 | pod 'FlipperKit/FBDefines', versions['Flipper'], :configuration => 'Debug' 30 | pod 'FlipperKit/FKPortForwarding', versions['Flipper'], :configuration => 'Debug' 31 | pod 'FlipperKit/FlipperKitHighlightOverlay', versions['Flipper'], :configuration => 'Debug' 32 | pod 'FlipperKit/FlipperKitLayoutTextSearchable', versions['Flipper'], :configuration => 'Debug' 33 | pod 'FlipperKit/FlipperKitNetworkPlugin', versions['Flipper'], :configuration => 'Debug' 34 | end 35 | 36 | # Post Install processing for Flipper 37 | def flipper_post_install(installer) 38 | installer.pods_project.targets.each do |target| 39 | if target.name == 'YogaKit' 40 | target.build_configurations.each do |config| 41 | config.build_settings['SWIFT_VERSION'] = '4.1' 42 | end 43 | end 44 | end 45 | end 46 | 47 | target 'example' do 48 | # Pods for example 49 | pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector" 50 | pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec" 51 | pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired" 52 | pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety" 53 | pod 'React', :path => '../node_modules/react-native/' 54 | pod 'React-Core', :path => '../node_modules/react-native/' 55 | pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules' 56 | pod 'React-Core/DevSupport', :path => '../node_modules/react-native/' 57 | pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS' 58 | pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation' 59 | pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob' 60 | pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image' 61 | pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS' 62 | pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network' 63 | pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings' 64 | pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text' 65 | pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration' 66 | pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/' 67 | 68 | pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact' 69 | pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi' 70 | pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor' 71 | pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector' 72 | pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon" 73 | pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon" 74 | pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true 75 | 76 | pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' 77 | pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' 78 | pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' 79 | 80 | pod 'react-native-video', :path => '../node_modules/react-native-video' 81 | 82 | target 'exampleTests' do 83 | inherit! :complete 84 | # Pods for testing 85 | end 86 | 87 | use_native_modules! 88 | 89 | # Enables Flipper. 90 | # 91 | # Note that if you have use_frameworks! enabled, Flipper will not work and 92 | # you should disable these next few lines. 93 | add_flipper_pods! 94 | post_install do |installer| 95 | flipper_post_install(installer) 96 | end 97 | end 98 | 99 | target 'example-tvOS' do 100 | # Pods for example-tvOS 101 | 102 | target 'example-tvOSTests' do 103 | inherit! :search_paths 104 | # Pods for testing 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /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 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation. If none specified and 19 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is 20 | * // default. Can be overridden with ENTRY_FILE environment variable. 21 | * entryFile: "index.android.js", 22 | * 23 | * // https://reactnative.dev/docs/performance#enable-the-ram-format 24 | * bundleCommand: "ram-bundle", 25 | * 26 | * // whether to bundle JS and assets in debug mode 27 | * bundleInDebug: false, 28 | * 29 | * // whether to bundle JS and assets in release mode 30 | * bundleInRelease: true, 31 | * 32 | * // whether to bundle JS and assets in another build variant (if configured). 33 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 34 | * // The configuration property can be in the following formats 35 | * // 'bundleIn${productFlavor}${buildType}' 36 | * // 'bundleIn${buildType}' 37 | * // bundleInFreeDebug: true, 38 | * // bundleInPaidRelease: true, 39 | * // bundleInBeta: true, 40 | * 41 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 42 | * // for example: to disable dev mode in the staging build type (if configured) 43 | * devDisabledInStaging: true, 44 | * // The configuration property can be in the following formats 45 | * // 'devDisabledIn${productFlavor}${buildType}' 46 | * // 'devDisabledIn${buildType}' 47 | * 48 | * // the root of your project, i.e. where "package.json" lives 49 | * root: "../../", 50 | * 51 | * // where to put the JS bundle asset in debug mode 52 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 53 | * 54 | * // where to put the JS bundle asset in release mode 55 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 56 | * 57 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 58 | * // require('./image.png')), in debug mode 59 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 60 | * 61 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 62 | * // require('./image.png')), in release mode 63 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 64 | * 65 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 66 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 67 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 68 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 69 | * // for example, you might want to remove it from here. 70 | * inputExcludes: ["android/**", "ios/**"], 71 | * 72 | * // override which node gets called and with what additional arguments 73 | * nodeExecutableAndArgs: ["node"], 74 | * 75 | * // supply additional arguments to the packager 76 | * extraPackagerArgs: [] 77 | * ] 78 | */ 79 | 80 | project.ext.react = [ 81 | enableHermes: false, // clean and rebuild if changing 82 | ] 83 | 84 | apply from: "../../node_modules/react-native/react.gradle" 85 | 86 | /** 87 | * Set this to true to create two separate APKs instead of one: 88 | * - An APK that only works on ARM devices 89 | * - An APK that only works on x86 devices 90 | * The advantage is the size of the APK is reduced by about 4MB. 91 | * Upload all the APKs to the Play Store and people will download 92 | * the correct one based on the CPU architecture of their device. 93 | */ 94 | def enableSeparateBuildPerCPUArchitecture = false 95 | 96 | /** 97 | * Run Proguard to shrink the Java bytecode in release builds. 98 | */ 99 | def enableProguardInReleaseBuilds = false 100 | 101 | /** 102 | * The preferred build flavor of JavaScriptCore. 103 | * 104 | * For example, to use the international variant, you can use: 105 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 106 | * 107 | * The international variant includes ICU i18n library and necessary data 108 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 109 | * give correct results when using with locales other than en-US. Note that 110 | * this variant is about 6MiB larger per architecture than default. 111 | */ 112 | def jscFlavor = 'org.webkit:android-jsc:+' 113 | 114 | /** 115 | * Whether to enable the Hermes VM. 116 | * 117 | * This should be set on project.ext.react and mirrored here. If it is not set 118 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode 119 | * and the benefits of using Hermes will therefore be sharply reduced. 120 | */ 121 | def enableHermes = project.ext.react.get("enableHermes", false); 122 | 123 | android { 124 | compileSdkVersion rootProject.ext.compileSdkVersion 125 | 126 | compileOptions { 127 | sourceCompatibility JavaVersion.VERSION_1_8 128 | targetCompatibility JavaVersion.VERSION_1_8 129 | } 130 | 131 | defaultConfig { 132 | applicationId "com.example" 133 | minSdkVersion rootProject.ext.minSdkVersion 134 | targetSdkVersion rootProject.ext.targetSdkVersion 135 | versionCode 1 136 | versionName "1.0" 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 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] 173 | def abi = output.getFilter(OutputFile.ABI) 174 | if (abi != null) { // null for the universal-debug, universal-release variants 175 | output.versionCodeOverride = 176 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 177 | } 178 | 179 | } 180 | } 181 | } 182 | 183 | dependencies { 184 | implementation fileTree(dir: "libs", include: ["*.jar"]) 185 | //noinspection GradleDynamicVersion 186 | implementation "com.facebook.react:react-native:+" // From node_modules 187 | 188 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" 189 | 190 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { 191 | exclude group:'com.facebook.fbjni' 192 | } 193 | 194 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { 195 | exclude group:'com.facebook.flipper' 196 | exclude group:'com.squareup.okhttp3', module:'okhttp' 197 | } 198 | 199 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { 200 | exclude group:'com.facebook.flipper' 201 | } 202 | 203 | if (enableHermes) { 204 | def hermesPath = "../../node_modules/hermes-engine/android/"; 205 | debugImplementation files(hermesPath + "hermes-debug.aar") 206 | releaseImplementation files(hermesPath + "hermes-release.aar") 207 | } else { 208 | implementation jscFlavor 209 | } 210 | } 211 | 212 | // Run this once to be able to run the application with BUCK 213 | // puts all compile dependencies into folder libs for BUCK to use 214 | task copyDownloadableDepsToLibs(type: Copy) { 215 | from configurations.compile 216 | into 'libs' 217 | } 218 | 219 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 220 | -------------------------------------------------------------------------------- /example/react-native-media-controls/react-native-media-controls.esm.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { StyleSheet, ActivityIndicator, TouchableOpacity, Image, View, Text, Animated, TouchableWithoutFeedback } from 'react-native'; 3 | import RNSlider from 'react-native-slider'; 4 | 5 | var containerBackgroundColor = "rgba(45, 59, 62, 0.4)"; 6 | var playButtonBorderColor = "rgba(255,255,255,0.5)"; 7 | var white = "#fff"; 8 | var styles = /*#__PURE__*/StyleSheet.create({ 9 | container: { 10 | alignItems: "center", 11 | backgroundColor: containerBackgroundColor, 12 | bottom: 0, 13 | flex: 1, 14 | flexDirection: "column", 15 | justifyContent: "space-between", 16 | left: 0, 17 | paddingHorizontal: 20, 18 | paddingVertical: 13, 19 | position: "absolute", 20 | right: 0, 21 | top: 0 22 | }, 23 | controlsRow: { 24 | alignItems: "center", 25 | alignSelf: "stretch", 26 | flex: 1, 27 | justifyContent: "center" 28 | }, 29 | fullScreenContainer: { 30 | alignItems: "center", 31 | alignSelf: "stretch", 32 | justifyContent: "center", 33 | paddingLeft: 20 34 | }, 35 | playButton: { 36 | alignItems: "center", 37 | borderColor: playButtonBorderColor, 38 | borderRadius: 3, 39 | borderWidth: 1.5, 40 | height: 50, 41 | justifyContent: "center", 42 | width: 50 43 | }, 44 | playIcon: { 45 | height: 22, 46 | resizeMode: "contain", 47 | width: 22 48 | }, 49 | progressColumnContainer: { 50 | flex: 1 51 | }, 52 | progressContainer: { 53 | flexDirection: "row", 54 | justifyContent: "flex-end", 55 | marginBottom: -25 56 | }, 57 | progressSlider: { 58 | alignSelf: "stretch" 59 | }, 60 | replayIcon: { 61 | height: 20, 62 | resizeMode: "stretch", 63 | width: 25 64 | }, 65 | thumb: { 66 | backgroundColor: white, 67 | borderRadius: 50, 68 | borderWidth: 3, 69 | height: 20, 70 | width: 20 71 | }, 72 | timeRow: { 73 | alignSelf: "stretch" 74 | }, 75 | timerLabel: { 76 | color: white, 77 | fontSize: 12 78 | }, 79 | timerLabelsContainer: { 80 | alignSelf: "stretch", 81 | flexDirection: "row", 82 | justifyContent: "space-between", 83 | marginBottom: -7 84 | }, 85 | toolbar: { 86 | flexDirection: "row", 87 | flex: 1, 88 | justifyContent: "flex-end" 89 | }, 90 | toolbarRow: { 91 | alignItems: "flex-start", 92 | flexDirection: "row", 93 | justifyContent: "flex-start" 94 | }, 95 | track: { 96 | borderRadius: 1, 97 | height: 5 98 | } 99 | }); 100 | 101 | var PLAYER_STATES; 102 | 103 | (function (PLAYER_STATES) { 104 | PLAYER_STATES[PLAYER_STATES["PLAYING"] = 0] = "PLAYING"; 105 | PLAYER_STATES[PLAYER_STATES["PAUSED"] = 1] = "PAUSED"; 106 | PLAYER_STATES[PLAYER_STATES["ENDED"] = 2] = "ENDED"; 107 | })(PLAYER_STATES || (PLAYER_STATES = {})); 108 | 109 | var humanizeVideoDuration = function humanizeVideoDuration(seconds) { 110 | var _ref = seconds >= 3600 ? [11, 8] : [14, 5], 111 | begin = _ref[0], 112 | end = _ref[1]; 113 | 114 | var date = new Date(0); 115 | date.setSeconds(seconds); 116 | return date.toISOString().substr(begin, end); 117 | }; 118 | var getPlayerStateIcon = function getPlayerStateIcon(playerState) { 119 | switch (playerState) { 120 | case PLAYER_STATES.PAUSED: 121 | return require("./assets/ic_play.png"); 122 | 123 | case PLAYER_STATES.PLAYING: 124 | return require("./assets/ic_pause.png"); 125 | 126 | case PLAYER_STATES.ENDED: 127 | return require("./assets/ic_replay.png"); 128 | 129 | default: 130 | return null; 131 | } 132 | }; 133 | 134 | var Controls = function Controls(props) { 135 | var isLoading = props.isLoading, 136 | mainColor = props.mainColor, 137 | playerState = props.playerState, 138 | onReplay = props.onReplay, 139 | onPause = props.onPause; 140 | var icon = getPlayerStateIcon(playerState); 141 | var pressAction = playerState === PLAYER_STATES.ENDED ? onReplay : onPause; 142 | var content = isLoading ? React.createElement(ActivityIndicator, { 143 | size: "large", 144 | color: "#FFF" 145 | }) : React.createElement(TouchableOpacity, { 146 | style: [styles.playButton, { 147 | backgroundColor: mainColor 148 | }], 149 | onPress: pressAction, 150 | accessibilityLabel: PLAYER_STATES.PAUSED ? "Tap to Play" : "Tap to Pause", 151 | accessibilityHint: "Plays and Pauses the Video" 152 | }, React.createElement(Image, { 153 | source: icon, 154 | style: styles.playIcon 155 | })); 156 | return React.createElement(View, { 157 | style: [styles.controlsRow] 158 | }, content); 159 | }; 160 | 161 | var fullScreenImage = /*#__PURE__*/require("./assets/ic_fullscreen.png"); 162 | 163 | var Slider = function Slider(props) { 164 | var customSliderStyle = props.customSliderStyle, 165 | duration = props.duration, 166 | mainColor = props.mainColor, 167 | onFullScreen = props.onFullScreen, 168 | onPause = props.onPause, 169 | progress = props.progress; 170 | var containerStyle = (customSliderStyle === null || customSliderStyle === void 0 ? void 0 : customSliderStyle.containerStyle) || {}; 171 | var customTrackStyle = (customSliderStyle === null || customSliderStyle === void 0 ? void 0 : customSliderStyle.trackStyle) || {}; 172 | var customThumbStyle = (customSliderStyle === null || customSliderStyle === void 0 ? void 0 : customSliderStyle.thumbStyle) || {}; 173 | 174 | var dragging = function dragging(value) { 175 | var onSeeking = props.onSeeking, 176 | playerState = props.playerState; 177 | onSeeking(value); 178 | 179 | if (playerState === PLAYER_STATES.PAUSED) { 180 | return; 181 | } 182 | 183 | onPause(); 184 | }; 185 | 186 | var seekVideo = function seekVideo(value) { 187 | props.onSeek(value); 188 | onPause(); 189 | }; 190 | 191 | return React.createElement(View, { 192 | style: [styles.controlsRow, styles.progressContainer, containerStyle] 193 | }, React.createElement(View, { 194 | style: styles.progressColumnContainer 195 | }, React.createElement(View, { 196 | style: [styles.timerLabelsContainer] 197 | }, React.createElement(Text, { 198 | style: styles.timerLabel 199 | }, humanizeVideoDuration(progress)), React.createElement(Text, { 200 | style: styles.timerLabel 201 | }, humanizeVideoDuration(duration))), React.createElement(RNSlider, { 202 | style: [styles.progressSlider], 203 | onValueChange: dragging, 204 | onSlidingComplete: seekVideo, 205 | maximumValue: Math.floor(duration), 206 | value: Math.floor(progress), 207 | trackStyle: [styles.track, customTrackStyle], 208 | thumbStyle: [styles.thumb, customThumbStyle, { 209 | borderColor: mainColor 210 | }], 211 | minimumTrackTintColor: mainColor 212 | })), Boolean(onFullScreen) && React.createElement(TouchableOpacity, { 213 | style: styles.fullScreenContainer, 214 | onPress: onFullScreen 215 | }, React.createElement(Image, { 216 | source: fullScreenImage 217 | }))); 218 | }; 219 | 220 | var Toolbar = function Toolbar(_ref) { 221 | var children = _ref.children; 222 | return React.createElement(React.Fragment, null, children); 223 | }; 224 | 225 | var MediaControls = function MediaControls(props) { 226 | var children = props.children, 227 | _props$containerStyle = props.containerStyle, 228 | customContainerStyle = _props$containerStyle === void 0 ? {} : _props$containerStyle, 229 | duration = props.duration, 230 | _props$fadeOutDelay = props.fadeOutDelay, 231 | fadeOutDelay = _props$fadeOutDelay === void 0 ? 5000 : _props$fadeOutDelay, 232 | _props$isLoading = props.isLoading, 233 | isLoading = _props$isLoading === void 0 ? false : _props$isLoading, 234 | _props$mainColor = props.mainColor, 235 | mainColor = _props$mainColor === void 0 ? "rgba(12, 83, 175, 0.9)" : _props$mainColor, 236 | onFullScreen = props.onFullScreen, 237 | onReplayCallback = props.onReplay, 238 | onSeek = props.onSeek, 239 | onSeeking = props.onSeeking, 240 | playerState = props.playerState, 241 | progress = props.progress, 242 | _props$showOnStart = props.showOnStart, 243 | showOnStart = _props$showOnStart === void 0 ? true : _props$showOnStart, 244 | sliderStyle = props.sliderStyle, 245 | _props$toolbarStyle = props.toolbarStyle, 246 | customToolbarStyle = _props$toolbarStyle === void 0 ? {} : _props$toolbarStyle; 247 | 248 | var _ref = function () { 249 | if (showOnStart) { 250 | return { 251 | initialOpacity: 1, 252 | initialIsVisible: true 253 | }; 254 | } 255 | 256 | return { 257 | initialOpacity: 0, 258 | initialIsVisible: false 259 | }; 260 | }(), 261 | initialOpacity = _ref.initialOpacity, 262 | initialIsVisible = _ref.initialIsVisible; 263 | 264 | var _useState = useState(new Animated.Value(initialOpacity)), 265 | opacity = _useState[0]; 266 | 267 | var _useState2 = useState(initialIsVisible), 268 | isVisible = _useState2[0], 269 | setIsVisible = _useState2[1]; 270 | 271 | useEffect(function () { 272 | fadeOutControls(fadeOutDelay); 273 | }, []); 274 | 275 | var fadeOutControls = function fadeOutControls(delay) { 276 | if (delay === void 0) { 277 | delay = 0; 278 | } 279 | 280 | Animated.timing(opacity, { 281 | toValue: 0, 282 | duration: 300, 283 | delay: delay, 284 | useNativeDriver: false 285 | }).start(function (result) { 286 | /* I noticed that the callback is called twice, when it is invoked and when it completely finished 287 | This prevents some flickering */ 288 | if (result.finished) { 289 | setIsVisible(false); 290 | } 291 | }); 292 | }; 293 | 294 | var fadeInControls = function fadeInControls(loop) { 295 | if (loop === void 0) { 296 | loop = true; 297 | } 298 | 299 | setIsVisible(true); 300 | Animated.timing(opacity, { 301 | toValue: 1, 302 | duration: 300, 303 | delay: 0, 304 | useNativeDriver: false 305 | }).start(function () { 306 | if (loop) { 307 | fadeOutControls(fadeOutDelay); 308 | } 309 | }); 310 | }; 311 | 312 | var onReplay = function onReplay() { 313 | fadeOutControls(fadeOutDelay); 314 | onReplayCallback(); 315 | }; 316 | 317 | var cancelAnimation = function cancelAnimation() { 318 | return opacity.stopAnimation(function () { 319 | return setIsVisible(true); 320 | }); 321 | }; 322 | 323 | var onPause = function onPause() { 324 | var playerState = props.playerState, 325 | onPaused = props.onPaused; 326 | var PLAYING = PLAYER_STATES.PLAYING, 327 | PAUSED = PLAYER_STATES.PAUSED; 328 | 329 | switch (playerState) { 330 | case PLAYING: 331 | { 332 | cancelAnimation(); 333 | break; 334 | } 335 | 336 | case PAUSED: 337 | { 338 | fadeOutControls(fadeOutDelay); 339 | break; 340 | } 341 | } 342 | 343 | var newPlayerState = playerState === PLAYING ? PAUSED : PLAYING; 344 | return onPaused(newPlayerState); 345 | }; 346 | 347 | var toggleControls = function toggleControls() { 348 | // value is the last value of the animation when stop animation was called. 349 | // As this is an opacity effect, I (Charlie) used the value (0 or 1) as a boolean 350 | opacity.stopAnimation(function (value) { 351 | setIsVisible(!!value); 352 | return value ? fadeOutControls() : fadeInControls(); 353 | }); 354 | }; 355 | 356 | return React.createElement(TouchableWithoutFeedback, { 357 | accessible: false, 358 | onPress: toggleControls 359 | }, React.createElement(Animated.View, { 360 | style: [styles.container, { 361 | opacity: opacity 362 | }] 363 | }, isVisible && React.createElement(View, { 364 | style: [styles.container, customContainerStyle] 365 | }, React.createElement(View, { 366 | style: [styles.controlsRow, styles.toolbarRow, customToolbarStyle] 367 | }, children), React.createElement(Controls, { 368 | onPause: onPause, 369 | onReplay: onReplay, 370 | isLoading: isLoading, 371 | mainColor: mainColor, 372 | playerState: playerState 373 | }), React.createElement(Slider, { 374 | progress: progress, 375 | duration: duration, 376 | mainColor: mainColor, 377 | onFullScreen: onFullScreen, 378 | playerState: playerState, 379 | onSeek: onSeek, 380 | onSeeking: onSeeking, 381 | onPause: onPause, 382 | customSliderStyle: sliderStyle 383 | })))); 384 | }; 385 | 386 | MediaControls.Toolbar = Toolbar; 387 | 388 | export default MediaControls; 389 | export { PLAYER_STATES }; 390 | //# sourceMappingURL=react-native-media-controls.esm.js.map 391 | -------------------------------------------------------------------------------- /example/react-native-media-controls/react-native-media-controls.cjs.development.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { value: true }); 4 | 5 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } 6 | 7 | var React = require('react'); 8 | var React__default = _interopDefault(React); 9 | var reactNative = require('react-native'); 10 | var RNSlider = _interopDefault(require('react-native-slider')); 11 | 12 | var containerBackgroundColor = "rgba(45, 59, 62, 0.4)"; 13 | var playButtonBorderColor = "rgba(255,255,255,0.5)"; 14 | var white = "#fff"; 15 | var styles = /*#__PURE__*/reactNative.StyleSheet.create({ 16 | container: { 17 | alignItems: "center", 18 | backgroundColor: containerBackgroundColor, 19 | bottom: 0, 20 | flex: 1, 21 | flexDirection: "column", 22 | justifyContent: "space-between", 23 | left: 0, 24 | paddingHorizontal: 20, 25 | paddingVertical: 13, 26 | position: "absolute", 27 | right: 0, 28 | top: 0 29 | }, 30 | controlsRow: { 31 | alignItems: "center", 32 | alignSelf: "stretch", 33 | flex: 1, 34 | justifyContent: "center" 35 | }, 36 | fullScreenContainer: { 37 | alignItems: "center", 38 | alignSelf: "stretch", 39 | justifyContent: "center", 40 | paddingLeft: 20 41 | }, 42 | playButton: { 43 | alignItems: "center", 44 | borderColor: playButtonBorderColor, 45 | borderRadius: 3, 46 | borderWidth: 1.5, 47 | height: 50, 48 | justifyContent: "center", 49 | width: 50 50 | }, 51 | playIcon: { 52 | height: 22, 53 | resizeMode: "contain", 54 | width: 22 55 | }, 56 | progressColumnContainer: { 57 | flex: 1 58 | }, 59 | progressContainer: { 60 | flexDirection: "row", 61 | justifyContent: "flex-end", 62 | marginBottom: -25 63 | }, 64 | progressSlider: { 65 | alignSelf: "stretch" 66 | }, 67 | replayIcon: { 68 | height: 20, 69 | resizeMode: "stretch", 70 | width: 25 71 | }, 72 | thumb: { 73 | backgroundColor: white, 74 | borderRadius: 50, 75 | borderWidth: 3, 76 | height: 20, 77 | width: 20 78 | }, 79 | timeRow: { 80 | alignSelf: "stretch" 81 | }, 82 | timerLabel: { 83 | color: white, 84 | fontSize: 12 85 | }, 86 | timerLabelsContainer: { 87 | alignSelf: "stretch", 88 | flexDirection: "row", 89 | justifyContent: "space-between", 90 | marginBottom: -7 91 | }, 92 | toolbar: { 93 | flexDirection: "row", 94 | flex: 1, 95 | justifyContent: "flex-end" 96 | }, 97 | toolbarRow: { 98 | alignItems: "flex-start", 99 | flexDirection: "row", 100 | justifyContent: "flex-start" 101 | }, 102 | track: { 103 | borderRadius: 1, 104 | height: 5 105 | } 106 | }); 107 | 108 | (function (PLAYER_STATES) { 109 | PLAYER_STATES[PLAYER_STATES["PLAYING"] = 0] = "PLAYING"; 110 | PLAYER_STATES[PLAYER_STATES["PAUSED"] = 1] = "PAUSED"; 111 | PLAYER_STATES[PLAYER_STATES["ENDED"] = 2] = "ENDED"; 112 | })(exports.PLAYER_STATES || (exports.PLAYER_STATES = {})); 113 | 114 | var humanizeVideoDuration = function humanizeVideoDuration(seconds) { 115 | var _ref = seconds >= 3600 ? [11, 8] : [14, 5], 116 | begin = _ref[0], 117 | end = _ref[1]; 118 | 119 | var date = new Date(0); 120 | date.setSeconds(seconds); 121 | return date.toISOString().substr(begin, end); 122 | }; 123 | var getPlayerStateIcon = function getPlayerStateIcon(playerState) { 124 | switch (playerState) { 125 | case exports.PLAYER_STATES.PAUSED: 126 | return require("./assets/ic_play.png"); 127 | 128 | case exports.PLAYER_STATES.PLAYING: 129 | return require("./assets/ic_pause.png"); 130 | 131 | case exports.PLAYER_STATES.ENDED: 132 | return require("./assets/ic_replay.png"); 133 | 134 | default: 135 | return null; 136 | } 137 | }; 138 | 139 | var Controls = function Controls(props) { 140 | var isLoading = props.isLoading, 141 | mainColor = props.mainColor, 142 | playerState = props.playerState, 143 | onReplay = props.onReplay, 144 | onPause = props.onPause; 145 | var icon = getPlayerStateIcon(playerState); 146 | var pressAction = playerState === exports.PLAYER_STATES.ENDED ? onReplay : onPause; 147 | var content = isLoading ? React__default.createElement(reactNative.ActivityIndicator, { 148 | size: "large", 149 | color: "#FFF" 150 | }) : React__default.createElement(reactNative.TouchableOpacity, { 151 | style: [styles.playButton, { 152 | backgroundColor: mainColor 153 | }], 154 | onPress: pressAction, 155 | accessibilityLabel: exports.PLAYER_STATES.PAUSED ? "Tap to Play" : "Tap to Pause", 156 | accessibilityHint: "Plays and Pauses the Video" 157 | }, React__default.createElement(reactNative.Image, { 158 | source: icon, 159 | style: styles.playIcon 160 | })); 161 | return React__default.createElement(reactNative.View, { 162 | style: [styles.controlsRow] 163 | }, content); 164 | }; 165 | 166 | var fullScreenImage = /*#__PURE__*/require("./assets/ic_fullscreen.png"); 167 | 168 | var Slider = function Slider(props) { 169 | var customSliderStyle = props.customSliderStyle, 170 | duration = props.duration, 171 | mainColor = props.mainColor, 172 | onFullScreen = props.onFullScreen, 173 | onPause = props.onPause, 174 | progress = props.progress; 175 | var containerStyle = (customSliderStyle === null || customSliderStyle === void 0 ? void 0 : customSliderStyle.containerStyle) || {}; 176 | var customTrackStyle = (customSliderStyle === null || customSliderStyle === void 0 ? void 0 : customSliderStyle.trackStyle) || {}; 177 | var customThumbStyle = (customSliderStyle === null || customSliderStyle === void 0 ? void 0 : customSliderStyle.thumbStyle) || {}; 178 | 179 | var dragging = function dragging(value) { 180 | var onSeeking = props.onSeeking, 181 | playerState = props.playerState; 182 | onSeeking(value); 183 | 184 | if (playerState === exports.PLAYER_STATES.PAUSED) { 185 | return; 186 | } 187 | 188 | onPause(); 189 | }; 190 | 191 | var seekVideo = function seekVideo(value) { 192 | props.onSeek(value); 193 | onPause(); 194 | }; 195 | 196 | return React__default.createElement(reactNative.View, { 197 | style: [styles.controlsRow, styles.progressContainer, containerStyle] 198 | }, React__default.createElement(reactNative.View, { 199 | style: styles.progressColumnContainer 200 | }, React__default.createElement(reactNative.View, { 201 | style: [styles.timerLabelsContainer] 202 | }, React__default.createElement(reactNative.Text, { 203 | style: styles.timerLabel 204 | }, humanizeVideoDuration(progress)), React__default.createElement(reactNative.Text, { 205 | style: styles.timerLabel 206 | }, humanizeVideoDuration(duration))), React__default.createElement(RNSlider, { 207 | style: [styles.progressSlider], 208 | onValueChange: dragging, 209 | onSlidingComplete: seekVideo, 210 | maximumValue: Math.floor(duration), 211 | value: Math.floor(progress), 212 | trackStyle: [styles.track, customTrackStyle], 213 | thumbStyle: [styles.thumb, customThumbStyle, { 214 | borderColor: mainColor 215 | }], 216 | minimumTrackTintColor: mainColor 217 | })), Boolean(onFullScreen) && React__default.createElement(reactNative.TouchableOpacity, { 218 | style: styles.fullScreenContainer, 219 | onPress: onFullScreen 220 | }, React__default.createElement(reactNative.Image, { 221 | source: fullScreenImage 222 | }))); 223 | }; 224 | 225 | var Toolbar = function Toolbar(_ref) { 226 | var children = _ref.children; 227 | return React__default.createElement(React__default.Fragment, null, children); 228 | }; 229 | 230 | var MediaControls = function MediaControls(props) { 231 | var children = props.children, 232 | _props$containerStyle = props.containerStyle, 233 | customContainerStyle = _props$containerStyle === void 0 ? {} : _props$containerStyle, 234 | duration = props.duration, 235 | _props$fadeOutDelay = props.fadeOutDelay, 236 | fadeOutDelay = _props$fadeOutDelay === void 0 ? 5000 : _props$fadeOutDelay, 237 | _props$isLoading = props.isLoading, 238 | isLoading = _props$isLoading === void 0 ? false : _props$isLoading, 239 | _props$mainColor = props.mainColor, 240 | mainColor = _props$mainColor === void 0 ? "rgba(12, 83, 175, 0.9)" : _props$mainColor, 241 | onFullScreen = props.onFullScreen, 242 | onReplayCallback = props.onReplay, 243 | onSeek = props.onSeek, 244 | onSeeking = props.onSeeking, 245 | playerState = props.playerState, 246 | progress = props.progress, 247 | _props$showOnStart = props.showOnStart, 248 | showOnStart = _props$showOnStart === void 0 ? true : _props$showOnStart, 249 | sliderStyle = props.sliderStyle, 250 | _props$toolbarStyle = props.toolbarStyle, 251 | customToolbarStyle = _props$toolbarStyle === void 0 ? {} : _props$toolbarStyle; 252 | 253 | var _ref = function () { 254 | if (showOnStart) { 255 | return { 256 | initialOpacity: 1, 257 | initialIsVisible: true 258 | }; 259 | } 260 | 261 | return { 262 | initialOpacity: 0, 263 | initialIsVisible: false 264 | }; 265 | }(), 266 | initialOpacity = _ref.initialOpacity, 267 | initialIsVisible = _ref.initialIsVisible; 268 | 269 | var _useState = React.useState(new reactNative.Animated.Value(initialOpacity)), 270 | opacity = _useState[0]; 271 | 272 | var _useState2 = React.useState(initialIsVisible), 273 | isVisible = _useState2[0], 274 | setIsVisible = _useState2[1]; 275 | 276 | React.useEffect(function () { 277 | fadeOutControls(fadeOutDelay); 278 | }, []); 279 | 280 | var fadeOutControls = function fadeOutControls(delay) { 281 | if (delay === void 0) { 282 | delay = 0; 283 | } 284 | 285 | reactNative.Animated.timing(opacity, { 286 | toValue: 0, 287 | duration: 300, 288 | delay: delay, 289 | useNativeDriver: false 290 | }).start(function (result) { 291 | /* I noticed that the callback is called twice, when it is invoked and when it completely finished 292 | This prevents some flickering */ 293 | if (result.finished) { 294 | setIsVisible(false); 295 | } 296 | }); 297 | }; 298 | 299 | var fadeInControls = function fadeInControls(loop) { 300 | if (loop === void 0) { 301 | loop = true; 302 | } 303 | 304 | setIsVisible(true); 305 | reactNative.Animated.timing(opacity, { 306 | toValue: 1, 307 | duration: 300, 308 | delay: 0, 309 | useNativeDriver: false 310 | }).start(function () { 311 | if (loop) { 312 | fadeOutControls(fadeOutDelay); 313 | } 314 | }); 315 | }; 316 | 317 | var onReplay = function onReplay() { 318 | fadeOutControls(fadeOutDelay); 319 | onReplayCallback(); 320 | }; 321 | 322 | var cancelAnimation = function cancelAnimation() { 323 | return opacity.stopAnimation(function () { 324 | return setIsVisible(true); 325 | }); 326 | }; 327 | 328 | var onPause = function onPause() { 329 | var playerState = props.playerState, 330 | onPaused = props.onPaused; 331 | var PLAYING = exports.PLAYER_STATES.PLAYING, 332 | PAUSED = exports.PLAYER_STATES.PAUSED; 333 | 334 | switch (playerState) { 335 | case PLAYING: 336 | { 337 | cancelAnimation(); 338 | break; 339 | } 340 | 341 | case PAUSED: 342 | { 343 | fadeOutControls(fadeOutDelay); 344 | break; 345 | } 346 | } 347 | 348 | var newPlayerState = playerState === PLAYING ? PAUSED : PLAYING; 349 | return onPaused(newPlayerState); 350 | }; 351 | 352 | var toggleControls = function toggleControls() { 353 | // value is the last value of the animation when stop animation was called. 354 | // As this is an opacity effect, I (Charlie) used the value (0 or 1) as a boolean 355 | opacity.stopAnimation(function (value) { 356 | setIsVisible(!!value); 357 | return value ? fadeOutControls() : fadeInControls(); 358 | }); 359 | }; 360 | 361 | return React__default.createElement(reactNative.TouchableWithoutFeedback, { 362 | accessible: false, 363 | onPress: toggleControls 364 | }, React__default.createElement(reactNative.Animated.View, { 365 | style: [styles.container, { 366 | opacity: opacity 367 | }] 368 | }, isVisible && React__default.createElement(reactNative.View, { 369 | style: [styles.container, customContainerStyle] 370 | }, React__default.createElement(reactNative.View, { 371 | style: [styles.controlsRow, styles.toolbarRow, customToolbarStyle] 372 | }, children), React__default.createElement(Controls, { 373 | onPause: onPause, 374 | onReplay: onReplay, 375 | isLoading: isLoading, 376 | mainColor: mainColor, 377 | playerState: playerState 378 | }), React__default.createElement(Slider, { 379 | progress: progress, 380 | duration: duration, 381 | mainColor: mainColor, 382 | onFullScreen: onFullScreen, 383 | playerState: playerState, 384 | onSeek: onSeek, 385 | onSeeking: onSeeking, 386 | onPause: onPause, 387 | customSliderStyle: sliderStyle 388 | })))); 389 | }; 390 | 391 | MediaControls.Toolbar = Toolbar; 392 | 393 | exports.default = MediaControls; 394 | //# sourceMappingURL=react-native-media-controls.cjs.development.js.map 395 | -------------------------------------------------------------------------------- /example/react-native-media-controls/react-native-media-controls.cjs.production.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"react-native-media-controls.cjs.production.min.js","sources":["../../src/constants/playerStates.ts","../../src/MediaControls.style.ts","../../src/utils.ts","../../src/Controls.tsx","../../src/Slider.tsx","../../src/MediaControls.tsx","../../src/Toolbar.tsx"],"sourcesContent":["enum PLAYER_STATES {\n PLAYING = 0,\n PAUSED = 1,\n ENDED = 2,\n}\n\nexport { PLAYER_STATES };\n","import { StyleSheet } from \"react-native\";\n\nconst containerBackgroundColor = \"rgba(45, 59, 62, 0.4)\";\nconst playButtonBorderColor = \"rgba(255,255,255,0.5)\";\nconst white = \"#fff\";\n\nexport default StyleSheet.create({\n container: {\n alignItems: \"center\",\n backgroundColor: containerBackgroundColor,\n bottom: 0,\n flex: 1,\n flexDirection: \"column\",\n justifyContent: \"space-between\",\n left: 0,\n paddingHorizontal: 20,\n paddingVertical: 13,\n position: \"absolute\",\n right: 0,\n top: 0,\n },\n controlsRow: {\n alignItems: \"center\",\n alignSelf: \"stretch\",\n flex: 1,\n justifyContent: \"center\",\n },\n fullScreenContainer: {\n alignItems: \"center\",\n alignSelf: \"stretch\",\n justifyContent: \"center\",\n paddingLeft: 20,\n },\n playButton: {\n alignItems: \"center\",\n borderColor: playButtonBorderColor,\n borderRadius: 3,\n borderWidth: 1.5,\n height: 50,\n justifyContent: \"center\",\n width: 50,\n },\n playIcon: {\n height: 22,\n resizeMode: \"contain\",\n width: 22,\n },\n progressColumnContainer: {\n flex: 1,\n },\n progressContainer: {\n flexDirection: \"row\",\n justifyContent: \"flex-end\",\n marginBottom: -25,\n },\n progressSlider: {\n alignSelf: \"stretch\",\n },\n replayIcon: {\n height: 20,\n resizeMode: \"stretch\",\n width: 25,\n },\n thumb: {\n backgroundColor: white,\n borderRadius: 50,\n borderWidth: 3,\n height: 20,\n width: 20,\n },\n timeRow: {\n alignSelf: \"stretch\",\n },\n timerLabel: {\n color: white,\n fontSize: 12,\n },\n timerLabelsContainer: {\n alignSelf: \"stretch\",\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n marginBottom: -7,\n },\n toolbar: {\n flexDirection: \"row\",\n flex: 1,\n justifyContent: \"flex-end\",\n },\n toolbarRow: {\n alignItems: \"flex-start\",\n flexDirection: \"row\",\n justifyContent: \"flex-start\",\n },\n track: {\n borderRadius: 1,\n height: 5,\n },\n});\n","import { PLAYER_STATES } from \"./constants/playerStates\";\n\nexport const humanizeVideoDuration = (seconds: number) => {\n const [begin, end] = seconds >= 3600 ? [11, 8] : [14, 5];\n const date = new Date(0);\n\n date.setSeconds(seconds);\n return date.toISOString().substr(begin, end);\n};\n\nexport const noop = () => {};\n\nexport const getPlayerStateIcon = (playerState: PLAYER_STATES) => {\n switch (playerState) {\n case PLAYER_STATES.PAUSED:\n return require(\"./assets/ic_play.png\");\n case PLAYER_STATES.PLAYING:\n return require(\"./assets/ic_pause.png\");\n case PLAYER_STATES.ENDED:\n return require(\"./assets/ic_replay.png\");\n default:\n return null;\n }\n};\n","import React from \"react\";\nimport { TouchableOpacity, View, ActivityIndicator, Image } from \"react-native\";\nimport styles from \"./MediaControls.style\";\nimport { getPlayerStateIcon } from \"./utils\";\nimport { Props } from \"./MediaControls\";\nimport { PLAYER_STATES } from \"./constants/playerStates\";\n\ntype ControlsProps = Pick<\n Props,\n \"isLoading\" | \"mainColor\" | \"playerState\" | \"onReplay\"\n> & {\n onPause: () => void;\n};\n\nconst Controls = (props: ControlsProps) => {\n const { isLoading, mainColor, playerState, onReplay, onPause } = props;\n const icon = getPlayerStateIcon(playerState);\n const pressAction = playerState === PLAYER_STATES.ENDED ? onReplay : onPause;\n\n const content = isLoading ? (\n \n ) : (\n \n \n \n );\n\n return {content};\n};\n\nexport { Controls };\n","import React from \"react\";\nimport { TouchableOpacity, View, Text, Image, ViewStyle } from \"react-native\";\nimport RNSlider from \"react-native-slider\";\nimport styles from \"./MediaControls.style\";\nimport { humanizeVideoDuration } from \"./utils\";\nimport { Props as MediaControlsProps } from \"./MediaControls\";\nimport { PLAYER_STATES } from \"./constants/playerStates\";\n\nexport type CustomSliderStyle = {\n containerStyle: ViewStyle;\n trackStyle: ViewStyle;\n thumbStyle: ViewStyle;\n};\n\ntype Props = Pick<\n MediaControlsProps,\n | \"progress\"\n | \"duration\"\n | \"mainColor\"\n | \"onFullScreen\"\n | \"playerState\"\n | \"onSeek\"\n | \"onSeeking\"\n> & {\n onPause: () => void;\n customSliderStyle?: CustomSliderStyle;\n};\n\nconst fullScreenImage = require(\"./assets/ic_fullscreen.png\");\n\nconst Slider = (props: Props) => {\n const {\n customSliderStyle,\n duration,\n mainColor,\n onFullScreen,\n onPause,\n progress,\n } = props;\n\n const containerStyle = customSliderStyle?.containerStyle || {};\n const customTrackStyle = customSliderStyle?.trackStyle || {};\n const customThumbStyle = customSliderStyle?.thumbStyle || {};\n\n const dragging = (value: number) => {\n const { onSeeking, playerState } = props;\n onSeeking(value);\n\n if (playerState === PLAYER_STATES.PAUSED) {\n return;\n }\n\n onPause();\n };\n\n const seekVideo = (value: number) => {\n props.onSeek(value);\n onPause();\n };\n\n return (\n \n \n \n \n {humanizeVideoDuration(progress)}\n \n \n {humanizeVideoDuration(duration)}\n \n \n \n \n {Boolean(onFullScreen) && (\n \n \n \n )}\n \n );\n};\n\nexport { Slider };\n","import React, { useState, useEffect } from \"react\";\nimport {\n View,\n Animated,\n TouchableWithoutFeedback,\n GestureResponderEvent,\n ViewStyle,\n} from \"react-native\";\nimport styles from \"./MediaControls.style\";\nimport { PLAYER_STATES } from \"./constants/playerStates\";\nimport { Controls } from \"./Controls\";\nimport { Slider, CustomSliderStyle } from \"./Slider\";\nimport { Toolbar } from \"./Toolbar\";\n\nexport type Props = {\n children: React.ReactNode;\n containerStyle: ViewStyle;\n duration: number;\n fadeOutDelay?: number;\n isFullScreen: boolean;\n isLoading: boolean;\n mainColor: string;\n onFullScreen?: (event: GestureResponderEvent) => void;\n onPaused: (playerState: PLAYER_STATES) => void;\n onReplay: () => void;\n onSeek: (value: number) => void;\n onSeeking: (value: number) => void;\n playerState: PLAYER_STATES;\n progress: number;\n showOnStart?: boolean;\n sliderStyle: CustomSliderStyle;\n toolbarStyle: ViewStyle;\n};\n\nconst MediaControls = (props: Props) => {\n const {\n children,\n containerStyle: customContainerStyle = {},\n duration,\n fadeOutDelay = 5000,\n isLoading = false,\n mainColor = \"rgba(12, 83, 175, 0.9)\",\n onFullScreen,\n onReplay: onReplayCallback,\n onSeek,\n onSeeking,\n playerState,\n progress,\n showOnStart = true,\n sliderStyle, // defaults are applied in Slider.tsx\n toolbarStyle: customToolbarStyle = {},\n } = props;\n const { initialOpacity, initialIsVisible } = (() => {\n if (showOnStart) {\n return {\n initialOpacity: 1,\n initialIsVisible: true,\n };\n }\n\n return {\n initialOpacity: 0,\n initialIsVisible: false,\n };\n })();\n\n const [opacity] = useState(new Animated.Value(initialOpacity));\n const [isVisible, setIsVisible] = useState(initialIsVisible);\n\n useEffect(() => {\n fadeOutControls(fadeOutDelay);\n }, []);\n\n const fadeOutControls = (delay = 0) => {\n Animated.timing(opacity, {\n toValue: 0,\n duration: 300,\n delay,\n useNativeDriver: false,\n }).start(result => {\n /* I noticed that the callback is called twice, when it is invoked and when it completely finished\n This prevents some flickering */\n if (result.finished) {\n setIsVisible(false);\n }\n });\n };\n\n const fadeInControls = (loop = true) => {\n setIsVisible(true);\n Animated.timing(opacity, {\n toValue: 1,\n duration: 300,\n delay: 0,\n useNativeDriver: false,\n }).start(() => {\n if (loop) {\n fadeOutControls(fadeOutDelay);\n }\n });\n };\n\n const onReplay = () => {\n fadeOutControls(fadeOutDelay);\n onReplayCallback();\n };\n\n const cancelAnimation = () => opacity.stopAnimation(() => setIsVisible(true));\n\n const onPause = () => {\n const { playerState, onPaused } = props;\n const { PLAYING, PAUSED, ENDED } = PLAYER_STATES;\n switch (playerState) {\n case PLAYING: {\n cancelAnimation();\n break;\n }\n case PAUSED: {\n fadeOutControls(fadeOutDelay);\n break;\n }\n case ENDED:\n break;\n }\n\n const newPlayerState = playerState === PLAYING ? PAUSED : PLAYING;\n return onPaused(newPlayerState);\n };\n\n const toggleControls = () => {\n // value is the last value of the animation when stop animation was called.\n // As this is an opacity effect, I (Charlie) used the value (0 or 1) as a boolean\n opacity.stopAnimation((value: number) => {\n setIsVisible(!!value);\n return value ? fadeOutControls() : fadeInControls();\n });\n };\n\n return (\n \n \n {isVisible && (\n \n \n {children}\n \n \n \n \n )}\n \n \n );\n};\n\nMediaControls.Toolbar = Toolbar;\n\nexport default MediaControls;\n","import React from \"react\";\n\ntype Props = {\n children: React.ReactNode;\n};\n\nconst Toolbar = ({ children }: Props) => <>{children};\n\nexport { Toolbar };\n"],"names":["PLAYER_STATES","StyleSheet","create","container","alignItems","backgroundColor","bottom","flex","flexDirection","justifyContent","left","paddingHorizontal","paddingVertical","position","right","top","controlsRow","alignSelf","fullScreenContainer","paddingLeft","playButton","borderColor","borderRadius","borderWidth","height","width","playIcon","resizeMode","progressColumnContainer","progressContainer","marginBottom","progressSlider","replayIcon","thumb","timeRow","timerLabel","color","fontSize","timerLabelsContainer","toolbar","toolbarRow","track","humanizeVideoDuration","seconds","begin","end","date","Date","setSeconds","toISOString","substr","Controls","props","isLoading","mainColor","playerState","onReplay","onPause","icon","PAUSED","require","PLAYING","ENDED","getPlayerStateIcon","pressAction","content","React","ActivityIndicator","size","TouchableOpacity","style","styles","onPress","accessibilityLabel","accessibilityHint","Image","source","View","fullScreenImage","Slider","customSliderStyle","duration","onFullScreen","progress","customTrackStyle","trackStyle","customThumbStyle","thumbStyle","containerStyle","Text","RNSlider","onValueChange","value","onSeeking","onSlidingComplete","onSeek","maximumValue","Math","floor","minimumTrackTintColor","Boolean","MediaControls","children","customContainerStyle","fadeOutDelay","onReplayCallback","showOnStart","sliderStyle","toolbarStyle","customToolbarStyle","initialOpacity","initialIsVisible","opacity","useState","Animated","Value","isVisible","setIsVisible","useEffect","fadeOutControls","delay","timing","toValue","useNativeDriver","start","result","finished","onPaused","stopAnimation","TouchableWithoutFeedback","accessible","loop","Toolbar"],"mappings":"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"} -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - boost-for-react-native (1.63.0) 3 | - CocoaAsyncSocket (7.6.4) 4 | - CocoaLibEvent (1.0.0) 5 | - DoubleConversion (1.1.6) 6 | - FBLazyVector (0.62.2) 7 | - FBReactNativeSpec (0.62.2): 8 | - Folly (= 2018.10.22.00) 9 | - RCTRequired (= 0.62.2) 10 | - RCTTypeSafety (= 0.62.2) 11 | - React-Core (= 0.62.2) 12 | - React-jsi (= 0.62.2) 13 | - ReactCommon/turbomodule/core (= 0.62.2) 14 | - Flipper (0.33.1): 15 | - Flipper-Folly (~> 2.1) 16 | - Flipper-RSocket (~> 1.0) 17 | - Flipper-DoubleConversion (1.1.7) 18 | - Flipper-Folly (2.2.0): 19 | - boost-for-react-native 20 | - CocoaLibEvent (~> 1.0) 21 | - Flipper-DoubleConversion 22 | - Flipper-Glog 23 | - OpenSSL-Universal (= 1.0.2.19) 24 | - Flipper-Glog (0.3.6) 25 | - Flipper-PeerTalk (0.0.4) 26 | - Flipper-RSocket (1.1.0): 27 | - Flipper-Folly (~> 2.2) 28 | - FlipperKit (0.33.1): 29 | - FlipperKit/Core (= 0.33.1) 30 | - FlipperKit/Core (0.33.1): 31 | - Flipper (~> 0.33.1) 32 | - FlipperKit/CppBridge 33 | - FlipperKit/FBCxxFollyDynamicConvert 34 | - FlipperKit/FBDefines 35 | - FlipperKit/FKPortForwarding 36 | - FlipperKit/CppBridge (0.33.1): 37 | - Flipper (~> 0.33.1) 38 | - FlipperKit/FBCxxFollyDynamicConvert (0.33.1): 39 | - Flipper-Folly (~> 2.1) 40 | - FlipperKit/FBDefines (0.33.1) 41 | - FlipperKit/FKPortForwarding (0.33.1): 42 | - CocoaAsyncSocket (~> 7.6) 43 | - Flipper-PeerTalk (~> 0.0.4) 44 | - FlipperKit/FlipperKitHighlightOverlay (0.33.1) 45 | - FlipperKit/FlipperKitLayoutPlugin (0.33.1): 46 | - FlipperKit/Core 47 | - FlipperKit/FlipperKitHighlightOverlay 48 | - FlipperKit/FlipperKitLayoutTextSearchable 49 | - YogaKit (~> 1.18) 50 | - FlipperKit/FlipperKitLayoutTextSearchable (0.33.1) 51 | - FlipperKit/FlipperKitNetworkPlugin (0.33.1): 52 | - FlipperKit/Core 53 | - FlipperKit/FlipperKitReactPlugin (0.33.1): 54 | - FlipperKit/Core 55 | - FlipperKit/FlipperKitUserDefaultsPlugin (0.33.1): 56 | - FlipperKit/Core 57 | - FlipperKit/SKIOSNetworkPlugin (0.33.1): 58 | - FlipperKit/Core 59 | - FlipperKit/FlipperKitNetworkPlugin 60 | - Folly (2018.10.22.00): 61 | - boost-for-react-native 62 | - DoubleConversion 63 | - Folly/Default (= 2018.10.22.00) 64 | - glog 65 | - Folly/Default (2018.10.22.00): 66 | - boost-for-react-native 67 | - DoubleConversion 68 | - glog 69 | - glog (0.3.5) 70 | - OpenSSL-Universal (1.0.2.19): 71 | - OpenSSL-Universal/Static (= 1.0.2.19) 72 | - OpenSSL-Universal/Static (1.0.2.19) 73 | - RCTRequired (0.62.2) 74 | - RCTTypeSafety (0.62.2): 75 | - FBLazyVector (= 0.62.2) 76 | - Folly (= 2018.10.22.00) 77 | - RCTRequired (= 0.62.2) 78 | - React-Core (= 0.62.2) 79 | - React (0.62.2): 80 | - React-Core (= 0.62.2) 81 | - React-Core/DevSupport (= 0.62.2) 82 | - React-Core/RCTWebSocket (= 0.62.2) 83 | - React-RCTActionSheet (= 0.62.2) 84 | - React-RCTAnimation (= 0.62.2) 85 | - React-RCTBlob (= 0.62.2) 86 | - React-RCTImage (= 0.62.2) 87 | - React-RCTLinking (= 0.62.2) 88 | - React-RCTNetwork (= 0.62.2) 89 | - React-RCTSettings (= 0.62.2) 90 | - React-RCTText (= 0.62.2) 91 | - React-RCTVibration (= 0.62.2) 92 | - React-Core (0.62.2): 93 | - Folly (= 2018.10.22.00) 94 | - glog 95 | - React-Core/Default (= 0.62.2) 96 | - React-cxxreact (= 0.62.2) 97 | - React-jsi (= 0.62.2) 98 | - React-jsiexecutor (= 0.62.2) 99 | - Yoga 100 | - React-Core/CoreModulesHeaders (0.62.2): 101 | - Folly (= 2018.10.22.00) 102 | - glog 103 | - React-Core/Default 104 | - React-cxxreact (= 0.62.2) 105 | - React-jsi (= 0.62.2) 106 | - React-jsiexecutor (= 0.62.2) 107 | - Yoga 108 | - React-Core/Default (0.62.2): 109 | - Folly (= 2018.10.22.00) 110 | - glog 111 | - React-cxxreact (= 0.62.2) 112 | - React-jsi (= 0.62.2) 113 | - React-jsiexecutor (= 0.62.2) 114 | - Yoga 115 | - React-Core/DevSupport (0.62.2): 116 | - Folly (= 2018.10.22.00) 117 | - glog 118 | - React-Core/Default (= 0.62.2) 119 | - React-Core/RCTWebSocket (= 0.62.2) 120 | - React-cxxreact (= 0.62.2) 121 | - React-jsi (= 0.62.2) 122 | - React-jsiexecutor (= 0.62.2) 123 | - React-jsinspector (= 0.62.2) 124 | - Yoga 125 | - React-Core/RCTActionSheetHeaders (0.62.2): 126 | - Folly (= 2018.10.22.00) 127 | - glog 128 | - React-Core/Default 129 | - React-cxxreact (= 0.62.2) 130 | - React-jsi (= 0.62.2) 131 | - React-jsiexecutor (= 0.62.2) 132 | - Yoga 133 | - React-Core/RCTAnimationHeaders (0.62.2): 134 | - Folly (= 2018.10.22.00) 135 | - glog 136 | - React-Core/Default 137 | - React-cxxreact (= 0.62.2) 138 | - React-jsi (= 0.62.2) 139 | - React-jsiexecutor (= 0.62.2) 140 | - Yoga 141 | - React-Core/RCTBlobHeaders (0.62.2): 142 | - Folly (= 2018.10.22.00) 143 | - glog 144 | - React-Core/Default 145 | - React-cxxreact (= 0.62.2) 146 | - React-jsi (= 0.62.2) 147 | - React-jsiexecutor (= 0.62.2) 148 | - Yoga 149 | - React-Core/RCTImageHeaders (0.62.2): 150 | - Folly (= 2018.10.22.00) 151 | - glog 152 | - React-Core/Default 153 | - React-cxxreact (= 0.62.2) 154 | - React-jsi (= 0.62.2) 155 | - React-jsiexecutor (= 0.62.2) 156 | - Yoga 157 | - React-Core/RCTLinkingHeaders (0.62.2): 158 | - Folly (= 2018.10.22.00) 159 | - glog 160 | - React-Core/Default 161 | - React-cxxreact (= 0.62.2) 162 | - React-jsi (= 0.62.2) 163 | - React-jsiexecutor (= 0.62.2) 164 | - Yoga 165 | - React-Core/RCTNetworkHeaders (0.62.2): 166 | - Folly (= 2018.10.22.00) 167 | - glog 168 | - React-Core/Default 169 | - React-cxxreact (= 0.62.2) 170 | - React-jsi (= 0.62.2) 171 | - React-jsiexecutor (= 0.62.2) 172 | - Yoga 173 | - React-Core/RCTSettingsHeaders (0.62.2): 174 | - Folly (= 2018.10.22.00) 175 | - glog 176 | - React-Core/Default 177 | - React-cxxreact (= 0.62.2) 178 | - React-jsi (= 0.62.2) 179 | - React-jsiexecutor (= 0.62.2) 180 | - Yoga 181 | - React-Core/RCTTextHeaders (0.62.2): 182 | - Folly (= 2018.10.22.00) 183 | - glog 184 | - React-Core/Default 185 | - React-cxxreact (= 0.62.2) 186 | - React-jsi (= 0.62.2) 187 | - React-jsiexecutor (= 0.62.2) 188 | - Yoga 189 | - React-Core/RCTVibrationHeaders (0.62.2): 190 | - Folly (= 2018.10.22.00) 191 | - glog 192 | - React-Core/Default 193 | - React-cxxreact (= 0.62.2) 194 | - React-jsi (= 0.62.2) 195 | - React-jsiexecutor (= 0.62.2) 196 | - Yoga 197 | - React-Core/RCTWebSocket (0.62.2): 198 | - Folly (= 2018.10.22.00) 199 | - glog 200 | - React-Core/Default (= 0.62.2) 201 | - React-cxxreact (= 0.62.2) 202 | - React-jsi (= 0.62.2) 203 | - React-jsiexecutor (= 0.62.2) 204 | - Yoga 205 | - React-CoreModules (0.62.2): 206 | - FBReactNativeSpec (= 0.62.2) 207 | - Folly (= 2018.10.22.00) 208 | - RCTTypeSafety (= 0.62.2) 209 | - React-Core/CoreModulesHeaders (= 0.62.2) 210 | - React-RCTImage (= 0.62.2) 211 | - ReactCommon/turbomodule/core (= 0.62.2) 212 | - React-cxxreact (0.62.2): 213 | - boost-for-react-native (= 1.63.0) 214 | - DoubleConversion 215 | - Folly (= 2018.10.22.00) 216 | - glog 217 | - React-jsinspector (= 0.62.2) 218 | - React-jsi (0.62.2): 219 | - boost-for-react-native (= 1.63.0) 220 | - DoubleConversion 221 | - Folly (= 2018.10.22.00) 222 | - glog 223 | - React-jsi/Default (= 0.62.2) 224 | - React-jsi/Default (0.62.2): 225 | - boost-for-react-native (= 1.63.0) 226 | - DoubleConversion 227 | - Folly (= 2018.10.22.00) 228 | - glog 229 | - React-jsiexecutor (0.62.2): 230 | - DoubleConversion 231 | - Folly (= 2018.10.22.00) 232 | - glog 233 | - React-cxxreact (= 0.62.2) 234 | - React-jsi (= 0.62.2) 235 | - React-jsinspector (0.62.2) 236 | - react-native-video (4.4.5): 237 | - React 238 | - react-native-video/Video (= 4.4.5) 239 | - react-native-video/Video (4.4.5): 240 | - React 241 | - React-RCTActionSheet (0.62.2): 242 | - React-Core/RCTActionSheetHeaders (= 0.62.2) 243 | - React-RCTAnimation (0.62.2): 244 | - FBReactNativeSpec (= 0.62.2) 245 | - Folly (= 2018.10.22.00) 246 | - RCTTypeSafety (= 0.62.2) 247 | - React-Core/RCTAnimationHeaders (= 0.62.2) 248 | - ReactCommon/turbomodule/core (= 0.62.2) 249 | - React-RCTBlob (0.62.2): 250 | - FBReactNativeSpec (= 0.62.2) 251 | - Folly (= 2018.10.22.00) 252 | - React-Core/RCTBlobHeaders (= 0.62.2) 253 | - React-Core/RCTWebSocket (= 0.62.2) 254 | - React-jsi (= 0.62.2) 255 | - React-RCTNetwork (= 0.62.2) 256 | - ReactCommon/turbomodule/core (= 0.62.2) 257 | - React-RCTImage (0.62.2): 258 | - FBReactNativeSpec (= 0.62.2) 259 | - Folly (= 2018.10.22.00) 260 | - RCTTypeSafety (= 0.62.2) 261 | - React-Core/RCTImageHeaders (= 0.62.2) 262 | - React-RCTNetwork (= 0.62.2) 263 | - ReactCommon/turbomodule/core (= 0.62.2) 264 | - React-RCTLinking (0.62.2): 265 | - FBReactNativeSpec (= 0.62.2) 266 | - React-Core/RCTLinkingHeaders (= 0.62.2) 267 | - ReactCommon/turbomodule/core (= 0.62.2) 268 | - React-RCTNetwork (0.62.2): 269 | - FBReactNativeSpec (= 0.62.2) 270 | - Folly (= 2018.10.22.00) 271 | - RCTTypeSafety (= 0.62.2) 272 | - React-Core/RCTNetworkHeaders (= 0.62.2) 273 | - ReactCommon/turbomodule/core (= 0.62.2) 274 | - React-RCTSettings (0.62.2): 275 | - FBReactNativeSpec (= 0.62.2) 276 | - Folly (= 2018.10.22.00) 277 | - RCTTypeSafety (= 0.62.2) 278 | - React-Core/RCTSettingsHeaders (= 0.62.2) 279 | - ReactCommon/turbomodule/core (= 0.62.2) 280 | - React-RCTText (0.62.2): 281 | - React-Core/RCTTextHeaders (= 0.62.2) 282 | - React-RCTVibration (0.62.2): 283 | - FBReactNativeSpec (= 0.62.2) 284 | - Folly (= 2018.10.22.00) 285 | - React-Core/RCTVibrationHeaders (= 0.62.2) 286 | - ReactCommon/turbomodule/core (= 0.62.2) 287 | - ReactCommon/callinvoker (0.62.2): 288 | - DoubleConversion 289 | - Folly (= 2018.10.22.00) 290 | - glog 291 | - React-cxxreact (= 0.62.2) 292 | - ReactCommon/turbomodule/core (0.62.2): 293 | - DoubleConversion 294 | - Folly (= 2018.10.22.00) 295 | - glog 296 | - React-Core (= 0.62.2) 297 | - React-cxxreact (= 0.62.2) 298 | - React-jsi (= 0.62.2) 299 | - ReactCommon/callinvoker (= 0.62.2) 300 | - Yoga (1.14.0) 301 | - YogaKit (1.18.1): 302 | - Yoga (~> 1.14) 303 | 304 | DEPENDENCIES: 305 | - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) 306 | - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) 307 | - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`) 308 | - Flipper (~> 0.33.1) 309 | - Flipper-DoubleConversion (= 1.1.7) 310 | - Flipper-Folly (~> 2.1) 311 | - Flipper-Glog (= 0.3.6) 312 | - Flipper-PeerTalk (~> 0.0.4) 313 | - Flipper-RSocket (~> 1.0) 314 | - FlipperKit (~> 0.33.1) 315 | - FlipperKit/Core (~> 0.33.1) 316 | - FlipperKit/CppBridge (~> 0.33.1) 317 | - FlipperKit/FBCxxFollyDynamicConvert (~> 0.33.1) 318 | - FlipperKit/FBDefines (~> 0.33.1) 319 | - FlipperKit/FKPortForwarding (~> 0.33.1) 320 | - FlipperKit/FlipperKitHighlightOverlay (~> 0.33.1) 321 | - FlipperKit/FlipperKitLayoutPlugin (~> 0.33.1) 322 | - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.33.1) 323 | - FlipperKit/FlipperKitNetworkPlugin (~> 0.33.1) 324 | - FlipperKit/FlipperKitReactPlugin (~> 0.33.1) 325 | - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.33.1) 326 | - FlipperKit/SKIOSNetworkPlugin (~> 0.33.1) 327 | - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) 328 | - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) 329 | - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) 330 | - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) 331 | - React (from `../node_modules/react-native/`) 332 | - React-Core (from `../node_modules/react-native/`) 333 | - React-Core/DevSupport (from `../node_modules/react-native/`) 334 | - React-Core/RCTWebSocket (from `../node_modules/react-native/`) 335 | - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) 336 | - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) 337 | - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) 338 | - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) 339 | - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) 340 | - react-native-video (from `../node_modules/react-native-video`) 341 | - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) 342 | - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) 343 | - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) 344 | - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) 345 | - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) 346 | - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) 347 | - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) 348 | - React-RCTText (from `../node_modules/react-native/Libraries/Text`) 349 | - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) 350 | - ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`) 351 | - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) 352 | - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) 353 | 354 | SPEC REPOS: 355 | trunk: 356 | - boost-for-react-native 357 | - CocoaAsyncSocket 358 | - CocoaLibEvent 359 | - Flipper 360 | - Flipper-DoubleConversion 361 | - Flipper-Folly 362 | - Flipper-Glog 363 | - Flipper-PeerTalk 364 | - Flipper-RSocket 365 | - FlipperKit 366 | - OpenSSL-Universal 367 | - YogaKit 368 | 369 | EXTERNAL SOURCES: 370 | DoubleConversion: 371 | :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" 372 | FBLazyVector: 373 | :path: "../node_modules/react-native/Libraries/FBLazyVector" 374 | FBReactNativeSpec: 375 | :path: "../node_modules/react-native/Libraries/FBReactNativeSpec" 376 | Folly: 377 | :podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec" 378 | glog: 379 | :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" 380 | RCTRequired: 381 | :path: "../node_modules/react-native/Libraries/RCTRequired" 382 | RCTTypeSafety: 383 | :path: "../node_modules/react-native/Libraries/TypeSafety" 384 | React: 385 | :path: "../node_modules/react-native/" 386 | React-Core: 387 | :path: "../node_modules/react-native/" 388 | React-CoreModules: 389 | :path: "../node_modules/react-native/React/CoreModules" 390 | React-cxxreact: 391 | :path: "../node_modules/react-native/ReactCommon/cxxreact" 392 | React-jsi: 393 | :path: "../node_modules/react-native/ReactCommon/jsi" 394 | React-jsiexecutor: 395 | :path: "../node_modules/react-native/ReactCommon/jsiexecutor" 396 | React-jsinspector: 397 | :path: "../node_modules/react-native/ReactCommon/jsinspector" 398 | react-native-video: 399 | :path: "../node_modules/react-native-video" 400 | React-RCTActionSheet: 401 | :path: "../node_modules/react-native/Libraries/ActionSheetIOS" 402 | React-RCTAnimation: 403 | :path: "../node_modules/react-native/Libraries/NativeAnimation" 404 | React-RCTBlob: 405 | :path: "../node_modules/react-native/Libraries/Blob" 406 | React-RCTImage: 407 | :path: "../node_modules/react-native/Libraries/Image" 408 | React-RCTLinking: 409 | :path: "../node_modules/react-native/Libraries/LinkingIOS" 410 | React-RCTNetwork: 411 | :path: "../node_modules/react-native/Libraries/Network" 412 | React-RCTSettings: 413 | :path: "../node_modules/react-native/Libraries/Settings" 414 | React-RCTText: 415 | :path: "../node_modules/react-native/Libraries/Text" 416 | React-RCTVibration: 417 | :path: "../node_modules/react-native/Libraries/Vibration" 418 | ReactCommon: 419 | :path: "../node_modules/react-native/ReactCommon" 420 | Yoga: 421 | :path: "../node_modules/react-native/ReactCommon/yoga" 422 | 423 | SPEC CHECKSUMS: 424 | boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c 425 | CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845 426 | CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f 427 | DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 428 | FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245 429 | FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e 430 | Flipper: 6c1f484f9a88d30ab3e272800d53688439e50f69 431 | Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 432 | Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3 433 | Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 434 | Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 435 | Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7 436 | FlipperKit: 6dc9b8f4ef60d9e5ded7f0264db299c91f18832e 437 | Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 438 | glog: 1f3da668190260b06b429bb211bfbee5cd790c28 439 | OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355 440 | RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035 441 | RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce 442 | React: 29a8b1a02bd764fb7644ef04019270849b9a7ac3 443 | React-Core: b12bffb3f567fdf99510acb716ef1abd426e0e05 444 | React-CoreModules: 4a9b87bbe669d6c3173c0132c3328e3b000783d0 445 | React-cxxreact: e65f9c2ba0ac5be946f53548c1aaaee5873a8103 446 | React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161 447 | React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da 448 | React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493 449 | react-native-video: eee541262ada11bc7ffe8ec5508e6a877937c462 450 | React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c 451 | React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0 452 | React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71 453 | React-RCTImage: e70be9b9c74fe4e42d0005f42cace7981c994ac3 454 | React-RCTLinking: c1b9739a88d56ecbec23b7f63650e44672ab2ad2 455 | React-RCTNetwork: 73138b6f45e5a2768ad93f3d57873c2a18d14b44 456 | React-RCTSettings: 6e3738a87e21b39a8cb08d627e68c44acf1e325a 457 | React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d 458 | React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256 459 | ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3 460 | Yoga: 3ebccbdd559724312790e7742142d062476b698e 461 | YogaKit: f782866e155069a2cca2517aafea43200b01fd5a 462 | 463 | PODFILE CHECKSUM: 9a118b5160ac9d27316919f2f9ff382174431a2c 464 | 465 | COCOAPODS: 1.9.1 466 | -------------------------------------------------------------------------------- /example/react-native-media-controls/react-native-media-controls.esm.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"react-native-media-controls.esm.js","sources":["../../src/MediaControls.style.ts","../../src/constants/playerStates.ts","../../src/utils.ts","../../src/Controls.tsx","../../src/Slider.tsx","../../src/Toolbar.tsx","../../src/MediaControls.tsx"],"sourcesContent":["import { StyleSheet } from \"react-native\";\n\nconst containerBackgroundColor = \"rgba(45, 59, 62, 0.4)\";\nconst playButtonBorderColor = \"rgba(255,255,255,0.5)\";\nconst white = \"#fff\";\n\nexport default StyleSheet.create({\n container: {\n alignItems: \"center\",\n backgroundColor: containerBackgroundColor,\n bottom: 0,\n flex: 1,\n flexDirection: \"column\",\n justifyContent: \"space-between\",\n left: 0,\n paddingHorizontal: 20,\n paddingVertical: 13,\n position: \"absolute\",\n right: 0,\n top: 0,\n },\n controlsRow: {\n alignItems: \"center\",\n alignSelf: \"stretch\",\n flex: 1,\n justifyContent: \"center\",\n },\n fullScreenContainer: {\n alignItems: \"center\",\n alignSelf: \"stretch\",\n justifyContent: \"center\",\n paddingLeft: 20,\n },\n playButton: {\n alignItems: \"center\",\n borderColor: playButtonBorderColor,\n borderRadius: 3,\n borderWidth: 1.5,\n height: 50,\n justifyContent: \"center\",\n width: 50,\n },\n playIcon: {\n height: 22,\n resizeMode: \"contain\",\n width: 22,\n },\n progressColumnContainer: {\n flex: 1,\n },\n progressContainer: {\n flexDirection: \"row\",\n justifyContent: \"flex-end\",\n marginBottom: -25,\n },\n progressSlider: {\n alignSelf: \"stretch\",\n },\n replayIcon: {\n height: 20,\n resizeMode: \"stretch\",\n width: 25,\n },\n thumb: {\n backgroundColor: white,\n borderRadius: 50,\n borderWidth: 3,\n height: 20,\n width: 20,\n },\n timeRow: {\n alignSelf: \"stretch\",\n },\n timerLabel: {\n color: white,\n fontSize: 12,\n },\n timerLabelsContainer: {\n alignSelf: \"stretch\",\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n marginBottom: -7,\n },\n toolbar: {\n flexDirection: \"row\",\n flex: 1,\n justifyContent: \"flex-end\",\n },\n toolbarRow: {\n alignItems: \"flex-start\",\n flexDirection: \"row\",\n justifyContent: \"flex-start\",\n },\n track: {\n borderRadius: 1,\n height: 5,\n },\n});\n","enum PLAYER_STATES {\n PLAYING = 0,\n PAUSED = 1,\n ENDED = 2,\n}\n\nexport { PLAYER_STATES };\n","import { PLAYER_STATES } from \"./constants/playerStates\";\n\nexport const humanizeVideoDuration = (seconds: number) => {\n const [begin, end] = seconds >= 3600 ? [11, 8] : [14, 5];\n const date = new Date(0);\n\n date.setSeconds(seconds);\n return date.toISOString().substr(begin, end);\n};\n\nexport const noop = () => {};\n\nexport const getPlayerStateIcon = (playerState: PLAYER_STATES) => {\n switch (playerState) {\n case PLAYER_STATES.PAUSED:\n return require(\"./assets/ic_play.png\");\n case PLAYER_STATES.PLAYING:\n return require(\"./assets/ic_pause.png\");\n case PLAYER_STATES.ENDED:\n return require(\"./assets/ic_replay.png\");\n default:\n return null;\n }\n};\n","import React from \"react\";\nimport { TouchableOpacity, View, ActivityIndicator, Image } from \"react-native\";\nimport styles from \"./MediaControls.style\";\nimport { getPlayerStateIcon } from \"./utils\";\nimport { Props } from \"./MediaControls\";\nimport { PLAYER_STATES } from \"./constants/playerStates\";\n\ntype ControlsProps = Pick<\n Props,\n \"isLoading\" | \"mainColor\" | \"playerState\" | \"onReplay\"\n> & {\n onPause: () => void;\n};\n\nconst Controls = (props: ControlsProps) => {\n const { isLoading, mainColor, playerState, onReplay, onPause } = props;\n const icon = getPlayerStateIcon(playerState);\n const pressAction = playerState === PLAYER_STATES.ENDED ? onReplay : onPause;\n\n const content = isLoading ? (\n \n ) : (\n \n \n \n );\n\n return {content};\n};\n\nexport { Controls };\n","import React from \"react\";\nimport { TouchableOpacity, View, Text, Image, ViewStyle } from \"react-native\";\nimport RNSlider from \"react-native-slider\";\nimport styles from \"./MediaControls.style\";\nimport { humanizeVideoDuration } from \"./utils\";\nimport { Props as MediaControlsProps } from \"./MediaControls\";\nimport { PLAYER_STATES } from \"./constants/playerStates\";\n\nexport type CustomSliderStyle = {\n containerStyle: ViewStyle;\n trackStyle: ViewStyle;\n thumbStyle: ViewStyle;\n};\n\ntype Props = Pick<\n MediaControlsProps,\n | \"progress\"\n | \"duration\"\n | \"mainColor\"\n | \"onFullScreen\"\n | \"playerState\"\n | \"onSeek\"\n | \"onSeeking\"\n> & {\n onPause: () => void;\n customSliderStyle?: CustomSliderStyle;\n};\n\nconst fullScreenImage = require(\"./assets/ic_fullscreen.png\");\n\nconst Slider = (props: Props) => {\n const {\n customSliderStyle,\n duration,\n mainColor,\n onFullScreen,\n onPause,\n progress,\n } = props;\n\n const containerStyle = customSliderStyle?.containerStyle || {};\n const customTrackStyle = customSliderStyle?.trackStyle || {};\n const customThumbStyle = customSliderStyle?.thumbStyle || {};\n\n const dragging = (value: number) => {\n const { onSeeking, playerState } = props;\n onSeeking(value);\n\n if (playerState === PLAYER_STATES.PAUSED) {\n return;\n }\n\n onPause();\n };\n\n const seekVideo = (value: number) => {\n props.onSeek(value);\n onPause();\n };\n\n return (\n \n \n \n \n {humanizeVideoDuration(progress)}\n \n \n {humanizeVideoDuration(duration)}\n \n \n \n \n {Boolean(onFullScreen) && (\n \n \n \n )}\n \n );\n};\n\nexport { Slider };\n","import React from \"react\";\n\ntype Props = {\n children: React.ReactNode;\n};\n\nconst Toolbar = ({ children }: Props) => <>{children};\n\nexport { Toolbar };\n","import React, { useState, useEffect } from \"react\";\nimport {\n View,\n Animated,\n TouchableWithoutFeedback,\n GestureResponderEvent,\n ViewStyle,\n} from \"react-native\";\nimport styles from \"./MediaControls.style\";\nimport { PLAYER_STATES } from \"./constants/playerStates\";\nimport { Controls } from \"./Controls\";\nimport { Slider, CustomSliderStyle } from \"./Slider\";\nimport { Toolbar } from \"./Toolbar\";\n\nexport type Props = {\n children: React.ReactNode;\n containerStyle: ViewStyle;\n duration: number;\n fadeOutDelay?: number;\n isFullScreen: boolean;\n isLoading: boolean;\n mainColor: string;\n onFullScreen?: (event: GestureResponderEvent) => void;\n onPaused: (playerState: PLAYER_STATES) => void;\n onReplay: () => void;\n onSeek: (value: number) => void;\n onSeeking: (value: number) => void;\n playerState: PLAYER_STATES;\n progress: number;\n showOnStart?: boolean;\n sliderStyle: CustomSliderStyle;\n toolbarStyle: ViewStyle;\n};\n\nconst MediaControls = (props: Props) => {\n const {\n children,\n containerStyle: customContainerStyle = {},\n duration,\n fadeOutDelay = 5000,\n isLoading = false,\n mainColor = \"rgba(12, 83, 175, 0.9)\",\n onFullScreen,\n onReplay: onReplayCallback,\n onSeek,\n onSeeking,\n playerState,\n progress,\n showOnStart = true,\n sliderStyle, // defaults are applied in Slider.tsx\n toolbarStyle: customToolbarStyle = {},\n } = props;\n const { initialOpacity, initialIsVisible } = (() => {\n if (showOnStart) {\n return {\n initialOpacity: 1,\n initialIsVisible: true,\n };\n }\n\n return {\n initialOpacity: 0,\n initialIsVisible: false,\n };\n })();\n\n const [opacity] = useState(new Animated.Value(initialOpacity));\n const [isVisible, setIsVisible] = useState(initialIsVisible);\n\n useEffect(() => {\n fadeOutControls(fadeOutDelay);\n }, []);\n\n const fadeOutControls = (delay = 0) => {\n Animated.timing(opacity, {\n toValue: 0,\n duration: 300,\n delay,\n useNativeDriver: false,\n }).start(result => {\n /* I noticed that the callback is called twice, when it is invoked and when it completely finished\n This prevents some flickering */\n if (result.finished) {\n setIsVisible(false);\n }\n });\n };\n\n const fadeInControls = (loop = true) => {\n setIsVisible(true);\n Animated.timing(opacity, {\n toValue: 1,\n duration: 300,\n delay: 0,\n useNativeDriver: false,\n }).start(() => {\n if (loop) {\n fadeOutControls(fadeOutDelay);\n }\n });\n };\n\n const onReplay = () => {\n fadeOutControls(fadeOutDelay);\n onReplayCallback();\n };\n\n const cancelAnimation = () => opacity.stopAnimation(() => setIsVisible(true));\n\n const onPause = () => {\n const { playerState, onPaused } = props;\n const { PLAYING, PAUSED, ENDED } = PLAYER_STATES;\n switch (playerState) {\n case PLAYING: {\n cancelAnimation();\n break;\n }\n case PAUSED: {\n fadeOutControls(fadeOutDelay);\n break;\n }\n case ENDED:\n break;\n }\n\n const newPlayerState = playerState === PLAYING ? PAUSED : PLAYING;\n return onPaused(newPlayerState);\n };\n\n const toggleControls = () => {\n // value is the last value of the animation when stop animation was called.\n // As this is an opacity effect, I (Charlie) used the value (0 or 1) as a boolean\n opacity.stopAnimation((value: number) => {\n setIsVisible(!!value);\n return value ? fadeOutControls() : fadeInControls();\n });\n };\n\n return (\n \n \n {isVisible && (\n \n \n {children}\n \n \n \n \n )}\n \n \n );\n};\n\nMediaControls.Toolbar = Toolbar;\n\nexport default MediaControls;\n"],"names":["containerBackgroundColor","playButtonBorderColor","white","StyleSheet","create","container","alignItems","backgroundColor","bottom","flex","flexDirection","justifyContent","left","paddingHorizontal","paddingVertical","position","right","top","controlsRow","alignSelf","fullScreenContainer","paddingLeft","playButton","borderColor","borderRadius","borderWidth","height","width","playIcon","resizeMode","progressColumnContainer","progressContainer","marginBottom","progressSlider","replayIcon","thumb","timeRow","timerLabel","color","fontSize","timerLabelsContainer","toolbar","toolbarRow","track","PLAYER_STATES","humanizeVideoDuration","seconds","begin","end","date","Date","setSeconds","toISOString","substr","getPlayerStateIcon","playerState","PAUSED","require","PLAYING","ENDED","Controls","props","isLoading","mainColor","onReplay","onPause","icon","pressAction","content","React","ActivityIndicator","size","TouchableOpacity","style","styles","onPress","accessibilityLabel","accessibilityHint","Image","source","View","fullScreenImage","Slider","customSliderStyle","duration","onFullScreen","progress","containerStyle","customTrackStyle","trackStyle","customThumbStyle","thumbStyle","dragging","value","onSeeking","seekVideo","onSeek","Text","RNSlider","onValueChange","onSlidingComplete","maximumValue","Math","floor","minimumTrackTintColor","Boolean","Toolbar","children","MediaControls","customContainerStyle","fadeOutDelay","onReplayCallback","showOnStart","sliderStyle","toolbarStyle","customToolbarStyle","initialOpacity","initialIsVisible","useState","Animated","Value","opacity","isVisible","setIsVisible","useEffect","fadeOutControls","delay","timing","toValue","useNativeDriver","start","result","finished","fadeInControls","loop","cancelAnimation","stopAnimation","onPaused","newPlayerState","toggleControls","TouchableWithoutFeedback","accessible"],"mappings":"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"} -------------------------------------------------------------------------------- /example/react-native-media-controls/react-native-media-controls.cjs.development.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"react-native-media-controls.cjs.development.js","sources":["../../src/MediaControls.style.ts","../../src/constants/playerStates.ts","../../src/utils.ts","../../src/Controls.tsx","../../src/Slider.tsx","../../src/Toolbar.tsx","../../src/MediaControls.tsx"],"sourcesContent":["import { StyleSheet } from \"react-native\";\n\nconst containerBackgroundColor = \"rgba(45, 59, 62, 0.4)\";\nconst playButtonBorderColor = \"rgba(255,255,255,0.5)\";\nconst white = \"#fff\";\n\nexport default StyleSheet.create({\n container: {\n alignItems: \"center\",\n backgroundColor: containerBackgroundColor,\n bottom: 0,\n flex: 1,\n flexDirection: \"column\",\n justifyContent: \"space-between\",\n left: 0,\n paddingHorizontal: 20,\n paddingVertical: 13,\n position: \"absolute\",\n right: 0,\n top: 0,\n },\n controlsRow: {\n alignItems: \"center\",\n alignSelf: \"stretch\",\n flex: 1,\n justifyContent: \"center\",\n },\n fullScreenContainer: {\n alignItems: \"center\",\n alignSelf: \"stretch\",\n justifyContent: \"center\",\n paddingLeft: 20,\n },\n playButton: {\n alignItems: \"center\",\n borderColor: playButtonBorderColor,\n borderRadius: 3,\n borderWidth: 1.5,\n height: 50,\n justifyContent: \"center\",\n width: 50,\n },\n playIcon: {\n height: 22,\n resizeMode: \"contain\",\n width: 22,\n },\n progressColumnContainer: {\n flex: 1,\n },\n progressContainer: {\n flexDirection: \"row\",\n justifyContent: \"flex-end\",\n marginBottom: -25,\n },\n progressSlider: {\n alignSelf: \"stretch\",\n },\n replayIcon: {\n height: 20,\n resizeMode: \"stretch\",\n width: 25,\n },\n thumb: {\n backgroundColor: white,\n borderRadius: 50,\n borderWidth: 3,\n height: 20,\n width: 20,\n },\n timeRow: {\n alignSelf: \"stretch\",\n },\n timerLabel: {\n color: white,\n fontSize: 12,\n },\n timerLabelsContainer: {\n alignSelf: \"stretch\",\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n marginBottom: -7,\n },\n toolbar: {\n flexDirection: \"row\",\n flex: 1,\n justifyContent: \"flex-end\",\n },\n toolbarRow: {\n alignItems: \"flex-start\",\n flexDirection: \"row\",\n justifyContent: \"flex-start\",\n },\n track: {\n borderRadius: 1,\n height: 5,\n },\n});\n","enum PLAYER_STATES {\n PLAYING = 0,\n PAUSED = 1,\n ENDED = 2,\n}\n\nexport { PLAYER_STATES };\n","import { PLAYER_STATES } from \"./constants/playerStates\";\n\nexport const humanizeVideoDuration = (seconds: number) => {\n const [begin, end] = seconds >= 3600 ? [11, 8] : [14, 5];\n const date = new Date(0);\n\n date.setSeconds(seconds);\n return date.toISOString().substr(begin, end);\n};\n\nexport const noop = () => {};\n\nexport const getPlayerStateIcon = (playerState: PLAYER_STATES) => {\n switch (playerState) {\n case PLAYER_STATES.PAUSED:\n return require(\"./assets/ic_play.png\");\n case PLAYER_STATES.PLAYING:\n return require(\"./assets/ic_pause.png\");\n case PLAYER_STATES.ENDED:\n return require(\"./assets/ic_replay.png\");\n default:\n return null;\n }\n};\n","import React from \"react\";\nimport { TouchableOpacity, View, ActivityIndicator, Image } from \"react-native\";\nimport styles from \"./MediaControls.style\";\nimport { getPlayerStateIcon } from \"./utils\";\nimport { Props } from \"./MediaControls\";\nimport { PLAYER_STATES } from \"./constants/playerStates\";\n\ntype ControlsProps = Pick<\n Props,\n \"isLoading\" | \"mainColor\" | \"playerState\" | \"onReplay\"\n> & {\n onPause: () => void;\n};\n\nconst Controls = (props: ControlsProps) => {\n const { isLoading, mainColor, playerState, onReplay, onPause } = props;\n const icon = getPlayerStateIcon(playerState);\n const pressAction = playerState === PLAYER_STATES.ENDED ? onReplay : onPause;\n\n const content = isLoading ? (\n \n ) : (\n \n \n \n );\n\n return {content};\n};\n\nexport { Controls };\n","import React from \"react\";\nimport { TouchableOpacity, View, Text, Image, ViewStyle } from \"react-native\";\nimport RNSlider from \"react-native-slider\";\nimport styles from \"./MediaControls.style\";\nimport { humanizeVideoDuration } from \"./utils\";\nimport { Props as MediaControlsProps } from \"./MediaControls\";\nimport { PLAYER_STATES } from \"./constants/playerStates\";\n\nexport type CustomSliderStyle = {\n containerStyle: ViewStyle;\n trackStyle: ViewStyle;\n thumbStyle: ViewStyle;\n};\n\ntype Props = Pick<\n MediaControlsProps,\n | \"progress\"\n | \"duration\"\n | \"mainColor\"\n | \"onFullScreen\"\n | \"playerState\"\n | \"onSeek\"\n | \"onSeeking\"\n> & {\n onPause: () => void;\n customSliderStyle?: CustomSliderStyle;\n};\n\nconst fullScreenImage = require(\"./assets/ic_fullscreen.png\");\n\nconst Slider = (props: Props) => {\n const {\n customSliderStyle,\n duration,\n mainColor,\n onFullScreen,\n onPause,\n progress,\n } = props;\n\n const containerStyle = customSliderStyle?.containerStyle || {};\n const customTrackStyle = customSliderStyle?.trackStyle || {};\n const customThumbStyle = customSliderStyle?.thumbStyle || {};\n\n const dragging = (value: number) => {\n const { onSeeking, playerState } = props;\n onSeeking(value);\n\n if (playerState === PLAYER_STATES.PAUSED) {\n return;\n }\n\n onPause();\n };\n\n const seekVideo = (value: number) => {\n props.onSeek(value);\n onPause();\n };\n\n return (\n \n \n \n \n {humanizeVideoDuration(progress)}\n \n \n {humanizeVideoDuration(duration)}\n \n \n \n \n {Boolean(onFullScreen) && (\n \n \n \n )}\n \n );\n};\n\nexport { Slider };\n","import React from \"react\";\n\ntype Props = {\n children: React.ReactNode;\n};\n\nconst Toolbar = ({ children }: Props) => <>{children};\n\nexport { Toolbar };\n","import React, { useState, useEffect } from \"react\";\nimport {\n View,\n Animated,\n TouchableWithoutFeedback,\n GestureResponderEvent,\n ViewStyle,\n} from \"react-native\";\nimport styles from \"./MediaControls.style\";\nimport { PLAYER_STATES } from \"./constants/playerStates\";\nimport { Controls } from \"./Controls\";\nimport { Slider, CustomSliderStyle } from \"./Slider\";\nimport { Toolbar } from \"./Toolbar\";\n\nexport type Props = {\n children: React.ReactNode;\n containerStyle: ViewStyle;\n duration: number;\n fadeOutDelay?: number;\n isFullScreen: boolean;\n isLoading: boolean;\n mainColor: string;\n onFullScreen?: (event: GestureResponderEvent) => void;\n onPaused: (playerState: PLAYER_STATES) => void;\n onReplay: () => void;\n onSeek: (value: number) => void;\n onSeeking: (value: number) => void;\n playerState: PLAYER_STATES;\n progress: number;\n showOnStart?: boolean;\n sliderStyle: CustomSliderStyle;\n toolbarStyle: ViewStyle;\n};\n\nconst MediaControls = (props: Props) => {\n const {\n children,\n containerStyle: customContainerStyle = {},\n duration,\n fadeOutDelay = 5000,\n isLoading = false,\n mainColor = \"rgba(12, 83, 175, 0.9)\",\n onFullScreen,\n onReplay: onReplayCallback,\n onSeek,\n onSeeking,\n playerState,\n progress,\n showOnStart = true,\n sliderStyle, // defaults are applied in Slider.tsx\n toolbarStyle: customToolbarStyle = {},\n } = props;\n const { initialOpacity, initialIsVisible } = (() => {\n if (showOnStart) {\n return {\n initialOpacity: 1,\n initialIsVisible: true,\n };\n }\n\n return {\n initialOpacity: 0,\n initialIsVisible: false,\n };\n })();\n\n const [opacity] = useState(new Animated.Value(initialOpacity));\n const [isVisible, setIsVisible] = useState(initialIsVisible);\n\n useEffect(() => {\n fadeOutControls(fadeOutDelay);\n }, []);\n\n const fadeOutControls = (delay = 0) => {\n Animated.timing(opacity, {\n toValue: 0,\n duration: 300,\n delay,\n useNativeDriver: false,\n }).start(result => {\n /* I noticed that the callback is called twice, when it is invoked and when it completely finished\n This prevents some flickering */\n if (result.finished) {\n setIsVisible(false);\n }\n });\n };\n\n const fadeInControls = (loop = true) => {\n setIsVisible(true);\n Animated.timing(opacity, {\n toValue: 1,\n duration: 300,\n delay: 0,\n useNativeDriver: false,\n }).start(() => {\n if (loop) {\n fadeOutControls(fadeOutDelay);\n }\n });\n };\n\n const onReplay = () => {\n fadeOutControls(fadeOutDelay);\n onReplayCallback();\n };\n\n const cancelAnimation = () => opacity.stopAnimation(() => setIsVisible(true));\n\n const onPause = () => {\n const { playerState, onPaused } = props;\n const { PLAYING, PAUSED, ENDED } = PLAYER_STATES;\n switch (playerState) {\n case PLAYING: {\n cancelAnimation();\n break;\n }\n case PAUSED: {\n fadeOutControls(fadeOutDelay);\n break;\n }\n case ENDED:\n break;\n }\n\n const newPlayerState = playerState === PLAYING ? PAUSED : PLAYING;\n return onPaused(newPlayerState);\n };\n\n const toggleControls = () => {\n // value is the last value of the animation when stop animation was called.\n // As this is an opacity effect, I (Charlie) used the value (0 or 1) as a boolean\n opacity.stopAnimation((value: number) => {\n setIsVisible(!!value);\n return value ? fadeOutControls() : fadeInControls();\n });\n };\n\n return (\n \n \n {isVisible && (\n \n \n {children}\n \n \n \n \n )}\n \n \n );\n};\n\nMediaControls.Toolbar = Toolbar;\n\nexport default MediaControls;\n"],"names":["containerBackgroundColor","playButtonBorderColor","white","StyleSheet","create","container","alignItems","backgroundColor","bottom","flex","flexDirection","justifyContent","left","paddingHorizontal","paddingVertical","position","right","top","controlsRow","alignSelf","fullScreenContainer","paddingLeft","playButton","borderColor","borderRadius","borderWidth","height","width","playIcon","resizeMode","progressColumnContainer","progressContainer","marginBottom","progressSlider","replayIcon","thumb","timeRow","timerLabel","color","fontSize","timerLabelsContainer","toolbar","toolbarRow","track","PLAYER_STATES","humanizeVideoDuration","seconds","begin","end","date","Date","setSeconds","toISOString","substr","getPlayerStateIcon","playerState","PAUSED","require","PLAYING","ENDED","Controls","props","isLoading","mainColor","onReplay","onPause","icon","pressAction","content","React","ActivityIndicator","size","TouchableOpacity","style","styles","onPress","accessibilityLabel","accessibilityHint","Image","source","View","fullScreenImage","Slider","customSliderStyle","duration","onFullScreen","progress","containerStyle","customTrackStyle","trackStyle","customThumbStyle","thumbStyle","dragging","value","onSeeking","seekVideo","onSeek","Text","RNSlider","onValueChange","onSlidingComplete","maximumValue","Math","floor","minimumTrackTintColor","Boolean","Toolbar","children","MediaControls","customContainerStyle","fadeOutDelay","onReplayCallback","showOnStart","sliderStyle","toolbarStyle","customToolbarStyle","initialOpacity","initialIsVisible","useState","Animated","Value","opacity","isVisible","setIsVisible","useEffect","fadeOutControls","delay","timing","toValue","useNativeDriver","start","result","finished","fadeInControls","loop","cancelAnimation","stopAnimation","onPaused","newPlayerState","toggleControls","TouchableWithoutFeedback","accessible"],"mappings":";;;;;;;;;;;AAEA,IAAMA,wBAAwB,GAAG,uBAAjC;AACA,IAAMC,qBAAqB,GAAG,uBAA9B;AACA,IAAMC,KAAK,GAAG,MAAd;AAEA,0BAAeC,sBAAU,CAACC,MAAX,CAAkB;AAC/BC,EAAAA,SAAS,EAAE;AACTC,IAAAA,UAAU,EAAE,QADH;AAETC,IAAAA,eAAe,EAAEP,wBAFR;AAGTQ,IAAAA,MAAM,EAAE,CAHC;AAITC,IAAAA,IAAI,EAAE,CAJG;AAKTC,IAAAA,aAAa,EAAE,QALN;AAMTC,IAAAA,cAAc,EAAE,eANP;AAOTC,IAAAA,IAAI,EAAE,CAPG;AAQTC,IAAAA,iBAAiB,EAAE,EARV;AASTC,IAAAA,eAAe,EAAE,EATR;AAUTC,IAAAA,QAAQ,EAAE,UAVD;AAWTC,IAAAA,KAAK,EAAE,CAXE;AAYTC,IAAAA,GAAG,EAAE;AAZI,GADoB;AAe/BC,EAAAA,WAAW,EAAE;AACXZ,IAAAA,UAAU,EAAE,QADD;AAEXa,IAAAA,SAAS,EAAE,SAFA;AAGXV,IAAAA,IAAI,EAAE,CAHK;AAIXE,IAAAA,cAAc,EAAE;AAJL,GAfkB;AAqB/BS,EAAAA,mBAAmB,EAAE;AACnBd,IAAAA,UAAU,EAAE,QADO;AAEnBa,IAAAA,SAAS,EAAE,SAFQ;AAGnBR,IAAAA,cAAc,EAAE,QAHG;AAInBU,IAAAA,WAAW,EAAE;AAJM,GArBU;AA2B/BC,EAAAA,UAAU,EAAE;AACVhB,IAAAA,UAAU,EAAE,QADF;AAEViB,IAAAA,WAAW,EAAEtB,qBAFH;AAGVuB,IAAAA,YAAY,EAAE,CAHJ;AAIVC,IAAAA,WAAW,EAAE,GAJH;AAKVC,IAAAA,MAAM,EAAE,EALE;AAMVf,IAAAA,cAAc,EAAE,QANN;AAOVgB,IAAAA,KAAK,EAAE;AAPG,GA3BmB;AAoC/BC,EAAAA,QAAQ,EAAE;AACRF,IAAAA,MAAM,EAAE,EADA;AAERG,IAAAA,UAAU,EAAE,SAFJ;AAGRF,IAAAA,KAAK,EAAE;AAHC,GApCqB;AAyC/BG,EAAAA,uBAAuB,EAAE;AACvBrB,IAAAA,IAAI,EAAE;AADiB,GAzCM;AA4C/BsB,EAAAA,iBAAiB,EAAE;AACjBrB,IAAAA,aAAa,EAAE,KADE;AAEjBC,IAAAA,cAAc,EAAE,UAFC;AAGjBqB,IAAAA,YAAY,EAAE,CAAC;AAHE,GA5CY;AAiD/BC,EAAAA,cAAc,EAAE;AACdd,IAAAA,SAAS,EAAE;AADG,GAjDe;AAoD/Be,EAAAA,UAAU,EAAE;AACVR,IAAAA,MAAM,EAAE,EADE;AAEVG,IAAAA,UAAU,EAAE,SAFF;AAGVF,IAAAA,KAAK,EAAE;AAHG,GApDmB;AAyD/BQ,EAAAA,KAAK,EAAE;AACL5B,IAAAA,eAAe,EAAEL,KADZ;AAELsB,IAAAA,YAAY,EAAE,EAFT;AAGLC,IAAAA,WAAW,EAAE,CAHR;AAILC,IAAAA,MAAM,EAAE,EAJH;AAKLC,IAAAA,KAAK,EAAE;AALF,GAzDwB;AAgE/BS,EAAAA,OAAO,EAAE;AACPjB,IAAAA,SAAS,EAAE;AADJ,GAhEsB;AAmE/BkB,EAAAA,UAAU,EAAE;AACVC,IAAAA,KAAK,EAAEpC,KADG;AAEVqC,IAAAA,QAAQ,EAAE;AAFA,GAnEmB;AAuE/BC,EAAAA,oBAAoB,EAAE;AACpBrB,IAAAA,SAAS,EAAE,SADS;AAEpBT,IAAAA,aAAa,EAAE,KAFK;AAGpBC,IAAAA,cAAc,EAAE,eAHI;AAIpBqB,IAAAA,YAAY,EAAE,CAAC;AAJK,GAvES;AA6E/BS,EAAAA,OAAO,EAAE;AACP/B,IAAAA,aAAa,EAAE,KADR;AAEPD,IAAAA,IAAI,EAAE,CAFC;AAGPE,IAAAA,cAAc,EAAE;AAHT,GA7EsB;AAkF/B+B,EAAAA,UAAU,EAAE;AACVpC,IAAAA,UAAU,EAAE,YADF;AAEVI,IAAAA,aAAa,EAAE,KAFL;AAGVC,IAAAA,cAAc,EAAE;AAHN,GAlFmB;AAuF/BgC,EAAAA,KAAK,EAAE;AACLnB,IAAAA,YAAY,EAAE,CADT;AAELE,IAAAA,MAAM,EAAE;AAFH;AAvFwB,CAAlB,CAAf;;ACNA,WAAKkB;AACHA,EAAAA,2CAAA,YAAA;AACAA,EAAAA,0CAAA,WAAA;AACAA,EAAAA,yCAAA,UAAA;AACD,CAJD,EAAKA,qBAAa,KAAbA,qBAAa,KAAA,CAAlB;;ACEO,IAAMC,qBAAqB,GAAG,SAAxBA,qBAAwB,CAACC,OAAD;aACdA,OAAO,IAAI,IAAX,GAAkB,CAAC,EAAD,EAAK,CAAL,CAAlB,GAA4B,CAAC,EAAD,EAAK,CAAL;MAA1CC;MAAOC;;AACd,MAAMC,IAAI,GAAG,IAAIC,IAAJ,CAAS,CAAT,CAAb;AAEAD,EAAAA,IAAI,CAACE,UAAL,CAAgBL,OAAhB;AACA,SAAOG,IAAI,CAACG,WAAL,GAAmBC,MAAnB,CAA0BN,KAA1B,EAAiCC,GAAjC,CAAP;AACD,CANM;AAQP,AAEO,IAAMM,kBAAkB,GAAG,SAArBA,kBAAqB,CAACC,WAAD;AAChC,UAAQA,WAAR;AACE,SAAKX,qBAAa,CAACY,MAAnB;AACE,aAAOC,OAAO,CAAC,sBAAD,CAAd;;AACF,SAAKb,qBAAa,CAACc,OAAnB;AACE,aAAOD,OAAO,CAAC,uBAAD,CAAd;;AACF,SAAKb,qBAAa,CAACe,KAAnB;AACE,aAAOF,OAAO,CAAC,wBAAD,CAAd;;AACF;AACE,aAAO,IAAP;AARJ;AAUD,CAXM;;ACEP,IAAMG,QAAQ,GAAG,SAAXA,QAAW,CAACC,KAAD;MACPC,YAAyDD,MAAzDC;MAAWC,YAA8CF,MAA9CE;MAAWR,cAAmCM,MAAnCN;MAAaS,WAAsBH,MAAtBG;MAAUC,UAAYJ,MAAZI;AACrD,MAAMC,IAAI,GAAGZ,kBAAkB,CAACC,WAAD,CAA/B;AACA,MAAMY,WAAW,GAAGZ,WAAW,KAAKX,qBAAa,CAACe,KAA9B,GAAsCK,QAAtC,GAAiDC,OAArE;AAEA,MAAMG,OAAO,GAAGN,SAAS,GACvBO,4BAAA,CAACC,6BAAD;AAAmBC,IAAAA,IAAI,EAAC;AAAQjC,IAAAA,KAAK,EAAC;GAAtC,CADuB,GAGvB+B,4BAAA,CAACG,4BAAD;AACEC,IAAAA,KAAK,EAAE,CAACC,MAAM,CAACpD,UAAR,EAAoB;AAAEf,MAAAA,eAAe,EAAEwD;AAAnB,KAApB;AACPY,IAAAA,OAAO,EAAER;AACTS,IAAAA,kBAAkB,EAAEhC,qBAAa,CAACY,MAAd,GAAuB,aAAvB,GAAuC;AAC3DqB,IAAAA,iBAAiB,EAAE;GAJrB,EAMER,4BAAA,CAACS,iBAAD;AAAOC,IAAAA,MAAM,EAAEb;AAAMO,IAAAA,KAAK,EAAEC,MAAM,CAAC9C;GAAnC,CANF,CAHF;AAaA,SAAOyC,4BAAA,CAACW,gBAAD;AAAMP,IAAAA,KAAK,EAAE,CAACC,MAAM,CAACxD,WAAR;GAAb,EAAoCkD,OAApC,CAAP;AACD,CAnBD;;ACcA,IAAMa,eAAe,gBAAGxB,OAAO,CAAC,4BAAD,CAA/B;;AAEA,IAAMyB,MAAM,GAAG,SAATA,MAAS,CAACrB,KAAD;MAEXsB,oBAMEtB,MANFsB;MACAC,WAKEvB,MALFuB;MACArB,YAIEF,MAJFE;MACAsB,eAGExB,MAHFwB;MACApB,UAEEJ,MAFFI;MACAqB,WACEzB,MADFyB;AAGF,MAAMC,cAAc,GAAG,CAAAJ,iBAAiB,SAAjB,IAAAA,iBAAiB,WAAjB,YAAAA,iBAAiB,CAAEI,cAAnB,KAAqC,EAA5D;AACA,MAAMC,gBAAgB,GAAG,CAAAL,iBAAiB,SAAjB,IAAAA,iBAAiB,WAAjB,YAAAA,iBAAiB,CAAEM,UAAnB,KAAiC,EAA1D;AACA,MAAMC,gBAAgB,GAAG,CAAAP,iBAAiB,SAAjB,IAAAA,iBAAiB,WAAjB,YAAAA,iBAAiB,CAAEQ,UAAnB,KAAiC,EAA1D;;AAEA,MAAMC,QAAQ,GAAG,SAAXA,QAAW,CAACC,KAAD;QACPC,YAA2BjC,MAA3BiC;QAAWvC,cAAgBM,MAAhBN;AACnBuC,IAAAA,SAAS,CAACD,KAAD,CAAT;;AAEA,QAAItC,WAAW,KAAKX,qBAAa,CAACY,MAAlC,EAA0C;AACxC;AACD;;AAEDS,IAAAA,OAAO;AACR,GATD;;AAWA,MAAM8B,SAAS,GAAG,SAAZA,SAAY,CAACF,KAAD;AAChBhC,IAAAA,KAAK,CAACmC,MAAN,CAAaH,KAAb;AACA5B,IAAAA,OAAO;AACR,GAHD;;AAKA,SACEI,4BAAA,CAACW,gBAAD;AACEP,IAAAA,KAAK,EAAE,CAACC,MAAM,CAACxD,WAAR,EAAqBwD,MAAM,CAAC3C,iBAA5B,EAA+CwD,cAA/C;GADT,EAGElB,4BAAA,CAACW,gBAAD;AAAMP,IAAAA,KAAK,EAAEC,MAAM,CAAC5C;GAApB,EACEuC,4BAAA,CAACW,gBAAD;AAAMP,IAAAA,KAAK,EAAE,CAACC,MAAM,CAAClC,oBAAR;GAAb,EACE6B,4BAAA,CAAC4B,gBAAD;AAAMxB,IAAAA,KAAK,EAAEC,MAAM,CAACrC;GAApB,EACGQ,qBAAqB,CAACyC,QAAD,CADxB,CADF,EAIEjB,4BAAA,CAAC4B,gBAAD;AAAMxB,IAAAA,KAAK,EAAEC,MAAM,CAACrC;GAApB,EACGQ,qBAAqB,CAACuC,QAAD,CADxB,CAJF,CADF,EASEf,4BAAA,CAAC6B,QAAD;AACEzB,IAAAA,KAAK,EAAE,CAACC,MAAM,CAACzC,cAAR;AACPkE,IAAAA,aAAa,EAAEP;AACfQ,IAAAA,iBAAiB,EAAEL;AACnBM,IAAAA,YAAY,EAAEC,IAAI,CAACC,KAAL,CAAWnB,QAAX;AACdS,IAAAA,KAAK,EAAES,IAAI,CAACC,KAAL,CAAWjB,QAAX;AACPG,IAAAA,UAAU,EAAE,CAACf,MAAM,CAAC/B,KAAR,EAAe6C,gBAAf;AACZG,IAAAA,UAAU,EAAE,CACVjB,MAAM,CAACvC,KADG,EAEVuD,gBAFU,EAGV;AAAEnE,MAAAA,WAAW,EAAEwC;AAAf,KAHU;AAKZyC,IAAAA,qBAAqB,EAAEzC;GAZzB,CATF,CAHF,EA2BG0C,OAAO,CAACpB,YAAD,CAAP,IACChB,4BAAA,CAACG,4BAAD;AACEC,IAAAA,KAAK,EAAEC,MAAM,CAACtD;AACduD,IAAAA,OAAO,EAAEU;GAFX,EAIEhB,4BAAA,CAACS,iBAAD;AAAOC,IAAAA,MAAM,EAAEE;GAAf,CAJF,CA5BJ,CADF;AAsCD,CApED;;ACxBA,IAAMyB,OAAO,GAAG,SAAVA,OAAU;AAAA,MAAGC,QAAH,QAAGA,QAAH;AAAA,SAAyBtC,4BAAA,wBAAA,MAAA,EAAGsC,QAAH,CAAzB;AAAA,CAAhB;;AC4BA,IAAMC,aAAa,GAAG,SAAhBA,aAAgB,CAAC/C,KAAD;MAElB8C,WAeE9C,MAfF8C;8BAeE9C,MAdF0B;MAAgBsB,0DAAuB;MACvCzB,WAaEvB,MAbFuB;4BAaEvB,MAZFiD;MAAAA,gDAAe;yBAYbjD,MAXFC;MAAAA,0CAAY;yBAWVD,MAVFE;MAAAA,0CAAY;MACZsB,eASExB,MATFwB;MACU0B,mBAQRlD,MARFG;MACAgC,SAOEnC,MAPFmC;MACAF,YAMEjC,MANFiC;MACAvC,cAKEM,MALFN;MACA+B,WAIEzB,MAJFyB;2BAIEzB,MAHFmD;MAAAA,8CAAc;MACdC,cAEEpD,MAFFoD;4BAEEpD,MADFqD;MAAcC,sDAAqB;;aAES;AAC5C,QAAIH,WAAJ,EAAiB;AACf,aAAO;AACLI,QAAAA,cAAc,EAAE,CADX;AAELC,QAAAA,gBAAgB,EAAE;AAFb,OAAP;AAID;;AAED,WAAO;AACLD,MAAAA,cAAc,EAAE,CADX;AAELC,MAAAA,gBAAgB,EAAE;AAFb,KAAP;AAID,GAZ4C;MAArCD,sBAAAA;MAAgBC,wBAAAA;;kBAcNC,cAAQ,CAAC,IAAIC,oBAAQ,CAACC,KAAb,CAAmBJ,cAAnB,CAAD;MAAnBK;;mBAC2BH,cAAQ,CAACD,gBAAD;MAAnCK;MAAWC;;AAElBC,EAAAA,eAAS,CAAC;AACRC,IAAAA,eAAe,CAACf,YAAD,CAAf;AACD,GAFQ,EAEN,EAFM,CAAT;;AAIA,MAAMe,eAAe,GAAG,SAAlBA,eAAkB,CAACC,KAAD;QAACA;AAAAA,MAAAA,QAAQ;;;AAC/BP,IAAAA,oBAAQ,CAACQ,MAAT,CAAgBN,OAAhB,EAAyB;AACvBO,MAAAA,OAAO,EAAE,CADc;AAEvB5C,MAAAA,QAAQ,EAAE,GAFa;AAGvB0C,MAAAA,KAAK,EAALA,KAHuB;AAIvBG,MAAAA,eAAe,EAAE;AAJM,KAAzB,EAKGC,KALH,CAKS,UAAAC,MAAM;AACb;;AAEA,UAAIA,MAAM,CAACC,QAAX,EAAqB;AACnBT,QAAAA,YAAY,CAAC,KAAD,CAAZ;AACD;AACF,KAXD;AAYD,GAbD;;AAeA,MAAMU,cAAc,GAAG,SAAjBA,cAAiB,CAACC,IAAD;QAACA;AAAAA,MAAAA,OAAO;;;AAC7BX,IAAAA,YAAY,CAAC,IAAD,CAAZ;AACAJ,IAAAA,oBAAQ,CAACQ,MAAT,CAAgBN,OAAhB,EAAyB;AACvBO,MAAAA,OAAO,EAAE,CADc;AAEvB5C,MAAAA,QAAQ,EAAE,GAFa;AAGvB0C,MAAAA,KAAK,EAAE,CAHgB;AAIvBG,MAAAA,eAAe,EAAE;AAJM,KAAzB,EAKGC,KALH,CAKS;AACP,UAAII,IAAJ,EAAU;AACRT,QAAAA,eAAe,CAACf,YAAD,CAAf;AACD;AACF,KATD;AAUD,GAZD;;AAcA,MAAM9C,QAAQ,GAAG,SAAXA,QAAW;AACf6D,IAAAA,eAAe,CAACf,YAAD,CAAf;AACAC,IAAAA,gBAAgB;AACjB,GAHD;;AAKA,MAAMwB,eAAe,GAAG,SAAlBA,eAAkB;AAAA,WAAMd,OAAO,CAACe,aAAR,CAAsB;AAAA,aAAMb,YAAY,CAAC,IAAD,CAAlB;AAAA,KAAtB,CAAN;AAAA,GAAxB;;AAEA,MAAM1D,OAAO,GAAG,SAAVA,OAAU;QACNV,cAA0BM,MAA1BN;QAAakF,WAAa5E,MAAb4E;QACb/E,UAA2Bd,sBAA3Bc;QAASF,SAAkBZ,sBAAlBY;;AACjB,YAAQD,WAAR;AACE,WAAKG,OAAL;AAAc;AACZ6E,UAAAA,eAAe;AACf;AACD;;AACD,WAAK/E,MAAL;AAAa;AACXqE,UAAAA,eAAe,CAACf,YAAD,CAAf;AACA;AACD;AARH;;AAaA,QAAM4B,cAAc,GAAGnF,WAAW,KAAKG,OAAhB,GAA0BF,MAA1B,GAAmCE,OAA1D;AACA,WAAO+E,QAAQ,CAACC,cAAD,CAAf;AACD,GAlBD;;AAoBA,MAAMC,cAAc,GAAG,SAAjBA,cAAiB;AACrB;AACA;AACAlB,IAAAA,OAAO,CAACe,aAAR,CAAsB,UAAC3C,KAAD;AACpB8B,MAAAA,YAAY,CAAC,CAAC,CAAC9B,KAAH,CAAZ;AACA,aAAOA,KAAK,GAAGgC,eAAe,EAAlB,GAAuBQ,cAAc,EAAjD;AACD,KAHD;AAID,GAPD;;AASA,SACEhE,4BAAA,CAACuE,oCAAD;AAA0BC,IAAAA,UAAU,EAAE;AAAOlE,IAAAA,OAAO,EAAEgE;GAAtD,EACEtE,4BAAA,CAACkD,oBAAQ,CAACvC,IAAV;AAAeP,IAAAA,KAAK,EAAE,CAACC,MAAM,CAACrE,SAAR,EAAmB;AAAEoH,MAAAA,OAAO,EAAPA;AAAF,KAAnB;GAAtB,EACGC,SAAS,IACRrD,4BAAA,CAACW,gBAAD;AAAMP,IAAAA,KAAK,EAAE,CAACC,MAAM,CAACrE,SAAR,EAAmBwG,oBAAnB;GAAb,EACExC,4BAAA,CAACW,gBAAD;AACEP,IAAAA,KAAK,EAAE,CACLC,MAAM,CAACxD,WADF,EAELwD,MAAM,CAAChC,UAFF,EAGLyE,kBAHK;GADT,EAOGR,QAPH,CADF,EAUEtC,4BAAA,CAACT,QAAD;AACEK,IAAAA,OAAO,EAAEA;AACTD,IAAAA,QAAQ,EAAEA;AACVF,IAAAA,SAAS,EAAEA;AACXC,IAAAA,SAAS,EAAEA;AACXR,IAAAA,WAAW,EAAEA;GALf,CAVF,EAiBEc,4BAAA,CAACa,MAAD;AACEI,IAAAA,QAAQ,EAAEA;AACVF,IAAAA,QAAQ,EAAEA;AACVrB,IAAAA,SAAS,EAAEA;AACXsB,IAAAA,YAAY,EAAEA;AACd9B,IAAAA,WAAW,EAAEA;AACbyC,IAAAA,MAAM,EAAEA;AACRF,IAAAA,SAAS,EAAEA;AACX7B,IAAAA,OAAO,EAAEA;AACTkB,IAAAA,iBAAiB,EAAE8B;GATrB,CAjBF,CAFJ,CADF,CADF;AAqCD,CA7ID;;AA+IAL,aAAa,CAACF,OAAd,GAAwBA,OAAxB;;;;"} --------------------------------------------------------------------------------