chore: rework examples (#4225)
* remove unused examples * init bare example with test app * add react-native-video * add test app suport in expo plugin * expo plugin: skip keys that are already in pod file * fix podfile * add src files * fix metro config * finalize react native test app configuration * init expo example * remove old examples * add guide for example * Add link to examples apps in docs * adopt bare example to CI tests * update CI workflows * CI build lib after node_modules install * fix examples readme * fix iOS CI * Add Example for DRM * Update examples/README.md * fix links * update examples README * sync example code * update README
14
examples/expo/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
node_modules/
|
||||
.expo/
|
||||
dist/
|
||||
npm-debug.*
|
||||
*.jks
|
||||
*.p8
|
||||
*.p12
|
||||
*.key
|
||||
*.mobileprovision
|
||||
*.orig.*
|
||||
web-build/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
6
examples/expo/App.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
import BasicExample from './src/BasicExample';
|
||||
|
||||
export default function App() {
|
||||
return <BasicExample />;
|
||||
}
|
64
examples/expo/App.tv.tsx
Normal file
34
examples/expo/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# TV Example
|
||||
|
||||
Creates a project that can build for Apple TV and Android TV targets.
|
||||
|
||||
This project uses
|
||||
|
||||
- the [React Native TV fork](https://github.com/react-native-tvos/react-native-tvos), which supports both phone (Android and iOS) and TV (Android TV and Apple TV) targets
|
||||
- the [React Native TV config plugin](https://github.com/react-native-tvos/config-tv/tree/main/packages/config-tv) to allow Expo prebuild to modify the project's native files for TV builds
|
||||
|
||||
## 🚀 How to use
|
||||
|
||||
#### Creating a new project
|
||||
|
||||
- Create a project: `npx create-expo-app -e with-tv`
|
||||
- `cd` into the project
|
||||
|
||||
```sh
|
||||
export EXPO_TV=1
|
||||
npx expo prebuild
|
||||
yarn ios # Build for Apple TV
|
||||
yarn android # Build for Android TV
|
||||
```
|
||||
|
||||
> **_NOTE:_**
|
||||
> Setting the environment variable `EXPO_TV=1` enables the `@react-native-tvos/config-tv` plugin to modify the project for TV.
|
||||
> This can also be done by setting the parameter `isTV` to true in the `app.json`.
|
||||
|
||||
#### TV specific file extensions
|
||||
|
||||
This project contains an [example Metro configuration](./metro.config.js) that allows Metro to resolve application source files with TV-specific code, indicated by specific file extensions (`*.ios.tv.tsx`, `*.android.tv.tsx`, `*.tv.tsx`). This config is not enabled by default, since it will impact bundling performance, but is available for developers who need this capability.
|
||||
|
||||
#### TV specific app icons and banners
|
||||
|
||||
This project contains placeholder images for the Android TV banner and for Apple TV brand assets (app icon and top shelf images). The `config-tv` plugin will use these images to construct the required native image files and make the right modifications in project files. You can simply replace these images with your own app images. Note that for Apple TV, the images must be the exact sizes indicated.
|
16
examples/expo/android/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
*.hprof
|
||||
.cxx/
|
||||
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
204
examples/expo/android/app/build.gradle
Normal file
@@ -0,0 +1,204 @@
|
||||
apply plugin: "com.android.application"
|
||||
apply plugin: "org.jetbrains.kotlin.android"
|
||||
apply plugin: "com.facebook.react"
|
||||
|
||||
def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
|
||||
|
||||
static def versionToNumber(major, minor, patch) {
|
||||
return patch * 100 + minor * 10000 + major * 1000000
|
||||
}
|
||||
|
||||
def getRNVersion() {
|
||||
def version = providers.exec {
|
||||
workingDir(projectDir)
|
||||
commandLine("node", "-e", "console.log(require('react-native/package.json').version);")
|
||||
}.standardOutput.asText.get().trim()
|
||||
|
||||
def coreVersion = version.split("-")[0]
|
||||
def (major, minor, patch) = coreVersion.tokenize('.').collect { it.toInteger() }
|
||||
|
||||
return versionToNumber(
|
||||
major,
|
||||
minor,
|
||||
patch
|
||||
)
|
||||
}
|
||||
def rnVersion = getRNVersion()
|
||||
|
||||
/**
|
||||
* This is the configuration block to customize your React Native Android app.
|
||||
* By default you don't need to apply any configuration, just uncomment the lines you need.
|
||||
*/
|
||||
react {
|
||||
entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
|
||||
reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
|
||||
hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
|
||||
codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
|
||||
|
||||
// Use Expo CLI to bundle the app, this ensures the Metro config
|
||||
// works correctly with Expo projects.
|
||||
cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim())
|
||||
bundleCommand = "export:embed"
|
||||
|
||||
/* Folders */
|
||||
// The root of your project, i.e. where "package.json" lives. Default is '..'
|
||||
// root = file("../")
|
||||
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
|
||||
// reactNativeDir = file("../node_modules/react-native")
|
||||
// The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
|
||||
// codegenDir = file("../node_modules/@react-native/codegen")
|
||||
|
||||
/* Variants */
|
||||
// The list of variants to that are debuggable. For those we're going to
|
||||
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
|
||||
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
|
||||
// debuggableVariants = ["liteDebug", "prodDebug"]
|
||||
|
||||
/* Bundling */
|
||||
// A list containing the node command and its flags. Default is just 'node'.
|
||||
// nodeExecutableAndArgs = ["node"]
|
||||
|
||||
//
|
||||
// The path to the CLI configuration file. Default is empty.
|
||||
// bundleConfig = file(../rn-cli.config.js)
|
||||
//
|
||||
// The name of the generated asset file containing your JS bundle
|
||||
// bundleAssetName = "MyApplication.android.bundle"
|
||||
//
|
||||
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
|
||||
// entryFile = file("../js/MyApplication.android.js")
|
||||
//
|
||||
// A list of extra flags to pass to the 'bundle' commands.
|
||||
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
|
||||
// extraPackagerArgs = []
|
||||
|
||||
/* Hermes Commands */
|
||||
// The hermes compiler command to run. By default it is 'hermesc'
|
||||
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
|
||||
//
|
||||
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
||||
// hermesFlags = ["-O", "-output-source-map"]
|
||||
|
||||
if (rnVersion >= versionToNumber(0, 75, 0)) {
|
||||
/* Autolinking */
|
||||
autolinkLibrariesWithApp()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean()
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore (JSC)
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
*
|
||||
* The international variant includes ICU i18n library and necessary data
|
||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
compileSdk rootProject.ext.compileSdkVersion
|
||||
|
||||
namespace 'com.anonymous.ExpoExample'
|
||||
defaultConfig {
|
||||
applicationId 'com.anonymous.ExpoExample'
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0.0"
|
||||
}
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('debug.keystore')
|
||||
storePassword 'android'
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://reactnative.dev/docs/signed-apk-android.
|
||||
signingConfig signingConfigs.debug
|
||||
shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false)
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true)
|
||||
}
|
||||
}
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
useLegacyPackaging (findProperty('expo.useLegacyPackaging')?.toBoolean() ?: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply static values from `gradle.properties` to the `android.packagingOptions`
|
||||
// Accepts values in comma delimited lists, example:
|
||||
// android.packagingOptions.pickFirsts=/LICENSE,**/picasa.ini
|
||||
["pickFirsts", "excludes", "merges", "doNotStrip"].each { prop ->
|
||||
// Split option: 'foo,bar' -> ['foo', 'bar']
|
||||
def options = (findProperty("android.packagingOptions.$prop") ?: "").split(",");
|
||||
// Trim all elements in place.
|
||||
for (i in 0..<options.size()) options[i] = options[i].trim();
|
||||
// `[] - ""` is essentially `[""].filter(Boolean)` removing all empty strings.
|
||||
options -= ""
|
||||
|
||||
if (options.length > 0) {
|
||||
println "android.packagingOptions.$prop += $options ($options.length)"
|
||||
// Ex: android.packagingOptions.pickFirsts += '**/SCCS/**'
|
||||
options.each {
|
||||
android.packagingOptions[prop] += it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// The version of react-native is set by the React Native Gradle Plugin
|
||||
implementation("com.facebook.react:react-android")
|
||||
|
||||
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
|
||||
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
|
||||
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
|
||||
|
||||
if (isGifEnabled) {
|
||||
// For animated gif support
|
||||
implementation("com.facebook.fresco:animated-gif:${reactAndroidLibs.versions.fresco.get()}")
|
||||
}
|
||||
|
||||
if (isWebpEnabled) {
|
||||
// For webp support
|
||||
implementation("com.facebook.fresco:webpsupport:${reactAndroidLibs.versions.fresco.get()}")
|
||||
if (isWebpAnimatedEnabled) {
|
||||
// Animated webp support
|
||||
implementation("com.facebook.fresco:animated-webp:${reactAndroidLibs.versions.fresco.get()}")
|
||||
}
|
||||
}
|
||||
|
||||
if (hermesEnabled.toBoolean()) {
|
||||
implementation("com.facebook.react:hermes-android")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
|
||||
implementation project(':react-native-video')
|
||||
implementation project(':react-native-video-plugin-sample')
|
||||
}
|
||||
|
||||
if (rnVersion < versionToNumber(0, 75, 0)) {
|
||||
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
|
||||
applyNativeModulesAppBuildGradle(project)
|
||||
}
|
BIN
examples/expo/android/app/debug.keystore
Normal file
14
examples/expo/android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# react-native-reanimated
|
||||
-keep class com.swmansion.reanimated.** { *; }
|
||||
-keep class com.facebook.react.turbomodule.** { *; }
|
||||
|
||||
# Add any project specific keep options here:
|
7
examples/expo/android/app/src/debug/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" tools:replace="android:usesCleartextTraffic" />
|
||||
</manifest>
|
39
examples/expo/android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="https"/>
|
||||
</intent>
|
||||
</queries>
|
||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme">
|
||||
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
||||
<service android:name="com.brentvatne.exoplayer.VideoPlaybackService" android:exported="false" android:foregroundServiceType="mediaPlayback">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.session.MediaSessionService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="com.anonymous.ExpoExample"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
|
||||
</application>
|
||||
</manifest>
|
@@ -0,0 +1,61 @@
|
||||
package com.anonymous.ExpoExample
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
|
||||
import com.facebook.react.ReactActivity
|
||||
import com.facebook.react.ReactActivityDelegate
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate
|
||||
|
||||
import expo.modules.ReactActivityDelegateWrapper
|
||||
|
||||
class MainActivity : ReactActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Set the theme to AppTheme BEFORE onCreate to support
|
||||
// coloring the background, status bar, and navigation bar.
|
||||
// This is required for expo-splash-screen.
|
||||
setTheme(R.style.AppTheme);
|
||||
super.onCreate(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript. This is used to schedule
|
||||
* rendering of the component.
|
||||
*/
|
||||
override fun getMainComponentName(): String = "main"
|
||||
|
||||
/**
|
||||
* Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
|
||||
* which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
|
||||
*/
|
||||
override fun createReactActivityDelegate(): ReactActivityDelegate {
|
||||
return ReactActivityDelegateWrapper(
|
||||
this,
|
||||
BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
|
||||
object : DefaultReactActivityDelegate(
|
||||
this,
|
||||
mainComponentName,
|
||||
fabricEnabled
|
||||
){})
|
||||
}
|
||||
|
||||
/**
|
||||
* Align the back button behavior with Android S
|
||||
* where moving root activities to background instead of finishing activities.
|
||||
* @see <a href="https://developer.android.com/reference/android/app/Activity#onBackPressed()">onBackPressed</a>
|
||||
*/
|
||||
override fun invokeDefaultOnBackPressed() {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
|
||||
if (!moveTaskToBack(false)) {
|
||||
// For non-root activities, use the default implementation to finish them.
|
||||
super.invokeDefaultOnBackPressed()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Use the default back button implementation on Android S
|
||||
// because it's doing more than [Activity.moveTaskToBack] in fact.
|
||||
super.invokeDefaultOnBackPressed()
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
package com.anonymous.ExpoExample
|
||||
|
||||
import android.app.Application
|
||||
import android.content.res.Configuration
|
||||
|
||||
import com.facebook.react.PackageList
|
||||
import com.facebook.react.ReactApplication
|
||||
import com.facebook.react.ReactNativeHost
|
||||
import com.facebook.react.ReactPackage
|
||||
import com.facebook.react.ReactHost
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost
|
||||
import com.facebook.soloader.SoLoader
|
||||
|
||||
import expo.modules.ApplicationLifecycleDispatcher
|
||||
import expo.modules.ReactNativeHostWrapper
|
||||
|
||||
class MainApplication : Application(), ReactApplication {
|
||||
|
||||
override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
|
||||
this,
|
||||
object : DefaultReactNativeHost(this) {
|
||||
override fun getPackages(): List<ReactPackage> {
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
return PackageList(this).packages
|
||||
}
|
||||
|
||||
override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
|
||||
|
||||
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
|
||||
|
||||
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
||||
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
|
||||
}
|
||||
)
|
||||
|
||||
override val reactHost: ReactHost
|
||||
get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost)
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
SoLoader.init(this, false)
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
load()
|
||||
}
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetTop="@dimen/abc_edit_text_inset_top_material"
|
||||
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material"
|
||||
>
|
||||
|
||||
<selector>
|
||||
<!--
|
||||
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
|
||||
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
|
||||
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
|
||||
|
||||
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||
|
||||
For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
|
||||
-->
|
||||
<item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||
<item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
|
||||
</selector>
|
||||
|
||||
</inset>
|
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/splashscreen_background"/>
|
||||
</layer-list>
|
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1 @@
|
||||
<resources/>
|
4
examples/expo/android/app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<resources>
|
||||
<color name="colorPrimary">#023c69</color>
|
||||
<color name="colorPrimaryDark">#ffffff</color>
|
||||
</resources>
|
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">ExpoExample</string>
|
||||
</resources>
|
17
examples/expo/android/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
<item name="android:editTextStyle">@style/ResetEditText</item>
|
||||
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="android:statusBarColor">#ffffff</item>
|
||||
</style>
|
||||
<style name="ResetEditText" parent="@android:style/Widget.EditText">
|
||||
<item name="android:padding">0dp</item>
|
||||
<item name="android:textColorHint">#c8c8c8</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
<style name="Theme.App.SplashScreen" parent="AppTheme">
|
||||
<item name="android:windowBackground">@drawable/splashscreen</item>
|
||||
</style>
|
||||
</resources>
|
41
examples/expo/android/build.gradle
Normal file
@@ -0,0 +1,41 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = findProperty('android.buildToolsVersion') ?: '34.0.0'
|
||||
minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '23')
|
||||
compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '34')
|
||||
targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34')
|
||||
kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.23'
|
||||
|
||||
ndkVersion = "26.1.10909125"
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath('com.android.tools.build:gradle')
|
||||
classpath('com.facebook.react:react-native-gradle-plugin')
|
||||
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "com.facebook.react.rootproject"
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android'))
|
||||
}
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url(new File(['node', '--print', "require.resolve('jsc-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), '../dist'))
|
||||
}
|
||||
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
64
examples/expo/android/gradle.properties
Normal file
@@ -0,0 +1,64 @@
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
|
||||
# Enable AAPT2 PNG crunching
|
||||
android.enablePngCrunchInReleaseBuilds=true
|
||||
|
||||
# Use this property to specify which architecture you want to build.
|
||||
# You can also override it from the CLI using
|
||||
# ./gradlew <task> -PreactNativeArchitectures=x86_64
|
||||
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
|
||||
# Use this property to enable support to the new architecture.
|
||||
# This will allow you to use TurboModules and the Fabric render in
|
||||
# your application. You should enable this flag either if you want
|
||||
# to write custom TurboModules/Fabric components OR use libraries that
|
||||
# are providing them.
|
||||
newArchEnabled=false
|
||||
|
||||
# Use this property to enable or disable the Hermes JS engine.
|
||||
# If set to false, you will be using JSC instead.
|
||||
hermesEnabled=true
|
||||
|
||||
# Enable GIF support in React Native images (~200 B increase)
|
||||
expo.gif.enabled=true
|
||||
# Enable webp support in React Native images (~85 KB increase)
|
||||
expo.webp.enabled=true
|
||||
# Enable animated webp support (~3.4 MB increase)
|
||||
# Disabled by default because iOS doesn't support animated webp
|
||||
expo.webp.animated=false
|
||||
|
||||
# Enable network inspector
|
||||
EX_DEV_CLIENT_NETWORK_INSPECTOR=true
|
||||
|
||||
# Use legacy packaging to compress native libraries in the resulting APK.
|
||||
expo.useLegacyPackaging=false
|
||||
|
||||
RNVideo_useExoplayerRtsp=true
|
||||
RNVideo_useExoplayerSmoothStreaming=true
|
||||
RNVideo_useExoplayerDash=true
|
||||
RNVideo_useExoplayerHls=true
|
BIN
examples/expo/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
7
examples/expo/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
249
examples/expo/android/gradlew
vendored
Executable file
@@ -0,0 +1,249 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
92
examples/expo/android/gradlew.bat
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
19
examples/expo/android/react-settings-plugin/build.gradle.kts
Normal file
@@ -0,0 +1,19 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.9.24"
|
||||
id("java-gradle-plugin")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("reactSettingsPlugin") {
|
||||
id = "com.facebook.react.settings"
|
||||
implementationClass = "expo.plugins.ReactSettingsPlugin"
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package expo.plugins
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.initialization.Settings
|
||||
|
||||
class ReactSettingsPlugin : Plugin<Settings> {
|
||||
override fun apply(settings: Settings) {
|
||||
// Do nothing, just register the plugin.
|
||||
}
|
||||
}
|
72
examples/expo/android/settings.gradle
Normal file
@@ -0,0 +1,72 @@
|
||||
pluginManagement {
|
||||
def version = providers.exec {
|
||||
commandLine("node", "-e", "console.log(require('react-native/package.json').version);")
|
||||
}.standardOutput.asText.get().trim()
|
||||
def (_, reactNativeMinor, reactNativePatch) = version.split("-")[0].tokenize('.').collect { it.toInteger() }
|
||||
|
||||
includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json')"].execute(null, rootDir).text.trim()).getParentFile().toString())
|
||||
if(reactNativeMinor == 74 && reactNativePatch <= 3){
|
||||
includeBuild("react-settings-plugin")
|
||||
}
|
||||
}
|
||||
|
||||
plugins { id("com.facebook.react.settings") }
|
||||
|
||||
include ':react-native-video-plugin-sample'
|
||||
project (':react-native-video-plugin-sample').projectDir = new File(rootProject.projectDir, '../../react-native-video-plugin-sample/android')
|
||||
|
||||
include ':react-native-video'
|
||||
project (':react-native-video').projectDir = new File(rootProject.projectDir, '../../../android')
|
||||
|
||||
def getRNMinorVersion() {
|
||||
def version = providers.exec {
|
||||
commandLine("node", "-e", "console.log(require('react-native/package.json').version);")
|
||||
}.standardOutput.asText.get().trim()
|
||||
|
||||
def coreVersion = version.split("-")[0]
|
||||
def (major, minor, patch) = coreVersion.tokenize('.').collect { it.toInteger() }
|
||||
|
||||
return minor
|
||||
}
|
||||
|
||||
if (getRNMinorVersion() >= 75) {
|
||||
extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
|
||||
if (System.getenv('EXPO_UNSTABLE_CORE_AUTOLINKING') == '1') {
|
||||
println('\u001B[32mUsing expo-modules-autolinking as core autolinking source\u001B[0m')
|
||||
def command = [
|
||||
'node',
|
||||
'--no-warnings',
|
||||
'--eval',
|
||||
'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))',
|
||||
'react-native-config',
|
||||
'--json',
|
||||
'--platform',
|
||||
'android'
|
||||
].toList()
|
||||
ex.autolinkLibrariesFromCommand(command)
|
||||
} else {
|
||||
ex.autolinkLibrariesFromCommand()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'ExpoExample'
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
reactAndroidLibs {
|
||||
from(files(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../gradle/libs.versions.toml")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
|
||||
useExpoModules()
|
||||
|
||||
if (getRNMinorVersion() < 75) {
|
||||
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
|
||||
applyNativeModulesSettingsGradle(settings)
|
||||
}
|
||||
|
||||
include ':app'
|
||||
includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile())
|
45
examples/expo/app.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"expo": {
|
||||
"plugins": [
|
||||
[
|
||||
"@react-native-tvos/config-tv",
|
||||
{
|
||||
"androidTVBanner": "./assets/images/icon-400x240.png",
|
||||
"appleTVImages": {
|
||||
"icon": "./assets/images/icon-1280x768.png",
|
||||
"iconSmall": "./assets/images/icon-400x240.png",
|
||||
"iconSmall2x": "./assets/images/icon-800x480.png",
|
||||
"topShelf": "./assets/images/icon-1920x720.png",
|
||||
"topShelf2x": "./assets/images/icon-3840x1440.png",
|
||||
"topShelfWide": "./assets/images/icon-2320x720.png",
|
||||
"topShelfWide2x": "./assets/images/icon-4640x1440.png"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"../../lib/expo-plugins/withRNVideo.js",
|
||||
{
|
||||
"reactNativeTestApp": true,
|
||||
"enableNotificationControls": true,
|
||||
"enableBackgroundAudio": true,
|
||||
"enableADSExtension": false,
|
||||
"enableCacheExtension": false,
|
||||
"androidExtensions": {
|
||||
"useExoplayerRtsp": true,
|
||||
"useExoplayerSmoothStreaming": true,
|
||||
"useExoplayerHls": true,
|
||||
"useExoplayerDash": true
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"name": "ExpoExample",
|
||||
"slug": "ExpoExample",
|
||||
"android": {
|
||||
"package": "com.anonymous.ExpoExample"
|
||||
},
|
||||
"ios": {
|
||||
"bundleIdentifier": "com.anonymous.ExpoExample"
|
||||
}
|
||||
}
|
||||
}
|
BIN
examples/expo/assets/images/icon-1280x768.png
Normal file
After Width: | Height: | Size: 155 KiB |
BIN
examples/expo/assets/images/icon-1920x720.png
Normal file
After Width: | Height: | Size: 212 KiB |
BIN
examples/expo/assets/images/icon-2320x720.png
Normal file
After Width: | Height: | Size: 246 KiB |
BIN
examples/expo/assets/images/icon-3840x1440.png
Normal file
After Width: | Height: | Size: 523 KiB |
BIN
examples/expo/assets/images/icon-400x240.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
examples/expo/assets/images/icon-4640x1440.png
Normal file
After Width: | Height: | Size: 616 KiB |
BIN
examples/expo/assets/images/icon-800x480.png
Normal file
After Width: | Height: | Size: 95 KiB |
20
examples/expo/babel.config.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const path = require('path');
|
||||
const pak = require('../../package.json');
|
||||
|
||||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ['babel-preset-expo'],
|
||||
plugins: [
|
||||
[
|
||||
'module-resolver',
|
||||
{
|
||||
alias: {
|
||||
[pak.name]: path.join(__dirname, '../..', pak.source),
|
||||
extensions: ['.tsx', '.ts', '.js', '.json'],
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
};
|
5
examples/expo/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import {registerRootComponent} from 'expo';
|
||||
|
||||
import App from './App';
|
||||
|
||||
registerRootComponent(App);
|
30
examples/expo/ios/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
#
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
.xcode.env.local
|
||||
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
||||
|
||||
# CocoaPods
|
||||
/Pods/
|
11
examples/expo/ios/.xcode.env
Normal file
@@ -0,0 +1,11 @@
|
||||
# This `.xcode.env` file is versioned and is used to source the environment
|
||||
# used when running script phases inside Xcode.
|
||||
# To customize your local environment, you can create an `.xcode.env.local`
|
||||
# file that is not versioned.
|
||||
|
||||
# NODE_BINARY variable contains the PATH to the node executable.
|
||||
#
|
||||
# Customize the NODE_BINARY variable here.
|
||||
# For example, to use nvm with brew, add the following line
|
||||
# . "$(brew --prefix nvm)/nvm.sh" --no-use
|
||||
export NODE_BINARY=$(command -v node)
|
529
examples/expo/ios/ExpoExample.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,529 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
||||
6E3E1907139F4681BB29BA3F /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CD8985E3A84FEF8F310E21 /* noop-file.swift */; };
|
||||
96905EF65AED1B983A6B3ABC /* libPods-ExpoExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-ExpoExample.a */; };
|
||||
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
|
||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
||||
F6DD9B61853ED7F5FE2C32F9 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3BCFB4767DA89C6653566DA4 /* PrivacyInfo.xcprivacy */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
13B07F961A680F5B00A75B9A /* ExpoExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExpoExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ExpoExample/AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = ExpoExample/AppDelegate.mm; sourceTree = "<group>"; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ExpoExample/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ExpoExample/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ExpoExample/main.m; sourceTree = "<group>"; };
|
||||
3BCFB4767DA89C6653566DA4 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = ExpoExample/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-ExpoExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExpoExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6C2E3173556A471DD304B334 /* Pods-ExpoExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpoExample.debug.xcconfig"; path = "Target Support Files/Pods-ExpoExample/Pods-ExpoExample.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
7A4D352CD337FB3A3BF06240 /* Pods-ExpoExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpoExample.release.xcconfig"; path = "Target Support Files/Pods-ExpoExample/Pods-ExpoExample.release.xcconfig"; sourceTree = "<group>"; };
|
||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = ExpoExample/SplashScreen.storyboard; sourceTree = "<group>"; };
|
||||
B7DF2D7F8A384D51B801CEE7 /* ExpoExample-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "ExpoExample-Bridging-Header.h"; path = "ExpoExample/ExpoExample-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
||||
E1CD8985E3A84FEF8F310E21 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "ExpoExample/noop-file.swift"; sourceTree = "<group>"; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-ExpoExample/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
96905EF65AED1B983A6B3ABC /* libPods-ExpoExample.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
13B07FAE1A68108700A75B9A /* ExpoExample */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB2F792B24A3F905000567C9 /* Supporting */,
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
|
||||
E1CD8985E3A84FEF8F310E21 /* noop-file.swift */,
|
||||
B7DF2D7F8A384D51B801CEE7 /* ExpoExample-Bridging-Header.h */,
|
||||
3BCFB4767DA89C6653566DA4 /* PrivacyInfo.xcprivacy */,
|
||||
);
|
||||
name = ExpoExample;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-ExpoExample.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83CBB9F61A601CBA00E9B192 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07FAE1A68108700A75B9A /* ExpoExample */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||
D65327D7A22EEC0BE12398D9 /* Pods */,
|
||||
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
usesTabs = 0;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07F961A680F5B00A75B9A /* ExpoExample.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92DBD88DE9BF7D494EA9DA96 /* ExpoExample */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */,
|
||||
);
|
||||
name = ExpoExample;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BB2F792B24A3F905000567C9 /* Supporting */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB2F792C24A3F905000567C9 /* Expo.plist */,
|
||||
);
|
||||
name = Supporting;
|
||||
path = ExpoExample/Supporting;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D65327D7A22EEC0BE12398D9 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6C2E3173556A471DD304B334 /* Pods-ExpoExample.debug.xcconfig */,
|
||||
7A4D352CD337FB3A3BF06240 /* Pods-ExpoExample.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92DBD88DE9BF7D494EA9DA96 /* ExpoExample */,
|
||||
);
|
||||
name = ExpoModulesProviders;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
13B07F861A680F5B00A75B9A /* ExpoExample */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ExpoExample" */;
|
||||
buildPhases = (
|
||||
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
|
||||
6E9FB0155D627BF633A3CB08 /* [Expo] Configure project */,
|
||||
13B07F871A680F5B00A75B9A /* Sources */,
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
|
||||
CED90EB9939E56B1E5D48A24 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = ExpoExample;
|
||||
productName = ExpoExample;
|
||||
productReference = 13B07F961A680F5B00A75B9A /* ExpoExample.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1130;
|
||||
TargetAttributes = {
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1250;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "ExpoExample" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 83CBB9F61A601CBA00E9B192;
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
13B07F861A680F5B00A75B9A /* ExpoExample */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
|
||||
F6DD9B61853ED7F5FE2C32F9 /* PrivacyInfo.xcprivacy in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Bundle React Native code and images";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n";
|
||||
};
|
||||
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-ExpoExample-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
6E9FB0155D627BF633A3CB08 /* [Expo] Configure project */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[Expo] Configure project";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-ExpoExample/expo-configure-project.sh\"\n";
|
||||
};
|
||||
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-ExpoExample/Pods-ExpoExample-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpoExample/Pods-ExpoExample-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
CED90EB9939E56B1E5D48A24 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-ExpoExample/Pods-ExpoExample-frameworks.sh",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpoExample/Pods-ExpoExample-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
13B07F871A680F5B00A75B9A /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */,
|
||||
6E3E1907139F4681BB29BA3F /* noop-file.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-ExpoExample.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = ExpoExample/ExpoExample.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
"FB_SONARKIT_ENABLED=1",
|
||||
);
|
||||
INFOPLIST_FILE = ExpoExample/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.ExpoExample;
|
||||
PRODUCT_NAME = ExpoExample;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "ExpoExample/ExpoExample-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
13B07F951A680F5B00A75B9A /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-ExpoExample.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = ExpoExample/ExpoExample.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
INFOPLIST_FILE = ExpoExample/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.ExpoExample;
|
||||
PRODUCT_NAME = ExpoExample;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "ExpoExample/ExpoExample-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
83CBBA201A601CBA00E9B192 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
83CBBA211A601CBA00E9B192 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ExpoExample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
13B07F941A680F5B00A75B9A /* Debug */,
|
||||
13B07F951A680F5B00A75B9A /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "ExpoExample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
83CBBA201A601CBA00E9B192 /* Debug */,
|
||||
83CBBA211A601CBA00E9B192 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1130"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "ExpoExample.app"
|
||||
BlueprintName = "ExpoExample"
|
||||
ReferencedContainer = "container:ExpoExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
||||
BuildableName = "ExpoExampleTests.xctest"
|
||||
BlueprintName = "ExpoExampleTests"
|
||||
ReferencedContainer = "container:ExpoExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "ExpoExample.app"
|
||||
BlueprintName = "ExpoExample"
|
||||
ReferencedContainer = "container:ExpoExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "ExpoExample.app"
|
||||
BlueprintName = "ExpoExample"
|
||||
ReferencedContainer = "container:ExpoExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
10
examples/expo/ios/ExpoExample.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:ExpoExample.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
7
examples/expo/ios/ExpoExample/AppDelegate.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#import <RCTAppDelegate.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Expo/Expo.h>
|
||||
|
||||
@interface AppDelegate : EXAppDelegateWrapper
|
||||
|
||||
@end
|
62
examples/expo/ios/ExpoExample/AppDelegate.mm
Normal file
@@ -0,0 +1,62 @@
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTLinkingManager.h>
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
self.moduleName = @"main";
|
||||
|
||||
// You can add your custom initial props in the dictionary below.
|
||||
// They will be passed down to the ViewController used by React Native.
|
||||
self.initialProps = @{};
|
||||
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
return [self bundleURL];
|
||||
}
|
||||
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
#if DEBUG
|
||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"];
|
||||
#else
|
||||
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
#endif
|
||||
}
|
||||
|
||||
// Linking API
|
||||
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
|
||||
}
|
||||
|
||||
// Universal Links
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
|
||||
BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
|
||||
return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
|
||||
}
|
||||
|
||||
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
|
||||
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
|
||||
{
|
||||
return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
|
||||
}
|
||||
|
||||
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
|
||||
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
|
||||
{
|
||||
return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
|
||||
}
|
||||
|
||||
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
|
||||
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
|
||||
{
|
||||
return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
|
||||
}
|
||||
|
||||
@end
|
@@ -0,0 +1,3 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
5
examples/expo/ios/ExpoExample/ExpoExample.entitlements
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "App-Icon-1024x1024@1x.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "expo"
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "expo"
|
||||
}
|
||||
}
|
21
examples/expo/ios/ExpoExample/Images.xcassets/SplashScreenBackground.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"idiom": "universal",
|
||||
"filename": "image.png",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"scale": "3x"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "expo"
|
||||
}
|
||||
}
|
BIN
examples/expo/ios/ExpoExample/Images.xcassets/SplashScreenBackground.imageset/image.png
vendored
Normal file
After Width: | Height: | Size: 68 B |
78
examples/expo/ios/ExpoExample/Info.plist
Normal file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>ExpoExample</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>com.anonymous.ExpoExample</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>SplashScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<false/>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleDefault</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Light</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
48
examples/expo/ios/ExpoExample/PrivacyInfo.xcprivacy
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>0A2A.1</string>
|
||||
<string>3B52.1</string>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>E174.1</string>
|
||||
<string>85F4.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
43
examples/expo/ios/ExpoExample/SplashScreen.storyboard
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EXPO-VIEWCONTROLLER-1">
|
||||
<device id="retina5_5" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<scene sceneID="EXPO-SCENE-1">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="SplashScreenViewController" id="EXPO-VIEWCONTROLLER-1" sceneMemberID="viewController">
|
||||
<view key="view" userInteractionEnabled="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="EXPO-ContainerView" userLabel="ContainerView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" insetsLayoutMarginsFromSafeArea="NO" image="SplashScreenBackground" translatesAutoresizingMaskIntoConstraints="NO" id="EXPO-SplashScreenBackground" userLabel="SplashScreenBackground">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="EXPO-SplashScreenBackground" firstAttribute="top" secondItem="EXPO-ContainerView" secondAttribute="top" id="1gX-mQ-vu6"/>
|
||||
<constraint firstItem="EXPO-SplashScreenBackground" firstAttribute="leading" secondItem="EXPO-ContainerView" secondAttribute="leading" id="6tX-OG-Sck"/>
|
||||
<constraint firstItem="EXPO-SplashScreenBackground" firstAttribute="trailing" secondItem="EXPO-ContainerView" secondAttribute="trailing" id="ABX-8g-7v4"/>
|
||||
<constraint firstItem="EXPO-SplashScreenBackground" firstAttribute="bottom" secondItem="EXPO-ContainerView" secondAttribute="bottom" id="jkI-2V-eW5"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="top" secondItem="EXPO-ContainerView" secondAttribute="top" id="2VS-Uz-0LU"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="leading" secondItem="EXPO-ContainerView" secondAttribute="leading" id="LhH-Ei-DKo"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="trailing" secondItem="EXPO-ContainerView" secondAttribute="trailing" id="I6l-TP-6fn"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="bottom" secondItem="EXPO-ContainerView" secondAttribute="bottom" id="nbp-HC-eaG"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="Rmq-lb-GrQ"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="EXPO-PLACEHOLDER-1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="140.625" y="129.4921875"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="SplashScreenBackground" width="1" height="1"/>
|
||||
</resources>
|
||||
</document>
|
12
examples/expo/ios/ExpoExample/Supporting/Expo.plist
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>EXUpdatesCheckOnLaunch</key>
|
||||
<string>ALWAYS</string>
|
||||
<key>EXUpdatesEnabled</key>
|
||||
<false/>
|
||||
<key>EXUpdatesLaunchWaitMs</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</plist>
|
10
examples/expo/ios/ExpoExample/main.m
Normal file
@@ -0,0 +1,10 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||
|
4
examples/expo/ios/ExpoExample/noop-file.swift
Normal file
@@ -0,0 +1,4 @@
|
||||
//
|
||||
// @generated
|
||||
// A blank Swift file must be created for native modules with Swift files to work correctly.
|
||||
//
|
88
examples/expo/ios/Podfile
Normal file
@@ -0,0 +1,88 @@
|
||||
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
|
||||
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
|
||||
|
||||
require 'json'
|
||||
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
|
||||
|
||||
ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0'
|
||||
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
|
||||
|
||||
use_autolinking_method_symbol = ('use' + '_native' + '_modules!').to_sym
|
||||
origin_autolinking_method = self.method(use_autolinking_method_symbol)
|
||||
self.define_singleton_method(use_autolinking_method_symbol) do |*args|
|
||||
if ENV['EXPO_UNSTABLE_CORE_AUTOLINKING'] == '1'
|
||||
Pod::UI.puts('Using expo-modules-autolinking as core autolinking source'.green)
|
||||
config_command = [
|
||||
'node',
|
||||
'--no-warnings',
|
||||
'--eval',
|
||||
'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))',
|
||||
'react-native-config',
|
||||
'--json',
|
||||
'--platform',
|
||||
'ios'
|
||||
]
|
||||
origin_autolinking_method.call(config_command)
|
||||
else
|
||||
origin_autolinking_method.call()
|
||||
end
|
||||
end
|
||||
|
||||
platform :ios, podfile_properties['ios.deploymentTarget'] || '13.4'
|
||||
install! 'cocoapods',
|
||||
:deterministic_uuids => false
|
||||
|
||||
prepare_react_native_project!
|
||||
|
||||
target 'ExpoExample' do
|
||||
use_expo_modules!
|
||||
config = use_native_modules!
|
||||
|
||||
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
|
||||
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
:hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
|
||||
# An absolute path to your application root.
|
||||
:app_path => "#{Pod::Config.instance.installation_root}/..",
|
||||
:privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false',
|
||||
)
|
||||
|
||||
# Manually links local version of react-native-video
|
||||
pod 'react-native-video', path: '../../..'
|
||||
pod 'react-native-video-plugin-sample', path: '../../react-native-video-plugin-sample'
|
||||
|
||||
# Chache dependencies need to have modular headers
|
||||
if defined?($RNVideoUseVideoCaching)
|
||||
use_modular_headers!
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
react_native_post_install(
|
||||
installer,
|
||||
config[:reactNativePath],
|
||||
:mac_catalyst_enabled => false,
|
||||
:ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true',
|
||||
)
|
||||
|
||||
# This is necessary for Xcode 14, because it signs resource bundles by default
|
||||
# when building for devices.
|
||||
installer.target_installation_results.pod_target_installation_results
|
||||
.each do |pod_name, target_installation_result|
|
||||
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
|
||||
resource_bundle_target.build_configurations.each do |config|
|
||||
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post_integrate do |installer|
|
||||
begin
|
||||
expo_patch_react_imports!(installer)
|
||||
rescue => e
|
||||
Pod::UI.warn e
|
||||
end
|
||||
end
|
||||
end
|
1541
examples/expo/ios/Podfile.lock
Normal file
4
examples/expo/ios/Podfile.properties.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"expo.jsEngine": "hermes",
|
||||
"EX_DEV_CLIENT_NETWORK_INSPECTOR": "true"
|
||||
}
|
54
examples/expo/metro.config.js
Normal file
@@ -0,0 +1,54 @@
|
||||
// Learn more https://docs.expo.io/guides/customizing-metro
|
||||
const {getDefaultConfig} = require('expo/metro-config');
|
||||
const path = require('path');
|
||||
const blacklist = require('metro-config/src/defaults/exclusionList');
|
||||
const escape = require('escape-string-regexp');
|
||||
|
||||
const config = getDefaultConfig(__dirname);
|
||||
|
||||
// When enabled, the optional code below will allow Metro to resolve
|
||||
// and bundle source files with TV-specific extensions
|
||||
// (e.g., *.ios.tv.tsx, *.android.tv.tsx, *.tv.tsx)
|
||||
//
|
||||
// Metro will still resolve source files with standard extensions
|
||||
// as usual if TV-specific files are not found for a module.
|
||||
if (process.env?.EXPO_TV === '1') {
|
||||
const originalSourceExts = config.resolver.sourceExts;
|
||||
const tvSourceExts = [
|
||||
...originalSourceExts.map((e) => `tv.${e}`),
|
||||
...originalSourceExts,
|
||||
];
|
||||
config.resolver.sourceExts = tvSourceExts;
|
||||
}
|
||||
|
||||
const projectRoot = __dirname;
|
||||
const repoRoot = path.resolve(projectRoot, '../..');
|
||||
const pak = require('../../package.json');
|
||||
const modules = Object.keys({...pak.peerDependencies});
|
||||
|
||||
// Watch the
|
||||
config.watchFolders = [repoRoot, path.resolve(projectRoot, '../..')];
|
||||
|
||||
// Add the root node_modules to the resolver's search path
|
||||
config.resolver.nodeModulesPaths = [
|
||||
path.resolve(path.join(__dirname, './node_modules')),
|
||||
path.resolve(path.join(__dirname, '../../node_modules')),
|
||||
];
|
||||
|
||||
// We need to make sure that only one version is loaded for peerDependencies
|
||||
// So we block them at the root, and alias them to the versions in example's node_modules
|
||||
config.resolver.extraNodeModules = modules.reduce((acc, name) => {
|
||||
acc[name] = path.join(__dirname, 'node_modules', name);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// We need to make sure that only one version is loaded for peerDependencies
|
||||
// So we block them at the root, and alias them to the versions in example's node_modules
|
||||
config.resolver.blacklistRE = blacklist([
|
||||
...modules.map(
|
||||
(name) =>
|
||||
new RegExp(`^${escape(path.join(repoRoot, 'node_modules', name))}\\/.*$`),
|
||||
),
|
||||
]);
|
||||
|
||||
module.exports = config;
|
43
examples/expo/package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "expoexample",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"postinstall": "yarn update-src",
|
||||
"start": "expo start",
|
||||
"start:tv": "EXPO_TV=1 expo start",
|
||||
"android:tv": "EXPO_TV=1 expo run:android",
|
||||
"android": "EXPO_TV=0 expo run:android",
|
||||
"ios:tv": "EXPO_TV=1 expo run:ios",
|
||||
"ios": "EXPO_TV=0 expo run:ios",
|
||||
"web": "expo start --web",
|
||||
"prebuild:tv": "EXPO_TV=1 expo prebuild",
|
||||
"prebuild": "EXPO_TV=0 expo prebuild",
|
||||
"update-src": "echo 'Updating src from ../bare/src' && rm -r ./src && cp -r ../bare/src ./src && echo 'Updated src from ../bare/src'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native-picker/picker": "2.8.1",
|
||||
"expo": "~51.0.31",
|
||||
"expo-splash-screen": "~0.27.5",
|
||||
"expo-status-bar": "~1.12.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-native": "npm:react-native-tvos@~0.74.5-0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.0",
|
||||
"@expo/config-plugins": "^8.0.10",
|
||||
"@react-native-tvos/config-tv": "^0.0.10",
|
||||
"@react-native/metro-config": "^0.75.4",
|
||||
"@types/react": "~18.2.45",
|
||||
"babel-plugin-module-resolver": "^5.0.2",
|
||||
"typescript": "~5.3.3"
|
||||
},
|
||||
"expo": {
|
||||
"install": {
|
||||
"exclude": [
|
||||
"react-native"
|
||||
]
|
||||
}
|
||||
},
|
||||
"private": true
|
||||
}
|
344
examples/expo/src/BasicExample.tsx
Normal file
@@ -0,0 +1,344 @@
|
||||
import React, {type FC, useCallback, useRef, useState, useEffect} from 'react';
|
||||
|
||||
import {Platform, TouchableOpacity, View, StatusBar} from 'react-native';
|
||||
|
||||
import Video, {
|
||||
VideoRef,
|
||||
SelectedVideoTrackType,
|
||||
BufferingStrategyType,
|
||||
SelectedTrackType,
|
||||
ResizeMode,
|
||||
type AudioTrack,
|
||||
type OnAudioTracksData,
|
||||
type OnLoadData,
|
||||
type OnProgressData,
|
||||
type OnTextTracksData,
|
||||
type OnVideoAspectRatioData,
|
||||
type TextTrack,
|
||||
type OnBufferData,
|
||||
type OnAudioFocusChangedData,
|
||||
type OnVideoErrorData,
|
||||
type OnTextTrackDataChangedData,
|
||||
type OnSeekData,
|
||||
type OnPlaybackStateChangedData,
|
||||
type OnPlaybackRateChangeData,
|
||||
type OnVideoTracksData,
|
||||
type ReactVideoSource,
|
||||
type VideoTrack,
|
||||
type SelectedTrack,
|
||||
type SelectedVideoTrack,
|
||||
type EnumValues,
|
||||
OnBandwidthUpdateData,
|
||||
ControlsStyles,
|
||||
} from 'react-native-video';
|
||||
import styles from './styles';
|
||||
import {type AdditionalSourceInfo} from './types';
|
||||
import {
|
||||
bufferConfig,
|
||||
isAndroid,
|
||||
srcList,
|
||||
textTracksSelectionBy,
|
||||
audioTracksSelectionBy,
|
||||
} from './constants';
|
||||
import {Overlay, toast, VideoLoader} from './components';
|
||||
|
||||
type Props = NonNullable<unknown>;
|
||||
|
||||
const VideoPlayer: FC<Props> = ({}) => {
|
||||
const [rate, setRate] = useState(1);
|
||||
const [volume, setVolume] = useState(1);
|
||||
const [muted, setMuted] = useState(false);
|
||||
const [resizeMode, setResizeMode] = useState<EnumValues<ResizeMode>>(
|
||||
ResizeMode.CONTAIN,
|
||||
);
|
||||
const [duration, setDuration] = useState(0);
|
||||
const [currentTime, setCurrentTime] = useState(0);
|
||||
const [_, setVideoSize] = useState({videoWidth: 0, videoHeight: 0});
|
||||
const [paused, setPaused] = useState(false);
|
||||
const [fullscreen, setFullscreen] = useState(true);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [audioTracks, setAudioTracks] = useState<AudioTrack[]>([]);
|
||||
const [textTracks, setTextTracks] = useState<TextTrack[]>([]);
|
||||
const [videoTracks, setVideoTracks] = useState<VideoTrack[]>([]);
|
||||
const [selectedAudioTrack, setSelectedAudioTrack] = useState<
|
||||
SelectedTrack | undefined
|
||||
>(undefined);
|
||||
const [selectedTextTrack, setSelectedTextTrack] = useState<
|
||||
SelectedTrack | undefined
|
||||
>(undefined);
|
||||
const [selectedVideoTrack, setSelectedVideoTrack] =
|
||||
useState<SelectedVideoTrack>({
|
||||
type: SelectedVideoTrackType.AUTO,
|
||||
});
|
||||
const [srcListId, setSrcListId] = useState(0);
|
||||
const [repeat, setRepeat] = useState(false);
|
||||
const [controls, setControls] = useState(false);
|
||||
const [useCache, setUseCache] = useState(false);
|
||||
const [showPoster, setShowPoster] = useState<boolean>(false);
|
||||
const [showNotificationControls, setShowNotificationControls] =
|
||||
useState(false);
|
||||
const [isSeeking, setIsSeeking] = useState(false);
|
||||
|
||||
const videoRef = useRef<VideoRef>(null);
|
||||
const viewStyle = fullscreen ? styles.fullScreen : styles.halfScreen;
|
||||
const currentSrc = srcList[srcListId];
|
||||
const additional = currentSrc as AdditionalSourceInfo;
|
||||
|
||||
const goToChannel = useCallback((channel: number) => {
|
||||
setSrcListId(channel);
|
||||
setDuration(0);
|
||||
setCurrentTime(0);
|
||||
setVideoSize({videoWidth: 0, videoHeight: 0});
|
||||
setIsLoading(false);
|
||||
setAudioTracks([]);
|
||||
setTextTracks([]);
|
||||
setSelectedAudioTrack(undefined);
|
||||
setSelectedTextTrack(undefined);
|
||||
setSelectedVideoTrack({
|
||||
type: SelectedVideoTrackType.AUTO,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const channelUp = useCallback(() => {
|
||||
console.log('channel up');
|
||||
goToChannel((srcListId + 1) % srcList.length);
|
||||
}, [goToChannel, srcListId]);
|
||||
|
||||
const channelDown = useCallback(() => {
|
||||
console.log('channel down');
|
||||
goToChannel((srcListId + srcList.length - 1) % srcList.length);
|
||||
}, [goToChannel, srcListId]);
|
||||
|
||||
const onAudioTracks = (data: OnAudioTracksData) => {
|
||||
console.log('onAudioTracks', data);
|
||||
const selectedTrack = data.audioTracks?.find((x: AudioTrack) => {
|
||||
return x.selected;
|
||||
});
|
||||
let value;
|
||||
if (audioTracksSelectionBy === SelectedTrackType.INDEX) {
|
||||
value = selectedTrack?.index;
|
||||
} else if (audioTracksSelectionBy === SelectedTrackType.LANGUAGE) {
|
||||
value = selectedTrack?.language;
|
||||
} else if (audioTracksSelectionBy === SelectedTrackType.TITLE) {
|
||||
value = selectedTrack?.title;
|
||||
}
|
||||
setAudioTracks(data.audioTracks);
|
||||
setSelectedAudioTrack({
|
||||
type: audioTracksSelectionBy,
|
||||
value: value,
|
||||
});
|
||||
};
|
||||
|
||||
const onVideoTracks = (data: OnVideoTracksData) => {
|
||||
console.log('onVideoTracks', data.videoTracks);
|
||||
setVideoTracks(data.videoTracks);
|
||||
};
|
||||
|
||||
const onTextTracks = (data: OnTextTracksData) => {
|
||||
const selectedTrack = data.textTracks?.find((x: TextTrack) => {
|
||||
return x?.selected;
|
||||
});
|
||||
|
||||
setTextTracks(data.textTracks);
|
||||
let value;
|
||||
if (textTracksSelectionBy === SelectedTrackType.INDEX) {
|
||||
value = selectedTrack?.index;
|
||||
} else if (textTracksSelectionBy === SelectedTrackType.LANGUAGE) {
|
||||
value = selectedTrack?.language;
|
||||
} else if (textTracksSelectionBy === SelectedTrackType.TITLE) {
|
||||
value = selectedTrack?.title;
|
||||
}
|
||||
setSelectedTextTrack({
|
||||
type: textTracksSelectionBy,
|
||||
value: value,
|
||||
});
|
||||
};
|
||||
|
||||
const onLoad = (data: OnLoadData) => {
|
||||
setDuration(data.duration);
|
||||
onAudioTracks(data);
|
||||
onTextTracks(data);
|
||||
onVideoTracks(data);
|
||||
};
|
||||
|
||||
const onProgress = (data: OnProgressData) => {
|
||||
setCurrentTime(data.currentTime);
|
||||
};
|
||||
|
||||
const onSeek = (data: OnSeekData) => {
|
||||
setCurrentTime(data.currentTime);
|
||||
setIsSeeking(false);
|
||||
};
|
||||
|
||||
const onVideoLoadStart = () => {
|
||||
console.log('onVideoLoadStart');
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const onTextTrackDataChanged = (data: OnTextTrackDataChangedData) => {
|
||||
console.log(`Subtitles: ${JSON.stringify(data, null, 2)}`);
|
||||
};
|
||||
|
||||
const onAspectRatio = (data: OnVideoAspectRatioData) => {
|
||||
console.log('onAspectRadio called ' + JSON.stringify(data));
|
||||
setVideoSize({videoWidth: data.width, videoHeight: data.height});
|
||||
};
|
||||
|
||||
const onVideoBuffer = (param: OnBufferData) => {
|
||||
console.log('onVideoBuffer');
|
||||
setIsLoading(param.isBuffering);
|
||||
};
|
||||
|
||||
const onReadyForDisplay = () => {
|
||||
console.log('onReadyForDisplay');
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const onAudioBecomingNoisy = () => {
|
||||
setPaused(true);
|
||||
};
|
||||
|
||||
const onAudioFocusChanged = (event: OnAudioFocusChangedData) => {
|
||||
setPaused(!event.hasAudioFocus);
|
||||
};
|
||||
|
||||
const onError = (err: OnVideoErrorData) => {
|
||||
console.log(JSON.stringify(err));
|
||||
toast(true, 'error: ' + JSON.stringify(err));
|
||||
};
|
||||
|
||||
const onEnd = () => {
|
||||
if (!repeat) {
|
||||
channelUp();
|
||||
}
|
||||
};
|
||||
|
||||
const onPlaybackRateChange = (data: OnPlaybackRateChangeData) => {
|
||||
console.log('onPlaybackRateChange', data);
|
||||
};
|
||||
|
||||
const onPlaybackStateChanged = (data: OnPlaybackStateChangedData) => {
|
||||
console.log('onPlaybackStateChanged', data);
|
||||
};
|
||||
|
||||
const onVideoBandwidthUpdate = (data: OnBandwidthUpdateData) => {
|
||||
console.log('onVideoBandwidthUpdate', data);
|
||||
};
|
||||
|
||||
const onFullScreenExit = () => {
|
||||
// iOS pauses video on exit from full screen
|
||||
Platform.OS === 'ios' && setPaused(true);
|
||||
};
|
||||
|
||||
const _renderLoader = showPoster ? () => <VideoLoader /> : undefined;
|
||||
|
||||
const _subtitleStyle = {subtitlesFollowVideo: true};
|
||||
const _controlsStyles: ControlsStyles = {
|
||||
hideNavigationBarOnFullScreenMode: true,
|
||||
hideNotificationBarOnFullScreenMode: true,
|
||||
liveLabel: 'LIVE',
|
||||
};
|
||||
const _bufferConfig = {
|
||||
...bufferConfig,
|
||||
cacheSizeMB: useCache ? 200 : 0,
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<StatusBar animated={true} backgroundColor="black" hidden={false} />
|
||||
|
||||
{(srcList[srcListId] as AdditionalSourceInfo)?.noView ? null : (
|
||||
<TouchableOpacity style={viewStyle}>
|
||||
<Video
|
||||
showNotificationControls={showNotificationControls}
|
||||
ref={videoRef}
|
||||
source={currentSrc as ReactVideoSource}
|
||||
adTagUrl={additional?.adTagUrl}
|
||||
drm={additional?.drm}
|
||||
style={viewStyle}
|
||||
rate={rate}
|
||||
paused={paused}
|
||||
volume={volume}
|
||||
muted={muted}
|
||||
controls={controls}
|
||||
resizeMode={resizeMode}
|
||||
onFullscreenPlayerWillDismiss={onFullScreenExit}
|
||||
onLoad={onLoad}
|
||||
onAudioTracks={onAudioTracks}
|
||||
onTextTracks={onTextTracks}
|
||||
onVideoTracks={onVideoTracks}
|
||||
onTextTrackDataChanged={onTextTrackDataChanged}
|
||||
onProgress={onProgress}
|
||||
onEnd={onEnd}
|
||||
progressUpdateInterval={1000}
|
||||
onError={onError}
|
||||
onAudioBecomingNoisy={onAudioBecomingNoisy}
|
||||
onAudioFocusChanged={onAudioFocusChanged}
|
||||
onLoadStart={onVideoLoadStart}
|
||||
onAspectRatio={onAspectRatio}
|
||||
onReadyForDisplay={onReadyForDisplay}
|
||||
onBuffer={onVideoBuffer}
|
||||
onBandwidthUpdate={onVideoBandwidthUpdate}
|
||||
onSeek={onSeek}
|
||||
repeat={repeat}
|
||||
selectedTextTrack={selectedTextTrack}
|
||||
selectedAudioTrack={selectedAudioTrack}
|
||||
selectedVideoTrack={selectedVideoTrack}
|
||||
playInBackground={false}
|
||||
bufferConfig={_bufferConfig}
|
||||
preventsDisplaySleepDuringVideoPlayback={true}
|
||||
renderLoader={_renderLoader}
|
||||
onPlaybackRateChange={onPlaybackRateChange}
|
||||
onPlaybackStateChanged={onPlaybackStateChanged}
|
||||
bufferingStrategy={BufferingStrategyType.DEFAULT}
|
||||
debug={{enable: true, thread: true}}
|
||||
subtitleStyle={_subtitleStyle}
|
||||
controlsStyles={_controlsStyles}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<Overlay
|
||||
channelDown={channelDown}
|
||||
channelUp={channelUp}
|
||||
ref={videoRef}
|
||||
videoTracks={videoTracks}
|
||||
selectedVideoTrack={selectedVideoTrack}
|
||||
setSelectedTextTrack={setSelectedTextTrack}
|
||||
audioTracks={audioTracks}
|
||||
controls={controls}
|
||||
resizeMode={resizeMode}
|
||||
textTracks={textTracks}
|
||||
selectedTextTrack={selectedTextTrack}
|
||||
selectedAudioTrack={selectedAudioTrack}
|
||||
setSelectedAudioTrack={setSelectedAudioTrack}
|
||||
setSelectedVideoTrack={setSelectedVideoTrack}
|
||||
currentTime={currentTime}
|
||||
setMuted={setMuted}
|
||||
muted={muted}
|
||||
duration={duration}
|
||||
paused={paused}
|
||||
volume={volume}
|
||||
setControls={setControls}
|
||||
showPoster={showPoster}
|
||||
rate={rate}
|
||||
setFullscreen={setFullscreen}
|
||||
setPaused={setPaused}
|
||||
isLoading={isLoading}
|
||||
isSeeking={isSeeking}
|
||||
setIsSeeking={setIsSeeking}
|
||||
repeat={repeat}
|
||||
setRepeat={setRepeat}
|
||||
setShowPoster={setShowPoster}
|
||||
setRate={setRate}
|
||||
setResizeMode={setResizeMode}
|
||||
setShowNotificationControls={setShowNotificationControls}
|
||||
showNotificationControls={showNotificationControls}
|
||||
setUseCache={setUseCache}
|
||||
setVolume={setVolume}
|
||||
useCache={useCache}
|
||||
srcListId={srcListId}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default VideoPlayer;
|
240
examples/expo/src/BasicExample.windows.tsx
Normal file
@@ -0,0 +1,240 @@
|
||||
'use strict';
|
||||
|
||||
import React, {Component} from 'react';
|
||||
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextStyle,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
import Video, {ResizeMode} from 'react-native-video';
|
||||
|
||||
class VideoPlayer extends Component {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.onLoad = this.onLoad.bind(this);
|
||||
this.onProgress = this.onProgress.bind(this);
|
||||
}
|
||||
|
||||
state = {
|
||||
rate: 1,
|
||||
volume: 1,
|
||||
muted: false,
|
||||
resizeMode: ResizeMode.CONTAIN,
|
||||
duration: 0.0,
|
||||
currentTime: 0.0,
|
||||
paused: false,
|
||||
};
|
||||
|
||||
onLoad(data: any) {
|
||||
this.setState({duration: data.duration});
|
||||
}
|
||||
|
||||
onProgress(data: any) {
|
||||
this.setState({currentTime: data.currentTime});
|
||||
}
|
||||
|
||||
getCurrentTimePercentage() {
|
||||
if (this.state.currentTime > 0 && this.state.duration !== 0) {
|
||||
return this.state.currentTime / this.state.duration;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
renderRateControl(rate: number) {
|
||||
const isSelected = this.state.rate === rate;
|
||||
const style: TextStyle = StyleSheet.flatten([
|
||||
styles.controlOption,
|
||||
{fontWeight: isSelected ? 'bold' : 'normal'},
|
||||
]);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
this.setState({rate: rate});
|
||||
}}>
|
||||
<Text style={style}>{rate}x</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
renderResizeModeControl(resizeMode: string) {
|
||||
const isSelected = this.state.resizeMode === resizeMode;
|
||||
const style: TextStyle = StyleSheet.flatten([
|
||||
styles.controlOption,
|
||||
{fontWeight: isSelected ? 'bold' : 'normal'},
|
||||
]);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
this.setState({resizeMode: resizeMode});
|
||||
}}>
|
||||
<Text style={style}>{resizeMode}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
renderVolumeControl(volume: number) {
|
||||
const isSelected = this.state.volume === volume;
|
||||
const style: TextStyle = StyleSheet.flatten([
|
||||
styles.controlOption,
|
||||
{fontWeight: isSelected ? 'bold' : 'normal'},
|
||||
]);
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
this.setState({volume: volume});
|
||||
}}>
|
||||
<Text style={style}>{volume * 100}%</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const flexCompleted = this.getCurrentTimePercentage() * 100;
|
||||
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity
|
||||
style={styles.fullScreen}
|
||||
onPress={() => {
|
||||
this.setState({paused: !this.state.paused});
|
||||
}}>
|
||||
<Video
|
||||
source={require('./assets/videos/broadchurch.mp4')}
|
||||
style={styles.fullScreen}
|
||||
rate={this.state.rate}
|
||||
paused={this.state.paused}
|
||||
volume={this.state.volume}
|
||||
muted={this.state.muted}
|
||||
resizeMode={this.state.resizeMode}
|
||||
onLoad={this.onLoad}
|
||||
onProgress={this.onProgress}
|
||||
onEnd={() => {
|
||||
console.log('Done!');
|
||||
}}
|
||||
repeat={true}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.controls}>
|
||||
<View style={styles.generalControls}>
|
||||
<View style={styles.rateControl}>
|
||||
{this.renderRateControl(0.25)}
|
||||
{this.renderRateControl(0.5)}
|
||||
{this.renderRateControl(1.0)}
|
||||
{this.renderRateControl(1.5)}
|
||||
{this.renderRateControl(2.0)}
|
||||
</View>
|
||||
|
||||
<View style={styles.volumeControl}>
|
||||
{this.renderVolumeControl(0.5)}
|
||||
{this.renderVolumeControl(1)}
|
||||
{this.renderVolumeControl(1.5)}
|
||||
</View>
|
||||
|
||||
<View style={styles.resizeModeControl}>
|
||||
{this.renderResizeModeControl('cover')}
|
||||
{this.renderResizeModeControl('contain')}
|
||||
{this.renderResizeModeControl('stretch')}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.trackingControls}>
|
||||
<View style={styles.progress}>
|
||||
<View
|
||||
style={[styles.innerProgressCompleted, {flex: flexCompleted}]}
|
||||
/>
|
||||
<View
|
||||
style={[styles.innerProgressRemaining, {flex: flexRemaining}]}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'black',
|
||||
},
|
||||
fullScreen: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
},
|
||||
controls: {
|
||||
backgroundColor: 'transparent',
|
||||
borderRadius: 5,
|
||||
position: 'absolute',
|
||||
bottom: 20,
|
||||
left: 20,
|
||||
right: 20,
|
||||
},
|
||||
progress: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
borderRadius: 3,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
innerProgressCompleted: {
|
||||
height: 20,
|
||||
backgroundColor: '#cccccc',
|
||||
},
|
||||
innerProgressRemaining: {
|
||||
height: 20,
|
||||
backgroundColor: '#2C2C2C',
|
||||
},
|
||||
generalControls: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
borderRadius: 4,
|
||||
overflow: 'hidden',
|
||||
paddingBottom: 10,
|
||||
},
|
||||
rateControl: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
volumeControl: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
resizeModeControl: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
controlOption: {
|
||||
alignSelf: 'center',
|
||||
fontSize: 11,
|
||||
color: 'white',
|
||||
paddingLeft: 2,
|
||||
paddingRight: 2,
|
||||
lineHeight: 12,
|
||||
},
|
||||
trackingControls: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
export default VideoPlayer;
|
1
examples/expo/src/assets/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './videos';
|
BIN
examples/expo/src/assets/videos/broadchurch.mp4
Normal file
4
examples/expo/src/assets/videos/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const localeVideo = {
|
||||
broadchurch: require('./broadchurch.mp4'),
|
||||
portrait: require('./portrait.mp4'),
|
||||
};
|
BIN
examples/expo/src/assets/videos/portrait.mp4
Normal file
65
examples/expo/src/components/AudioTracksSelector.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import {Picker} from '@react-native-picker/picker';
|
||||
import {Text} from 'react-native';
|
||||
import {
|
||||
SelectedTrackType,
|
||||
type AudioTrack,
|
||||
type SelectedTrack,
|
||||
} from 'react-native-video';
|
||||
import styles from '../styles';
|
||||
import React from 'react';
|
||||
|
||||
export interface AudioTrackSelectorType {
|
||||
audioTracks: Array<AudioTrack>;
|
||||
selectedAudioTrack: SelectedTrack | undefined;
|
||||
onValueChange: (arg0: string | number) => void;
|
||||
audioTracksSelectionBy: SelectedTrackType;
|
||||
}
|
||||
|
||||
export const AudioTrackSelector = ({
|
||||
audioTracks,
|
||||
selectedAudioTrack,
|
||||
onValueChange,
|
||||
audioTracksSelectionBy,
|
||||
}: AudioTrackSelectorType) => {
|
||||
return (
|
||||
<>
|
||||
<Text style={styles.controlOption}>AudioTrack</Text>
|
||||
<Picker
|
||||
style={styles.picker}
|
||||
itemStyle={styles.pickerItem}
|
||||
selectedValue={selectedAudioTrack?.value}
|
||||
onValueChange={itemValue => {
|
||||
if (itemValue !== 'empty') {
|
||||
console.log('on audio value change ' + itemValue);
|
||||
onValueChange(itemValue);
|
||||
}
|
||||
}}>
|
||||
{audioTracks?.length <= 0 ? (
|
||||
<Picker.Item label={'empty'} value={'empty'} key={'empty'} />
|
||||
) : (
|
||||
<Picker.Item label={'none'} value={'none'} key={'none'} />
|
||||
)}
|
||||
{audioTracks.map(track => {
|
||||
if (!track) {
|
||||
return;
|
||||
}
|
||||
let value;
|
||||
if (audioTracksSelectionBy === SelectedTrackType.INDEX) {
|
||||
value = track.index;
|
||||
} else if (audioTracksSelectionBy === SelectedTrackType.LANGUAGE) {
|
||||
value = track.language;
|
||||
} else if (audioTracksSelectionBy === SelectedTrackType.TITLE) {
|
||||
value = track.title;
|
||||
}
|
||||
return (
|
||||
<Picker.Item
|
||||
label={`${value} - ${track.selected}`}
|
||||
value={`${value}`}
|
||||
key={`${value}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Picker>
|
||||
</>
|
||||
);
|
||||
};
|
8
examples/expo/src/components/Indicator.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import React, {memo} from 'react';
|
||||
import {ActivityIndicator} from 'react-native';
|
||||
|
||||
const _Indicator = () => {
|
||||
return <ActivityIndicator color="#3235fd" size="large" />;
|
||||
};
|
||||
|
||||
export const Indicator = memo(_Indicator);
|
74
examples/expo/src/components/MultiValueControl.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextStyle,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {ResizeMode} from 'react-native-video';
|
||||
|
||||
/*
|
||||
* MultiValueControl displays a list clickable text view
|
||||
*/
|
||||
|
||||
interface MultiValueControlType<T> {
|
||||
// a list a string or number to be displayed
|
||||
values: Array<T>;
|
||||
// The selected value in values
|
||||
selected?: T;
|
||||
// callback to press onPress
|
||||
onPress: (arg: T) => void;
|
||||
}
|
||||
|
||||
export const MultiValueControl = <T extends number | string | ResizeMode>({
|
||||
values,
|
||||
selected,
|
||||
onPress,
|
||||
}: MultiValueControlType<T>) => {
|
||||
const selectedStyle: TextStyle = StyleSheet.flatten([
|
||||
styles.option,
|
||||
{fontWeight: 'bold'},
|
||||
]);
|
||||
|
||||
const unselectedStyle: TextStyle = StyleSheet.flatten([
|
||||
styles.option,
|
||||
{fontWeight: 'normal'},
|
||||
]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{values.map(value => {
|
||||
const _style = value === selected ? selectedStyle : unselectedStyle;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={value}
|
||||
onPress={() => {
|
||||
onPress?.(value);
|
||||
}}>
|
||||
<Text style={_style}>{value}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
option: {
|
||||
alignSelf: 'center',
|
||||
fontSize: 11,
|
||||
color: 'white',
|
||||
paddingLeft: 2,
|
||||
paddingRight: 2,
|
||||
lineHeight: 12,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default MultiValueControl;
|
350
examples/expo/src/components/Overlay.tsx
Normal file
@@ -0,0 +1,350 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
memo,
|
||||
useCallback,
|
||||
type Dispatch,
|
||||
type SetStateAction,
|
||||
} from 'react';
|
||||
import {View} from 'react-native';
|
||||
import styles from '../styles.tsx';
|
||||
import {
|
||||
isAndroid,
|
||||
isIos,
|
||||
textTracksSelectionBy,
|
||||
audioTracksSelectionBy,
|
||||
} from '../constants';
|
||||
import {
|
||||
ResizeMode,
|
||||
VideoRef,
|
||||
SelectedTrackType,
|
||||
SelectedVideoTrackType,
|
||||
VideoDecoderProperties,
|
||||
type EnumValues,
|
||||
type TextTrack,
|
||||
type SelectedVideoTrack,
|
||||
type SelectedTrack,
|
||||
type VideoTrack,
|
||||
type AudioTrack,
|
||||
} from 'react-native-video';
|
||||
|
||||
import {toast} from './Toast';
|
||||
import {Seeker} from './Seeker';
|
||||
import {AudioTrackSelector} from './AudioTracksSelector';
|
||||
import {VideoTrackSelector} from './VideoTracksSelector';
|
||||
import {TextTrackSelector} from './TextTracksSelector';
|
||||
import {TopControl} from './TopControl';
|
||||
import {ToggleControl} from './ToggleControl';
|
||||
import {MultiValueControl} from './MultiValueControl';
|
||||
|
||||
type Props = {
|
||||
channelDown: () => void;
|
||||
channelUp: () => void;
|
||||
setFullscreen: Dispatch<SetStateAction<boolean>>;
|
||||
controls: boolean;
|
||||
setControls: Dispatch<SetStateAction<boolean>>;
|
||||
showNotificationControls: boolean;
|
||||
setShowNotificationControls: Dispatch<SetStateAction<boolean>>;
|
||||
selectedAudioTrack: SelectedTrack | undefined;
|
||||
setSelectedAudioTrack: Dispatch<SetStateAction<SelectedTrack | undefined>>;
|
||||
selectedTextTrack: SelectedTrack | undefined;
|
||||
setSelectedTextTrack: (value: SelectedTrack | undefined) => void;
|
||||
selectedVideoTrack: SelectedVideoTrack;
|
||||
setSelectedVideoTrack: (value: SelectedVideoTrack) => void;
|
||||
setIsSeeking: Dispatch<SetStateAction<boolean>>;
|
||||
rate: number;
|
||||
setRate: Dispatch<SetStateAction<number>>;
|
||||
volume: number;
|
||||
setVolume: (value: number) => void;
|
||||
resizeMode: EnumValues<ResizeMode>;
|
||||
setResizeMode: Dispatch<SetStateAction<EnumValues<ResizeMode>>>;
|
||||
isLoading: boolean;
|
||||
srcListId: number;
|
||||
useCache: boolean;
|
||||
setUseCache: Dispatch<SetStateAction<boolean>>;
|
||||
paused: boolean;
|
||||
setPaused: Dispatch<SetStateAction<boolean>>;
|
||||
repeat: boolean;
|
||||
setRepeat: Dispatch<SetStateAction<boolean>>;
|
||||
showPoster: boolean;
|
||||
setShowPoster: Dispatch<SetStateAction<boolean>>;
|
||||
muted: boolean;
|
||||
setMuted: Dispatch<SetStateAction<boolean>>;
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
isSeeking: boolean;
|
||||
audioTracks: AudioTrack[];
|
||||
textTracks: TextTrack[];
|
||||
videoTracks: VideoTrack[];
|
||||
};
|
||||
|
||||
const _Overlay = forwardRef<VideoRef, Props>((props, ref) => {
|
||||
const {
|
||||
channelUp,
|
||||
channelDown,
|
||||
setFullscreen,
|
||||
setControls,
|
||||
controls,
|
||||
setShowNotificationControls,
|
||||
showNotificationControls,
|
||||
setSelectedAudioTrack,
|
||||
setSelectedTextTrack,
|
||||
setSelectedVideoTrack,
|
||||
setIsSeeking,
|
||||
rate,
|
||||
setRate,
|
||||
volume,
|
||||
setVolume,
|
||||
resizeMode,
|
||||
setResizeMode,
|
||||
isLoading,
|
||||
srcListId,
|
||||
setUseCache,
|
||||
useCache,
|
||||
paused,
|
||||
setPaused,
|
||||
setRepeat,
|
||||
repeat,
|
||||
setShowPoster,
|
||||
showPoster,
|
||||
setMuted,
|
||||
muted,
|
||||
duration,
|
||||
isSeeking,
|
||||
currentTime,
|
||||
textTracks,
|
||||
videoTracks,
|
||||
audioTracks,
|
||||
selectedAudioTrack,
|
||||
selectedVideoTrack,
|
||||
selectedTextTrack,
|
||||
} = props;
|
||||
const popupInfo = useCallback(() => {
|
||||
VideoDecoderProperties.getWidevineLevel().then((widevineLevel: number) => {
|
||||
VideoDecoderProperties.isHEVCSupported().then((hevc: string) => {
|
||||
VideoDecoderProperties.isCodecSupported('video/avc', 1920, 1080).then(
|
||||
(avc: string) => {
|
||||
toast(
|
||||
true,
|
||||
'Widevine level: ' +
|
||||
widevineLevel +
|
||||
'\n hevc: ' +
|
||||
hevc +
|
||||
'\n avc: ' +
|
||||
avc,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
const toggleFullscreen = () => {
|
||||
setFullscreen(prev => !prev);
|
||||
};
|
||||
const toggleControls = () => {
|
||||
setControls(prev => !prev);
|
||||
};
|
||||
|
||||
const openDecoration = () => {
|
||||
typeof ref !== 'function' && ref?.current?.setFullScreen(true);
|
||||
};
|
||||
|
||||
const toggleShowNotificationControls = () => {
|
||||
setShowNotificationControls(prev => !prev);
|
||||
};
|
||||
|
||||
const onSelectedAudioTrackChange = (itemValue: string | number) => {
|
||||
console.log('on audio value change ' + itemValue);
|
||||
if (itemValue === 'none') {
|
||||
setSelectedAudioTrack({
|
||||
type: SelectedTrackType.DISABLED,
|
||||
});
|
||||
} else {
|
||||
setSelectedAudioTrack({type: audioTracksSelectionBy, value: itemValue});
|
||||
}
|
||||
};
|
||||
|
||||
const onSelectedTextTrackChange = (itemValue: string) => {
|
||||
console.log('on value change ' + itemValue);
|
||||
setSelectedTextTrack({type: textTracksSelectionBy, value: itemValue});
|
||||
};
|
||||
|
||||
const onSelectedVideoTrackChange = (itemValue: string) => {
|
||||
console.log('on value change ' + itemValue);
|
||||
if (itemValue === undefined || itemValue === 'auto') {
|
||||
setSelectedVideoTrack({
|
||||
type: SelectedVideoTrackType.AUTO,
|
||||
});
|
||||
} else {
|
||||
setSelectedVideoTrack({
|
||||
type: SelectedVideoTrackType.INDEX,
|
||||
value: itemValue,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const videoSeek = (position: number) => {
|
||||
setIsSeeking(true);
|
||||
typeof ref !== 'function' && ref?.current?.seek(position);
|
||||
};
|
||||
|
||||
const onRateSelected = (value: number) => {
|
||||
setRate(value);
|
||||
};
|
||||
|
||||
const onVolumeSelected = (value: number) => {
|
||||
setVolume(value);
|
||||
};
|
||||
|
||||
const onResizeModeSelected = (value: EnumValues<ResizeMode>) => {
|
||||
setResizeMode(value);
|
||||
};
|
||||
|
||||
const toggleCache = () => setUseCache(prev => !prev);
|
||||
|
||||
const togglePause = () => setPaused(prev => !prev);
|
||||
|
||||
const toggleRepeat = () => setRepeat(prev => !prev);
|
||||
|
||||
const togglePoster = () => setShowPoster(prev => !prev);
|
||||
|
||||
const toggleMuted = () => setMuted(prev => !prev);
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style={styles.topControls}>
|
||||
<View style={styles.resizeModeControl}>
|
||||
<TopControl
|
||||
srcListId={srcListId}
|
||||
showRNVControls={controls}
|
||||
toggleControls={toggleControls}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
{!controls ? (
|
||||
<>
|
||||
<View style={styles.leftControls}>
|
||||
<ToggleControl onPress={channelDown} text="ChDown" />
|
||||
</View>
|
||||
<View style={styles.rightControls}>
|
||||
<ToggleControl onPress={channelUp} text="ChUp" />
|
||||
</View>
|
||||
<View style={styles.bottomControls}>
|
||||
<View style={styles.generalControls}>
|
||||
{isAndroid ? (
|
||||
<View style={styles.generalControls}>
|
||||
<ToggleControl onPress={popupInfo} text="decoderInfo" />
|
||||
<ToggleControl
|
||||
isSelected={useCache}
|
||||
onPress={toggleCache}
|
||||
selectedText="enable cache"
|
||||
unselectedText="disable cache"
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
<ToggleControl
|
||||
isSelected={paused}
|
||||
onPress={togglePause}
|
||||
selectedText="pause"
|
||||
unselectedText="playing"
|
||||
/>
|
||||
<ToggleControl
|
||||
isSelected={repeat}
|
||||
onPress={toggleRepeat}
|
||||
selectedText="loop enable"
|
||||
unselectedText="loop disable"
|
||||
/>
|
||||
<ToggleControl onPress={toggleFullscreen} text="fullscreen" />
|
||||
<ToggleControl onPress={openDecoration} text="decoration" />
|
||||
<ToggleControl
|
||||
isSelected={showPoster}
|
||||
onPress={togglePoster}
|
||||
selectedText="poster"
|
||||
unselectedText="no poster"
|
||||
/>
|
||||
<ToggleControl
|
||||
isSelected={showNotificationControls}
|
||||
onPress={toggleShowNotificationControls}
|
||||
selectedText="hide notification controls"
|
||||
unselectedText="show notification controls"
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.generalControls}>
|
||||
{/* shall be replaced by slider */}
|
||||
<MultiValueControl
|
||||
values={[0, 0.25, 0.5, 1.0, 1.5, 2.0]}
|
||||
onPress={onRateSelected}
|
||||
selected={rate}
|
||||
/>
|
||||
{/* shall be replaced by slider */}
|
||||
<MultiValueControl
|
||||
values={[0.5, 1, 1.5]}
|
||||
onPress={onVolumeSelected}
|
||||
selected={volume}
|
||||
/>
|
||||
<MultiValueControl
|
||||
values={[
|
||||
ResizeMode.COVER,
|
||||
ResizeMode.CONTAIN,
|
||||
ResizeMode.STRETCH,
|
||||
]}
|
||||
onPress={onResizeModeSelected}
|
||||
selected={resizeMode}
|
||||
/>
|
||||
<ToggleControl
|
||||
isSelected={muted}
|
||||
onPress={toggleMuted}
|
||||
text="muted"
|
||||
/>
|
||||
{isIos ? (
|
||||
<ToggleControl
|
||||
isSelected={paused}
|
||||
onPress={() => {
|
||||
typeof ref !== 'function' &&
|
||||
ref?.current
|
||||
?.save({})
|
||||
?.then((response: unknown) => {
|
||||
console.log('Downloaded URI', response);
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
console.log('error during save ', error);
|
||||
});
|
||||
}}
|
||||
text="save"
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
<Seeker
|
||||
currentTime={currentTime}
|
||||
duration={duration}
|
||||
isLoading={isLoading}
|
||||
videoSeek={prop => videoSeek(prop)}
|
||||
isUISeeking={isSeeking}
|
||||
/>
|
||||
<View style={styles.generalControls}>
|
||||
<AudioTrackSelector
|
||||
audioTracks={audioTracks}
|
||||
selectedAudioTrack={selectedAudioTrack}
|
||||
onValueChange={onSelectedAudioTrackChange}
|
||||
audioTracksSelectionBy={audioTracksSelectionBy}
|
||||
/>
|
||||
<TextTrackSelector
|
||||
textTracks={textTracks}
|
||||
selectedTextTrack={selectedTextTrack}
|
||||
onValueChange={onSelectedTextTrackChange}
|
||||
textTracksSelectionBy={textTracksSelectionBy}
|
||||
/>
|
||||
<VideoTrackSelector
|
||||
videoTracks={videoTracks}
|
||||
selectedVideoTrack={selectedVideoTrack}
|
||||
onValueChange={onSelectedVideoTrackChange}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export const Overlay = memo(_Overlay);
|
152
examples/expo/src/components/Seeker.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import {PanResponder, View} from 'react-native';
|
||||
import styles from '../styles';
|
||||
|
||||
interface SeekerProps {
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
isLoading: boolean;
|
||||
isUISeeking: boolean;
|
||||
videoSeek: (arg0: number) => void;
|
||||
}
|
||||
|
||||
export const Seeker = ({
|
||||
currentTime,
|
||||
duration,
|
||||
isLoading,
|
||||
isUISeeking,
|
||||
videoSeek,
|
||||
}: SeekerProps) => {
|
||||
const [seeking, setSeeking] = useState(false);
|
||||
const [seekerPosition, setSeekerPosition] = useState(0);
|
||||
const [seekerWidth, setSeekerWidth] = useState(0);
|
||||
|
||||
/**
|
||||
* Set the position of the seekbar's components
|
||||
* (both fill and handle) according to the
|
||||
* position supplied.
|
||||
*
|
||||
* @param {float} position position in px of seeker handle}
|
||||
*/
|
||||
const updateSeekerPosition = useCallback(
|
||||
(position = 0) => {
|
||||
if (position <= 0) {
|
||||
position = 0;
|
||||
} else if (position >= seekerWidth) {
|
||||
position = seekerWidth;
|
||||
}
|
||||
setSeekerPosition(position);
|
||||
},
|
||||
[seekerWidth],
|
||||
);
|
||||
|
||||
/**
|
||||
* Return the time that the video should be at
|
||||
* based on where the seeker handle is.
|
||||
*
|
||||
* @return {float} time in ms based on seekerPosition.
|
||||
*/
|
||||
const calculateTimeFromSeekerPosition = () => {
|
||||
const percent = seekerPosition / seekerWidth;
|
||||
return duration * percent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get our seekbar responder going
|
||||
*/
|
||||
|
||||
const seekPanResponder = PanResponder.create({
|
||||
// Ask to be the responder.
|
||||
onStartShouldSetPanResponder: (_evt, _gestureState) => true,
|
||||
onMoveShouldSetPanResponder: (_evt, _gestureState) => true,
|
||||
|
||||
/**
|
||||
* When we start the pan tell the machine that we're
|
||||
* seeking. This stops it from updating the seekbar
|
||||
* position in the onProgress listener.
|
||||
*/
|
||||
onPanResponderGrant: (evt, _gestureState) => {
|
||||
const position = evt.nativeEvent.locationX;
|
||||
updateSeekerPosition(position);
|
||||
setSeeking(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* When panning, update the seekbar position, duh.
|
||||
*/
|
||||
onPanResponderMove: (evt, _gestureState) => {
|
||||
const position = evt.nativeEvent.locationX;
|
||||
updateSeekerPosition(position);
|
||||
},
|
||||
|
||||
/**
|
||||
* On release we update the time and seek to it in the video.
|
||||
* If you seek to the end of the video we fire the
|
||||
* onEnd callback
|
||||
*/
|
||||
onPanResponderRelease: (_evt, _gestureState) => {
|
||||
const time = calculateTimeFromSeekerPosition();
|
||||
if (time >= duration && !isLoading) {
|
||||
// FIXME ...
|
||||
// state.paused = true;
|
||||
// this.onEnd();
|
||||
} else {
|
||||
videoSeek(time);
|
||||
setSeeking(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !seeking && !isUISeeking) {
|
||||
const percent = currentTime / duration;
|
||||
const position = seekerWidth * percent;
|
||||
updateSeekerPosition(position);
|
||||
}
|
||||
}, [
|
||||
currentTime,
|
||||
duration,
|
||||
isLoading,
|
||||
seekerWidth,
|
||||
seeking,
|
||||
isUISeeking,
|
||||
updateSeekerPosition,
|
||||
]);
|
||||
|
||||
if (!seekPanResponder) {
|
||||
return null;
|
||||
}
|
||||
const seekerStyle = [
|
||||
styles.seekbarFill,
|
||||
{
|
||||
width: seekerPosition > 0 ? seekerPosition : 0,
|
||||
backgroundColor: '#FFF',
|
||||
},
|
||||
];
|
||||
|
||||
const seekerPositionStyle = [
|
||||
styles.seekbarHandle,
|
||||
{
|
||||
left: seekerPosition > 0 ? seekerPosition : 0,
|
||||
},
|
||||
];
|
||||
|
||||
const seekerPointerStyle = [styles.seekbarCircle, {backgroundColor: '#FFF'}];
|
||||
|
||||
return (
|
||||
<View
|
||||
style={styles.seekbarContainer}
|
||||
{...seekPanResponder.panHandlers}
|
||||
{...styles.generalControls}>
|
||||
<View
|
||||
style={styles.seekbarTrack}
|
||||
onLayout={event => setSeekerWidth(event.nativeEvent.layout.width)}
|
||||
pointerEvents={'none'}>
|
||||
<View style={seekerStyle} pointerEvents={'none'} />
|
||||
</View>
|
||||
<View style={seekerPositionStyle} pointerEvents={'none'}>
|
||||
<View style={seekerPointerStyle} pointerEvents={'none'} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
58
examples/expo/src/components/TextTracksSelector.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import {Picker} from '@react-native-picker/picker';
|
||||
import {Text} from 'react-native';
|
||||
import {
|
||||
type TextTrack,
|
||||
type SelectedTrack,
|
||||
SelectedTrackType,
|
||||
} from 'react-native-video';
|
||||
import styles from '../styles';
|
||||
import React from 'react';
|
||||
|
||||
export interface TextTrackSelectorType {
|
||||
textTracks: Array<TextTrack>;
|
||||
selectedTextTrack: SelectedTrack | undefined;
|
||||
onValueChange: (arg0: string) => void;
|
||||
textTracksSelectionBy: string;
|
||||
}
|
||||
|
||||
export const TextTrackSelector = ({
|
||||
textTracks,
|
||||
selectedTextTrack,
|
||||
onValueChange,
|
||||
textTracksSelectionBy,
|
||||
}: TextTrackSelectorType) => {
|
||||
return (
|
||||
<>
|
||||
<Text style={styles.controlOption}>TextTrack</Text>
|
||||
<Picker
|
||||
style={styles.picker}
|
||||
itemStyle={styles.pickerItem}
|
||||
selectedValue={`${selectedTextTrack?.value}`}
|
||||
onValueChange={itemValue => {
|
||||
if (itemValue !== 'empty') {
|
||||
onValueChange(itemValue);
|
||||
}
|
||||
}}>
|
||||
{textTracks?.length <= 0 ? (
|
||||
<Picker.Item label={'empty'} value={'empty'} key={'empty'} />
|
||||
) : (
|
||||
<Picker.Item label={'none'} value={'none'} key={'none'} />
|
||||
)}
|
||||
{textTracks.map(track => {
|
||||
if (!track) {
|
||||
return;
|
||||
}
|
||||
let value;
|
||||
if (textTracksSelectionBy === SelectedTrackType.INDEX) {
|
||||
value = track.index;
|
||||
} else if (textTracksSelectionBy === SelectedTrackType.LANGUAGE) {
|
||||
value = track.language;
|
||||
} else if (textTracksSelectionBy === SelectedTrackType.TITLE) {
|
||||
value = track.title;
|
||||
}
|
||||
return <Picker.Item label={`${value}`} value={value} key={value} />;
|
||||
})}
|
||||
</Picker>
|
||||
</>
|
||||
);
|
||||
};
|
18
examples/expo/src/components/Toast.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {Alert, ToastAndroid} from 'react-native';
|
||||
import {isAndroid} from '../constants';
|
||||
|
||||
export const toast = (visible: boolean, message: string) => {
|
||||
if (visible) {
|
||||
if (isAndroid) {
|
||||
ToastAndroid.showWithGravityAndOffset(
|
||||
message,
|
||||
ToastAndroid.LONG,
|
||||
ToastAndroid.BOTTOM,
|
||||
25,
|
||||
50,
|
||||
);
|
||||
} else {
|
||||
Alert.alert(message, message);
|
||||
}
|
||||
}
|
||||
};
|
73
examples/expo/src/components/ToggleControl.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextStyle,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
/*
|
||||
* ToggleControl displays a 2 states clickable text
|
||||
*/
|
||||
|
||||
interface ToggleControlType {
|
||||
// boolean indicating if text is selected state
|
||||
isSelected?: boolean;
|
||||
// value of text when selected
|
||||
selectedText?: string;
|
||||
// value of text when NOT selected
|
||||
unselectedText?: string;
|
||||
// default text if no only one text field is needed
|
||||
text?: string;
|
||||
// callback called when pressing the component
|
||||
onPress: () => void;
|
||||
}
|
||||
|
||||
export const ToggleControl = ({
|
||||
isSelected,
|
||||
selectedText,
|
||||
unselectedText,
|
||||
text,
|
||||
onPress,
|
||||
}: ToggleControlType) => {
|
||||
const selectedStyle: TextStyle = StyleSheet.flatten([
|
||||
styles.controlOption,
|
||||
{fontWeight: 'bold'},
|
||||
]);
|
||||
|
||||
const unselectedStyle: TextStyle = StyleSheet.flatten([
|
||||
styles.controlOption,
|
||||
{fontWeight: 'normal'},
|
||||
]);
|
||||
|
||||
const style = isSelected ? selectedStyle : unselectedStyle;
|
||||
const _text = text ? text : isSelected ? selectedText : unselectedText;
|
||||
return (
|
||||
<View style={styles.resizeModeControl}>
|
||||
<TouchableOpacity onPress={onPress}>
|
||||
<Text style={style}>{_text}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
controlOption: {
|
||||
alignSelf: 'center',
|
||||
fontSize: 11,
|
||||
color: 'white',
|
||||
paddingLeft: 2,
|
||||
paddingRight: 2,
|
||||
lineHeight: 12,
|
||||
},
|
||||
resizeModeControl: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default ToggleControl;
|
37
examples/expo/src/components/TopControl.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React, {FC, memo} from 'react';
|
||||
import {Text, TouchableOpacity, View} from 'react-native';
|
||||
import styles from '../styles.tsx';
|
||||
import {srcList} from '../constants';
|
||||
import {type AdditionalSourceInfo} from '../types';
|
||||
|
||||
type Props = {
|
||||
srcListId: number;
|
||||
showRNVControls: boolean;
|
||||
toggleControls: () => void;
|
||||
};
|
||||
|
||||
const _TopControl: FC<Props> = ({
|
||||
toggleControls,
|
||||
showRNVControls,
|
||||
srcListId,
|
||||
}) => {
|
||||
return (
|
||||
<View style={styles.topControlsContainer}>
|
||||
<Text style={styles.controlOption}>
|
||||
{(srcList[srcListId] as AdditionalSourceInfo)?.description ||
|
||||
'local file'}
|
||||
</Text>
|
||||
<View>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
toggleControls();
|
||||
}}>
|
||||
<Text style={styles.leftRightControlOption}>
|
||||
{showRNVControls ? 'Hide controls' : 'Show controls'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export const TopControl = memo(_TopControl);
|
15
examples/expo/src/components/VideoLoader.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import {Text, View} from 'react-native';
|
||||
import {Indicator} from './Indicator.tsx';
|
||||
import React, {memo} from 'react';
|
||||
import styles from '../styles.tsx';
|
||||
|
||||
const _VideoLoader = () => {
|
||||
return (
|
||||
<View style={styles.indicatorContainer}>
|
||||
<Text style={styles.indicatorText}>Loading...</Text>
|
||||
<Indicator />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export const VideoLoader = memo(_VideoLoader);
|
62
examples/expo/src/components/VideoTracksSelector.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import {Picker} from '@react-native-picker/picker';
|
||||
import {Text} from 'react-native';
|
||||
import {
|
||||
SelectedVideoTrackType,
|
||||
type SelectedVideoTrack,
|
||||
type VideoTrack,
|
||||
} from 'react-native-video';
|
||||
import styles from '../styles';
|
||||
import React from 'react';
|
||||
|
||||
export interface VideoTrackSelectorType {
|
||||
videoTracks: Array<VideoTrack>;
|
||||
selectedVideoTrack: SelectedVideoTrack | undefined;
|
||||
onValueChange: (arg0: string) => void;
|
||||
}
|
||||
|
||||
export const VideoTrackSelector = ({
|
||||
videoTracks,
|
||||
selectedVideoTrack,
|
||||
onValueChange,
|
||||
}: VideoTrackSelectorType) => {
|
||||
return (
|
||||
<>
|
||||
<Text style={styles.controlOption}>VideoTrack</Text>
|
||||
<Picker
|
||||
style={styles.picker}
|
||||
itemStyle={styles.pickerItem}
|
||||
selectedValue={
|
||||
selectedVideoTrack === undefined ||
|
||||
selectedVideoTrack?.type === SelectedVideoTrackType.AUTO
|
||||
? 'auto'
|
||||
: `${selectedVideoTrack?.value}`
|
||||
}
|
||||
onValueChange={itemValue => {
|
||||
if (itemValue !== 'empty') {
|
||||
onValueChange(itemValue);
|
||||
}
|
||||
}}>
|
||||
<Picker.Item label={'auto'} value={'auto'} key={'auto'} />
|
||||
{videoTracks?.length <= 0 || videoTracks?.length <= 0 ? (
|
||||
<Picker.Item label={'empty'} value={'empty'} key={'empty'} />
|
||||
) : (
|
||||
<Picker.Item label={'none'} value={'none'} key={'none'} />
|
||||
)}
|
||||
{videoTracks?.map(track => {
|
||||
if (!track) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<Picker.Item
|
||||
label={`${track.width}x${track.height} ${Math.floor(
|
||||
(track.bitrate || 0) / 8 / 1024,
|
||||
)} Kbps`}
|
||||
value={`${track.index}`}
|
||||
key={track.index}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Picker>
|
||||
</>
|
||||
);
|
||||
};
|
11
examples/expo/src/components/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export * from './VideoLoader';
|
||||
export * from './Indicator';
|
||||
export * from './Seeker';
|
||||
export * from './AudioTracksSelector';
|
||||
export * from './VideoTracksSelector';
|
||||
export * from './TextTracksSelector';
|
||||
export * from './Overlay';
|
||||
export * from './TopControl';
|
||||
export * from './Toast';
|
||||
export * from './ToggleControl';
|
||||
export * from './MultiValueControl';
|
182
examples/expo/src/constants/general.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import {
|
||||
BufferConfig,
|
||||
DRMType,
|
||||
ISO639_1,
|
||||
SelectedTrackType,
|
||||
TextTrackType,
|
||||
} from 'react-native-video';
|
||||
import {SampleVideoSource} from '../types';
|
||||
import {localeVideo} from '../assets';
|
||||
import {Platform} from 'react-native';
|
||||
|
||||
// This constant allows to change how the sample behaves regarding to audio and texts selection.
|
||||
// You can change it to change how selector will use tracks information.
|
||||
// by default, index will be displayed and index will be applied to selected tracks.
|
||||
// You can also use LANGUAGE or TITLE
|
||||
export const textTracksSelectionBy = SelectedTrackType.INDEX;
|
||||
export const audioTracksSelectionBy = SelectedTrackType.INDEX;
|
||||
|
||||
export const isIos = Platform.OS === 'ios';
|
||||
|
||||
export const isAndroid = Platform.OS === 'android';
|
||||
|
||||
export const srcAllPlatformList = [
|
||||
{
|
||||
description: 'local file landscape',
|
||||
uri: localeVideo.broadchurch,
|
||||
},
|
||||
{
|
||||
description: 'local file landscape cropped',
|
||||
uri: localeVideo.broadchurch,
|
||||
cropStart: 3000,
|
||||
cropEnd: 10000,
|
||||
},
|
||||
{
|
||||
description: 'video with 90° rotation',
|
||||
uri: 'https://bn-dev.fra1.digitaloceanspaces.com/km-tournament/uploads/rn_image_picker_lib_temp_2ee86a27_9312_4548_84af_7fd75d9ad4dd_ad8b20587a.mp4',
|
||||
},
|
||||
{
|
||||
description: 'local file portrait',
|
||||
uri: localeVideo.portrait,
|
||||
metadata: {
|
||||
title: 'Test Title',
|
||||
subtitle: 'Test Subtitle',
|
||||
artist: 'Test Artist',
|
||||
description: 'Test Description',
|
||||
imageUri:
|
||||
'https://pbs.twimg.com/profile_images/1498641868397191170/6qW2XkuI_400x400.png',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: '(hls|live) red bull tv',
|
||||
textTracksAllowChunklessPreparation: false,
|
||||
uri: 'https://rbmn-live.akamaized.net/hls/live/590964/BoRB-AT/master_928.m3u8',
|
||||
metadata: {
|
||||
title: 'Custom Title',
|
||||
subtitle: 'Custom Subtitle',
|
||||
artist: 'Custom Artist',
|
||||
description: 'Custom Description',
|
||||
imageUri:
|
||||
'https://pbs.twimg.com/profile_images/1498641868397191170/6qW2XkuI_400x400.png',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'invalid URL',
|
||||
uri: 'mmt://www.youtube.com',
|
||||
type: 'mpd',
|
||||
},
|
||||
{description: '(no url) Stopped playback', uri: undefined},
|
||||
{
|
||||
description: '(no view) no View',
|
||||
noView: true,
|
||||
},
|
||||
{
|
||||
description: 'Another live sample',
|
||||
uri: 'https://live.forstreet.cl/live/livestream.m3u8',
|
||||
},
|
||||
{
|
||||
description: 'another bunny (can be saved)',
|
||||
uri: 'https://rawgit.com/mediaelement/mediaelement-files/master/big_buck_bunny.mp4',
|
||||
headers: {referer: 'www.github.com', 'User-Agent': 'react.native.video'},
|
||||
},
|
||||
{
|
||||
description: 'sintel with subtitles',
|
||||
uri: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8',
|
||||
},
|
||||
{
|
||||
description: 'sintel starts at 20sec',
|
||||
uri: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8',
|
||||
startPosition: 50000,
|
||||
},
|
||||
{
|
||||
description: 'mp3 with texttrack',
|
||||
uri: 'https://traffic.libsyn.com/democracynow/wx2024-0702_SOT_DeadCalm-LucileSmith-FULL-V2.mxf-audio.mp3', // an mp3 file
|
||||
textTracks: [], // empty text track list
|
||||
},
|
||||
{
|
||||
description: 'BigBugBunny sideLoaded subtitles',
|
||||
// sideloaded subtitles wont work for streaming like HLS on ios
|
||||
// mp4
|
||||
uri: 'https://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4',
|
||||
textTracks: [
|
||||
{
|
||||
title: 'test',
|
||||
language: 'en' as ISO639_1,
|
||||
type: TextTrackType.VTT,
|
||||
uri: 'https://bitdash-a.akamaihd.net/content/sintel/subtitles/subtitles_en.vtt',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const srcIosList: SampleVideoSource[] = [];
|
||||
|
||||
export const srcAndroidList: SampleVideoSource[] = [
|
||||
{
|
||||
description: 'Another live sample',
|
||||
uri: 'https://live.forstreet.cl/live/livestream.m3u8',
|
||||
},
|
||||
{
|
||||
description: 'asset file',
|
||||
uri: 'asset:///broadchurch.mp4',
|
||||
},
|
||||
{
|
||||
description: '(dash) sintel subtitles',
|
||||
uri: 'https://bitmovin-a.akamaihd.net/content/sintel/sintel.mpd',
|
||||
},
|
||||
{
|
||||
description: '(mp4) big buck bunny',
|
||||
uri: 'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4',
|
||||
},
|
||||
{
|
||||
description: '(mp4|subtitles) demo with sintel Subtitles',
|
||||
uri: 'http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7.8506521BFC350652163895D4C26DEE124209AA9E&key=ik0',
|
||||
type: 'mpd',
|
||||
},
|
||||
{
|
||||
description: '(mp4) big buck bunny With Ads',
|
||||
adTagUrl:
|
||||
'https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpostoptimizedpodbumper&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator=',
|
||||
uri: 'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4',
|
||||
},
|
||||
{
|
||||
description: 'WV: Secure SD & HD (cbcs,MP4,H264)',
|
||||
uri: 'https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs.mpd',
|
||||
drm: {
|
||||
type: DRMType.WIDEVINE,
|
||||
licenseServer:
|
||||
'https://proxy.uat.widevine.com/proxy?provider=widevine_test',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'Secure UHD (cenc)',
|
||||
uri: 'https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_uhd.mpd',
|
||||
drm: {
|
||||
type: DRMType.WIDEVINE,
|
||||
licenseServer:
|
||||
'https://proxy.uat.widevine.com/proxy?provider=widevine_test',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'rtsp big bug bunny',
|
||||
uri: 'rtsp://rtspstream:3cfa3c36a9c00f4aa38f3cd35816b287@zephyr.rtsp.stream/movie',
|
||||
type: 'rtsp',
|
||||
},
|
||||
];
|
||||
|
||||
const platformSrc: SampleVideoSource[] = isAndroid
|
||||
? srcAndroidList
|
||||
: srcIosList;
|
||||
|
||||
export const srcList: SampleVideoSource[] =
|
||||
platformSrc.concat(srcAllPlatformList);
|
||||
|
||||
export const bufferConfig: BufferConfig = {
|
||||
minBufferMs: 15000,
|
||||
maxBufferMs: 50000,
|
||||
bufferForPlaybackMs: 2500,
|
||||
bufferForPlaybackAfterRebufferMs: 5000,
|
||||
live: {
|
||||
targetOffsetMs: 500,
|
||||
},
|
||||
};
|
1
examples/expo/src/constants/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './general';
|
173
examples/expo/src/styles.tsx
Normal file
@@ -0,0 +1,173 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'black',
|
||||
},
|
||||
halfScreen: {
|
||||
position: 'absolute',
|
||||
top: 50,
|
||||
left: 50,
|
||||
bottom: 100,
|
||||
right: 100,
|
||||
},
|
||||
fullScreen: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
},
|
||||
bottomControls: {
|
||||
backgroundColor: 'transparent',
|
||||
borderRadius: 5,
|
||||
position: 'absolute',
|
||||
bottom: 20,
|
||||
left: 20,
|
||||
right: 20,
|
||||
},
|
||||
leftControls: {
|
||||
backgroundColor: 'transparent',
|
||||
borderRadius: 5,
|
||||
position: 'absolute',
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
left: 20,
|
||||
},
|
||||
rightControls: {
|
||||
backgroundColor: 'transparent',
|
||||
borderRadius: 5,
|
||||
position: 'absolute',
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
right: 20,
|
||||
},
|
||||
topControls: {
|
||||
backgroundColor: 'transparent',
|
||||
borderRadius: 4,
|
||||
position: 'absolute',
|
||||
top: 20,
|
||||
left: 20,
|
||||
right: 20,
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
overflow: 'hidden',
|
||||
paddingBottom: 10,
|
||||
},
|
||||
generalControls: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
borderRadius: 4,
|
||||
overflow: 'hidden',
|
||||
paddingBottom: 10,
|
||||
},
|
||||
rateControl: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
volumeControl: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
resizeModeControl: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
leftRightControlOption: {
|
||||
alignSelf: 'center',
|
||||
fontSize: 11,
|
||||
color: 'white',
|
||||
padding: 10,
|
||||
lineHeight: 12,
|
||||
},
|
||||
controlOption: {
|
||||
alignSelf: 'center',
|
||||
fontSize: 11,
|
||||
color: 'white',
|
||||
paddingLeft: 2,
|
||||
paddingRight: 2,
|
||||
lineHeight: 12,
|
||||
},
|
||||
pickerContainer: {
|
||||
width: 100,
|
||||
alignSelf: 'center',
|
||||
color: 'white',
|
||||
borderWidth: 1,
|
||||
borderColor: 'red',
|
||||
},
|
||||
indicatorContainer: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
indicatorText: {
|
||||
color: 'white',
|
||||
},
|
||||
seekbarContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
borderRadius: 4,
|
||||
height: 30,
|
||||
},
|
||||
seekbarTrack: {
|
||||
backgroundColor: '#333',
|
||||
height: 1,
|
||||
position: 'relative',
|
||||
top: 14,
|
||||
width: '100%',
|
||||
},
|
||||
seekbarFill: {
|
||||
backgroundColor: '#FFF',
|
||||
height: 1,
|
||||
width: '100%',
|
||||
},
|
||||
seekbarHandle: {
|
||||
position: 'absolute',
|
||||
marginLeft: -7,
|
||||
height: 28,
|
||||
width: 28,
|
||||
},
|
||||
seekbarCircle: {
|
||||
borderRadius: 12,
|
||||
position: 'relative',
|
||||
top: 8,
|
||||
left: 8,
|
||||
height: 12,
|
||||
width: 12,
|
||||
},
|
||||
picker: {
|
||||
flex: 1,
|
||||
color: 'white',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
width: 100,
|
||||
height: 40,
|
||||
},
|
||||
pickerItem: {
|
||||
color: 'white',
|
||||
width: 100,
|
||||
height: 40,
|
||||
},
|
||||
emptyPickerItem: {
|
||||
color: 'white',
|
||||
marginTop: 20,
|
||||
marginLeft: 20,
|
||||
flex: 1,
|
||||
width: 100,
|
||||
height: 40,
|
||||
},
|
||||
topControlsContainer: {
|
||||
paddingTop: 30,
|
||||
},
|
||||
});
|
||||
|
||||
export default styles;
|
1
examples/expo/src/types/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './types';
|
11
examples/expo/src/types/types.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import {Drm, ReactVideoSource, TextTracks} from 'react-native-video';
|
||||
|
||||
export type AdditionalSourceInfo = {
|
||||
textTracks?: TextTracks;
|
||||
adTagUrl?: string;
|
||||
description?: string;
|
||||
drm?: Drm;
|
||||
noView?: boolean;
|
||||
};
|
||||
|
||||
export type SampleVideoSource = ReactVideoSource | AdditionalSourceInfo;
|
4
examples/expo/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"compilerOptions": {},
|
||||
"extends": "expo/tsconfig.base"
|
||||
}
|