createViewManagers(ReactApplicationContext reactContext) {
- if (config == null) {
- config = new DefaultReactExoplayerConfig(reactContext);
- }
- return Collections.singletonList(new ReactExoplayerViewManager(config));
- }
-}
diff --git a/android-exoplayer/README.md b/android/README.md
similarity index 96%
rename from android-exoplayer/README.md
rename to android/README.md
index 9396763b26..07c85afd8a 100644
--- a/android-exoplayer/README.md
+++ b/android/README.md
@@ -9,7 +9,7 @@ https://github.com/google/ExoPlayer
## Benefits over `react-native-video@0.9.0`:
- Android Video library built by Google, with a lot of support
-- Supports DASH, HlS, & SmoothStreaming adaptive streams
+- Supports DASH, HLS, & SmoothStreaming adaptive streams
- Supports formats such as MP4, M4A, FMP4, WebM, MKV, MP3, Ogg, WAV, MPEG-TS, MPEG-PS, FLV and ADTS (AAC).
- Fewer device specific issues
- Highly customisable
diff --git a/android/build.gradle b/android/build.gradle
index 2fb8dfd2ac..0c3969c9ca 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -4,23 +4,75 @@ def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
+def useExoplayerIMA = safeExtGet("RNVUseExoplayerIMA", false)
+
+println "useExoplayerIMA:" + useExoplayerIMA
+
+// This string is used to define build path.
+// As react native build output directory is react-native path of the module.
+// We need to force a new path on each configuration change.
+// If you add a new build parameter, please add the new value in this string
+def configStringPath = (
+ 'useExoplayerIMA' + useExoplayerIMA \
+).md5()
+
android {
- compileSdkVersion safeExtGet('compileSdkVersion', 28)
- buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
+ compileSdkVersion safeExtGet('compileSdkVersion', 31)
+ buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
+
+ compileOptions {
+ targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_1_8
+ }
defaultConfig {
- minSdkVersion safeExtGet('minSdkVersion', 16)
+ minSdkVersion safeExtGet('minSdkVersion', 21)
targetSdkVersion safeExtGet('targetSdkVersion', 28)
versionCode 1
versionName "1.0"
- ndk {
- abiFilters "armeabi-v7a", "x86"
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ buildDir 'buildOutput_' + configStringPath
+
+ sourceSets {
+ main {
+ java {
+ if (useExoplayerIMA) {
+ exclude 'com/google/ads/interactivemedia/v3/api'
+ exclude 'com/google/android/exoplayer2/ext/ima'
+ }
+ }
}
}
}
+repositories {
+ google()
+}
+
dependencies {
- //noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
- implementation 'com.yqritc:android-scalablevideoview:1.0.4'
+ implementation('com.google.android.exoplayer:exoplayer:2.18.1') {
+ exclude group: 'com.android.support'
+ }
+
+ // All support libs must use the same version
+ implementation "androidx.annotation:annotation:1.1.0"
+ implementation "androidx.core:core:1.1.0"
+ implementation "androidx.media:media:1.1.0"
+ implementation "androidx.activity:activity:1.4.0"
+
+ implementation('com.google.android.exoplayer:extension-okhttp:2.18.1') {
+ exclude group: 'com.squareup.okhttp3', module: 'okhttp'
+ }
+
+ if (useExoplayerIMA) {
+ implementation 'com.google.android.exoplayer:extension-ima:2.18.1'
+ }
+ implementation "com.squareup.okhttp3:okhttp:" + '$OKHTTP_VERSION'
}
diff --git a/android/src/main/java/com/android/vending/expansion/zipfile/APEZProvider.java b/android/src/main/java/com/android/vending/expansion/zipfile/APEZProvider.java
deleted file mode 100644
index 1ba910bf0f..0000000000
--- a/android/src/main/java/com/android/vending/expansion/zipfile/APEZProvider.java
+++ /dev/null
@@ -1,287 +0,0 @@
-package com.android.vending.expansion.zipfile;
-
-/*
- * Copyright (C) 2012 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.
- */
-
-//To implement APEZProvider in your application, you'll want to change
-//the AUTHORITY to match what you define in the manifest.
-
-import com.android.vending.expansion.zipfile.ZipResourceFile.ZipEntryRO;
-
-import android.content.ContentProvider;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.OperationApplicationException;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ProviderInfo;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.provider.BaseColumns;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-
-/**
- * This content provider is an optional part of the library.
- *
- * Most apps don't need to use this class. This defines a
- * ContentProvider that marshalls the data from the ZIP files through a
- * content provider Uri in order to provide file access for certain Android APIs
- * that expect Uri access to media files.
- *
- */
-public abstract class APEZProvider extends ContentProvider {
-
- private ZipResourceFile mAPKExtensionFile;
- private boolean mInit;
-
- public static final String FILEID = BaseColumns._ID;
- public static final String FILENAME = "ZPFN";
- public static final String ZIPFILE = "ZFIL";
- public static final String MODIFICATION = "ZMOD";
- public static final String CRC32 = "ZCRC";
- public static final String COMPRESSEDLEN = "ZCOL";
- public static final String UNCOMPRESSEDLEN = "ZUNL";
- public static final String COMPRESSIONTYPE = "ZTYP";
-
- public static final String[] ALL_FIELDS = {
- FILEID,
- FILENAME,
- ZIPFILE,
- MODIFICATION,
- CRC32,
- COMPRESSEDLEN,
- UNCOMPRESSEDLEN,
- COMPRESSIONTYPE
- };
-
- public static final int FILEID_IDX = 0;
- public static final int FILENAME_IDX = 1;
- public static final int ZIPFILE_IDX = 2;
- public static final int MOD_IDX = 3;
- public static final int CRC_IDX = 4;
- public static final int COMPLEN_IDX = 5;
- public static final int UNCOMPLEN_IDX = 6;
- public static final int COMPTYPE_IDX = 7;
-
- public static final int[] ALL_FIELDS_INT = {
- FILEID_IDX,
- FILENAME_IDX,
- ZIPFILE_IDX,
- MOD_IDX,
- CRC_IDX,
- COMPLEN_IDX,
- UNCOMPLEN_IDX,
- COMPTYPE_IDX
- };
-
- /**
- * This needs to match the authority in your manifest
- */
- public abstract String getAuthority();
-
- @Override
- public int delete(Uri arg0, String arg1, String[] arg2) {
- // TODO Auto-generated method stub
- return 0;
- }
-
- @Override
- public String getType(Uri uri) {
- return "vnd.android.cursor.item/asset";
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- // TODO Auto-generated method stub
- return null;
- }
-
- static private final String NO_FILE = "N";
-
- private boolean initIfNecessary() {
- if ( !mInit ) {
- Context ctx = getContext();
- PackageManager pm = ctx.getPackageManager();
- ProviderInfo pi = pm.resolveContentProvider(getAuthority(), PackageManager.GET_META_DATA);
- PackageInfo packInfo;
- try {
- packInfo = pm.getPackageInfo(ctx.getPackageName(), 0);
- } catch (NameNotFoundException e1) {
- e1.printStackTrace();
- return false;
- }
- int patchFileVersion;
- int mainFileVersion;
- int appVersionCode = packInfo.versionCode;
- String[] resourceFiles = null;
- if ( null != pi.metaData ) {
- mainFileVersion = pi.metaData.getInt("mainVersion", appVersionCode);
- patchFileVersion = pi.metaData.getInt("patchVersion", appVersionCode);
- String mainFileName = pi.metaData.getString("mainFilename", NO_FILE);
- if ( NO_FILE != mainFileName ) {
- String patchFileName = pi.metaData.getString("patchFilename", NO_FILE);
- if ( NO_FILE != patchFileName ) {
- resourceFiles = new String[] { mainFileName, patchFileName };
- } else {
- resourceFiles = new String[] { mainFileName };
- }
- }
- } else {
- mainFileVersion = patchFileVersion = appVersionCode;
- }
- try {
- if ( null == resourceFiles ) {
- mAPKExtensionFile = APKExpansionSupport.getAPKExpansionZipFile(ctx, mainFileVersion, patchFileVersion);
- } else {
- mAPKExtensionFile = APKExpansionSupport.getResourceZipFile(resourceFiles);
- }
- mInit = true;
- return true;
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return false;
- }
-
- @Override
- public boolean onCreate() {
- return true;
- }
-
- @Override
- public AssetFileDescriptor openAssetFile(Uri uri, String mode)
- throws FileNotFoundException {
- initIfNecessary();
- String path = uri.getEncodedPath();
- if ( path.startsWith("/") ) {
- path = path.substring(1);
- }
- return mAPKExtensionFile.getAssetFileDescriptor(path);
- }
-
- @Override
- public ContentProviderResult[] applyBatch(
- ArrayList operations)
- throws OperationApplicationException {
- initIfNecessary();
- return super.applyBatch(operations);
- }
-
- @Override
- public ParcelFileDescriptor openFile(Uri uri, String mode)
- throws FileNotFoundException {
- initIfNecessary();
- AssetFileDescriptor af = openAssetFile(uri, mode);
- if ( null != af ) {
- return af.getParcelFileDescriptor();
- }
- return null;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- initIfNecessary();
- // lists all of the items in the file that match
- ZipEntryRO[] zipEntries;
- if ( null == mAPKExtensionFile ) {
- zipEntries = new ZipEntryRO[0];
- } else {
- zipEntries = mAPKExtensionFile.getAllEntries();
- }
- int[] intProjection;
- if ( null == projection ) {
- intProjection = ALL_FIELDS_INT;
- projection = ALL_FIELDS;
- } else {
- int len = projection.length;
- intProjection = new int[len];
- for ( int i = 0; i < len; i++ ) {
- if ( projection[i].equals(FILEID) ) {
- intProjection[i] = FILEID_IDX;
- } else if ( projection[i].equals(FILENAME) ) {
- intProjection[i] = FILENAME_IDX;
- } else if ( projection[i].equals(ZIPFILE) ) {
- intProjection[i] = ZIPFILE_IDX;
- } else if ( projection[i].equals(MODIFICATION) ) {
- intProjection[i] = MOD_IDX;
- } else if ( projection[i].equals(CRC32) ) {
- intProjection[i] = CRC_IDX;
- } else if ( projection[i].equals(COMPRESSEDLEN) ) {
- intProjection[i] = COMPLEN_IDX;
- } else if ( projection[i].equals(UNCOMPRESSEDLEN) ) {
- intProjection[i] = UNCOMPLEN_IDX;
- } else if ( projection[i].equals(COMPRESSIONTYPE) ) {
- intProjection[i] = COMPTYPE_IDX;
- } else {
- throw new RuntimeException();
- }
- }
- }
- MatrixCursor mc = new MatrixCursor(projection, zipEntries.length);
- int len = intProjection.length;
- for ( ZipEntryRO zer : zipEntries ) {
- MatrixCursor.RowBuilder rb = mc.newRow();
- for ( int i = 0; i < len; i++ ) {
- switch (intProjection[i]) {
- case FILEID_IDX:
- rb.add(i);
- break;
- case FILENAME_IDX:
- rb.add(zer.mFileName);
- break;
- case ZIPFILE_IDX:
- rb.add(zer.getZipFileName());
- break;
- case MOD_IDX:
- rb.add(zer.mWhenModified);
- break;
- case CRC_IDX:
- rb.add(zer.mCRC32);
- break;
- case COMPLEN_IDX:
- rb.add(zer.mCompressedLength);
- break;
- case UNCOMPLEN_IDX:
- rb.add(zer.mUncompressedLength);
- break;
- case COMPTYPE_IDX:
- rb.add(zer.mMethod);
- break;
- }
- }
- }
- return mc;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
- // TODO Auto-generated method stub
- return 0;
- }
-
-}
diff --git a/android/src/main/java/com/android/vending/expansion/zipfile/APKExpansionSupport.java b/android/src/main/java/com/android/vending/expansion/zipfile/APKExpansionSupport.java
deleted file mode 100644
index 34669c1d6f..0000000000
--- a/android/src/main/java/com/android/vending/expansion/zipfile/APKExpansionSupport.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.android.vending.expansion.zipfile;
-/*
- * Copyright (C) 2012 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.
- */
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
-import java.util.Vector;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import android.content.Context;
-import android.os.Environment;
-import android.util.Log;
-
-public class APKExpansionSupport {
- // The shared path to all app expansion files
- private final static String EXP_PATH = "/Android/obb/";
-
- static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) {
- String packageName = ctx.getPackageName();
- Vector ret = new Vector();
- if (Environment.getExternalStorageState().equals(
- Environment.MEDIA_MOUNTED)) {
- // Build the full path to the app's expansion files
- File root = Environment.getExternalStorageDirectory();
- File expPath = new File(root.toString() + EXP_PATH + packageName);
-
- // Check that expansion file path exists
- if (expPath.exists()) {
- if ( mainVersion > 0 ) {
- String strMainPath = expPath + File.separator + "main." + mainVersion + "." + packageName + ".obb";
-// Log.d("APKEXPANSION", strMainPath);
- File main = new File(strMainPath);
- if ( main.isFile() ) {
- ret.add(strMainPath);
- }
- }
- if ( patchVersion > 0 ) {
- String strPatchPath = expPath + File.separator + "patch." + patchVersion + "." + packageName + ".obb";
- File main = new File(strPatchPath);
- if ( main.isFile() ) {
- ret.add(strPatchPath);
- }
- }
- }
- }
- String[] retArray = new String[ret.size()];
- ret.toArray(retArray);
- return retArray;
- }
-
- static public ZipResourceFile getResourceZipFile(String[] expansionFiles) throws IOException {
- ZipResourceFile apkExpansionFile = null;
- for (String expansionFilePath : expansionFiles) {
- if ( null == apkExpansionFile ) {
- apkExpansionFile = new ZipResourceFile(expansionFilePath);
- } else {
- apkExpansionFile.addPatchFile(expansionFilePath);
- }
- }
- return apkExpansionFile;
- }
-
- static public ZipResourceFile getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion) throws IOException{
- String[] expansionFiles = getAPKExpansionFiles(ctx, mainVersion, patchVersion);
- return getResourceZipFile(expansionFiles);
- }
-}
diff --git a/android/src/main/java/com/android/vending/expansion/zipfile/ZipResourceFile.java b/android/src/main/java/com/android/vending/expansion/zipfile/ZipResourceFile.java
deleted file mode 100644
index 902af3c46e..0000000000
--- a/android/src/main/java/com/android/vending/expansion/zipfile/ZipResourceFile.java
+++ /dev/null
@@ -1,428 +0,0 @@
-
-package com.android.vending.expansion.zipfile;
-
-/*
- * Copyright (C) 2012 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.
- */
-import android.content.res.AssetFileDescriptor;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.io.EOFException;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.FileChannel;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Vector;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-public class ZipResourceFile {
-
- //
- // Read-only access to Zip archives, with minimal heap allocation.
- //
- static final String LOG_TAG = "zipro";
- static final boolean LOGV = false;
-
- // 4-byte number
- static private int swapEndian(int i)
- {
- return ((i & 0xff) << 24) + ((i & 0xff00) << 8) + ((i & 0xff0000) >>> 8)
- + ((i >>> 24) & 0xff);
- }
-
- // 2-byte number
- static private int swapEndian(short i)
- {
- return ((i & 0x00FF) << 8 | (i & 0xFF00) >>> 8);
- }
-
- /*
- * Zip file constants.
- */
- static final int kEOCDSignature = 0x06054b50;
- static final int kEOCDLen = 22;
- static final int kEOCDNumEntries = 8; // offset to #of entries in file
- static final int kEOCDSize = 12; // size of the central directory
- static final int kEOCDFileOffset = 16; // offset to central directory
-
- static final int kMaxCommentLen = 65535; // longest possible in ushort
- static final int kMaxEOCDSearch = (kMaxCommentLen + kEOCDLen);
-
- static final int kLFHSignature = 0x04034b50;
- static final int kLFHLen = 30; // excluding variable-len fields
- static final int kLFHNameLen = 26; // offset to filename length
- static final int kLFHExtraLen = 28; // offset to extra length
-
- static final int kCDESignature = 0x02014b50;
- static final int kCDELen = 46; // excluding variable-len fields
- static final int kCDEMethod = 10; // offset to compression method
- static final int kCDEModWhen = 12; // offset to modification timestamp
- static final int kCDECRC = 16; // offset to entry CRC
- static final int kCDECompLen = 20; // offset to compressed length
- static final int kCDEUncompLen = 24; // offset to uncompressed length
- static final int kCDENameLen = 28; // offset to filename length
- static final int kCDEExtraLen = 30; // offset to extra length
- static final int kCDECommentLen = 32; // offset to comment length
- static final int kCDELocalOffset = 42; // offset to local hdr
-
- static final int kCompressStored = 0; // no compression
- static final int kCompressDeflated = 8; // standard deflate
-
- /*
- * The values we return for ZipEntryRO use 0 as an invalid value, so we want
- * to adjust the hash table index by a fixed amount. Using a large value
- * helps insure that people don't mix & match arguments, e.g. to
- * findEntryByIndex().
- */
- static final int kZipEntryAdj = 10000;
-
- static public final class ZipEntryRO {
- public ZipEntryRO(final String zipFileName, final File file, final String fileName) {
- mFileName = fileName;
- mZipFileName = zipFileName;
- mFile = file;
- }
-
- public final File mFile;
- public final String mFileName;
- public final String mZipFileName;
- public long mLocalHdrOffset; // offset of local file header
-
- /* useful stuff from the directory entry */
- public int mMethod;
- public long mWhenModified;
- public long mCRC32;
- public long mCompressedLength;
- public long mUncompressedLength;
-
- public long mOffset = -1;
-
- public void setOffsetFromFile(RandomAccessFile f, ByteBuffer buf) throws IOException {
- long localHdrOffset = mLocalHdrOffset;
- try {
- f.seek(localHdrOffset);
- f.readFully(buf.array());
- if (buf.getInt(0) != kLFHSignature) {
- Log.w(LOG_TAG, "didn't find signature at start of lfh");
- throw new IOException();
- }
- int nameLen = buf.getShort(kLFHNameLen) & 0xFFFF;
- int extraLen = buf.getShort(kLFHExtraLen) & 0xFFFF;
- mOffset = localHdrOffset + kLFHLen + nameLen + extraLen;
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
-
- /**
- * Calculates the offset of the start of the Zip file entry within the
- * Zip file.
- *
- * @return the offset, in bytes from the start of the file of the entry
- */
- public long getOffset() {
- return mOffset;
- }
-
- /**
- * isUncompressed
- *
- * @return true if the file is stored in uncompressed form
- */
- public boolean isUncompressed() {
- return mMethod == kCompressStored;
- }
-
- public AssetFileDescriptor getAssetFileDescriptor() {
- if (mMethod == kCompressStored) {
- ParcelFileDescriptor pfd;
- try {
- pfd = ParcelFileDescriptor.open(mFile, ParcelFileDescriptor.MODE_READ_ONLY);
- return new AssetFileDescriptor(pfd, getOffset(), mUncompressedLength);
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- return null;
- }
-
- public String getZipFileName() {
- return mZipFileName;
- }
-
- public File getZipFile() {
- return mFile;
- }
-
- }
-
- private HashMap mHashMap = new HashMap();
-
- /* for reading compressed files */
- public HashMap mZipFiles = new HashMap();
-
- public ZipResourceFile(String zipFileName) throws IOException {
- addPatchFile(zipFileName);
- }
-
- ZipEntryRO[] getEntriesAt(String path) {
- Vector zev = new Vector();
- Collection values = mHashMap.values();
- if (null == path)
- path = "";
- int length = path.length();
- for (ZipEntryRO ze : values) {
- if (ze.mFileName.startsWith(path)) {
- if (-1 == ze.mFileName.indexOf('/', length)) {
- zev.add(ze);
- }
- }
- }
- ZipEntryRO[] entries = new ZipEntryRO[zev.size()];
- return zev.toArray(entries);
- }
-
- public ZipEntryRO[] getAllEntries() {
- Collection values = mHashMap.values();
- return values.toArray(new ZipEntryRO[values.size()]);
- }
-
- /**
- * getAssetFileDescriptor allows for ZipResourceFile to directly feed
- * Android API's that want an fd, offset, and length such as the
- * MediaPlayer. It also allows for the class to be used in a content
- * provider that can feed video players. The file must be stored
- * (non-compressed) in the Zip file for this to work.
- *
- * @param assetPath
- * @return the asset file descriptor for the file, or null if the file isn't
- * present or is stored compressed
- */
- public AssetFileDescriptor getAssetFileDescriptor(String assetPath) {
- ZipEntryRO entry = mHashMap.get(assetPath);
- if (null != entry) {
- return entry.getAssetFileDescriptor();
- }
- return null;
- }
-
- /**
- * getInputStream returns an AssetFileDescriptor.AutoCloseInputStream
- * associated with the asset that is contained in the Zip file, or a
- * standard ZipInputStream if necessary to uncompress the file
- *
- * @param assetPath
- * @return an input stream for the named asset path, or null if not found
- * @throws IOException
- */
- public InputStream getInputStream(String assetPath) throws IOException {
- ZipEntryRO entry = mHashMap.get(assetPath);
- if (null != entry) {
- if (entry.isUncompressed()) {
- return entry.getAssetFileDescriptor().createInputStream();
- } else {
- ZipFile zf = mZipFiles.get(entry.getZipFile());
- /** read compressed files **/
- if (null == zf) {
- zf = new ZipFile(entry.getZipFile(), ZipFile.OPEN_READ);
- mZipFiles.put(entry.getZipFile(), zf);
- }
- ZipEntry zi = zf.getEntry(assetPath);
- if (null != zi)
- return zf.getInputStream(zi);
- }
- }
- return null;
- }
-
- ByteBuffer mLEByteBuffer = ByteBuffer.allocate(4);
-
- static private int read4LE(RandomAccessFile f) throws EOFException, IOException {
- return swapEndian(f.readInt());
- }
-
- /*
- * Opens the specified file read-only. We memory-map the entire thing and
- * close the file before returning.
- */
- void addPatchFile(String zipFileName) throws IOException
- {
- File file = new File(zipFileName);
- RandomAccessFile f = new RandomAccessFile(file, "r");
- long fileLength = f.length();
-
- if (fileLength < kEOCDLen) {
- throw new java.io.IOException();
- }
-
- long readAmount = kMaxEOCDSearch;
- if (readAmount > fileLength)
- readAmount = fileLength;
-
- /*
- * Make sure this is a Zip archive.
- */
- f.seek(0);
-
- int header = read4LE(f);
- if (header == kEOCDSignature) {
- Log.i(LOG_TAG, "Found Zip archive, but it looks empty");
- throw new IOException();
- } else if (header != kLFHSignature) {
- Log.v(LOG_TAG, "Not a Zip archive");
- throw new IOException();
- }
-
- /*
- * Perform the traditional EOCD snipe hunt. We're searching for the End
- * of Central Directory magic number, which appears at the start of the
- * EOCD block. It's followed by 18 bytes of EOCD stuff and up to 64KB of
- * archive comment. We need to read the last part of the file into a
- * buffer, dig through it to find the magic number, parse some values
- * out, and use those to determine the extent of the CD. We start by
- * pulling in the last part of the file.
- */
- long searchStart = fileLength - readAmount;
-
- f.seek(searchStart);
- ByteBuffer bbuf = ByteBuffer.allocate((int) readAmount);
- byte[] buffer = bbuf.array();
- f.readFully(buffer);
- bbuf.order(ByteOrder.LITTLE_ENDIAN);
-
- /*
- * Scan backward for the EOCD magic. In an archive without a trailing
- * comment, we'll find it on the first try. (We may want to consider
- * doing an initial minimal read; if we don't find it, retry with a
- * second read as above.)
- */
-
- // EOCD == 0x50, 0x4b, 0x05, 0x06
- int eocdIdx;
- for (eocdIdx = buffer.length - kEOCDLen; eocdIdx >= 0; eocdIdx--) {
- if (buffer[eocdIdx] == 0x50 && bbuf.getInt(eocdIdx) == kEOCDSignature)
- {
- if (LOGV) {
- Log.v(LOG_TAG, "+++ Found EOCD at index: " + eocdIdx);
- }
- break;
- }
- }
-
- if (eocdIdx < 0) {
- Log.d(LOG_TAG, "Zip: EOCD not found, " + zipFileName + " is not zip");
- }
-
- /*
- * Grab the CD offset and size, and the number of entries in the
- * archive. After that, we can release our EOCD hunt buffer.
- */
-
- int numEntries = bbuf.getShort(eocdIdx + kEOCDNumEntries);
- long dirSize = bbuf.getInt(eocdIdx + kEOCDSize) & 0xffffffffL;
- long dirOffset = bbuf.getInt(eocdIdx + kEOCDFileOffset) & 0xffffffffL;
-
- // Verify that they look reasonable.
- if (dirOffset + dirSize > fileLength) {
- Log.w(LOG_TAG, "bad offsets (dir " + dirOffset + ", size " + dirSize + ", eocd "
- + eocdIdx + ")");
- throw new IOException();
- }
- if (numEntries == 0) {
- Log.w(LOG_TAG, "empty archive?");
- throw new IOException();
- }
-
- if (LOGV) {
- Log.v(LOG_TAG, "+++ numEntries=" + numEntries + " dirSize=" + dirSize + " dirOffset="
- + dirOffset);
- }
-
- MappedByteBuffer directoryMap = f.getChannel()
- .map(FileChannel.MapMode.READ_ONLY, dirOffset, dirSize);
- directoryMap.order(ByteOrder.LITTLE_ENDIAN);
-
- byte[] tempBuf = new byte[0xffff];
-
- /*
- * Walk through the central directory, adding entries to the hash table.
- */
-
- int currentOffset = 0;
-
- /*
- * Allocate the local directory information
- */
- ByteBuffer buf = ByteBuffer.allocate(kLFHLen);
- buf.order(ByteOrder.LITTLE_ENDIAN);
-
- for (int i = 0; i < numEntries; i++) {
- if (directoryMap.getInt(currentOffset) != kCDESignature) {
- Log.w(LOG_TAG, "Missed a central dir sig (at " + currentOffset + ")");
- throw new IOException();
- }
-
- /* useful stuff from the directory entry */
- int fileNameLen = directoryMap.getShort(currentOffset + kCDENameLen) & 0xffff;
- int extraLen = directoryMap.getShort(currentOffset + kCDEExtraLen) & 0xffff;
- int commentLen = directoryMap.getShort(currentOffset + kCDECommentLen) & 0xffff;
-
- /* get the CDE filename */
-
- directoryMap.position(currentOffset + kCDELen);
- directoryMap.get(tempBuf, 0, fileNameLen);
- directoryMap.position(0);
-
- /* UTF-8 on Android */
- String str = new String(tempBuf, 0, fileNameLen);
- if (LOGV) {
- Log.v(LOG_TAG, "Filename: " + str);
- }
-
- ZipEntryRO ze = new ZipEntryRO(zipFileName, file, str);
- ze.mMethod = directoryMap.getShort(currentOffset + kCDEMethod) & 0xffff;
- ze.mWhenModified = directoryMap.getInt(currentOffset + kCDEModWhen) & 0xffffffffL;
- ze.mCRC32 = directoryMap.getLong(currentOffset + kCDECRC) & 0xffffffffL;
- ze.mCompressedLength = directoryMap.getLong(currentOffset + kCDECompLen) & 0xffffffffL;
- ze.mUncompressedLength = directoryMap.getLong(currentOffset + kCDEUncompLen) & 0xffffffffL;
- ze.mLocalHdrOffset = directoryMap.getInt(currentOffset + kCDELocalOffset) & 0xffffffffL;
-
- // set the offsets
- buf.clear();
- ze.setOffsetFromFile(f, buf);
-
- // put file into hash
- mHashMap.put(str, ze);
-
- // go to next directory entry
- currentOffset += kCDELen + fileNameLen + extraLen + commentLen;
- }
- if (LOGV) {
- Log.v(LOG_TAG, "+++ zip good scan " + numEntries + " entries");
- }
- }
-}
diff --git a/android/src/main/java/com/brentvatne/ReactBridgeUtils.java b/android/src/main/java/com/brentvatne/ReactBridgeUtils.java
new file mode 100644
index 0000000000..c339128399
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/ReactBridgeUtils.java
@@ -0,0 +1,22 @@
+package com.brentvatne;
+
+import com.facebook.react.bridge.ReadableMap;
+
+/*
+* This file define static helpers to parse in an easier way input props
+ */
+public class ReactBridgeUtils {
+ /*
+ retrieve key from map as int. fallback is returned if not available
+ */
+ static public int safeGetInt(ReadableMap map, String key, int fallback) {
+ return map != null && map.hasKey(key) && !map.isNull(key) ? map.getInt(key) : fallback;
+ }
+
+ /*
+ retrieve key from map as double. fallback is returned if not available
+ */
+ static public double safeGetDouble(ReadableMap map, String key, double fallback) {
+ return map != null && map.hasKey(key) && !map.isNull(key) ? map.getDouble(key) : fallback;
+ }
+}
diff --git a/android/src/main/java/com/brentvatne/common/Track.java b/android/src/main/java/com/brentvatne/common/Track.java
new file mode 100644
index 0000000000..198f9ec764
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/common/Track.java
@@ -0,0 +1,13 @@
+package com.brentvatne.common;
+import android.net.Uri;
+
+public class Track
+{
+ public String m_title;
+ public Uri m_uri;
+ public String m_mimeType;
+ public String m_language;
+ public boolean m_isSelected;
+ public int m_bitrate;
+ public int m_index;
+}
diff --git a/android/src/main/java/com/brentvatne/common/VideoTrack.java b/android/src/main/java/com/brentvatne/common/VideoTrack.java
new file mode 100644
index 0000000000..6eccd983f2
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/common/VideoTrack.java
@@ -0,0 +1,12 @@
+package com.brentvatne.common;
+
+public class VideoTrack
+{
+ public int m_width = 0;
+ public int m_height = 0;
+ public int m_bitrate = 0;
+ public String m_codecs = "";
+ public int m_id = -1;
+ public String m_trackId = "";
+ public boolean m_isSelected = false;
+}
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.java b/android/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.java
similarity index 100%
rename from android-exoplayer/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.java
rename to android/src/main/java/com/brentvatne/exoplayer/AspectRatioFrameLayout.java
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java b/android/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java
similarity index 89%
rename from android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java
rename to android/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java
index 19dda002d6..c5e7047b90 100644
--- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java
+++ b/android/src/main/java/com/brentvatne/exoplayer/DataSourceUtil.java
@@ -4,13 +4,14 @@
import com.facebook.react.modules.network.CookieJarContainer;
import com.facebook.react.modules.network.ForwardingCookieHandler;
import com.facebook.react.modules.network.OkHttpClientProvider;
-import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory;
+import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
-import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
+import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Util;
+import okhttp3.Call;
import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient;
import java.util.Map;
@@ -75,7 +76,7 @@ private static DataSource.Factory buildRawDataSourceFactory(ReactContext context
}
private static DataSource.Factory buildDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map requestHeaders) {
- return new DefaultDataSourceFactory(context, bandwidthMeter,
+ return new DefaultDataSource.Factory(context,
buildHttpDataSourceFactory(context, bandwidthMeter, requestHeaders));
}
@@ -84,10 +85,12 @@ private static HttpDataSource.Factory buildHttpDataSourceFactory(ReactContext co
CookieJarContainer container = (CookieJarContainer) client.cookieJar();
ForwardingCookieHandler handler = new ForwardingCookieHandler(context);
container.setCookieJar(new JavaNetCookieJar(handler));
- OkHttpDataSourceFactory okHttpDataSourceFactory = new OkHttpDataSourceFactory(client, getUserAgent(context), bandwidthMeter);
+ OkHttpDataSource.Factory okHttpDataSourceFactory = new OkHttpDataSource.Factory((Call.Factory) client)
+ .setUserAgent(getUserAgent(context))
+ .setTransferListener(bandwidthMeter);
if (requestHeaders != null)
- okHttpDataSourceFactory.getDefaultRequestProperties().set(requestHeaders);
+ okHttpDataSourceFactory.setDefaultRequestProperties(requestHeaders);
return okHttpDataSourceFactory;
}
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DefaultReactExoplayerConfig.java b/android/src/main/java/com/brentvatne/exoplayer/DefaultReactExoplayerConfig.java
similarity index 60%
rename from android-exoplayer/src/main/java/com/brentvatne/exoplayer/DefaultReactExoplayerConfig.java
rename to android/src/main/java/com/brentvatne/exoplayer/DefaultReactExoplayerConfig.java
index d68274b7de..3475273c6e 100644
--- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/DefaultReactExoplayerConfig.java
+++ b/android/src/main/java/com/brentvatne/exoplayer/DefaultReactExoplayerConfig.java
@@ -9,16 +9,28 @@
public class DefaultReactExoplayerConfig implements ReactExoplayerConfig {
private final DefaultBandwidthMeter bandwidthMeter;
+ private boolean disableDisconnectError = false;
public DefaultReactExoplayerConfig(Context context) {
this.bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build();
}
- @Override
public LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount) {
+ if (this.disableDisconnectError) {
+ // Use custom error handling policy to prevent throwing an error when losing network connection
+ return new ReactExoplayerLoadErrorHandlingPolicy(minLoadRetryCount);
+ }
return new DefaultLoadErrorHandlingPolicy(minLoadRetryCount);
}
+ public void setDisableDisconnectError(boolean disableDisconnectError) {
+ this.disableDisconnectError = disableDisconnectError;
+ }
+
+ public boolean getDisableDisconnectError() {
+ return this.disableDisconnectError;
+ }
+
@Override
public DefaultBandwidthMeter getBandwidthMeter() {
return bandwidthMeter;
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java
similarity index 70%
rename from android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java
rename to android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java
index 61cb0dd611..3b7df5b20b 100644
--- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java
+++ b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java
@@ -4,7 +4,7 @@
import android.content.Context;
import androidx.core.content.ContextCompat;
import android.util.AttributeSet;
-import android.util.Log;
+import android.util.TypedValue;
import android.view.Gravity;
import android.view.SurfaceView;
import android.view.TextureView;
@@ -13,34 +13,35 @@
import android.widget.FrameLayout;
import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlaybackException;
-import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
-import com.google.android.exoplayer2.SimpleExoPlayer;
-import com.google.android.exoplayer2.video.VideoListener;
+import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline;
-import com.google.android.exoplayer2.source.TrackGroupArray;
+import com.google.android.exoplayer2.Tracks;
import com.google.android.exoplayer2.text.Cue;
-import com.google.android.exoplayer2.text.TextRenderer;
-import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
+import com.google.android.exoplayer2.ui.AdViewProvider;
import com.google.android.exoplayer2.ui.SubtitleView;
+import com.google.android.exoplayer2.util.Assertions;
+import com.google.android.exoplayer2.video.VideoSize;
import java.util.List;
-@TargetApi(16)
-public final class ExoPlayerView extends FrameLayout {
+public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
private View surfaceView;
private final View shutterView;
private final SubtitleView subtitleLayout;
private final AspectRatioFrameLayout layout;
private final ComponentListener componentListener;
- private SimpleExoPlayer player;
+ private ExoPlayer player;
private Context context;
private ViewGroup.LayoutParams layoutParams;
+ private final FrameLayout adOverlayFrameLayout;
private boolean useTextureView = true;
+ private boolean useSecureView = false;
private boolean hideShutterView = false;
public ExoPlayerView(Context context) {
@@ -80,8 +81,11 @@ public ExoPlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
updateSurfaceView();
+ adOverlayFrameLayout = new FrameLayout(context);
+
layout.addView(shutterView, 1, layoutParams);
layout.addView(subtitleLayout, 2, layoutParams);
+ layout.addView(adOverlayFrameLayout, 3, layoutParams);
addViewInLayout(layout, 0, aspectRatioParams);
}
@@ -101,9 +105,27 @@ private void setVideoView() {
player.setVideoSurfaceView((SurfaceView) surfaceView);
}
}
+ public void setSubtitleStyle(SubtitleStyle style) {
+ // ensure we reset subtile style before reapplying it
+ subtitleLayout.setUserDefaultStyle();
+ subtitleLayout.setUserDefaultTextSize();
+
+ if (style.getFontSize() > 0) {
+ subtitleLayout.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, style.getFontSize());
+ }
+ subtitleLayout.setPadding(style.getPaddingLeft(), style.getPaddingTop(), style.getPaddingRight(), style.getPaddingBottom());
+ }
private void updateSurfaceView() {
- View view = useTextureView ? new TextureView(context) : new SurfaceView(context);
+ View view;
+ if (!useTextureView || useSecureView) {
+ view = new SurfaceView(context);
+ if (useSecureView) {
+ ((SurfaceView)view).setSecure(true);
+ }
+ } else {
+ view = new TextureView(context);
+ }
view.setLayoutParams(layoutParams);
surfaceView = view;
@@ -121,30 +143,39 @@ private void updateShutterViewVisibility() {
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
}
+ @Override
+ public void requestLayout() {
+ super.requestLayout();
+ post(measureAndLayout);
+ }
+
+ // AdsLoader.AdViewProvider implementation.
+
+ @Override
+ public ViewGroup getAdViewGroup() {
+ return Assertions.checkNotNull(adOverlayFrameLayout, "exo_ad_overlay must be present for ad playback");
+ }
+
/**
- * Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#addTextOutput} and
- * {@link SimpleExoPlayer#addVideoListener} method of the player will be called and previous
+ * Set the {@link ExoPlayer} to use. The {@link ExoPlayer#addListener} method of the
+ * player will be called and previous
* assignments are overridden.
*
- * @param player The {@link SimpleExoPlayer} to use.
+ * @param player The {@link ExoPlayer} to use.
*/
- public void setPlayer(SimpleExoPlayer player) {
+ public void setPlayer(ExoPlayer player) {
if (this.player == player) {
return;
}
if (this.player != null) {
- this.player.removeTextOutput(componentListener);
- this.player.removeVideoListener(componentListener);
this.player.removeListener(componentListener);
clearVideoView();
}
this.player = player;
- shutterView.setVisibility(VISIBLE);
+ shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
if (player != null) {
setVideoView();
- player.addVideoListener(componentListener);
player.addListener(componentListener);
- player.addTextOutput(componentListener);
}
}
@@ -178,6 +209,13 @@ public void setUseTextureView(boolean useTextureView) {
}
}
+ public void useSecureView(boolean useSecureView) {
+ if (useSecureView != this.useSecureView) {
+ this.useSecureView = useSecureView;
+ updateSurfaceView();
+ }
+ }
+
public void setHideShutterView(boolean hideShutterView) {
this.hideShutterView = hideShutterView;
updateShutterViewVisibility();
@@ -206,7 +244,7 @@ private void updateForCurrentTrackSelections() {
}
}
// Video disabled so the shutter must be closed.
- shutterView.setVisibility(VISIBLE);
+ shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
}
public void invalidateAspectRatio() {
@@ -214,22 +252,21 @@ public void invalidateAspectRatio() {
layout.invalidateAspectRatio();
}
- private final class ComponentListener implements VideoListener,
- TextOutput, ExoPlayer.EventListener {
+ private final class ComponentListener implements Player.Listener {
// TextRenderer.Output implementation
@Override
public void onCues(List cues) {
- subtitleLayout.onCues(cues);
+ subtitleLayout.setCues(cues);
}
- // SimpleExoPlayer.VideoListener implementation
+ // ExoPlayer.VideoListener implementation
@Override
- public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
+ public void onVideoSizeChanged(VideoSize videoSize) {
boolean isInitialRatio = layout.getAspectRatio() == 0;
- layout.setAspectRatio(height == 0 ? 1 : (width * pixelWidthHeightRatio) / height);
+ layout.setAspectRatio(videoSize.height == 0 ? 1 : (videoSize.width * videoSize.pixelWidthHeightRatio) / videoSize.height);
// React native workaround for measuring and layout on initial load.
if (isInitialRatio) {
@@ -245,43 +282,43 @@ public void onRenderedFirstFrame() {
// ExoPlayer.EventListener implementation
@Override
- public void onLoadingChanged(boolean isLoading) {
+ public void onIsLoadingChanged(boolean isLoading) {
// Do nothing.
}
@Override
- public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
+ public void onPlaybackStateChanged(int playbackState) {
// Do nothing.
}
@Override
- public void onPlayerError(ExoPlaybackException e) {
+ public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
// Do nothing.
}
@Override
- public void onPositionDiscontinuity(int reason) {
+ public void onPlayerError(PlaybackException e) {
// Do nothing.
}
@Override
- public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
+ public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
// Do nothing.
}
@Override
- public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
- updateForCurrentTrackSelections();
+ public void onTimelineChanged(Timeline timeline, int reason) {
+ // Do nothing.
}
@Override
- public void onPlaybackParametersChanged(PlaybackParameters params) {
- // Do nothing
+ public void onTracksChanged(Tracks tracks) {
+ updateForCurrentTrackSelections();
}
@Override
- public void onSeekProcessed() {
- // Do nothing.
+ public void onPlaybackParametersChanged(PlaybackParameters params) {
+ // Do nothing
}
@Override
diff --git a/android/src/main/java/com/brentvatne/exoplayer/FullScreenPlayerView.java b/android/src/main/java/com/brentvatne/exoplayer/FullScreenPlayerView.java
new file mode 100644
index 0000000000..f57bb96425
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/exoplayer/FullScreenPlayerView.java
@@ -0,0 +1,80 @@
+package com.brentvatne.exoplayer;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+
+import androidx.activity.OnBackPressedCallback;
+
+import com.google.android.exoplayer2.ui.PlayerControlView;
+
+public class FullScreenPlayerView extends Dialog {
+ private final PlayerControlView playerControlView;
+ private final ExoPlayerView exoPlayerView;
+ private ViewGroup parent;
+ private final FrameLayout containerView;
+ private final OnBackPressedCallback onBackPressedCallback;
+
+ public FullScreenPlayerView(Context context, ExoPlayerView exoPlayerView, PlayerControlView playerControlView, OnBackPressedCallback onBackPressedCallback) {
+ super(context, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
+ this.playerControlView = playerControlView;
+ this.exoPlayerView = exoPlayerView;
+ this.onBackPressedCallback = onBackPressedCallback;
+ containerView = new FrameLayout(context);
+ setContentView(containerView, generateDefaultLayoutParams());
+ }
+
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ onBackPressedCallback.handleOnBackPressed();
+ }
+
+ @Override
+ protected void onStart() {
+ parent = (FrameLayout)(exoPlayerView.getParent());
+
+ parent.removeView(exoPlayerView);
+ containerView.addView(exoPlayerView, generateDefaultLayoutParams());
+
+ if (playerControlView != null) {
+ ImageButton imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen);
+ imageButton.setImageResource(com.google.android.exoplayer2.ui.R.drawable.exo_icon_fullscreen_exit);
+ imageButton.setContentDescription(getContext().getString(com.google.android.exoplayer2.ui.R.string.exo_controls_fullscreen_exit_description));
+ parent.removeView(playerControlView);
+ containerView.addView(playerControlView, generateDefaultLayoutParams());
+ }
+
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ containerView.removeView(exoPlayerView);
+ parent.addView(exoPlayerView, generateDefaultLayoutParams());
+
+ if (playerControlView != null) {
+ ImageButton imageButton = playerControlView.findViewById(com.brentvatne.react.R.id.exo_fullscreen);
+ imageButton.setImageResource(com.google.android.exoplayer2.ui.R.drawable.exo_icon_fullscreen_enter);
+ imageButton.setContentDescription(getContext().getString(com.google.android.exoplayer2.ui.R.string.exo_controls_fullscreen_enter_description));
+ containerView.removeView(playerControlView);
+ parent.addView(playerControlView, generateDefaultLayoutParams());
+ }
+
+ parent.requestLayout();
+ parent = null;
+
+ super.onStop();
+ }
+
+ private FrameLayout.LayoutParams generateDefaultLayoutParams() {
+ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ );
+ layoutParams.setMargins(0, 0, 0, 0);
+ return layoutParams;
+ }
+}
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/RawResourceDataSourceFactory.java b/android/src/main/java/com/brentvatne/exoplayer/RawResourceDataSourceFactory.java
similarity index 100%
rename from android-exoplayer/src/main/java/com/brentvatne/exoplayer/RawResourceDataSourceFactory.java
rename to android/src/main/java/com/brentvatne/exoplayer/RawResourceDataSourceFactory.java
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerConfig.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerConfig.java
similarity index 78%
rename from android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerConfig.java
rename to android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerConfig.java
index 522578b775..2cc56f9a7c 100644
--- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerConfig.java
+++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerConfig.java
@@ -9,5 +9,8 @@
public interface ReactExoplayerConfig {
LoadErrorHandlingPolicy buildLoadErrorHandlingPolicy(int minLoadRetryCount);
+ void setDisableDisconnectError(boolean disableDisconnectError);
+ boolean getDisableDisconnectError();
+
DefaultBandwidthMeter getBandwidthMeter();
}
diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java
new file mode 100644
index 0000000000..d0d68de17a
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerLoadErrorHandlingPolicy.java
@@ -0,0 +1,36 @@
+package com.brentvatne.exoplayer;
+
+import java.io.IOException;
+import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy;
+import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException;
+import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy.LoadErrorInfo;
+import com.google.android.exoplayer2.C;
+
+public final class ReactExoplayerLoadErrorHandlingPolicy extends DefaultLoadErrorHandlingPolicy {
+ private int minLoadRetryCount = Integer.MAX_VALUE;
+
+ public ReactExoplayerLoadErrorHandlingPolicy(int minLoadRetryCount) {
+ super(minLoadRetryCount);
+ this.minLoadRetryCount = minLoadRetryCount;
+ }
+
+ @Override
+ public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
+ if (
+ loadErrorInfo.exception instanceof HttpDataSourceException &&
+ (loadErrorInfo.exception.getMessage() == "Unable to connect" || loadErrorInfo.exception.getMessage() == "Software caused connection abort")
+ ) {
+ // Capture the error we get when there is no network connectivity and keep retrying it
+ return 1000; // Retry every second
+ } else if(loadErrorInfo.errorCount < this.minLoadRetryCount) {
+ return Math.min((loadErrorInfo.errorCount - 1) * 1000, 5000); // Default timeout handling
+ } else {
+ return C.TIME_UNSET; // Done retrying and will return the error immediately
+ }
+ }
+
+ @Override
+ public int getMinimumLoadableRetryCount(int dataType) {
+ return Integer.MAX_VALUE;
+ }
+}
\ No newline at end of file
diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
new file mode 100644
index 0000000000..ebecbdde0d
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
@@ -0,0 +1,2039 @@
+package com.brentvatne.exoplayer;
+
+import static com.google.android.exoplayer2.C.CONTENT_TYPE_DASH;
+import static com.google.android.exoplayer2.C.CONTENT_TYPE_HLS;
+import static com.google.android.exoplayer2.C.CONTENT_TYPE_OTHER;
+import static com.google.android.exoplayer2.C.CONTENT_TYPE_SS;
+import static com.google.android.exoplayer2.C.TIME_END_OF_SOURCE;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.view.accessibility.CaptioningManager;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+
+import androidx.annotation.WorkerThread;
+import androidx.activity.OnBackPressedCallback;
+
+import com.brentvatne.common.Track;
+import com.brentvatne.common.VideoTrack;
+import com.brentvatne.react.R;
+import com.brentvatne.receiver.AudioBecomingNoisyReceiver;
+import com.brentvatne.receiver.BecomingNoisyListener;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.LifecycleEventListener;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.uimanager.ThemedReactContext;
+import com.google.ads.interactivemedia.v3.api.AdEvent;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.DefaultLoadControl;
+import com.google.android.exoplayer2.DefaultRenderersFactory;
+import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.MediaItem;
+import com.google.android.exoplayer2.PlaybackException;
+import com.google.android.exoplayer2.PlaybackParameters;
+import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.Tracks;
+import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
+import com.google.android.exoplayer2.drm.DefaultDrmSessionManagerProvider;
+import com.google.android.exoplayer2.drm.DrmSessionEventListener;
+import com.google.android.exoplayer2.drm.DrmSessionManager;
+import com.google.android.exoplayer2.drm.DrmSessionManagerProvider;
+import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
+import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
+import com.google.android.exoplayer2.drm.UnsupportedDrmException;
+import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
+import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
+import com.google.android.exoplayer2.metadata.Metadata;
+import com.google.android.exoplayer2.source.MediaSource;
+import com.google.android.exoplayer2.source.MergingMediaSource;
+import com.google.android.exoplayer2.source.ProgressiveMediaSource;
+import com.google.android.exoplayer2.source.SingleSampleMediaSource;
+import com.google.android.exoplayer2.source.TrackGroup;
+import com.google.android.exoplayer2.source.TrackGroupArray;
+import com.google.android.exoplayer2.source.dash.DashMediaSource;
+import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
+import com.google.android.exoplayer2.source.hls.HlsMediaSource;
+import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource;
+import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
+import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
+import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
+import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
+import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
+import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
+import com.google.android.exoplayer2.trackselection.TrackSelectionOverride;
+import com.google.android.exoplayer2.ui.PlayerControlView;
+import com.google.android.exoplayer2.upstream.BandwidthMeter;
+import com.google.android.exoplayer2.upstream.DataSource;
+import com.google.android.exoplayer2.upstream.DataSpec;
+import com.google.android.exoplayer2.upstream.DefaultAllocator;
+import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
+import com.google.android.exoplayer2.upstream.HttpDataSource;
+import com.google.android.exoplayer2.util.Util;
+import com.google.android.exoplayer2.trackselection.TrackSelection;
+import com.google.android.exoplayer2.source.dash.DashUtil;
+import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
+import com.google.android.exoplayer2.source.dash.manifest.Period;
+import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
+import com.google.android.exoplayer2.source.dash.manifest.Representation;
+
+import com.google.android.exoplayer2.ext.ima.ImaAdsLoader;
+import com.google.android.exoplayer2.source.ads.AdsMediaSource;
+import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
+import com.google.android.exoplayer2.source.ClippingMediaSource;
+
+import com.google.common.collect.ImmutableList;
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.CookiePolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
+import java.util.Map;
+import java.lang.Thread;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.lang.Integer;
+
+@SuppressLint("ViewConstructor")
+class ReactExoplayerView extends FrameLayout implements
+ LifecycleEventListener,
+ Player.Listener,
+ BandwidthMeter.EventListener,
+ BecomingNoisyListener,
+ AudioManager.OnAudioFocusChangeListener,
+ DrmSessionEventListener,
+ AdEvent.AdEventListener {
+
+ public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1;
+ public static final double DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE = 0;
+ public static final double DEFAULT_MIN_BUFFER_MEMORY_RESERVE = 0;
+
+ private static final String TAG = "ReactExoplayerView";
+
+ private static final CookieManager DEFAULT_COOKIE_MANAGER;
+ private static final int SHOW_PROGRESS = 1;
+
+ static {
+ DEFAULT_COOKIE_MANAGER = new CookieManager();
+ DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
+ }
+
+ private final VideoEventEmitter eventEmitter;
+ private final ReactExoplayerConfig config;
+ private final DefaultBandwidthMeter bandwidthMeter;
+ private PlayerControlView playerControlView;
+ private View playPauseControlContainer;
+ private Player.Listener eventListener;
+
+ private ExoPlayerView exoPlayerView;
+ private FullScreenPlayerView fullScreenPlayerView;
+ private ImaAdsLoader adsLoader;
+
+ private DataSource.Factory mediaDataSourceFactory;
+ private ExoPlayer player;
+ private DefaultTrackSelector trackSelector;
+ private boolean playerNeedsSource;
+
+ private int resumeWindow;
+ private long resumePosition;
+ private boolean loadVideoStarted;
+ private boolean isFullscreen;
+ private boolean isInBackground;
+ private boolean isPaused;
+ private boolean isBuffering;
+ private boolean muted = false;
+ private boolean hasAudioFocus = false;
+ private float rate = 1f;
+ private float audioVolume = 1f;
+ private int minLoadRetryCount = 3;
+ private int maxBitRate = 0;
+ private long seekTime = C.TIME_UNSET;
+ private boolean hasDrmFailed = false;
+ private boolean isUsingContentResolution = false;
+ private boolean selectTrackWhenReady = false;
+
+ private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
+ private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
+ private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
+ private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
+ private double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
+ private double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE;
+ private double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
+ private Handler mainHandler;
+
+ // Props from React
+ private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS;
+ private Uri srcUri;
+ private long startTimeMs = -1;
+ private long endTimeMs = -1;
+ private String extension;
+ private boolean repeat;
+ private String audioTrackType;
+ private Dynamic audioTrackValue;
+ private String videoTrackType;
+ private Dynamic videoTrackValue;
+ private String textTrackType;
+ private Dynamic textTrackValue;
+ private ReadableArray textTracks;
+ private boolean disableFocus;
+ private boolean focusable = true;
+ private boolean disableBuffering;
+ private long contentStartTime = -1L;
+ private boolean disableDisconnectError;
+ private boolean preventsDisplaySleepDuringVideoPlayback = true;
+ private float mProgressUpdateInterval = 250.0f;
+ private boolean playInBackground = false;
+ private Map requestHeaders;
+ private boolean mReportBandwidth = false;
+ private UUID drmUUID = null;
+ private String drmLicenseUrl = null;
+ private String[] drmLicenseHeader = null;
+ private boolean controls;
+ private Uri adTagUrl;
+ // \ End props
+
+ // React
+ private final ThemedReactContext themedReactContext;
+ private final AudioManager audioManager;
+ private final AudioBecomingNoisyReceiver audioBecomingNoisyReceiver;
+
+ // store last progress event values to avoid sending unnecessary messages
+ private long lastPos = -1;
+ private long lastBufferDuration = -1;
+ private long lastDuration = -1;
+
+ private final Handler progressHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SHOW_PROGRESS:
+ if (player != null) {
+ if (playerControlView != null && isPlayingAd() && controls) {
+ playerControlView.hide();
+ }
+ long pos = player.getCurrentPosition();
+ long bufferedDuration = player.getBufferedPercentage() * player.getDuration() / 100;
+ long duration = player.getDuration();
+
+ if (lastPos != pos
+ || lastBufferDuration != bufferedDuration
+ || lastDuration != duration) {
+ lastPos = pos;
+ lastBufferDuration = bufferedDuration;
+ lastDuration = duration;
+ eventEmitter.progressChanged(pos, bufferedDuration, player.getDuration(), getPositionInFirstPeriodMsForCurrentWindow(pos));
+ }
+ msg = obtainMessage(SHOW_PROGRESS);
+ sendMessageDelayed(msg, Math.round(mProgressUpdateInterval));
+ }
+ break;
+ }
+ }
+ };
+
+ public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) {
+ Timeline.Window window = new Timeline.Window();
+ if(!player.getCurrentTimeline().isEmpty()) {
+ player.getCurrentTimeline().getWindow(player.getCurrentMediaItemIndex(), window);
+ }
+ return window.windowStartTimeMs + currentPosition;
+ }
+
+ public ReactExoplayerView(ThemedReactContext context, ReactExoplayerConfig config) {
+ super(context);
+ this.themedReactContext = context;
+ this.eventEmitter = new VideoEventEmitter(context);
+ this.config = config;
+ this.bandwidthMeter = config.getBandwidthMeter();
+
+ createViews();
+
+ audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ themedReactContext.addLifecycleEventListener(this);
+ audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext);
+ }
+
+ private boolean isPlayingAd() {
+ return player != null && player.isPlayingAd();
+ }
+
+ @Override
+ public void setId(int id) {
+ super.setId(id);
+ eventEmitter.setViewId(id);
+ }
+
+ private void createViews() {
+ clearResumePosition();
+ mediaDataSourceFactory = buildDataSourceFactory(true);
+ if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) {
+ CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER);
+ }
+
+ LayoutParams layoutParams = new LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT);
+ exoPlayerView = new ExoPlayerView(getContext());
+ exoPlayerView.setLayoutParams(layoutParams);
+
+ addView(exoPlayerView, 0, layoutParams);
+
+ exoPlayerView.setFocusable(this.focusable);
+
+ mainHandler = new Handler();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ initializePlayer();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ /* We want to be able to continue playing audio when switching tabs.
+ * Leave this here in case it causes issues.
+ */
+ // stopPlayback();
+ }
+
+ // LifecycleEventListener implementation
+
+ @Override
+ public void onHostResume() {
+ if (!playInBackground || !isInBackground) {
+ setPlayWhenReady(!isPaused);
+ }
+ isInBackground = false;
+ }
+
+ @Override
+ public void onHostPause() {
+ isInBackground = true;
+ if (playInBackground) {
+ return;
+ }
+ setPlayWhenReady(false);
+ }
+
+ @Override
+ public void onHostDestroy() {
+ stopPlayback();
+ }
+
+ public void cleanUpResources() {
+ stopPlayback();
+ }
+
+ //BandwidthMeter.EventListener implementation
+ @Override
+ public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
+ if (mReportBandwidth) {
+ if (player == null) {
+ eventEmitter.bandwidthReport(bitrate, 0, 0, "-1");
+ } else {
+ Format videoFormat = player.getVideoFormat();
+ int width = videoFormat != null ? videoFormat.width : 0;
+ int height = videoFormat != null ? videoFormat.height : 0;
+ String trackId = videoFormat != null ? videoFormat.id : "-1";
+ eventEmitter.bandwidthReport(bitrate, height, width, trackId);
+ }
+ }
+ }
+
+ // Internal methods
+
+ /**
+ * Toggling the visibility of the player control view
+ */
+ private void togglePlayerControlVisibility() {
+ if(player == null) return;
+ reLayout(playerControlView);
+ if (playerControlView.isVisible()) {
+ playerControlView.hide();
+ } else {
+ playerControlView.show();
+ }
+ }
+
+ /**
+ * Initializing Player control
+ */
+ private void initializePlayerControl() {
+ if (playerControlView == null) {
+ playerControlView = new PlayerControlView(getContext());
+ }
+
+ if (fullScreenPlayerView == null) {
+ fullScreenPlayerView = new FullScreenPlayerView(getContext(), exoPlayerView, playerControlView, new OnBackPressedCallback(true) {
+ @Override
+ public void handleOnBackPressed() {
+ setFullscreen(false);
+ }
+ });
+ }
+
+ // Setting the player for the playerControlView
+ playerControlView.setPlayer(player);
+ playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container);
+
+ // Invoking onClick event for exoplayerView
+ exoPlayerView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!isPlayingAd()) {
+ togglePlayerControlVisibility();
+ }
+ }
+ });
+
+ //Handling the playButton click event
+ ImageButton playButton = playerControlView.findViewById(R.id.exo_play);
+ playButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (player != null && player.getPlaybackState() == Player.STATE_ENDED) {
+ player.seekTo(0);
+ }
+ setPausedModifier(false);
+ }
+ });
+
+ //Handling the pauseButton click event
+ ImageButton pauseButton = playerControlView.findViewById(R.id.exo_pause);
+ pauseButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setPausedModifier(true);
+ }
+ });
+
+ //Handling the fullScreenButton click event
+ final ImageButton fullScreenButton = playerControlView.findViewById(R.id.exo_fullscreen);
+ fullScreenButton.setOnClickListener(v -> setFullscreen(!isFullscreen));
+ updateFullScreenButtonVisbility();
+
+ // Invoking onPlaybackStateChanged and onPlayWhenReadyChanged events for Player
+ eventListener = new Player.Listener() {
+ @Override
+ public void onPlaybackStateChanged(int playbackState) {
+ View playButton = playerControlView.findViewById(R.id.exo_play);
+ View pauseButton = playerControlView.findViewById(R.id.exo_pause);
+ if (playButton != null && playButton.getVisibility() == GONE) {
+ playButton.setVisibility(INVISIBLE);
+ }
+ if (pauseButton != null && pauseButton.getVisibility() == GONE) {
+ pauseButton.setVisibility(INVISIBLE);
+ }
+ reLayout(playPauseControlContainer);
+ //Remove this eventListener once its executed. since UI will work fine once after the reLayout is done
+ player.removeListener(eventListener);
+ }
+
+ @Override
+ public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
+ reLayout(playPauseControlContainer);
+ //Remove this eventListener once its executed. since UI will work fine once after the reLayout is done
+ player.removeListener(eventListener);
+ }
+ };
+ player.addListener(eventListener);
+ }
+
+ /**
+ * Adding Player control to the frame layout
+ */
+ private void addPlayerControl() {
+ if(playerControlView == null) return;
+ LayoutParams layoutParams = new LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT);
+ playerControlView.setLayoutParams(layoutParams);
+ int indexOfPC = indexOfChild(playerControlView);
+ if (indexOfPC != -1) {
+ removeViewAt(indexOfPC);
+ }
+ addView(playerControlView, 1, layoutParams);
+ reLayout(playerControlView);
+ }
+
+ /**
+ * Update the layout
+ * @param view view needs to update layout
+ *
+ * This is a workaround for the open bug in react-native: https://github.com/facebook/react-native/issues/17968
+ */
+ private void reLayout(View view) {
+ if (view == null) return;
+ view.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
+ view.layout(view.getLeft(), view.getTop(), view.getMeasuredWidth(), view.getMeasuredHeight());
+ }
+
+ private class RNVLoadControl extends DefaultLoadControl {
+ private int availableHeapInBytes = 0;
+ private Runtime runtime;
+ public RNVLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, int targetBufferBytes, boolean prioritizeTimeOverSizeThresholds, int backBufferDurationMs, boolean retainBackBufferFromKeyframe) {
+ super(allocator,
+ minBufferMs,
+ maxBufferMs,
+ bufferForPlaybackMs,
+ bufferForPlaybackAfterRebufferMs,
+ targetBufferBytes,
+ prioritizeTimeOverSizeThresholds,
+ backBufferDurationMs,
+ retainBackBufferFromKeyframe);
+ runtime = Runtime.getRuntime();
+ ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(themedReactContext.ACTIVITY_SERVICE);
+ availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeapAllocationPercent * 1024 * 1024);
+ }
+
+ @Override
+ public boolean shouldContinueLoading(long playbackPositionUs, long bufferedDurationUs, float playbackSpeed) {
+ if (ReactExoplayerView.this.disableBuffering) {
+ return false;
+ }
+ int loadedBytes = getAllocator().getTotalBytesAllocated();
+ boolean isHeapReached = availableHeapInBytes > 0 && loadedBytes >= availableHeapInBytes;
+ if (isHeapReached) {
+ return false;
+ }
+ long usedMemory = runtime.totalMemory() - runtime.freeMemory();
+ long freeMemory = runtime.maxMemory() - usedMemory;
+ long reserveMemory = (long)minBufferMemoryReservePercent * runtime.maxMemory();
+ long bufferedMs = bufferedDurationUs / (long)1000;
+ if (reserveMemory > freeMemory && bufferedMs > 2000) {
+ // We don't have enough memory in reserve so we stop buffering to allow other components to use it instead
+ return false;
+ }
+ if (runtime.freeMemory() == 0) {
+ Log.w("ExoPlayer Warning", "Free memory reached 0, forcing garbage collection");
+ runtime.gc();
+ return false;
+ }
+ return super.shouldContinueLoading(playbackPositionUs, bufferedDurationUs, playbackSpeed);
+ }
+ }
+
+ private void startBufferCheckTimer() {
+ Player player = this.player;
+ VideoEventEmitter eventEmitter = this.eventEmitter;
+ Handler mainHandler = this.mainHandler;
+
+ }
+
+ private void initializePlayer() {
+ ReactExoplayerView self = this;
+ Activity activity = themedReactContext.getCurrentActivity();
+ // This ensures all props have been settled, to avoid async racing conditions.
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (player == null) {
+ // Initialize core configuration and listeners
+ initializePlayerCore(self);
+ }
+ if (playerNeedsSource && srcUri != null) {
+ exoPlayerView.invalidateAspectRatio();
+ // DRM session manager creation must be done on a different thread to prevent crashes so we start a new thread
+ ExecutorService es = Executors.newSingleThreadExecutor();
+ es.execute(new Runnable() {
+ @Override
+ public void run() {
+ // DRM initialization must run on a different thread
+ DrmSessionManager drmSessionManager = initializePlayerDrm(self);
+ if (drmSessionManager == null && self.drmUUID != null) {
+ // Failed to intialize DRM session manager - cannot continue
+ Log.e("ExoPlayer Exception", "Failed to initialize DRM Session Manager Framework!");
+ eventEmitter.error("Failed to initialize DRM Session Manager Framework!", new Exception("DRM Session Manager Framework failure!"), "3003");
+ return;
+ }
+
+ if (activity == null) {
+ Log.e("ExoPlayer Exception", "Failed to initialize Player!");
+ eventEmitter.error("Failed to initialize Player!", new Exception("Current Activity is null!"), "1001");
+ return;
+ }
+
+ // Initialize handler to run on the main thread
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+ try {
+ // Source initialization must run on the main thread
+ initializePlayerSource(self, drmSessionManager);
+ } catch (Exception ex) {
+ self.playerNeedsSource = true;
+ Log.e("ExoPlayer Exception", "Failed to initialize Player!");
+ Log.e("ExoPlayer Exception", ex.toString());
+ self.eventEmitter.error(ex.toString(), ex, "1001");
+ }
+ }
+ });
+ }
+ });
+ } else if (srcUri != null) {
+ initializePlayerSource(self, null);
+ }
+ } catch (Exception ex) {
+ self.playerNeedsSource = true;
+ Log.e("ExoPlayer Exception", "Failed to initialize Player!");
+ Log.e("ExoPlayer Exception", ex.toString());
+ eventEmitter.error(ex.toString(), ex, "1001");
+ }
+ }
+ }, 1);
+
+ }
+
+ private void initializePlayerCore(ReactExoplayerView self) {
+ ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
+ self.trackSelector = new DefaultTrackSelector(getContext(), videoTrackSelectionFactory);
+ self.trackSelector.setParameters(trackSelector.buildUponParameters()
+ .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
+
+ DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
+ RNVLoadControl loadControl = new RNVLoadControl(
+ allocator,
+ minBufferMs,
+ maxBufferMs,
+ bufferForPlaybackMs,
+ bufferForPlaybackAfterRebufferMs,
+ -1,
+ true,
+ backBufferDurationMs,
+ DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME
+ );
+ DefaultRenderersFactory renderersFactory =
+ new DefaultRenderersFactory(getContext())
+ .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
+
+ // Create an AdsLoader.
+ adsLoader = new ImaAdsLoader.Builder(themedReactContext).setAdEventListener(this).build();
+
+ MediaSource.Factory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory)
+ .setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
+
+ player = new ExoPlayer.Builder(getContext(), renderersFactory)
+ .setTrackSelector(self.trackSelector)
+ .setBandwidthMeter(bandwidthMeter)
+ .setLoadControl(loadControl)
+ .setMediaSourceFactory(mediaSourceFactory)
+ .build();
+ player.addListener(self);
+ exoPlayerView.setPlayer(player);
+ if (adsLoader != null) {
+ adsLoader.setPlayer(player);
+ }
+ audioBecomingNoisyReceiver.setListener(self);
+ bandwidthMeter.addEventListener(new Handler(), self);
+ setPlayWhenReady(!isPaused);
+ playerNeedsSource = true;
+
+ PlaybackParameters params = new PlaybackParameters(rate, 1f);
+ player.setPlaybackParameters(params);
+ }
+
+ private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) {
+ DrmSessionManager drmSessionManager = null;
+ if (self.drmUUID != null) {
+ try {
+ drmSessionManager = self.buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl,
+ self.drmLicenseHeader);
+ } catch (UnsupportedDrmException e) {
+ int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
+ : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
+ ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
+ eventEmitter.error(getResources().getString(errorStringId), e, "3003");
+ return null;
+ }
+ }
+ return drmSessionManager;
+ }
+
+ private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager drmSessionManager) {
+ ArrayList mediaSourceList = buildTextSources();
+ MediaSource videoSource = buildMediaSource(self.srcUri, self.extension, drmSessionManager, startTimeMs, endTimeMs);
+ MediaSource mediaSourceWithAds = null;
+ if (adTagUrl != null) {
+ MediaSource.Factory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory)
+ .setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView);
+ DataSpec adTagDataSpec = new DataSpec(adTagUrl);
+ mediaSourceWithAds = new AdsMediaSource(videoSource, adTagDataSpec, ImmutableList.of(srcUri, adTagUrl), mediaSourceFactory, adsLoader, exoPlayerView);
+ }
+ MediaSource mediaSource;
+ if (mediaSourceList.size() == 0) {
+ if (mediaSourceWithAds != null) {
+ mediaSource = mediaSourceWithAds;
+ } else {
+ mediaSource = videoSource;
+ }
+ } else {
+ if (mediaSourceWithAds != null) {
+ mediaSourceList.add(0, mediaSourceWithAds);
+ } else {
+ mediaSourceList.add(0, videoSource);
+ }
+ MediaSource[] textSourceArray = mediaSourceList.toArray(
+ new MediaSource[mediaSourceList.size()]
+ );
+ mediaSource = new MergingMediaSource(textSourceArray);
+ }
+
+ // wait for player to be set
+ while (player == null) {
+ try {
+ wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ Log.e("ExoPlayer Exception", ex.toString());
+ }
+ }
+
+ boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
+ if (haveResumePosition) {
+ player.seekTo(resumeWindow, resumePosition);
+ }
+ player.prepare(mediaSource, !haveResumePosition, false);
+ playerNeedsSource = false;
+
+ reLayout(exoPlayerView);
+ eventEmitter.loadStart();
+ loadVideoStarted = true;
+
+ finishPlayerInitialization();
+ }
+
+ private void finishPlayerInitialization() {
+ // Initializing the playerControlView
+ initializePlayerControl();
+ setControls(controls);
+ applyModifiers();
+ startBufferCheckTimer();
+ }
+
+ private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException {
+ return buildDrmSessionManager(uuid, licenseUrl, keyRequestPropertiesArray, 0);
+ }
+
+ private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, int retryCount) throws UnsupportedDrmException {
+ if (Util.SDK_INT < 18) {
+ return null;
+ }
+ try {
+ HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl,
+ buildHttpDataSourceFactory(false));
+ if (keyRequestPropertiesArray != null) {
+ for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) {
+ drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i], keyRequestPropertiesArray[i + 1]);
+ }
+ }
+ FrameworkMediaDrm mediaDrm = FrameworkMediaDrm.newInstance(uuid);
+ if (hasDrmFailed) {
+ // When DRM fails using L1 we want to switch to L3
+ mediaDrm.setPropertyString("securityLevel", "L3");
+ }
+ return new DefaultDrmSessionManager(uuid, mediaDrm, drmCallback, null, false, 3);
+ } catch(UnsupportedDrmException ex) {
+ // Unsupported DRM exceptions are handled by the calling method
+ throw ex;
+ } catch (Exception ex) {
+ if (retryCount < 3) {
+ // Attempt retry 3 times in case where the OS Media DRM Framework fails for whatever reason
+ return buildDrmSessionManager(uuid, licenseUrl, keyRequestPropertiesArray, ++retryCount);
+ }
+ // Handle the unknow exception and emit to JS
+ eventEmitter.error(ex.toString(), ex, "3006");
+ return null;
+ }
+ }
+
+ private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager, long startTimeMs, long endTimeMs) {
+ if (uri == null) {
+ throw new IllegalStateException("Invalid video uri");
+ }
+ int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
+ : uri.getLastPathSegment());
+ config.setDisableDisconnectError(this.disableDisconnectError);
+
+ MediaItem.Builder mediaItemBuilder = new MediaItem.Builder().setUri(uri);
+
+ if (adTagUrl != null) {
+ mediaItemBuilder.setAdsConfiguration(
+ new MediaItem.AdsConfiguration.Builder(adTagUrl).build()
+ );
+ }
+
+ MediaItem mediaItem = mediaItemBuilder.build();
+ MediaSource mediaSource = null;
+ DrmSessionManagerProvider drmProvider = null;
+ if (drmSessionManager != null) {
+ drmProvider = new DrmSessionManagerProvider() {
+ @Override
+ public DrmSessionManager get(MediaItem mediaItem) {
+ return drmSessionManager;
+ }
+ };
+ } else {
+ drmProvider = new DefaultDrmSessionManagerProvider();
+ }
+ switch (type) {
+ case CONTENT_TYPE_SS:
+ mediaSource = new SsMediaSource.Factory(
+ new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
+ buildDataSourceFactory(false)
+ ).setDrmSessionManagerProvider(drmProvider)
+ .setLoadErrorHandlingPolicy(
+ config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
+ ).createMediaSource(mediaItem);
+ break;
+ case CONTENT_TYPE_DASH:
+ mediaSource = new DashMediaSource.Factory(
+ new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
+ buildDataSourceFactory(false)
+ ).setDrmSessionManagerProvider(drmProvider)
+ .setLoadErrorHandlingPolicy(
+ config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
+ ).createMediaSource(mediaItem);
+ break;
+ case CONTENT_TYPE_HLS:
+ mediaSource = new HlsMediaSource.Factory(
+ mediaDataSourceFactory
+ ).setDrmSessionManagerProvider(drmProvider)
+ .setLoadErrorHandlingPolicy(
+ config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
+ ).createMediaSource(mediaItem);
+ break;
+ case CONTENT_TYPE_OTHER:
+ mediaSource = new ProgressiveMediaSource.Factory(
+ mediaDataSourceFactory
+ ).setDrmSessionManagerProvider(drmProvider)
+ .setLoadErrorHandlingPolicy(
+ config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
+ ).createMediaSource(mediaItem);
+ break;
+ default: {
+ throw new IllegalStateException("Unsupported type: " + type);
+ }
+ }
+
+ if (startTimeMs >= 0 && endTimeMs >= 0)
+ {
+ return new ClippingMediaSource(mediaSource, startTimeMs * 1000, endTimeMs * 1000);
+ } else if (startTimeMs >= 0) {
+ return new ClippingMediaSource(mediaSource, startTimeMs * 1000, TIME_END_OF_SOURCE);
+ } else if (endTimeMs >= 0) {
+ return new ClippingMediaSource(mediaSource, 0, endTimeMs * 1000);
+ }
+
+ return mediaSource;
+ }
+
+ private ArrayList buildTextSources() {
+ ArrayList textSources = new ArrayList<>();
+ if (textTracks == null) {
+ return textSources;
+ }
+
+ for (int i = 0; i < textTracks.size(); ++i) {
+ ReadableMap textTrack = textTracks.getMap(i);
+ String language = textTrack.getString("language");
+ String title = textTrack.hasKey("title")
+ ? textTrack.getString("title") : language + " " + i;
+ Uri uri = Uri.parse(textTrack.getString("uri"));
+ MediaSource textSource = buildTextSource(title, uri, textTrack.getString("type"),
+ language);
+ if (textSource != null) {
+ textSources.add(textSource);
+ }
+ }
+ return textSources;
+ }
+
+ private MediaSource buildTextSource(String title, Uri uri, String mimeType, String language) {
+ MediaItem.SubtitleConfiguration subtitleConfiguration = new MediaItem.SubtitleConfiguration.Builder(uri)
+ .setMimeType(mimeType)
+ .setLanguage(language)
+ .setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
+ .setRoleFlags(C.ROLE_FLAG_SUBTITLE)
+ .setLabel(title)
+ .build();
+ return new SingleSampleMediaSource.Factory(mediaDataSourceFactory)
+ .createMediaSource(subtitleConfiguration, C.TIME_UNSET);
+ }
+
+ private void releasePlayer() {
+ if (player != null) {
+ if (adsLoader != null) {
+ adsLoader.setPlayer(null);
+ }
+ updateResumePosition();
+ player.release();
+ player.removeListener(this);
+ trackSelector = null;
+ player = null;
+ }
+ if (adsLoader != null) {
+ adsLoader.release();
+ }
+ adsLoader = null;
+ progressHandler.removeMessages(SHOW_PROGRESS);
+ themedReactContext.removeLifecycleEventListener(this);
+ audioBecomingNoisyReceiver.removeListener();
+ bandwidthMeter.removeEventListener(this);
+ }
+
+ private boolean requestAudioFocus() {
+ if (disableFocus || srcUri == null || this.hasAudioFocus) {
+ return true;
+ }
+ int result = audioManager.requestAudioFocus(this,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ }
+
+ private void setPlayWhenReady(boolean playWhenReady) {
+ if (player == null) {
+ return;
+ }
+
+ if (playWhenReady) {
+ this.hasAudioFocus = requestAudioFocus();
+ if (this.hasAudioFocus) {
+ player.setPlayWhenReady(true);
+ }
+ } else {
+ // ensure playback is not ENDED, else it will trigger another ended event
+ if (player.getPlaybackState() != Player.STATE_ENDED) {
+ player.setPlayWhenReady(false);
+ }
+ }
+ }
+
+ private void startPlayback() {
+ if (player != null) {
+ switch (player.getPlaybackState()) {
+ case Player.STATE_IDLE:
+ case Player.STATE_ENDED:
+ initializePlayer();
+ break;
+ case Player.STATE_BUFFERING:
+ case Player.STATE_READY:
+ if (!player.getPlayWhenReady()) {
+ setPlayWhenReady(true);
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ initializePlayer();
+ }
+ if (!disableFocus) {
+ setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
+ }
+ }
+
+ private void pausePlayback() {
+ if (player != null) {
+ if (player.getPlayWhenReady()) {
+ setPlayWhenReady(false);
+ }
+ }
+ setKeepScreenOn(false);
+ }
+
+ private void stopPlayback() {
+ onStopPlayback();
+ releasePlayer();
+ }
+
+ private void onStopPlayback() {
+ if (isFullscreen) {
+ setFullscreen(false);
+ }
+ audioManager.abandonAudioFocus(this);
+ }
+
+ private void updateResumePosition() {
+ resumeWindow = player.getCurrentMediaItemIndex();
+ resumePosition = player.isCurrentMediaItemSeekable() ? Math.max(0, player.getCurrentPosition())
+ : C.TIME_UNSET;
+ }
+
+ private void clearResumePosition() {
+ resumeWindow = C.INDEX_UNSET;
+ resumePosition = C.TIME_UNSET;
+ }
+
+ /**
+ * Returns a new DataSource factory.
+ *
+ * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new
+ * DataSource factory.
+ * @return A new DataSource factory.
+ */
+ private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) {
+ return DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext,
+ useBandwidthMeter ? bandwidthMeter : null, requestHeaders);
+ }
+
+ /**
+ * Returns a new HttpDataSource factory.
+ *
+ * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new
+ * DataSource factory.
+ * @return A new HttpDataSource factory.
+ */
+ private HttpDataSource.Factory buildHttpDataSourceFactory(boolean useBandwidthMeter) {
+ return DataSourceUtil.getDefaultHttpDataSourceFactory(this.themedReactContext, useBandwidthMeter ? bandwidthMeter : null, requestHeaders);
+ }
+
+
+ // AudioManager.OnAudioFocusChangeListener implementation
+
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ switch (focusChange) {
+ case AudioManager.AUDIOFOCUS_LOSS:
+ this.hasAudioFocus = false;
+ eventEmitter.audioFocusChanged(false);
+ pausePlayback();
+ audioManager.abandonAudioFocus(this);
+ break;
+ case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
+ eventEmitter.audioFocusChanged(false);
+ break;
+ case AudioManager.AUDIOFOCUS_GAIN:
+ this.hasAudioFocus = true;
+ eventEmitter.audioFocusChanged(true);
+ break;
+ default:
+ break;
+ }
+
+ if (player != null) {
+ if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+ // Lower the volume
+ if (!muted) {
+ player.setVolume(audioVolume * 0.8f);
+ }
+ } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ // Raise it back to normal
+ if (!muted) {
+ player.setVolume(audioVolume * 1);
+ }
+ }
+ }
+ }
+
+ // AudioBecomingNoisyListener implementation
+
+ @Override
+ public void onAudioBecomingNoisy() {
+ eventEmitter.audioBecomingNoisy();
+ }
+
+ // Player.Listener implementation
+
+ @Override
+ public void onIsLoadingChanged(boolean isLoading) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onEvents(Player player, Player.Events events) {
+ if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
+ int playbackState = player.getPlaybackState();
+ boolean playWhenReady = player.getPlayWhenReady();
+ String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState=";
+ eventEmitter.playbackRateChange(playWhenReady && playbackState == ExoPlayer.STATE_READY ? 1.0f : 0.0f);
+ switch (playbackState) {
+ case Player.STATE_IDLE:
+ text += "idle";
+ eventEmitter.idle();
+ clearProgressMessageHandler();
+ if (!player.getPlayWhenReady()) {
+ setKeepScreenOn(false);
+ }
+ break;
+ case Player.STATE_BUFFERING:
+ text += "buffering";
+ onBuffering(true);
+ clearProgressMessageHandler();
+ setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
+ break;
+ case Player.STATE_READY:
+ text += "ready";
+ eventEmitter.ready();
+ onBuffering(false);
+ clearProgressMessageHandler(); // ensure there is no other message
+ startProgressHandler();
+ videoLoaded();
+ if (selectTrackWhenReady && isUsingContentResolution) {
+ selectTrackWhenReady = false;
+ setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
+ }
+ // Setting the visibility for the playerControlView
+ if (playerControlView != null) {
+ playerControlView.show();
+ }
+ setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback);
+ break;
+ case Player.STATE_ENDED:
+ text += "ended";
+ eventEmitter.end();
+ onStopPlayback();
+ setKeepScreenOn(false);
+ break;
+ default:
+ text += "unknown";
+ break;
+ }
+ }
+ }
+
+ private void startProgressHandler() {
+ progressHandler.sendEmptyMessage(SHOW_PROGRESS);
+ }
+
+ /*
+ The progress message handler will duplicate recursions of the onProgressMessage handler
+ on change of player state from any state to STATE_READY with playWhenReady is true (when
+ the video is not paused). This clears all existing messages.
+ */
+ private void clearProgressMessageHandler() {
+ progressHandler.removeMessages(SHOW_PROGRESS);
+ }
+
+ private void videoLoaded() {
+ if (!player.isPlayingAd() && loadVideoStarted) {
+ loadVideoStarted = false;
+ if (audioTrackType != null) {
+ setSelectedAudioTrack(audioTrackType, audioTrackValue);
+ }
+ if (videoTrackType != null) {
+ setSelectedVideoTrack(videoTrackType, videoTrackValue);
+ }
+ if (textTrackType != null) {
+ setSelectedTextTrack(textTrackType, textTrackValue);
+ }
+ Format videoFormat = player.getVideoFormat();
+ int width = videoFormat != null ? videoFormat.width : 0;
+ int height = videoFormat != null ? videoFormat.height : 0;
+ String trackId = videoFormat != null ? videoFormat.id : "-1";
+
+ // Properties that must be accessed on the main thread
+ long duration = player.getDuration();
+ long currentPosition = player.getCurrentPosition();
+ ArrayList audioTracks = getAudioTrackInfo();
+ ArrayList textTracks = getTextTrackInfo();
+
+ if (this.contentStartTime != -1L) {
+ ExecutorService es = Executors.newSingleThreadExecutor();
+ es.execute(new Runnable() {
+ @Override
+ public void run() {
+ // To prevent ANRs caused by getVideoTrackInfo we run this on a different thread and notify the player only when we're done
+ ArrayList videoTracks = getVideoTrackInfoFromManifest();
+ if (videoTracks != null) {
+ isUsingContentResolution = true;
+ }
+ eventEmitter.load(duration, currentPosition, width, height,
+ audioTracks, textTracks, videoTracks, trackId );
+
+ }
+ });
+ return;
+ }
+
+ ArrayList videoTracks = getVideoTrackInfo();
+
+ eventEmitter.load(duration, currentPosition, width, height,
+ audioTracks, textTracks, videoTracks, trackId);
+ }
+ }
+
+ private static boolean isTrackSelected(TrackSelection selection, TrackGroup group,
+ int trackIndex){
+ return selection != null && selection.getTrackGroup() == group
+ && selection.indexOf( trackIndex ) != C.INDEX_UNSET;
+ }
+
+ private ArrayList getAudioTrackInfo() {
+ ArrayList audioTracks = new ArrayList<>();
+ if (trackSelector == null) {
+ // Likely player is unmounting so no audio tracks are available anymore
+ return audioTracks;
+ }
+
+ MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
+ int index = getTrackRendererIndex(C.TRACK_TYPE_AUDIO);
+ if (info == null || index == C.INDEX_UNSET) {
+ return audioTracks;
+ }
+ TrackGroupArray groups = info.getTrackGroups(index);
+ TrackSelectionArray selectionArray = player.getCurrentTrackSelections();
+ TrackSelection selection = selectionArray.get( C.TRACK_TYPE_AUDIO );
+
+ for (int i = 0; i < groups.length; ++i) {
+ TrackGroup group = groups.get(i);
+ Format format = group.getFormat(0);
+ Track audioTrack = new Track();
+ audioTrack.m_index = i;
+ audioTrack.m_title = format.id != null ? format.id : "";
+ audioTrack.m_mimeType = format.sampleMimeType;
+ audioTrack.m_language = format.language != null ? format.language : "";
+ audioTrack.m_bitrate = format.bitrate == Format.NO_VALUE ? 0 : format.bitrate;
+ audioTrack.m_isSelected = isTrackSelected(selection, group, 0 );
+ audioTracks.add(audioTrack);
+ }
+ return audioTracks;
+ }
+
+ private ArrayList getVideoTrackInfo() {
+ ArrayList videoTracks = new ArrayList<>();
+ if (trackSelector == null) {
+ // Likely player is unmounting so no audio tracks are available anymore
+ return videoTracks;
+ }
+ MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
+ int index = getTrackRendererIndex(C.TRACK_TYPE_VIDEO);
+ if (info == null || index == C.INDEX_UNSET) {
+ return videoTracks;
+ }
+
+ TrackGroupArray groups = info.getTrackGroups(index);
+ for (int i = 0; i < groups.length; ++i) {
+ TrackGroup group = groups.get(i);
+
+ for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
+ Format format = group.getFormat(trackIndex);
+ if (isFormatSupported(format)) {
+ VideoTrack videoTrack = new VideoTrack();
+ videoTrack.m_width = format.width == Format.NO_VALUE ? 0 : format.width;
+ videoTrack.m_height = format.height == Format.NO_VALUE ? 0 : format.height;
+ videoTrack.m_bitrate = format.bitrate == Format.NO_VALUE ? 0 : format.bitrate;
+ videoTrack.m_codecs = format.codecs != null ? format.codecs : "";
+ videoTrack.m_trackId = format.id == null ? String.valueOf(trackIndex) : format.id;
+ videoTracks.add(videoTrack);
+ }
+ }
+ }
+ return videoTracks;
+ }
+
+ private ArrayList getVideoTrackInfoFromManifest() {
+ return this.getVideoTrackInfoFromManifest(0);
+ }
+
+ // We need retry count to in case where minefest request fails from poor network conditions
+ @WorkerThread
+ private ArrayList getVideoTrackInfoFromManifest(int retryCount) {
+ ExecutorService es = Executors.newSingleThreadExecutor();
+ final DataSource dataSource = this.mediaDataSourceFactory.createDataSource();
+ final Uri sourceUri = this.srcUri;
+ final long startTime = this.contentStartTime * 1000 - 100; // s -> ms with 100ms offset
+
+ Future> result = es.submit(new Callable>() {
+ DataSource ds = dataSource;
+ Uri uri = sourceUri;
+ long startTimeUs = startTime * 1000; // ms -> us
+
+ public ArrayList call() throws Exception {
+ ArrayList videoTracks = new ArrayList<>();
+ try {
+ DashManifest manifest = DashUtil.loadManifest(this.ds, this.uri);
+ int periodCount = manifest.getPeriodCount();
+ for (int i = 0; i < periodCount; i++) {
+ Period period = manifest.getPeriod(i);
+ for (int adaptationIndex = 0; adaptationIndex < period.adaptationSets.size(); adaptationIndex++) {
+ AdaptationSet adaptation = period.adaptationSets.get(adaptationIndex);
+ if (adaptation.type != C.TRACK_TYPE_VIDEO) {
+ continue;
+ }
+ boolean hasFoundContentPeriod = false;
+ for (int representationIndex = 0; representationIndex < adaptation.representations.size(); representationIndex++) {
+ Representation representation = adaptation.representations.get(representationIndex);
+ Format format = representation.format;
+ if (isFormatSupported(format)) {
+ if (representation.presentationTimeOffsetUs <= startTimeUs) {
+ break;
+ }
+ hasFoundContentPeriod = true;
+ VideoTrack videoTrack = new VideoTrack();
+ videoTrack.m_width = format.width == Format.NO_VALUE ? 0 : format.width;
+ videoTrack.m_height = format.height == Format.NO_VALUE ? 0 : format.height;
+ videoTrack.m_bitrate = format.bitrate == Format.NO_VALUE ? 0 : format.bitrate;
+ videoTrack.m_codecs = format.codecs != null ? format.codecs : "";
+ videoTrack.m_trackId = format.id == null ? String.valueOf(representationIndex) : format.id;
+ videoTracks.add(videoTrack);
+ }
+ }
+ if (hasFoundContentPeriod) {
+ return videoTracks;
+ }
+ }
+ }
+ } catch (Exception e) {}
+ return null;
+ }
+ });
+
+ try {
+ ArrayList results = result.get(3000, TimeUnit.MILLISECONDS);
+ if (results == null && retryCount < 1) {
+ return this.getVideoTrackInfoFromManifest(++retryCount);
+ }
+ es.shutdown();
+ return results;
+ } catch (Exception e) {}
+
+ return null;
+ }
+
+ private ArrayList getTextTrackInfo() {
+ ArrayList textTracks = new ArrayList<>();
+ if (trackSelector == null) {
+ return textTracks;
+ }
+ MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
+ int index = getTrackRendererIndex(C.TRACK_TYPE_TEXT);
+ if (info == null || index == C.INDEX_UNSET) {
+ return textTracks;
+ }
+ TrackSelectionArray selectionArray = player.getCurrentTrackSelections();
+ TrackSelection selection = selectionArray.get( C.TRACK_TYPE_VIDEO );
+ TrackGroupArray groups = info.getTrackGroups(index);
+
+ for (int i = 0; i < groups.length; ++i) {
+ TrackGroup group = groups.get(i);
+ Format format = group.getFormat(0);
+
+ Track textTrack = new Track();
+ textTrack.m_index = i;
+ textTrack.m_title = format.id != null ? format.id : "";
+ textTrack.m_mimeType = format.sampleMimeType;
+ textTrack.m_language = format.language != null ? format.language : "";
+ textTrack.m_isSelected = isTrackSelected(selection, group, 0 );
+ textTracks.add(textTrack);
+ }
+ return textTracks;
+ }
+
+ private void onBuffering(boolean buffering) {
+ if (isBuffering == buffering) {
+ return;
+ }
+
+ isBuffering = buffering;
+ if (buffering) {
+ eventEmitter.buffering(true);
+ } else {
+ eventEmitter.buffering(false);
+ }
+ }
+
+ @Override
+ public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
+ if (playerNeedsSource) {
+ // This will only occur if the user has performed a seek whilst in the error state. Update the
+ // resume position so that if the user then retries, playback will resume from the position to
+ // which they seeked.
+ updateResumePosition();
+ }
+ if (isUsingContentResolution) {
+ // Discontinuity events might have a different track list so we update the selected track
+ setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
+ selectTrackWhenReady = true;
+ }
+ // When repeat is turned on, reaching the end of the video will not cause a state change
+ // so we need to explicitly detect it.
+ if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION
+ && player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
+ eventEmitter.end();
+ }
+
+ }
+
+ @Override
+ public void onTimelineChanged(Timeline timeline, int reason) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onPlaybackStateChanged(int playbackState) {
+ if (playbackState == Player.STATE_READY && seekTime != C.TIME_UNSET) {
+ eventEmitter.seek(player.getCurrentPosition(), seekTime);
+ seekTime = C.TIME_UNSET;
+ if (isUsingContentResolution) {
+ // We need to update the selected track to make sure that it still matches user selection if track list has changed in this period
+ setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
+ }
+ }
+ }
+
+ @Override
+ public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onRepeatModeChanged(int repeatMode) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onTracksChanged(Tracks tracks) {
+ eventEmitter.textTracks(getTextTrackInfo());
+ eventEmitter.audioTracks(getAudioTrackInfo());
+ eventEmitter.videoTracks(getVideoTrackInfo());
+ }
+
+ @Override
+ public void onPlaybackParametersChanged(PlaybackParameters params) {
+ eventEmitter.playbackRateChange(params.speed);
+ }
+
+ @Override
+ public void onIsPlayingChanged(boolean isPlaying) {
+ eventEmitter.playbackStateChanged(isPlaying);
+ }
+
+ @Override
+ public void onPlayerError(PlaybackException e) {
+ if (e == null) {
+ return;
+ }
+ String errorString = "ExoPlaybackException: " + PlaybackException.getErrorCodeName(e.errorCode);
+ String errorCode = "2" + String.valueOf(e.errorCode);
+ boolean needsReInitialization = false;
+ switch(e.errorCode) {
+ case PlaybackException.ERROR_CODE_DRM_DEVICE_REVOKED:
+ case PlaybackException.ERROR_CODE_DRM_LICENSE_ACQUISITION_FAILED:
+ case PlaybackException.ERROR_CODE_DRM_PROVISIONING_FAILED:
+ case PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR:
+ case PlaybackException.ERROR_CODE_DRM_UNSPECIFIED:
+ if (!hasDrmFailed) {
+ // When DRM fails to reach the app level certificate server it will fail with a source error so we assume that it is DRM related and try one more time
+ hasDrmFailed = true;
+ playerNeedsSource = true;
+ updateResumePosition();
+ initializePlayer();
+ setPlayWhenReady(true);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ eventEmitter.error(errorString, e, errorCode);
+ playerNeedsSource = true;
+ if (isBehindLiveWindow(e)) {
+ clearResumePosition();
+ initializePlayer();
+ } else {
+ updateResumePosition();
+ if (needsReInitialization) {
+ initializePlayer();
+ }
+ }
+ }
+
+ private static boolean isBehindLiveWindow(PlaybackException e) {
+ return e.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW;
+ }
+
+ public int getTrackRendererIndex(int trackType) {
+ if (player != null) {
+ int rendererCount = player.getRendererCount();
+ for (int rendererIndex = 0; rendererIndex < rendererCount; rendererIndex++) {
+ if (player.getRendererType(rendererIndex) == trackType) {
+ return rendererIndex;
+ }
+ }
+ }
+ return C.INDEX_UNSET;
+ }
+
+ @Override
+ public void onMetadata(Metadata metadata) {
+ eventEmitter.timedMetadata(metadata);
+ }
+
+ // ReactExoplayerViewManager public api
+
+ public void setSrc(final Uri uri, final long startTimeMs, final long endTimeMs, final String extension, Map headers) {
+ if (uri != null) {
+ boolean isSourceEqual = uri.equals(srcUri) && startTimeMs == this.startTimeMs && endTimeMs == this.endTimeMs;
+ hasDrmFailed = false;
+ this.srcUri = uri;
+ this.startTimeMs = startTimeMs;
+ this.endTimeMs = endTimeMs;
+ this.extension = extension;
+ this.requestHeaders = headers;
+ this.mediaDataSourceFactory =
+ DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter,
+ this.requestHeaders);
+
+ if (!isSourceEqual) {
+ reloadSource();
+ }
+ }
+ }
+
+ public void clearSrc() {
+ if (srcUri != null) {
+ player.stop();
+ player.clearMediaItems();
+ this.srcUri = null;
+ this.startTimeMs = -1;
+ this.endTimeMs = -1;
+ this.extension = null;
+ this.requestHeaders = null;
+ this.mediaDataSourceFactory = null;
+ clearResumePosition();
+ }
+ }
+
+ public void setProgressUpdateInterval(final float progressUpdateInterval) {
+ mProgressUpdateInterval = progressUpdateInterval;
+ }
+
+ public void setReportBandwidth(boolean reportBandwidth) {
+ mReportBandwidth = reportBandwidth;
+ }
+
+ public void setAdTagUrl(final Uri uri) {
+ adTagUrl = uri;
+ }
+
+ public void setRawSrc(final Uri uri, final String extension) {
+ if (uri != null) {
+ boolean isSourceEqual = uri.equals(srcUri);
+ this.srcUri = uri;
+ this.extension = extension;
+ this.mediaDataSourceFactory = buildDataSourceFactory(true);
+
+ if (!isSourceEqual) {
+ reloadSource();
+ }
+ }
+ }
+
+ public void setTextTracks(ReadableArray textTracks) {
+ this.textTracks = textTracks;
+ reloadSource();
+ }
+
+ private void reloadSource() {
+ playerNeedsSource = true;
+ initializePlayer();
+ }
+
+ public void setResizeModeModifier(@ResizeMode.Mode int resizeMode) {
+ exoPlayerView.setResizeMode(resizeMode);
+ }
+
+ private void applyModifiers() {
+ setRepeatModifier(repeat);
+ setMutedModifier(muted);
+ }
+
+ public void setRepeatModifier(boolean repeat) {
+ if (player != null) {
+ if (repeat) {
+ player.setRepeatMode(Player.REPEAT_MODE_ONE);
+ } else {
+ player.setRepeatMode(Player.REPEAT_MODE_OFF);
+ }
+ }
+ this.repeat = repeat;
+ }
+
+ public void setPreventsDisplaySleepDuringVideoPlayback(boolean preventsDisplaySleepDuringVideoPlayback) {
+ this.preventsDisplaySleepDuringVideoPlayback = preventsDisplaySleepDuringVideoPlayback;
+ }
+
+ public void disableTrack(int rendererIndex) {
+ DefaultTrackSelector.Parameters disableParameters = trackSelector.getParameters()
+ .buildUpon()
+ .setRendererDisabled(rendererIndex, true)
+ .build();
+ trackSelector.setParameters(disableParameters);
+ }
+
+ public void setSelectedTrack(int trackType, String type, Dynamic value) {
+ if (player == null) return;
+ int rendererIndex = getTrackRendererIndex(trackType);
+ if (rendererIndex == C.INDEX_UNSET) {
+ return;
+ }
+ MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
+ if (info == null) {
+ return;
+ }
+
+ TrackGroupArray groups = info.getTrackGroups(rendererIndex);
+ int groupIndex = C.INDEX_UNSET;
+ List tracks = new ArrayList<>();
+ tracks.add(0);
+
+ if (TextUtils.isEmpty(type)) {
+ type = "default";
+ }
+
+ if (type.equals("disabled")) {
+ disableTrack(rendererIndex);
+ return;
+ } else if (type.equals("language")) {
+ for (int i = 0; i < groups.length; ++i) {
+ Format format = groups.get(i).getFormat(0);
+ if (format.language != null && format.language.equals(value.asString())) {
+ groupIndex = i;
+ break;
+ }
+ }
+ } else if (type.equals("title")) {
+ for (int i = 0; i < groups.length; ++i) {
+ Format format = groups.get(i).getFormat(0);
+ if (format.id != null && format.id.equals(value.asString())) {
+ groupIndex = i;
+ break;
+ }
+ }
+ } else if (type.equals("index")) {
+ if (value.asInt() < groups.length) {
+ groupIndex = value.asInt();
+ }
+ } else if (type.equals("resolution")) {
+ int height = value.asInt();
+ for (int i = 0; i < groups.length; ++i) { // Search for the exact height
+ TrackGroup group = groups.get(i);
+ Format closestFormat = null;
+ int closestTrackIndex = -1;
+ boolean usingExactMatch = false;
+ for (int j = 0; j < group.length; j++) {
+ Format format = group.getFormat(j);
+ if (format.height == height) {
+ groupIndex = i;
+ tracks.set(0, j);
+ closestFormat = null;
+ closestTrackIndex = -1;
+ usingExactMatch = true;
+ break;
+ } else if (isUsingContentResolution) {
+ // When using content resolution rather than ads, we need to try and find the closest match if there is no exact match
+ if (closestFormat != null) {
+ if ((format.bitrate > closestFormat.bitrate || format.height > closestFormat.height) && format.height < height) {
+ // Higher quality match
+ closestFormat = format;
+ closestTrackIndex = j;
+ }
+ } else if(format.height < height) {
+ closestFormat = format;
+ closestTrackIndex = j;
+ }
+ }
+ }
+ // This is a fallback if the new period contains only higher resolutions than the user has selected
+ if (closestFormat == null && isUsingContentResolution && !usingExactMatch) {
+ // No close match found - so we pick the lowest quality
+ int minHeight = Integer.MAX_VALUE;
+ for (int j = 0; j < group.length; j++) {
+ Format format = group.getFormat(j);
+ if (format.height < minHeight) {
+ minHeight = format.height;
+ groupIndex = i;
+ tracks.set(0, j);
+ }
+ }
+ }
+ // Selecting the closest match found
+ if (closestFormat != null && closestTrackIndex != -1) {
+ // We found the closest match instead of an exact one
+ groupIndex = i;
+ tracks.set(0, closestTrackIndex);
+ }
+ }
+ } else if (trackType == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18) { // Text default
+ // Use system settings if possible
+ CaptioningManager captioningManager
+ = (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE);
+ if (captioningManager != null && captioningManager.isEnabled()) {
+ groupIndex = getGroupIndexForDefaultLocale(groups);
+ }
+ } else if (rendererIndex == C.TRACK_TYPE_AUDIO) { // Audio default
+ groupIndex = getGroupIndexForDefaultLocale(groups);
+ }
+
+ if (groupIndex == C.INDEX_UNSET && trackType == C.TRACK_TYPE_VIDEO && groups.length != 0) { // Video auto
+ // Add all tracks as valid options for ABR to choose from
+ TrackGroup group = groups.get(0);
+ tracks = new ArrayList(group.length);
+ ArrayList allTracks = new ArrayList(group.length);
+ groupIndex = 0;
+ for (int j = 0; j < group.length; j++) {
+ allTracks.add(j);
+ }
+
+ // Valiate list of all tracks and add only supported formats
+ int supportedFormatLength = 0;
+ ArrayList supportedTrackList = new ArrayList();
+ for (int g = 0; g < allTracks.size(); g++) {
+ Format format = group.getFormat(g);
+ if (isFormatSupported(format)) {
+ supportedFormatLength++;
+ }
+ }
+ if (allTracks.size() == 1) {
+ // With only one tracks we can't remove any tracks so attempt to play it anyway
+ tracks = allTracks;
+ } else {
+ tracks = new ArrayList<>(supportedFormatLength + 1);
+ for (int k = 0; k < allTracks.size(); k++) {
+ Format format = group.getFormat(k);
+ if (isFormatSupported(format)) {
+ tracks.add(allTracks.get(k));
+ supportedTrackList.add(allTracks.get(k));
+ }
+ }
+ }
+ }
+
+ if (groupIndex == C.INDEX_UNSET) {
+ disableTrack(rendererIndex);
+ return;
+ }
+
+ TrackSelectionOverride selectionOverride = new TrackSelectionOverride(groups.get(groupIndex), tracks);
+
+ DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters()
+ .buildUpon()
+ .setRendererDisabled(rendererIndex, false)
+ .clearOverridesOfType(selectionOverride.getType())
+ .addOverride(selectionOverride)
+ .build();
+ trackSelector.setParameters(selectionParameters);
+ }
+
+ private boolean isFormatSupported(Format format) {
+ int width = format.width == Format.NO_VALUE ? 0 : format.width;
+ int height = format.height == Format.NO_VALUE ? 0 : format.height;
+ float frameRate = format.frameRate == Format.NO_VALUE ? 0 : format.frameRate;
+ String mimeType = format.sampleMimeType;
+ if (mimeType == null) {
+ return true;
+ }
+ boolean isSupported = false;
+ try {
+ MediaCodecInfo codecInfo = MediaCodecUtil.getDecoderInfo(mimeType, false, false);
+ isSupported = codecInfo.isVideoSizeAndRateSupportedV21(width, height, frameRate);
+ } catch (Exception e) {
+ // Failed to get decoder info - assume it is supported
+ isSupported = true;
+ }
+ return isSupported;
+ }
+
+ private int getGroupIndexForDefaultLocale(TrackGroupArray groups) {
+ if (groups.length == 0){
+ return C.INDEX_UNSET;
+ }
+
+ int groupIndex = 0; // default if no match
+ String locale2 = Locale.getDefault().getLanguage(); // 2 letter code
+ String locale3 = Locale.getDefault().getISO3Language(); // 3 letter code
+ for (int i = 0; i < groups.length; ++i) {
+ Format format = groups.get(i).getFormat(0);
+ String language = format.language;
+ if (language != null && (language.equals(locale2) || language.equals(locale3))) {
+ groupIndex = i;
+ break;
+ }
+ }
+ return groupIndex;
+ }
+
+ public void setSelectedVideoTrack(String type, Dynamic value) {
+ videoTrackType = type;
+ videoTrackValue = value;
+ setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
+ }
+
+ public void setSelectedAudioTrack(String type, Dynamic value) {
+ audioTrackType = type;
+ audioTrackValue = value;
+ setSelectedTrack(C.TRACK_TYPE_AUDIO, audioTrackType, audioTrackValue);
+ }
+
+ public void setSelectedTextTrack(String type, Dynamic value) {
+ textTrackType = type;
+ textTrackValue = value;
+ setSelectedTrack(C.TRACK_TYPE_TEXT, textTrackType, textTrackValue);
+ }
+
+ public void setPausedModifier(boolean paused) {
+ isPaused = paused;
+ if (player != null) {
+ if (!paused) {
+ startPlayback();
+ } else {
+ pausePlayback();
+ }
+ }
+ }
+
+ public void setMutedModifier(boolean muted) {
+ this.muted = muted;
+ if (player != null) {
+ player.setVolume(muted ? 0.f : audioVolume);
+ }
+ }
+
+ public void setVolumeModifier(float volume) {
+ audioVolume = volume;
+ if (player != null) {
+ player.setVolume(audioVolume);
+ }
+ }
+
+ public void seekTo(long positionMs) {
+ if (player != null) {
+ player.seekTo(positionMs);
+ eventEmitter.seek(player.getCurrentPosition(), positionMs);
+ }
+ }
+
+ public void setRateModifier(float newRate) {
+ rate = newRate;
+
+ if (player != null) {
+ PlaybackParameters params = new PlaybackParameters(rate, 1f);
+ player.setPlaybackParameters(params);
+ }
+ }
+
+ public void setMaxBitRateModifier(int newMaxBitRate) {
+ maxBitRate = newMaxBitRate;
+ if (player != null) {
+ trackSelector.setParameters(trackSelector.buildUponParameters()
+ .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate));
+ }
+ }
+
+ public void setMinLoadRetryCountModifier(int newMinLoadRetryCount) {
+ minLoadRetryCount = newMinLoadRetryCount;
+ releasePlayer();
+ initializePlayer();
+ }
+
+ public void setPlayInBackground(boolean playInBackground) {
+ this.playInBackground = playInBackground;
+ }
+
+ public void setDisableFocus(boolean disableFocus) {
+ this.disableFocus = disableFocus;
+ }
+
+ public void setFocusable(boolean focusable) {
+ this.focusable = focusable;
+ exoPlayerView.setFocusable(this.focusable);
+ }
+
+ public void setBackBufferDurationMs(int backBufferDurationMs) {
+ Runtime runtime = Runtime.getRuntime();
+ long usedMemory = runtime.totalMemory() - runtime.freeMemory();
+ long freeMemory = runtime.maxMemory() - usedMemory;
+ long reserveMemory = (long)minBackBufferMemoryReservePercent * runtime.maxMemory();
+ if (reserveMemory > freeMemory) {
+ // We don't have enough memory in reserve so we will
+ Log.w("ExoPlayer Warning", "Not enough reserve memory, setting back buffer to 0ms to reduce memory pressure!");
+ this.backBufferDurationMs = 0;
+ return;
+ }
+ this.backBufferDurationMs = backBufferDurationMs;
+ }
+
+ public void setContentStartTime(int contentStartTime) {
+ this.contentStartTime = (long)contentStartTime;
+ }
+
+ public void setDisableBuffering(boolean disableBuffering) {
+ this.disableBuffering = disableBuffering;
+ }
+
+ private void updateFullScreenButtonVisbility() {
+ if (playerControlView != null) {
+ final ImageButton fullScreenButton = playerControlView.findViewById(R.id.exo_fullscreen);
+ if (controls) {
+ //Handling the fullScreenButton click event
+ if (isFullscreen && fullScreenPlayerView != null && !fullScreenPlayerView.isShowing()) {
+ fullScreenButton.setVisibility(GONE);
+ } else {
+ fullScreenButton.setVisibility(VISIBLE);
+ }
+ } else {
+ fullScreenButton.setVisibility(GONE);
+ }
+ }
+ }
+
+ public void setDisableDisconnectError(boolean disableDisconnectError) {
+ this.disableDisconnectError = disableDisconnectError;
+ }
+
+ public void setFullscreen(boolean fullscreen) {
+ if (fullscreen == isFullscreen) {
+ return; // Avoid generating events when nothing is changing
+ }
+ isFullscreen = fullscreen;
+
+ Activity activity = themedReactContext.getCurrentActivity();
+ if (activity == null) {
+ return;
+ }
+
+ Window window = activity.getWindow();
+ View decorView = window.getDecorView();
+ int uiOptions;
+ if (isFullscreen) {
+ if (Util.SDK_INT >= 19) { // 4.4+
+ uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ | SYSTEM_UI_FLAG_FULLSCREEN;
+ } else {
+ uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | SYSTEM_UI_FLAG_FULLSCREEN;
+ }
+ eventEmitter.fullscreenWillPresent();
+ if (controls && fullScreenPlayerView != null) {
+ fullScreenPlayerView.show();
+ }
+ post(() -> {
+ decorView.setSystemUiVisibility(uiOptions);
+ eventEmitter.fullscreenDidPresent();
+ });
+ } else {
+ uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
+ eventEmitter.fullscreenWillDismiss();
+ if (controls && fullScreenPlayerView != null) {
+ fullScreenPlayerView.dismiss();
+ reLayout(exoPlayerView);
+ }
+ post(() -> {
+ decorView.setSystemUiVisibility(uiOptions);
+ eventEmitter.fullscreenDidDismiss();
+ });
+ }
+ // need to be done at the end to avoid hiding fullscreen control button when fullScreenPlayerView is shown
+ updateFullScreenButtonVisbility();
+ }
+
+ public void setUseTextureView(boolean useTextureView) {
+ boolean finallyUseTextureView = useTextureView && this.drmUUID == null;
+ exoPlayerView.setUseTextureView(finallyUseTextureView);
+ }
+
+ public void useSecureView(boolean useSecureView) {
+ exoPlayerView.useSecureView(useSecureView);
+ }
+
+ public void setHideShutterView(boolean hideShutterView) {
+ exoPlayerView.setHideShutterView(hideShutterView);
+ }
+
+ public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs, double newMaxHeapAllocationPercent, double newMinBackBufferMemoryReservePercent, double newMinBufferMemoryReservePercent) {
+ minBufferMs = newMinBufferMs;
+ maxBufferMs = newMaxBufferMs;
+ bufferForPlaybackMs = newBufferForPlaybackMs;
+ bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs;
+ maxHeapAllocationPercent = newMaxHeapAllocationPercent;
+ minBackBufferMemoryReservePercent = newMinBackBufferMemoryReservePercent;
+ minBufferMemoryReservePercent = newMinBufferMemoryReservePercent;
+ releasePlayer();
+ initializePlayer();
+ }
+
+ public void setDrmType(UUID drmType) {
+ this.drmUUID = drmType;
+ }
+
+ public void setDrmLicenseUrl(String licenseUrl){
+ this.drmLicenseUrl = licenseUrl;
+ }
+
+ public void setDrmLicenseHeader(String[] header){
+ this.drmLicenseHeader = header;
+ }
+
+
+ @Override
+ public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
+ Log.d("DRM Info", "onDrmKeysLoaded");
+ }
+
+ @Override
+ public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) {
+ Log.d("DRM Info", "onDrmSessionManagerError");
+ eventEmitter.error("onDrmSessionManagerError", e, "3002");
+ }
+
+ @Override
+ public void onDrmKeysRestored(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
+ Log.d("DRM Info", "onDrmKeysRestored");
+ }
+
+ @Override
+ public void onDrmKeysRemoved(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
+ Log.d("DRM Info", "onDrmKeysRemoved");
+ }
+
+ /**
+ * Handling controls prop
+ *
+ * @param controls Controls prop, if true enable controls, if false disable them
+ */
+ public void setControls(boolean controls) {
+ this.controls = controls;
+ if (controls) {
+ addPlayerControl();
+ updateFullScreenButtonVisbility();
+ } else {
+ int indexOfPC = indexOfChild(playerControlView);
+ if (indexOfPC != -1) {
+ removeViewAt(indexOfPC);
+ }
+ }
+ }
+
+ public void setSubtitleStyle(SubtitleStyle style) {
+ exoPlayerView.setSubtitleStyle(style);
+ }
+
+ @Override
+ public void onAdEvent(AdEvent adEvent) {
+ eventEmitter.receiveAdEvent(adEvent.getType().name());
+ }
+}
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java
similarity index 78%
rename from android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java
rename to android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java
index eccbee7563..e0468985f2 100644
--- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java
+++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java
@@ -28,9 +28,11 @@
public class ReactExoplayerViewManager extends ViewGroupManager {
private static final String REACT_CLASS = "RCTVideo";
-
private static final String PROP_SRC = "src";
private static final String PROP_SRC_URI = "uri";
+ private static final String PROP_SRC_START_TIME = "startTime";
+ private static final String PROP_SRC_END_TIME = "endTime";
+ private static final String PROP_AD_TAG_URL = "adTagUrl";
private static final String PROP_SRC_TYPE = "type";
private static final String PROP_DRM = "drm";
private static final String PROP_DRM_TYPE = "type";
@@ -49,11 +51,15 @@ public class ReactExoplayerViewManager extends ViewGroupManager headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null;
@@ -152,7 +167,7 @@ public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src
Uri srcUri = Uri.parse(uriString);
if (srcUri != null) {
- videoView.setSrc(srcUri, extension, headers);
+ videoView.setSrc(srcUri, startTimeMs, endTimeMs, extension, headers);
}
} else {
int identifier = context.getResources().getIdentifier(
@@ -172,10 +187,24 @@ public void setSrc(final ReactExoplayerView videoView, @Nullable ReadableMap src
if (srcUri != null) {
videoView.setRawSrc(srcUri, extension);
}
+ } else {
+ videoView.clearSrc();
}
}
}
+ @ReactProp(name = PROP_AD_TAG_URL)
+ public void setAdTagUrl(final ReactExoplayerView videoView, final String uriString) {
+ if (TextUtils.isEmpty(uriString)) {
+ return;
+ }
+
+ Uri adTagUrl = Uri.parse(uriString);
+
+ videoView.setAdTagUrl(adTagUrl);
+ }
+
+
@ReactProp(name = PROP_RESIZE_MODE)
public void setResizeMode(final ReactExoplayerView videoView, final String resizeModeOrdinalString) {
videoView.setResizeModeModifier(convertToIntDef(resizeModeOrdinalString));
@@ -294,6 +323,31 @@ public void setDisableFocus(final ReactExoplayerView videoView, final boolean di
videoView.setDisableFocus(disableFocus);
}
+ @ReactProp(name = PROP_FOCUSABLE, defaultBoolean = true)
+ public void setFocusable(final ReactExoplayerView videoView, final boolean focusable) {
+ videoView.setFocusable(focusable);
+ }
+
+ @ReactProp(name = PROP_BACK_BUFFER_DURATION_MS, defaultInt = 0)
+ public void setBackBufferDurationMs(final ReactExoplayerView videoView, final int backBufferDurationMs) {
+ videoView.setBackBufferDurationMs(backBufferDurationMs);
+ }
+
+ @ReactProp(name = PROP_CONTENT_START_TIME, defaultInt = -1)
+ public void setContentStartTime(final ReactExoplayerView videoView, final int contentStartTime) {
+ videoView.setContentStartTime(contentStartTime);
+ }
+
+ @ReactProp(name = PROP_DISABLE_BUFFERING, defaultBoolean = false)
+ public void setDisableBuffering(final ReactExoplayerView videoView, final boolean disableBuffering) {
+ videoView.setDisableBuffering(disableBuffering);
+ }
+
+ @ReactProp(name = PROP_DISABLE_DISCONNECT_ERROR, defaultBoolean = false)
+ public void setDisableDisconnectError(final ReactExoplayerView videoView, final boolean disableDisconnectError) {
+ videoView.setDisableDisconnectError(disableDisconnectError);
+ }
+
@ReactProp(name = PROP_FULLSCREEN, defaultBoolean = false)
public void setFullscreen(final ReactExoplayerView videoView, final boolean fullscreen) {
videoView.setFullscreen(fullscreen);
@@ -304,6 +358,11 @@ public void setUseTextureView(final ReactExoplayerView videoView, final boolean
videoView.setUseTextureView(useTextureView);
}
+ @ReactProp(name = PROP_SECURE_VIEW, defaultBoolean = true)
+ public void useSecureView(final ReactExoplayerView videoView, final boolean useSecureView) {
+ videoView.useSecureView(useSecureView);
+ }
+
@ReactProp(name = PROP_HIDE_SHUTTER_VIEW, defaultBoolean = false)
public void setHideShutterView(final ReactExoplayerView videoView, final boolean hideShutterView) {
videoView.setHideShutterView(hideShutterView);
@@ -314,12 +373,21 @@ public void setControls(final ReactExoplayerView videoView, final boolean contro
videoView.setControls(controls);
}
+ @ReactProp(name = PROP_SUBTITLE_STYLE)
+ public void setSubtitleStyle(final ReactExoplayerView videoView, @Nullable final ReadableMap src) {
+ videoView.setSubtitleStyle(SubtitleStyle.parse(src));
+ }
+
@ReactProp(name = PROP_BUFFER_CONFIG)
public void setBufferConfig(final ReactExoplayerView videoView, @Nullable ReadableMap bufferConfig) {
int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
+ double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
+ double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE;
+ double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
+
if (bufferConfig != null) {
minBufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MIN_BUFFER_MS)
? bufferConfig.getInt(PROP_BUFFER_CONFIG_MIN_BUFFER_MS) : minBufferMs;
@@ -329,16 +397,23 @@ public void setBufferConfig(final ReactExoplayerView videoView, @Nullable Readab
? bufferConfig.getInt(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS) : bufferForPlaybackMs;
bufferForPlaybackAfterRebufferMs = bufferConfig.hasKey(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS)
? bufferConfig.getInt(PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS) : bufferForPlaybackAfterRebufferMs;
- videoView.setBufferConfig(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs);
+ maxHeapAllocationPercent = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT)
+ ? bufferConfig.getDouble(PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT) : maxHeapAllocationPercent;
+ minBackBufferMemoryReservePercent = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT)
+ ? bufferConfig.getDouble(PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT) : minBackBufferMemoryReservePercent;
+ minBufferMemoryReservePercent = bufferConfig.hasKey(PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT)
+ ? bufferConfig.getDouble(PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT) : minBufferMemoryReservePercent;
+ videoView.setBufferConfig(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, maxHeapAllocationPercent, minBackBufferMemoryReservePercent, minBufferMemoryReservePercent);
}
}
private boolean startsWithValidScheme(String uriString) {
- return uriString.startsWith("http://")
- || uriString.startsWith("https://")
- || uriString.startsWith("content://")
- || uriString.startsWith("file://")
- || uriString.startsWith("asset://");
+ String lowerCaseUri = uriString.toLowerCase();
+ return lowerCaseUri.startsWith("http://")
+ || lowerCaseUri.startsWith("https://")
+ || lowerCaseUri.startsWith("content://")
+ || lowerCaseUri.startsWith("file://")
+ || lowerCaseUri.startsWith("asset://");
}
private @ResizeMode.Mode int convertToIntDef(String resizeModeOrdinalString) {
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ResizeMode.java b/android/src/main/java/com/brentvatne/exoplayer/ResizeMode.java
similarity index 100%
rename from android-exoplayer/src/main/java/com/brentvatne/exoplayer/ResizeMode.java
rename to android/src/main/java/com/brentvatne/exoplayer/ResizeMode.java
diff --git a/android/src/main/java/com/brentvatne/exoplayer/SubtitleStyle.java b/android/src/main/java/com/brentvatne/exoplayer/SubtitleStyle.java
new file mode 100644
index 0000000000..bc4e3058dc
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/exoplayer/SubtitleStyle.java
@@ -0,0 +1,38 @@
+package com.brentvatne.exoplayer;
+import com.brentvatne.ReactBridgeUtils;
+import com.facebook.react.bridge.ReadableMap;
+
+/**
+ * Helper file to parse SubtitleStyle prop and build a dedicated class
+ */
+public class SubtitleStyle {
+ private static final String PROP_FONT_SIZE_TRACK = "fontSize";
+ private static final String PROP_PADDING_BOTTOM = "paddingBottom";
+ private static final String PROP_PADDING_TOP = "paddingTop";
+ private static final String PROP_PADDING_LEFT = "paddingLeft";
+ private static final String PROP_PADDING_RIGHT = "paddingRight";
+
+ private int fontSize = -1;
+ private int paddingLeft = 0;
+ private int paddingRight = 0;
+ private int paddingTop = 0;
+ private int paddingBottom = 0;
+
+ private SubtitleStyle() {}
+
+ int getFontSize() {return fontSize;}
+ int getPaddingBottom() {return paddingBottom;}
+ int getPaddingTop() {return paddingTop;}
+ int getPaddingLeft() {return paddingLeft;}
+ int getPaddingRight() {return paddingRight;}
+
+ public static SubtitleStyle parse(ReadableMap src) {
+ SubtitleStyle subtitleStyle = new SubtitleStyle();
+ subtitleStyle.fontSize = ReactBridgeUtils.safeGetInt(src, PROP_FONT_SIZE_TRACK, -1);
+ subtitleStyle.paddingBottom = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_BOTTOM, 0);
+ subtitleStyle.paddingTop = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_TOP, 0);
+ subtitleStyle.paddingLeft = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_LEFT, 0);
+ subtitleStyle.paddingRight = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_RIGHT, 0);
+ return subtitleStyle;
+ }
+}
diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java b/android/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java
similarity index 64%
rename from android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java
rename to android/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java
index ea0cc5ac6d..24d8eafb07 100644
--- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java
+++ b/android/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java
@@ -3,6 +3,8 @@
import androidx.annotation.StringDef;
import android.view.View;
+import com.brentvatne.common.Track;
+import com.brentvatne.common.VideoTrack;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableArray;
@@ -15,6 +17,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.io.StringWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
class VideoEventEmitter {
@@ -42,11 +47,16 @@ class VideoEventEmitter {
private static final String EVENT_RESUME = "onPlaybackResume";
private static final String EVENT_READY = "onReadyForDisplay";
private static final String EVENT_BUFFER = "onVideoBuffer";
+ private static final String EVENT_PLAYBACK_STATE_CHANGED = "onVideoPlaybackStateChanged";
private static final String EVENT_IDLE = "onVideoIdle";
private static final String EVENT_TIMED_METADATA = "onTimedMetadata";
private static final String EVENT_AUDIO_BECOMING_NOISY = "onVideoAudioBecomingNoisy";
private static final String EVENT_AUDIO_FOCUS_CHANGE = "onAudioFocusChanged";
private static final String EVENT_PLAYBACK_RATE_CHANGE = "onPlaybackRateChange";
+ private static final String EVENT_AUDIO_TRACKS = "onAudioTracks";
+ private static final String EVENT_TEXT_TRACKS = "onTextTracks";
+ private static final String EVENT_VIDEO_TRACKS = "onVideoTracks";
+ private static final String EVENT_ON_RECEIVE_AD_EVENT = "onReceiveAdEvent";
static final String[] Events = {
EVENT_LOAD_START,
@@ -63,12 +73,17 @@ class VideoEventEmitter {
EVENT_RESUME,
EVENT_READY,
EVENT_BUFFER,
+ EVENT_PLAYBACK_STATE_CHANGED,
EVENT_IDLE,
EVENT_TIMED_METADATA,
EVENT_AUDIO_BECOMING_NOISY,
EVENT_AUDIO_FOCUS_CHANGE,
EVENT_PLAYBACK_RATE_CHANGE,
+ EVENT_AUDIO_TRACKS,
+ EVENT_TEXT_TRACKS,
+ EVENT_VIDEO_TRACKS,
EVENT_BANDWIDTH,
+ EVENT_ON_RECEIVE_AD_EVENT
};
@Retention(RetentionPolicy.SOURCE)
@@ -87,12 +102,17 @@ class VideoEventEmitter {
EVENT_RESUME,
EVENT_READY,
EVENT_BUFFER,
+ EVENT_PLAYBACK_STATE_CHANGED,
EVENT_IDLE,
EVENT_TIMED_METADATA,
EVENT_AUDIO_BECOMING_NOISY,
EVENT_AUDIO_FOCUS_CHANGE,
EVENT_PLAYBACK_RATE_CHANGE,
+ EVENT_AUDIO_TRACKS,
+ EVENT_TEXT_TRACKS,
+ EVENT_VIDEO_TRACKS,
EVENT_BANDWIDTH,
+ EVENT_ON_RECEIVE_AD_EVENT
})
@interface VideoEvents {
}
@@ -104,6 +124,8 @@ class VideoEventEmitter {
private static final String EVENT_PROP_STEP_FORWARD = "canStepForward";
private static final String EVENT_PROP_STEP_BACKWARD = "canStepBackward";
+ private static final String EVENT_PROP_BUFFER_START = "bufferStart";
+ private static final String EVENT_PROP_BUFFER_END = "bufferEnd";
private static final String EVENT_PROP_DURATION = "duration";
private static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration";
private static final String EVENT_PROP_SEEKABLE_DURATION = "seekableDuration";
@@ -125,11 +147,14 @@ class VideoEventEmitter {
private static final String EVENT_PROP_ERROR = "error";
private static final String EVENT_PROP_ERROR_STRING = "errorString";
private static final String EVENT_PROP_ERROR_EXCEPTION = "errorException";
+ private static final String EVENT_PROP_ERROR_TRACE = "errorStackTrace";
+ private static final String EVENT_PROP_ERROR_CODE = "errorCode";
private static final String EVENT_PROP_TIMED_METADATA = "metadata";
- private static final String EVENT_PROP_BITRATE = "bitrate";
+ private static final String EVENT_PROP_BITRATE = "bitrate";
+ private static final String EVENT_PROP_IS_PLAYING = "isPlaying";
void setViewId(int viewId) {
this.viewId = viewId;
@@ -139,20 +164,90 @@ void loadStart() {
receiveEvent(EVENT_LOAD_START, null);
}
- void load(double duration, double currentPosition, int videoWidth, int videoHeight,
- WritableArray audioTracks, WritableArray textTracks, WritableArray videoTracks, String trackId) {
- WritableMap event = Arguments.createMap();
- event.putDouble(EVENT_PROP_DURATION, duration / 1000D);
- event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
-
+ WritableMap aspectRatioToNaturalSize(int videoWidth, int videoHeight) {
WritableMap naturalSize = Arguments.createMap();
naturalSize.putInt(EVENT_PROP_WIDTH, videoWidth);
naturalSize.putInt(EVENT_PROP_HEIGHT, videoHeight);
if (videoWidth > videoHeight) {
naturalSize.putString(EVENT_PROP_ORIENTATION, "landscape");
- } else {
+ } else if (videoWidth < videoHeight) {
naturalSize.putString(EVENT_PROP_ORIENTATION, "portrait");
+ } else {
+ naturalSize.putString(EVENT_PROP_ORIENTATION, "square");
}
+ return naturalSize;
+ }
+
+ WritableArray audioTracksToArray(ArrayList audioTracks) {
+ WritableArray waAudioTracks = Arguments.createArray();
+ if( audioTracks != null ){
+ for (int i = 0; i < audioTracks.size(); ++i) {
+ Track format = audioTracks.get(i);
+ WritableMap audioTrack = Arguments.createMap();
+ audioTrack.putInt("index", i);
+ audioTrack.putString("title", format.m_title != null ? format.m_title : "");
+ audioTrack.putString("type", format.m_mimeType != null ? format.m_mimeType : "");
+ audioTrack.putString("language", format.m_language != null ? format.m_language : "");
+ audioTrack.putInt("bitrate", format.m_bitrate);
+ audioTrack.putBoolean("selected", format.m_isSelected);
+ waAudioTracks.pushMap(audioTrack);
+ }
+ }
+ return waAudioTracks;
+ }
+
+ WritableArray videoTracksToArray(ArrayList videoTracks) {
+ WritableArray waVideoTracks = Arguments.createArray();
+ if( videoTracks != null ){
+ for (int i = 0; i < videoTracks.size(); ++i) {
+ VideoTrack vTrack = videoTracks.get(i);
+ WritableMap videoTrack = Arguments.createMap();
+ videoTrack.putInt("width", vTrack.m_width);
+ videoTrack.putInt("height",vTrack.m_height);
+ videoTrack.putInt("bitrate", vTrack.m_bitrate);
+ videoTrack.putString("codecs", vTrack.m_codecs);
+ videoTrack.putInt("trackId",vTrack.m_id);
+ videoTrack.putBoolean("selected", vTrack.m_isSelected);
+ waVideoTracks.pushMap(videoTrack);
+ }
+ }
+ return waVideoTracks;
+ }
+
+ WritableArray textTracksToArray(ArrayList textTracks) {
+ WritableArray waTextTracks = Arguments.createArray();
+ if (textTracks != null) {
+ for (int i = 0; i < textTracks.size(); ++i) {
+ Track format = textTracks.get(i);
+ WritableMap textTrack = Arguments.createMap();
+ textTrack.putInt("index", i);
+ textTrack.putString("title", format.m_title != null ? format.m_title : "");
+ textTrack.putString("type", format.m_mimeType != null ? format.m_mimeType : "");
+ textTrack.putString("language", format.m_language != null ? format.m_language : "");
+ textTrack.putBoolean("selected", format.m_isSelected);
+ waTextTracks.pushMap(textTrack);
+ }
+ }
+ return waTextTracks;
+ }
+
+ public void load(double duration, double currentPosition, int videoWidth, int videoHeight,
+ ArrayList audioTracks, ArrayList textTracks, ArrayList videoTracks, String trackId){
+ WritableArray waAudioTracks = audioTracksToArray(audioTracks);
+ WritableArray waVideoTracks = videoTracksToArray(videoTracks);
+ WritableArray waTextTracks = textTracksToArray(textTracks);
+
+ load( duration, currentPosition, videoWidth, videoHeight, waAudioTracks, waTextTracks, waVideoTracks, trackId);
+ }
+
+
+ private void load(double duration, double currentPosition, int videoWidth, int videoHeight,
+ WritableArray audioTracks, WritableArray textTracks, WritableArray videoTracks, String trackId) {
+ WritableMap event = Arguments.createMap();
+ event.putDouble(EVENT_PROP_DURATION, duration / 1000D);
+ event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
+
+ WritableMap naturalSize = aspectRatioToNaturalSize(videoWidth, videoHeight);
event.putMap(EVENT_PROP_NATURAL_SIZE, naturalSize);
event.putString(EVENT_PROP_TRACK_ID, trackId);
event.putArray(EVENT_PROP_VIDEO_TRACKS, videoTracks);
@@ -171,6 +266,26 @@ void load(double duration, double currentPosition, int videoWidth, int videoHeig
receiveEvent(EVENT_LOAD, event);
}
+
+
+ WritableMap arrayToObject(String field, WritableArray array) {
+ WritableMap event = Arguments.createMap();
+ event.putArray(field, array);
+ return event;
+ }
+
+ public void audioTracks(ArrayList audioTracks){
+ receiveEvent(EVENT_AUDIO_TRACKS, arrayToObject(EVENT_PROP_AUDIO_TRACKS, audioTracksToArray(audioTracks)));
+ }
+
+ public void textTracks(ArrayList textTracks){
+ receiveEvent(EVENT_TEXT_TRACKS, arrayToObject(EVENT_PROP_TEXT_TRACKS, textTracksToArray(textTracks)));
+ }
+
+ public void videoTracks(ArrayList videoTracks){
+ receiveEvent(EVENT_VIDEO_TRACKS, arrayToObject(EVENT_PROP_VIDEO_TRACKS, videoTracksToArray(videoTracks)));
+ }
+
void progressChanged(double currentPosition, double bufferedDuration, double seekableDuration, double currentPlaybackTime) {
WritableMap event = Arguments.createMap();
event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
@@ -206,6 +321,12 @@ void buffering(boolean isBuffering) {
receiveEvent(EVENT_BUFFER, map);
}
+ void playbackStateChanged(boolean isPlaying) {
+ WritableMap map = Arguments.createMap();
+ map.putBoolean(EVENT_PROP_IS_PLAYING, isPlaying);
+ receiveEvent(EVENT_PLAYBACK_STATE_CHANGED, map);
+ }
+
void idle() {
receiveEvent(EVENT_IDLE, null);
}
@@ -231,9 +352,25 @@ void fullscreenDidDismiss() {
}
void error(String errorString, Exception exception) {
+ _error(errorString, exception, "0001");
+ }
+
+ void error(String errorString, Exception exception, String errorCode) {
+ _error(errorString, exception, errorCode);
+ }
+
+ void _error(String errorString, Exception exception, String errorCode) {
+ // Prepare stack trace
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ exception.printStackTrace(pw);
+ String stackTrace = sw.toString();
+
WritableMap error = Arguments.createMap();
error.putString(EVENT_PROP_ERROR_STRING, errorString);
error.putString(EVENT_PROP_ERROR_EXCEPTION, exception.toString());
+ error.putString(EVENT_PROP_ERROR_CODE, errorCode);
+ error.putString(EVENT_PROP_ERROR_TRACE, stackTrace);
WritableMap event = Arguments.createMap();
event.putMap(EVENT_PROP_ERROR, error);
receiveEvent(EVENT_ERROR, event);
@@ -298,6 +435,13 @@ void audioBecomingNoisy() {
receiveEvent(EVENT_AUDIO_BECOMING_NOISY, null);
}
+ void receiveAdEvent(String event) {
+ WritableMap map = Arguments.createMap();
+ map.putString("event", event);
+
+ receiveEvent(EVENT_ON_RECEIVE_AD_EVENT, map);
+ }
+
private void receiveEvent(@VideoEvents String type, WritableMap event) {
eventEmitter.receiveEvent(viewId, type, event);
}
diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java b/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java
index b09e94148d..23bedad69e 100644
--- a/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java
+++ b/android/src/main/java/com/brentvatne/react/ReactVideoPackage.java
@@ -1,21 +1,33 @@
package com.brentvatne.react;
-import android.app.Activity;
+import com.brentvatne.exoplayer.DefaultReactExoplayerConfig;
+import com.brentvatne.exoplayer.ReactExoplayerConfig;
+import com.brentvatne.exoplayer.ReactExoplayerViewManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class ReactVideoPackage implements ReactPackage {
+ private ReactExoplayerConfig config;
+
+ public ReactVideoPackage() {
+ }
+
+ public ReactVideoPackage(ReactExoplayerConfig config) {
+ this.config = config;
+ }
+
@Override
public List createNativeModules(ReactApplicationContext reactContext) {
- return Collections.emptyList();
+ return Collections.singletonList(
+ new VideoDecoderPropertiesModule(reactContext)
+ );
}
// Deprecated RN 0.47
@@ -23,8 +35,12 @@ public List> createJSModules() {
return Collections.emptyList();
}
+
@Override
public List createViewManagers(ReactApplicationContext reactContext) {
- return Arrays.asList(new ReactVideoViewManager());
+ if (config == null) {
+ config = new DefaultReactExoplayerConfig(reactContext);
+ }
+ return Collections.singletonList(new ReactExoplayerViewManager(config));
}
}
diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoView.java b/android/src/main/java/com/brentvatne/react/ReactVideoView.java
deleted file mode 100644
index f3f5fe7b07..0000000000
--- a/android/src/main/java/com/brentvatne/react/ReactVideoView.java
+++ /dev/null
@@ -1,796 +0,0 @@
-package com.brentvatne.react;
-
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.content.res.AssetFileDescriptor;
-import android.graphics.Matrix;
-import android.media.MediaPlayer;
-import android.media.TimedMetaData;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Handler;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-import android.view.View;
-import android.view.Window;
-import android.webkit.CookieManager;
-import android.widget.MediaController;
-
-import com.android.vending.expansion.zipfile.APKExpansionSupport;
-import com.android.vending.expansion.zipfile.ZipResourceFile;
-import com.facebook.react.bridge.Arguments;
-import com.facebook.react.bridge.LifecycleEventListener;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.bridge.WritableMap;
-import com.facebook.react.bridge.WritableArray;
-import com.facebook.react.bridge.WritableNativeArray;
-import com.facebook.react.uimanager.ThemedReactContext;
-import com.facebook.react.uimanager.events.RCTEventEmitter;
-import com.yqritc.scalablevideoview.ScalableType;
-import com.yqritc.scalablevideoview.ScalableVideoView;
-import com.yqritc.scalablevideoview.ScaleManager;
-import com.yqritc.scalablevideoview.Size;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.HashMap;
-import java.util.Map;
-import java.lang.Math;
-import java.math.BigDecimal;
-
-import javax.annotation.Nullable;
-
-@SuppressLint("ViewConstructor")
-public class ReactVideoView extends ScalableVideoView implements
- MediaPlayer.OnPreparedListener,
- MediaPlayer.OnErrorListener,
- MediaPlayer.OnBufferingUpdateListener,
- MediaPlayer.OnSeekCompleteListener,
- MediaPlayer.OnCompletionListener,
- MediaPlayer.OnInfoListener,
- LifecycleEventListener,
- MediaController.MediaPlayerControl {
-
- public enum Events {
- EVENT_LOAD_START("onVideoLoadStart"),
- EVENT_LOAD("onVideoLoad"),
- EVENT_ERROR("onVideoError"),
- EVENT_PROGRESS("onVideoProgress"),
- EVENT_TIMED_METADATA("onTimedMetadata"),
- EVENT_SEEK("onVideoSeek"),
- EVENT_END("onVideoEnd"),
- EVENT_STALLED("onPlaybackStalled"),
- EVENT_RESUME("onPlaybackResume"),
- EVENT_READY_FOR_DISPLAY("onReadyForDisplay"),
- EVENT_FULLSCREEN_WILL_PRESENT("onVideoFullscreenPlayerWillPresent"),
- EVENT_FULLSCREEN_DID_PRESENT("onVideoFullscreenPlayerDidPresent"),
- EVENT_FULLSCREEN_WILL_DISMISS("onVideoFullscreenPlayerWillDismiss"),
- EVENT_FULLSCREEN_DID_DISMISS("onVideoFullscreenPlayerDidDismiss");
-
- private final String mName;
-
- Events(final String name) {
- mName = name;
- }
-
- @Override
- public String toString() {
- return mName;
- }
- }
-
- public static final String EVENT_PROP_FAST_FORWARD = "canPlayFastForward";
- public static final String EVENT_PROP_SLOW_FORWARD = "canPlaySlowForward";
- public static final String EVENT_PROP_SLOW_REVERSE = "canPlaySlowReverse";
- public static final String EVENT_PROP_REVERSE = "canPlayReverse";
- public static final String EVENT_PROP_STEP_FORWARD = "canStepForward";
- public static final String EVENT_PROP_STEP_BACKWARD = "canStepBackward";
-
- public static final String EVENT_PROP_DURATION = "duration";
- public static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration";
- public static final String EVENT_PROP_SEEKABLE_DURATION = "seekableDuration";
- public static final String EVENT_PROP_CURRENT_TIME = "currentTime";
- public static final String EVENT_PROP_SEEK_TIME = "seekTime";
- public static final String EVENT_PROP_NATURALSIZE = "naturalSize";
- public static final String EVENT_PROP_WIDTH = "width";
- public static final String EVENT_PROP_HEIGHT = "height";
- public static final String EVENT_PROP_ORIENTATION = "orientation";
- public static final String EVENT_PROP_METADATA = "metadata";
- public static final String EVENT_PROP_TARGET = "target";
- public static final String EVENT_PROP_METADATA_IDENTIFIER = "identifier";
- public static final String EVENT_PROP_METADATA_VALUE = "value";
-
- public static final String EVENT_PROP_ERROR = "error";
- public static final String EVENT_PROP_WHAT = "what";
- public static final String EVENT_PROP_EXTRA = "extra";
-
- private ThemedReactContext mThemedReactContext;
- private RCTEventEmitter mEventEmitter;
-
- private Handler mProgressUpdateHandler = new Handler();
- private Runnable mProgressUpdateRunnable = null;
- private Handler videoControlHandler = new Handler();
- private MediaController mediaController;
-
- private String mSrcUriString = null;
- private String mSrcType = "mp4";
- private ReadableMap mRequestHeaders = null;
- private boolean mSrcIsNetwork = false;
- private boolean mSrcIsAsset = false;
- private ScalableType mResizeMode = ScalableType.LEFT_TOP;
- private boolean mRepeat = false;
- private boolean mPaused = false;
- private boolean mMuted = false;
- private boolean mPreventsDisplaySleepDuringVideoPlayback = true;
- private float mVolume = 1.0f;
- private float mStereoPan = 0.0f;
- private float mProgressUpdateInterval = 250.0f;
- private float mRate = 1.0f;
- private float mActiveRate = 1.0f;
- private long mSeekTime = 0;
- private boolean mPlayInBackground = false;
- private boolean mBackgroundPaused = false;
- private boolean mIsFullscreen = false;
-
- private int mMainVer = 0;
- private int mPatchVer = 0;
-
- private boolean mMediaPlayerValid = false; // True if mMediaPlayer is in prepared, started, paused or completed state.
-
- private int mVideoDuration = 0;
- private int mVideoBufferedDuration = 0;
- private boolean isCompleted = false;
- private boolean mUseNativeControls = false;
-
- public ReactVideoView(ThemedReactContext themedReactContext) {
- super(themedReactContext);
-
- mThemedReactContext = themedReactContext;
- mEventEmitter = themedReactContext.getJSModule(RCTEventEmitter.class);
- themedReactContext.addLifecycleEventListener(this);
-
- initializeMediaPlayerIfNeeded();
- setSurfaceTextureListener(this);
-
- mProgressUpdateRunnable = new Runnable() {
- @Override
- public void run() {
-
- if (mMediaPlayerValid && !isCompleted && !mPaused && !mBackgroundPaused) {
- WritableMap event = Arguments.createMap();
- event.putDouble(EVENT_PROP_CURRENT_TIME, mMediaPlayer.getCurrentPosition() / 1000.0);
- event.putDouble(EVENT_PROP_PLAYABLE_DURATION, mVideoBufferedDuration / 1000.0); //TODO:mBufferUpdateRunnable
- event.putDouble(EVENT_PROP_SEEKABLE_DURATION, mVideoDuration / 1000.0);
- mEventEmitter.receiveEvent(getId(), Events.EVENT_PROGRESS.toString(), event);
-
- // Check for update after an interval
- mProgressUpdateHandler.postDelayed(mProgressUpdateRunnable, Math.round(mProgressUpdateInterval));
- }
- }
- };
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mUseNativeControls) {
- initializeMediaControllerIfNeeded();
- mediaController.show();
- }
-
- return super.onTouchEvent(event);
- }
-
- @Override
- @SuppressLint("DrawAllocation")
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- if (!changed || !mMediaPlayerValid) {
- return;
- }
-
- int videoWidth = getVideoWidth();
- int videoHeight = getVideoHeight();
-
- if (videoWidth == 0 || videoHeight == 0) {
- return;
- }
-
- Size viewSize = new Size(getWidth(), getHeight());
- Size videoSize = new Size(videoWidth, videoHeight);
- ScaleManager scaleManager = new ScaleManager(viewSize, videoSize);
- Matrix matrix = scaleManager.getScaleMatrix(mScalableType);
- if (matrix != null) {
- setTransform(matrix);
- }
- }
-
- private void initializeMediaPlayerIfNeeded() {
- if (mMediaPlayer == null) {
- mMediaPlayerValid = false;
- mMediaPlayer = new MediaPlayer();
- mMediaPlayer.setOnVideoSizeChangedListener(this);
- mMediaPlayer.setOnErrorListener(this);
- mMediaPlayer.setOnPreparedListener(this);
- mMediaPlayer.setOnBufferingUpdateListener(this);
- mMediaPlayer.setOnSeekCompleteListener(this);
- mMediaPlayer.setOnCompletionListener(this);
- mMediaPlayer.setOnInfoListener(this);
- if (Build.VERSION.SDK_INT >= 23) {
- mMediaPlayer.setOnTimedMetaDataAvailableListener(new TimedMetaDataAvailableListener());
- }
- }
- }
-
- private void initializeMediaControllerIfNeeded() {
- if (mediaController == null) {
- mediaController = new MediaController(this.getContext());
- }
- }
-
- public void cleanupMediaPlayerResources() {
- if ( mediaController != null ) {
- mediaController.hide();
- }
- if ( mMediaPlayer != null ) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- mMediaPlayer.setOnTimedMetaDataAvailableListener(null);
- }
- mMediaPlayerValid = false;
- release();
- }
- if (mIsFullscreen) {
- setFullscreen(false);
- }
- if (mThemedReactContext != null) {
- mThemedReactContext.removeLifecycleEventListener(this);
- mThemedReactContext = null;
- }
- }
-
- public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset, final ReadableMap requestHeaders) {
- setSrc(uriString, type, isNetwork, isAsset, requestHeaders, 0, 0);
- }
-
- public void setSrc(final String uriString, final String type, final boolean isNetwork, final boolean isAsset, final ReadableMap requestHeaders, final int expansionMainVersion, final int expansionPatchVersion) {
-
- mSrcUriString = uriString;
- mSrcType = type;
- mSrcIsNetwork = isNetwork;
- mSrcIsAsset = isAsset;
- mRequestHeaders = requestHeaders;
- mMainVer = expansionMainVersion;
- mPatchVer = expansionPatchVersion;
-
-
- mMediaPlayerValid = false;
- mVideoDuration = 0;
- mVideoBufferedDuration = 0;
-
- initializeMediaPlayerIfNeeded();
- mMediaPlayer.reset();
-
- try {
- if (isNetwork) {
- // Use the shared CookieManager to access the cookies
- // set by WebViews inside the same app
- CookieManager cookieManager = CookieManager.getInstance();
-
- Uri parsedUrl = Uri.parse(uriString);
- Uri.Builder builtUrl = parsedUrl.buildUpon();
-
- String cookie = cookieManager.getCookie(builtUrl.build().toString());
-
- Map headers = new HashMap();
-
- if (cookie != null) {
- headers.put("Cookie", cookie);
- }
-
- if (mRequestHeaders != null) {
- headers.putAll(toStringMap(mRequestHeaders));
- }
-
- /* According to https://github.com/react-native-community/react-native-video/pull/537
- * there is an issue with this where it can cause a IOException.
- * TODO: diagnose this exception and fix it
- */
- setDataSource(mThemedReactContext, parsedUrl, headers);
- } else if (isAsset) {
- if (uriString.startsWith("content://")) {
- Uri parsedUrl = Uri.parse(uriString);
- setDataSource(mThemedReactContext, parsedUrl);
- } else {
- setDataSource(uriString);
- }
- } else {
- ZipResourceFile expansionFile= null;
- AssetFileDescriptor fd= null;
- if(mMainVer>0) {
- try {
- expansionFile = APKExpansionSupport.getAPKExpansionZipFile(mThemedReactContext, mMainVer, mPatchVer);
- fd = expansionFile.getAssetFileDescriptor(uriString.replace(".mp4","") + ".mp4");
- } catch (IOException e) {
- e.printStackTrace();
- } catch (NullPointerException e) {
- e.printStackTrace();
- }
- }
- if(fd==null) {
- int identifier = mThemedReactContext.getResources().getIdentifier(
- uriString,
- "drawable",
- mThemedReactContext.getPackageName()
- );
- if (identifier == 0) {
- identifier = mThemedReactContext.getResources().getIdentifier(
- uriString,
- "raw",
- mThemedReactContext.getPackageName()
- );
- }
- setRawData(identifier);
- }
- else {
- setDataSource(fd.getFileDescriptor(), fd.getStartOffset(),fd.getLength());
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- return;
- }
-
- WritableMap src = Arguments.createMap();
-
- WritableMap wRequestHeaders = Arguments.createMap();
- wRequestHeaders.merge(mRequestHeaders);
-
- src.putString(ReactVideoViewManager.PROP_SRC_URI, uriString);
- src.putString(ReactVideoViewManager.PROP_SRC_TYPE, type);
- src.putMap(ReactVideoViewManager.PROP_SRC_HEADERS, wRequestHeaders);
- src.putBoolean(ReactVideoViewManager.PROP_SRC_IS_NETWORK, isNetwork);
- if(mMainVer>0) {
- src.putInt(ReactVideoViewManager.PROP_SRC_MAINVER, mMainVer);
- if(mPatchVer>0) {
- src.putInt(ReactVideoViewManager.PROP_SRC_PATCHVER, mPatchVer);
- }
- }
- WritableMap event = Arguments.createMap();
- event.putMap(ReactVideoViewManager.PROP_SRC, src);
- mEventEmitter.receiveEvent(getId(), Events.EVENT_LOAD_START.toString(), event);
- isCompleted = false;
-
- try {
- prepareAsync(this);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public void setResizeModeModifier(final ScalableType resizeMode) {
- mResizeMode = resizeMode;
-
- if (mMediaPlayerValid) {
- setScalableType(resizeMode);
- invalidate();
- }
- }
-
- public void setRepeatModifier(final boolean repeat) {
-
- mRepeat = repeat;
-
- if (mMediaPlayerValid) {
- setLooping(repeat);
- }
- }
-
- public void setPausedModifier(final boolean paused) {
- mPaused = paused;
-
- if (!mMediaPlayerValid) {
- return;
- }
-
- if (mPaused) {
- if (mMediaPlayer.isPlaying()) {
- pause();
- }
- } else {
- if (!mMediaPlayer.isPlaying()) {
- start();
- // Setting the rate unpauses, so we have to wait for an unpause
- if (mRate != mActiveRate) {
- setRateModifier(mRate);
- }
-
- // Also Start the Progress Update Handler
- mProgressUpdateHandler.post(mProgressUpdateRunnable);
- }
- }
- setKeepScreenOn(!mPaused && mPreventsDisplaySleepDuringVideoPlayback);
- }
-
- // reduces the volume based on stereoPan
- private float calulateRelativeVolume() {
- float relativeVolume = (mVolume * (1 - Math.abs(mStereoPan)));
- // only one decimal allowed
- BigDecimal roundRelativeVolume = new BigDecimal(relativeVolume).setScale(1, BigDecimal.ROUND_HALF_UP);
- return roundRelativeVolume.floatValue();
- }
-
- public void setPreventsDisplaySleepDuringVideoPlaybackModifier(final boolean preventsDisplaySleepDuringVideoPlayback) {
- mPreventsDisplaySleepDuringVideoPlayback = preventsDisplaySleepDuringVideoPlayback;
-
- if (!mMediaPlayerValid) {
- return;
- }
-
- mMediaPlayer.setScreenOnWhilePlaying(mPreventsDisplaySleepDuringVideoPlayback);
- setKeepScreenOn(mPreventsDisplaySleepDuringVideoPlayback);
- }
-
- public void setMutedModifier(final boolean muted) {
- mMuted = muted;
-
- if (!mMediaPlayerValid) {
- return;
- }
-
- if (mMuted) {
- setVolume(0, 0);
- } else if (mStereoPan < 0) {
- // louder on the left channel
- setVolume(mVolume, calulateRelativeVolume());
- } else if (mStereoPan > 0) {
- // louder on the right channel
- setVolume(calulateRelativeVolume(), mVolume);
- } else {
- // same volume on both channels
- setVolume(mVolume, mVolume);
- }
- }
-
- public void setVolumeModifier(final float volume) {
- mVolume = volume;
- setMutedModifier(mMuted);
- }
-
- public void setStereoPan(final float stereoPan) {
- mStereoPan = stereoPan;
- setMutedModifier(mMuted);
- }
-
- public void setProgressUpdateInterval(final float progressUpdateInterval) {
- mProgressUpdateInterval = progressUpdateInterval;
- }
-
- public void setRateModifier(final float rate) {
- mRate = rate;
-
- if (mMediaPlayerValid) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (!mPaused) { // Applying the rate while paused will cause the video to start
- /* Per https://stackoverflow.com/questions/39442522/setplaybackparams-causes-illegalstateexception
- * Some devices throw an IllegalStateException if you set the rate without first calling reset()
- * TODO: Call reset() then reinitialize the player
- */
- try {
- mMediaPlayer.setPlaybackParams(mMediaPlayer.getPlaybackParams().setSpeed(rate));
- mActiveRate = rate;
- } catch (Exception e) {
- Log.e(ReactVideoViewManager.REACT_CLASS, "Unable to set rate, unsupported on this device");
- }
- }
- } else {
- Log.e(ReactVideoViewManager.REACT_CLASS, "Setting playback rate is not yet supported on Android versions below 6.0");
- }
- }
- }
-
- public void setFullscreen(boolean isFullscreen) {
- if (isFullscreen == mIsFullscreen) {
- return; // Avoid generating events when nothing is changing
- }
- mIsFullscreen = isFullscreen;
-
- Activity activity = mThemedReactContext.getCurrentActivity();
- if (activity == null) {
- return;
- }
- Window window = activity.getWindow();
- View decorView = window.getDecorView();
- int uiOptions;
- if (mIsFullscreen) {
- if (Build.VERSION.SDK_INT >= 19) { // 4.4+
- uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | SYSTEM_UI_FLAG_FULLSCREEN;
- } else {
- uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | SYSTEM_UI_FLAG_FULLSCREEN;
- }
- mEventEmitter.receiveEvent(getId(), Events.EVENT_FULLSCREEN_WILL_PRESENT.toString(), null);
- decorView.setSystemUiVisibility(uiOptions);
- mEventEmitter.receiveEvent(getId(), Events.EVENT_FULLSCREEN_DID_PRESENT.toString(), null);
- } else {
- uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
- mEventEmitter.receiveEvent(getId(), Events.EVENT_FULLSCREEN_WILL_DISMISS.toString(), null);
- decorView.setSystemUiVisibility(uiOptions);
- mEventEmitter.receiveEvent(getId(), Events.EVENT_FULLSCREEN_DID_DISMISS.toString(), null);
- }
- }
-
- public void applyModifiers() {
- setResizeModeModifier(mResizeMode);
- setRepeatModifier(mRepeat);
- setPausedModifier(mPaused);
- setMutedModifier(mMuted);
- setPreventsDisplaySleepDuringVideoPlaybackModifier(mPreventsDisplaySleepDuringVideoPlayback);
- setProgressUpdateInterval(mProgressUpdateInterval);
- setRateModifier(mRate);
- }
-
- public void setPlayInBackground(final boolean playInBackground) {
-
- mPlayInBackground = playInBackground;
- }
-
- public void setControls(boolean controls) {
- this.mUseNativeControls = controls;
- }
-
- @Override
- public void onPrepared(MediaPlayer mp) {
-
- mMediaPlayerValid = true;
- mVideoDuration = mp.getDuration();
-
- WritableMap naturalSize = Arguments.createMap();
- naturalSize.putInt(EVENT_PROP_WIDTH, mp.getVideoWidth());
- naturalSize.putInt(EVENT_PROP_HEIGHT, mp.getVideoHeight());
- if (mp.getVideoWidth() > mp.getVideoHeight())
- naturalSize.putString(EVENT_PROP_ORIENTATION, "landscape");
- else
- naturalSize.putString(EVENT_PROP_ORIENTATION, "portrait");
-
- WritableMap event = Arguments.createMap();
- event.putDouble(EVENT_PROP_DURATION, mVideoDuration / 1000.0);
- event.putDouble(EVENT_PROP_CURRENT_TIME, mp.getCurrentPosition() / 1000.0);
- event.putMap(EVENT_PROP_NATURALSIZE, naturalSize);
- // TODO: Actually check if you can.
- event.putBoolean(EVENT_PROP_FAST_FORWARD, true);
- event.putBoolean(EVENT_PROP_SLOW_FORWARD, true);
- event.putBoolean(EVENT_PROP_SLOW_REVERSE, true);
- event.putBoolean(EVENT_PROP_REVERSE, true);
- event.putBoolean(EVENT_PROP_FAST_FORWARD, true);
- event.putBoolean(EVENT_PROP_STEP_BACKWARD, true);
- event.putBoolean(EVENT_PROP_STEP_FORWARD, true);
- mEventEmitter.receiveEvent(getId(), Events.EVENT_LOAD.toString(), event);
-
- applyModifiers();
-
- if (mUseNativeControls) {
- initializeMediaControllerIfNeeded();
- mediaController.setMediaPlayer(this);
- mediaController.setAnchorView(this);
-
- videoControlHandler.post(new Runnable() {
- @Override
- public void run() {
- mediaController.setEnabled(true);
- mediaController.show();
- }
- });
- }
-
- selectTimedMetadataTrack(mp);
- }
-
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
-
- WritableMap error = Arguments.createMap();
- error.putInt(EVENT_PROP_WHAT, what);
- error.putInt(EVENT_PROP_EXTRA, extra);
- WritableMap event = Arguments.createMap();
- event.putMap(EVENT_PROP_ERROR, error);
- mEventEmitter.receiveEvent(getId(), Events.EVENT_ERROR.toString(), event);
- return true;
- }
-
- @Override
- public boolean onInfo(MediaPlayer mp, int what, int extra) {
- switch (what) {
- case MediaPlayer.MEDIA_INFO_BUFFERING_START:
- mEventEmitter.receiveEvent(getId(), Events.EVENT_STALLED.toString(), Arguments.createMap());
- break;
- case MediaPlayer.MEDIA_INFO_BUFFERING_END:
- mEventEmitter.receiveEvent(getId(), Events.EVENT_RESUME.toString(), Arguments.createMap());
- break;
- case MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
- mEventEmitter.receiveEvent(getId(), Events.EVENT_READY_FOR_DISPLAY.toString(), Arguments.createMap());
- break;
-
- default:
- }
- return false;
- }
-
- @Override
- public void onBufferingUpdate(MediaPlayer mp, int percent) {
- selectTimedMetadataTrack(mp);
- mVideoBufferedDuration = (int) Math.round((double) (mVideoDuration * percent) / 100.0);
- }
-
- public void onSeekComplete(MediaPlayer mp) {
- WritableMap event = Arguments.createMap();
- event.putDouble(EVENT_PROP_CURRENT_TIME, getCurrentPosition() / 1000.0);
- event.putDouble(EVENT_PROP_SEEK_TIME, mSeekTime / 1000.0);
- mEventEmitter.receiveEvent(getId(), Events.EVENT_SEEK.toString(), event);
- mSeekTime = 0;
- }
-
- @Override
- public void seekTo(int msec) {
- if (mMediaPlayerValid) {
- mSeekTime = msec;
- super.seekTo(msec);
- if (isCompleted && mVideoDuration != 0 && msec < mVideoDuration) {
- isCompleted = false;
- }
- }
- }
-
- @Override
- public int getBufferPercentage() {
- return 0;
- }
-
- @Override
- public boolean canPause() {
- return true;
- }
-
- @Override
- public boolean canSeekBackward() {
- return true;
- }
-
- @Override
- public boolean canSeekForward() {
- return true;
- }
-
- @Override
- public int getAudioSessionId() {
- return 0;
- }
-
- @Override
- public void onCompletion(MediaPlayer mp) {
- isCompleted = true;
- mEventEmitter.receiveEvent(getId(), Events.EVENT_END.toString(), null);
- if (!mRepeat) {
- setKeepScreenOn(false);
- }
- }
-
- // This is not fully tested and does not work for all forms of timed metadata
- @TargetApi(23) // 6.0
- public class TimedMetaDataAvailableListener
- implements MediaPlayer.OnTimedMetaDataAvailableListener
- {
- public void onTimedMetaDataAvailable(MediaPlayer mp, TimedMetaData data) {
- WritableMap event = Arguments.createMap();
-
- try {
- String rawMeta = new String(data.getMetaData(), "UTF-8");
- WritableMap id3 = Arguments.createMap();
-
- id3.putString(EVENT_PROP_METADATA_VALUE, rawMeta.substring(rawMeta.lastIndexOf("\u0003") + 1));
- id3.putString(EVENT_PROP_METADATA_IDENTIFIER, "id3/TDEN");
-
- WritableArray metadata = new WritableNativeArray();
-
- metadata.pushMap(id3);
-
- event.putArray(EVENT_PROP_METADATA, metadata);
- event.putDouble(EVENT_PROP_TARGET, getId());
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
-
- mEventEmitter.receiveEvent(getId(), Events.EVENT_TIMED_METADATA.toString(), event);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- mMediaPlayerValid = false;
- super.onDetachedFromWindow();
- setKeepScreenOn(false);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if(mMainVer>0) {
- setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset, mRequestHeaders, mMainVer, mPatchVer);
- }
- else {
- setSrc(mSrcUriString, mSrcType, mSrcIsNetwork, mSrcIsAsset, mRequestHeaders);
- }
- setKeepScreenOn(mPreventsDisplaySleepDuringVideoPlayback);
- }
-
- @Override
- public void onHostPause() {
- if (mMediaPlayerValid && !mPaused && !mPlayInBackground) {
- /* Pause the video in background
- * Don't update the paused prop, developers should be able to update it on background
- * so that when you return to the app the video is paused
- */
- mBackgroundPaused = true;
- mMediaPlayer.pause();
- }
- }
-
- @Override
- public void onHostResume() {
- mBackgroundPaused = false;
- if (mMediaPlayerValid && !mPlayInBackground && !mPaused) {
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- // Restore original state
- setPausedModifier(false);
- }
- });
- }
- }
-
- @Override
- public void onHostDestroy() {
- }
-
- /**
- * toStringMap converts a {@link ReadableMap} into a HashMap.
- *
- * @param readableMap The ReadableMap to be conveted.
- * @return A HashMap containing the data that was in the ReadableMap.
- * @see 'Adapted from https://github.com/artemyarulin/react-native-eval/blob/master/android/src/main/java/com/evaluator/react/ConversionUtil.java'
- */
- public static Map toStringMap(@Nullable ReadableMap readableMap) {
- Map result = new HashMap<>();
- if (readableMap == null)
- return result;
-
- com.facebook.react.bridge.ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
- while (iterator.hasNextKey()) {
- String key = iterator.nextKey();
- result.put(key, readableMap.getString(key));
- }
-
- return result;
- }
-
- // Select track (so we can use it to listen to timed meta data updates)
- private void selectTimedMetadataTrack(MediaPlayer mp) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
- return;
- }
- try { // It's possible this could throw an exception if the framework doesn't support getting track info
- MediaPlayer.TrackInfo[] trackInfo = mp.getTrackInfo();
- for (int i = 0; i < trackInfo.length; ++i) {
- if (trackInfo[i].getTrackType() == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
- mp.selectTrack(i);
- break;
- }
- }
- } catch (Exception e) {}
- }
-}
diff --git a/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java b/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java
deleted file mode 100644
index 59efab497d..0000000000
--- a/android/src/main/java/com/brentvatne/react/ReactVideoViewManager.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package com.brentvatne.react;
-
-import com.brentvatne.react.ReactVideoView.Events;
-import com.facebook.react.bridge.Arguments;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.bridge.WritableMap;
-import com.facebook.react.common.MapBuilder;
-import com.facebook.react.uimanager.annotations.ReactProp;
-import com.facebook.react.uimanager.SimpleViewManager;
-import com.facebook.react.uimanager.ThemedReactContext;
-import com.facebook.react.uimanager.events.RCTEventEmitter;
-import com.yqritc.scalablevideoview.ScalableType;
-
-import javax.annotation.Nullable;
-import java.util.Map;
-
-public class ReactVideoViewManager extends SimpleViewManager {
-
- public static final String REACT_CLASS = "RCTVideo";
-
- public static final String PROP_SRC = "src";
- public static final String PROP_SRC_URI = "uri";
- public static final String PROP_SRC_TYPE = "type";
- public static final String PROP_SRC_HEADERS = "requestHeaders";
- public static final String PROP_SRC_IS_NETWORK = "isNetwork";
- public static final String PROP_SRC_MAINVER = "mainVer";
- public static final String PROP_SRC_PATCHVER = "patchVer";
- public static final String PROP_SRC_IS_ASSET = "isAsset";
- public static final String PROP_RESIZE_MODE = "resizeMode";
- public static final String PROP_REPEAT = "repeat";
- public static final String PROP_PAUSED = "paused";
- public static final String PROP_MUTED = "muted";
- public static final String PROP_PREVENTS_DISPLAY_SLEEP_DURING_VIDEO_PLAYBACK = "preventsDisplaySleepDuringVideoPlayback";
- public static final String PROP_VOLUME = "volume";
- public static final String PROP_STEREO_PAN = "stereoPan";
- public static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
- public static final String PROP_SEEK = "seek";
- public static final String PROP_RATE = "rate";
- public static final String PROP_FULLSCREEN = "fullscreen";
- public static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
- public static final String PROP_CONTROLS = "controls";
-
- @Override
- public String getName() {
- return REACT_CLASS;
- }
-
- @Override
- protected ReactVideoView createViewInstance(ThemedReactContext themedReactContext) {
- return new ReactVideoView(themedReactContext);
- }
-
- @Override
- public void onDropViewInstance(ReactVideoView view) {
- super.onDropViewInstance(view);
- view.cleanupMediaPlayerResources();
- }
-
- @Override
- @Nullable
- public Map getExportedCustomDirectEventTypeConstants() {
- MapBuilder.Builder builder = MapBuilder.builder();
- for (Events event : Events.values()) {
- builder.put(event.toString(), MapBuilder.of("registrationName", event.toString()));
- }
- return builder.build();
- }
-
- @Override
- @Nullable
- public Map getExportedViewConstants() {
- return MapBuilder.of(
- "ScaleNone", Integer.toString(ScalableType.LEFT_TOP.ordinal()),
- "ScaleToFill", Integer.toString(ScalableType.FIT_XY.ordinal()),
- "ScaleAspectFit", Integer.toString(ScalableType.FIT_CENTER.ordinal()),
- "ScaleAspectFill", Integer.toString(ScalableType.CENTER_CROP.ordinal())
- );
- }
-
- @ReactProp(name = PROP_SRC)
- public void setSrc(final ReactVideoView videoView, @Nullable ReadableMap src) {
- int mainVer = src.getInt(PROP_SRC_MAINVER);
- int patchVer = src.getInt(PROP_SRC_PATCHVER);
- if(mainVer<0) { mainVer = 0; }
- if(patchVer<0) { patchVer = 0; }
- if(mainVer>0) {
- videoView.setSrc(
- src.getString(PROP_SRC_URI),
- src.getString(PROP_SRC_TYPE),
- src.getBoolean(PROP_SRC_IS_NETWORK),
- src.getBoolean(PROP_SRC_IS_ASSET),
- src.getMap(PROP_SRC_HEADERS),
- mainVer,
- patchVer
- );
- }
- else {
- videoView.setSrc(
- src.getString(PROP_SRC_URI),
- src.getString(PROP_SRC_TYPE),
- src.getBoolean(PROP_SRC_IS_NETWORK),
- src.getBoolean(PROP_SRC_IS_ASSET),
- src.getMap(PROP_SRC_HEADERS)
- );
- }
- }
-
- @ReactProp(name = PROP_PREVENTS_DISPLAY_SLEEP_DURING_VIDEO_PLAYBACK)
- public void setPropPreventsDisplaySleepDuringVideoPlayback(final ReactVideoView videoView, final boolean doPreventSleep) {
- videoView.setPreventsDisplaySleepDuringVideoPlaybackModifier(doPreventSleep);
- }
-
- @ReactProp(name = PROP_RESIZE_MODE)
- public void setResizeMode(final ReactVideoView videoView, final String resizeModeOrdinalString) {
- videoView.setResizeModeModifier(ScalableType.values()[Integer.parseInt(resizeModeOrdinalString)]);
- }
-
- @ReactProp(name = PROP_REPEAT, defaultBoolean = false)
- public void setRepeat(final ReactVideoView videoView, final boolean repeat) {
- videoView.setRepeatModifier(repeat);
- }
-
- @ReactProp(name = PROP_PAUSED, defaultBoolean = false)
- public void setPaused(final ReactVideoView videoView, final boolean paused) {
- videoView.setPausedModifier(paused);
- }
-
- @ReactProp(name = PROP_MUTED, defaultBoolean = false)
- public void setMuted(final ReactVideoView videoView, final boolean muted) {
- videoView.setMutedModifier(muted);
- }
-
- @ReactProp(name = PROP_VOLUME, defaultFloat = 1.0f)
- public void setVolume(final ReactVideoView videoView, final float volume) {
- videoView.setVolumeModifier(volume);
- }
-
- @ReactProp(name = PROP_STEREO_PAN)
- public void setStereoPan(final ReactVideoView videoView, final float stereoPan) {
- videoView.setStereoPan(stereoPan);
- }
-
- @ReactProp(name = PROP_PROGRESS_UPDATE_INTERVAL, defaultFloat = 250.0f)
- public void setProgressUpdateInterval(final ReactVideoView videoView, final float progressUpdateInterval) {
- videoView.setProgressUpdateInterval(progressUpdateInterval);
- }
-
- @ReactProp(name = PROP_SEEK)
- public void setSeek(final ReactVideoView videoView, final float seek) {
- videoView.seekTo(Math.round(seek * 1000.0f));
- }
-
- @ReactProp(name = PROP_RATE)
- public void setRate(final ReactVideoView videoView, final float rate) {
- videoView.setRateModifier(rate);
- }
-
- @ReactProp(name = PROP_FULLSCREEN, defaultBoolean = false)
- public void setFullscreen(final ReactVideoView videoView, final boolean fullscreen) {
- videoView.setFullscreen(fullscreen);
- }
-
- @ReactProp(name = PROP_PLAY_IN_BACKGROUND, defaultBoolean = false)
- public void setPlayInBackground(final ReactVideoView videoView, final boolean playInBackground) {
- videoView.setPlayInBackground(playInBackground);
- }
-
- @ReactProp(name = PROP_CONTROLS, defaultBoolean = false)
- public void setControls(final ReactVideoView videoView, final boolean controls) {
- videoView.setControls(controls);
- }
-}
diff --git a/android/src/main/java/com/brentvatne/react/VideoDecoderPropertiesModule.java b/android/src/main/java/com/brentvatne/react/VideoDecoderPropertiesModule.java
new file mode 100644
index 0000000000..6622e7f9f1
--- /dev/null
+++ b/android/src/main/java/com/brentvatne/react/VideoDecoderPropertiesModule.java
@@ -0,0 +1,107 @@
+package com.brentvatne.react;
+
+import android.annotation.SuppressLint;
+import android.media.MediaCodecList;
+import android.media.MediaDrm;
+import android.media.MediaFormat;
+import android.media.UnsupportedSchemeException;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import com.facebook.react.bridge.Promise;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+import com.facebook.react.bridge.ReactMethod;
+
+import java.util.UUID;
+
+@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
+public class VideoDecoderPropertiesModule extends ReactContextBaseJavaModule {
+
+ ReactApplicationContext reactContext;
+
+ @NonNull
+ @Override
+ public String getName() {
+ return "VideoDecoderProperties";
+ }
+
+ @SuppressLint("ObsoleteSdkInt")
+ @ReactMethod
+ public void getWidevineLevel(Promise p) {
+ int widevineLevel = 0;
+
+ if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ p.resolve(widevineLevel);
+ return;
+ }
+ final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
+ final String WIDEVINE_SECURITY_LEVEL_1 = "L1";
+ final String WIDEVINE_SECURITY_LEVEL_2 = "L2";
+ final String WIDEVINE_SECURITY_LEVEL_3 = "L3";
+ final String SECURITY_LEVEL_PROPERTY = "securityLevel";
+
+ String securityProperty = null;
+ try {
+ MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID);
+ securityProperty = mediaDrm.getPropertyString(SECURITY_LEVEL_PROPERTY);
+ } catch (UnsupportedSchemeException e) {
+ e.printStackTrace();
+ }
+ if (securityProperty == null) {
+ p.resolve(widevineLevel);
+ return;
+ }
+
+ switch (securityProperty) {
+ case WIDEVINE_SECURITY_LEVEL_1: {
+ widevineLevel = 1;
+ break;
+ }
+ case WIDEVINE_SECURITY_LEVEL_2: {
+ widevineLevel = 2;
+ break;
+ }
+ case WIDEVINE_SECURITY_LEVEL_3: {
+ widevineLevel = 3;
+ break;
+ }
+ default: {
+ // widevineLevel 0
+ break;
+ }
+ }
+ p.resolve(widevineLevel);
+ }
+
+ @SuppressLint("ObsoleteSdkInt")
+ @ReactMethod
+ public void isCodecSupported(String mimeType, int width, int height, Promise p) {
+ if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
+ p.resolve(false);
+ return;
+ }
+ MediaCodecList mRegularCodecs = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ MediaFormat format = MediaFormat.createVideoFormat(mimeType, width, height);
+ String codecName = mRegularCodecs.findDecoderForFormat(format);
+ if (codecName == null) {
+ p.resolve(false);
+ } else {
+ p.resolve(true);
+ }
+ }
+
+
+ @ReactMethod
+ public void isHEVCSupported(Promise p) {
+ isCodecSupported("video/hevc", 1920, 1080, p);
+ }
+
+ public VideoDecoderPropertiesModule(ReactApplicationContext reactContext) {
+ super(reactContext);
+ this.reactContext = reactContext;
+ }
+
+}
diff --git a/android-exoplayer/src/main/java/com/brentvatne/receiver/AudioBecomingNoisyReceiver.java b/android/src/main/java/com/brentvatne/receiver/AudioBecomingNoisyReceiver.java
similarity index 100%
rename from android-exoplayer/src/main/java/com/brentvatne/receiver/AudioBecomingNoisyReceiver.java
rename to android/src/main/java/com/brentvatne/receiver/AudioBecomingNoisyReceiver.java
diff --git a/android-exoplayer/src/main/java/com/brentvatne/receiver/BecomingNoisyListener.java b/android/src/main/java/com/brentvatne/receiver/BecomingNoisyListener.java
similarity index 100%
rename from android-exoplayer/src/main/java/com/brentvatne/receiver/BecomingNoisyListener.java
rename to android/src/main/java/com/brentvatne/receiver/BecomingNoisyListener.java
diff --git a/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdEvent.java b/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdEvent.java
new file mode 100644
index 0000000000..1913df2937
--- /dev/null
+++ b/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdEvent.java
@@ -0,0 +1,11 @@
+package com.google.ads.interactivemedia.v3.api;
+
+import androidx.annotation.InspectableProperty;
+
+public abstract class AdEvent {
+ public abstract InspectableProperty getType();
+
+ public interface AdEventListener {
+ public void onAdEvent(AdEvent adEvent);
+ }
+}
diff --git a/android/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java b/android/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
new file mode 100644
index 0000000000..d23e7dd6b3
--- /dev/null
+++ b/android/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
@@ -0,0 +1,65 @@
+package com.google.android.exoplayer2.ext.ima;
+
+import androidx.annotation.Nullable;
+
+import com.facebook.react.uimanager.ThemedReactContext;
+import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.source.ads.AdsLoader;
+import com.google.android.exoplayer2.source.ads.AdsMediaSource;
+import com.google.android.exoplayer2.ui.AdViewProvider;
+import com.google.android.exoplayer2.upstream.DataSpec;
+
+import java.io.IOException;
+
+public class ImaAdsLoader implements AdsLoader {
+ public void setPlayer(ExoPlayer player) {
+ }
+
+ @Override
+ public void setPlayer(@Nullable Player player) {
+
+ }
+
+ public void release() {
+ }
+
+ @Override
+ public void setSupportedContentTypes(int... ints) {
+
+ }
+
+ @Override
+ public void start(AdsMediaSource adsMediaSource, DataSpec dataSpec, Object o, AdViewProvider adViewProvider, EventListener eventListener) {
+
+ }
+
+ @Override
+ public void stop(AdsMediaSource adsMediaSource, EventListener eventListener) {
+
+ }
+
+ @Override
+ public void handlePrepareComplete(AdsMediaSource adsMediaSource, int i, int i1) {
+
+ }
+
+ @Override
+ public void handlePrepareError(AdsMediaSource adsMediaSource, int i, int i1, IOException e) {
+
+ }
+
+ public static class Builder {
+ public Builder(ThemedReactContext themedReactContext) {
+
+ }
+
+ public Builder setAdEventListener(Object reactExoplayerView) {
+ return this;
+ }
+
+ public ImaAdsLoader build() {
+ return null;
+ }
+ }
+}
diff --git a/android-exoplayer/src/main/res/layout/exo_player_control_view.xml b/android/src/main/res/layout/exo_player_control_view.xml
similarity index 90%
rename from android-exoplayer/src/main/res/layout/exo_player_control_view.xml
rename to android/src/main/res/layout/exo_player_control_view.xml
index becee6a901..19440912d3 100644
--- a/android-exoplayer/src/main/res/layout/exo_player_control_view.xml
+++ b/android/src/main/res/layout/exo_player_control_view.xml
@@ -71,6 +71,14 @@
android:paddingRight="4dp"
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
+
+
diff --git a/android-exoplayer/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
similarity index 100%
rename from android-exoplayer/src/main/res/values/strings.xml
rename to android/src/main/res/values/strings.xml
diff --git a/android/src/main/res/values/styles.xml b/android/src/main/res/values/styles.xml
new file mode 100644
index 0000000000..3459d9d6e2
--- /dev/null
+++ b/android/src/main/res/values/styles.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/docs/DEBUGGING.md b/docs/DEBUGGING.md
new file mode 100644
index 0000000000..4ad9fda5ae
--- /dev/null
+++ b/docs/DEBUGGING.md
@@ -0,0 +1,47 @@
+# Advanced debuging and common issues
+
+### HTTP playback doesn't work or Black Screen on Release build (Android)
+If your video work on Debug mode, but on Release you see only black screen, please, check the link to your video. If you use 'http' protocol there, you will need to add next string to your AndroidManifest.xml file. [Details here](https://developer.android.com/guide/topics/manifest/application-element#usesCleartextTraffic)
+
+```
+
+```
+
+### Decoder Issue (Android)
+
+Devices have a maximum of simulataneous possible playback. It means you have reach this limit. Exoplayer returns: 'Unable to instantiate decoder'
+
+**known issue**: This issue happen really often in debug mode.
+
+## You cannot play clean content (all OS)
+
+Here are the steps to consider before opening a ticket in issue tracker
+
+### Check you can access to remote file
+
+Ensure you can download to manifest / content file with a browser for exemple
+
+### Check another player can read the content
+
+Usually clear playback can be read with all Video player. Then you should ensure content can be played without any issue with another player ([VideoLan/VLC](https://www.videolan.org/vlc/) is a good reference implementation)
+
+## You cannot play protected content (all OS)
+
+### Protected content gives error (token error / access forbidden)
+
+If content is protected with an access token or any other http header, ensure you can access to you data with a wget call or a rest client app. You need to provide all needed access token / authentication parameters.
+
+### Everything seems correct but content cannot be accessed
+
+You need to record network trace to ensure communications with server is correct.
+[Charles proxy](https://www.charlesproxy.com/) is a simple and usefull tool to sniff all http/https calls.
+With this tool you should be able to analyze what is going on with network. You will see all access to content and DRM, audio / vido chuncks, ...
+
+Then try to compare exchanges with previous tests you made.
+
+### It's still not working
+
+You can try to open a ticket now !
diff --git a/DRM.md b/docs/DRM.md
similarity index 100%
rename from DRM.md
rename to docs/DRM.md
diff --git a/docs/PROJECTS.md b/docs/PROJECTS.md
new file mode 100644
index 0000000000..fca32018de
--- /dev/null
+++ b/docs/PROJECTS.md
@@ -0,0 +1,9 @@
+This page links other open source projects which can be usefull for your player implementation
+
+# UI over react-native-video
+ - [react-native-video-controls](https://github.com/itsnubix/react-native-video-controls): First reference player UI
+ - [react-native-media-console](https://github.com/criszz77/react-native-media-console): React-native-video-controls updated and rewritten in typescript
+ - [react-native-corner-video](https://github.com/Lg0gs/react-native-corner-video): A floating video player
+
+# Other tools
+ - [react-native-music-control](https://github.com/tanguyantoine/react-native-music-control): A toolbox to control player over media session
diff --git a/dom/LICENSE.md b/dom/LICENSE.md
deleted file mode 100644
index aa185c82ad..0000000000
--- a/dom/LICENSE.md
+++ /dev/null
@@ -1,9 +0,0 @@
-MIT License
-
-Copyright (c) 2018 Vincent Riemer
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/dom/RCTVideo.js b/dom/RCTVideo.js
deleted file mode 100644
index 321289736c..0000000000
--- a/dom/RCTVideo.js
+++ /dev/null
@@ -1,278 +0,0 @@
-// @flow
-
-import { RCTEvent, RCTView, type RCTBridge } from "react-native-dom";
-import shaka from "shaka-player";
-
-import resizeModes from "./resizeModes";
-import type { VideoSource } from "./types";
-import RCTVideoEvent from "./RCTVideoEvent";
-
-class RCTVideo extends RCTView {
- playPromise: Promise = Promise.resolve();
- progressTimer: number;
- videoElement: HTMLVideoElement;
-
- onEnd: boolean = false;
- onLoad: boolean = false;
- onLoadStart: boolean = false;
- onProgress: boolean = false;
-
- _paused: boolean = false;
- _progressUpdateInterval: number = 250.0;
- _savedVolume: number = 1.0;
-
- constructor(bridge: RCTBridge) {
- super(bridge);
-
- this.eventDispatcher = bridge.getModuleByName("EventDispatcher");
-
- shaka.polyfill.installAll();
-
- this.onEnd = this.onEnd.bind(this);
- this.onLoad = this.onLoad.bind(this);
- this.onLoadStart = this.onLoadStart.bind(this);
- this.onPlay = this.onPlay.bind(this);
- this.onProgress = this.onProgress.bind(this);
-
- this.videoElement = this.initializeVideoElement();
- this.videoElement.addEventListener("ended", this.onEnd);
- this.videoElement.addEventListener("loadeddata", this.onLoad);
- this.videoElement.addEventListener("canplay", this.onReadyForDisplay);
- this.videoElement.addEventListener("loadstart", this.onLoadStart);
- this.videoElement.addEventListener("pause", this.onPause);
- this.videoElement.addEventListener("play", this.onPlay);
- this.player = new shaka.Player(this.videoElement);
-
- this.muted = false;
- this.rate = 1.0;
- this.volume = 1.0;
- this.childContainer.appendChild(this.videoElement);
- }
-
- detachFromView(view: UIView) {
- this.videoElement.removeEventListener("ended", this.onEnd);
- this.videoElement.removeEventListener("loadeddata", this.onLoad);
- this.videoElement.removeEventListener("canplay", this.onReadyForDisplay);
- this.videoElement.removeEventListener("loadstart", this.onLoadStart);
- this.videoElement.removeEventListener("pause", this.onPause);
- this.videoElement.removeEventListener("play", this.onPlay);
-
- this.stopProgressTimer();
- }
-
- initializeVideoElement() {
- const elem = document.createElement("video");
-
- Object.assign(elem.style, {
- display: "block",
- position: "absolute",
- top: "0",
- left: "0",
- width: "100%",
- height: "100%"
- });
-
- return elem;
- }
-
- presentFullscreenPlayer() {
- this.videoElement.webkitRequestFullScreen();
- }
-
- set controls(value: boolean) {
- this.videoElement.controls = value;
- this.videoElement.style.pointerEvents = value ? "auto" : "";
- }
-
- set id(value: string) {
- this.videoElement.id = value;
- }
-
- set muted(value: boolean) {
- this.videoElement.muted = true;
- }
-
- set paused(value: boolean) {
- if (value) {
- this.videoElement.pause();
- } else {
- this.requestPlay();
- }
- this._paused = value;
- }
-
- set progressUpdateInterval(value: number) {
- this._progressUpdateInterval = value;
- this.stopProgressTimer();
- if (!this._paused) {
- this.startProgressTimer();
- }
- }
-
- set rate(value: number) {
- this.videoElement.defaultPlaybackRate = value; // playbackRate doesn't work on Chrome
- this.videoElement.playbackRate = value;
- }
-
- set repeat(value: boolean) {
- this.videoElement.loop = value;
- }
-
- set resizeMode(value: number) {
- switch (value) {
- case resizeModes.ScaleNone: {
- this.videoElement.style.objectFit = "none";
- break;
- }
- case resizeModes.ScaleToFill: {
- this.videoElement.style.objectFit = "fill";
- break;
- }
- case resizeModes.ScaleAspectFit: {
- this.videoElement.style.objectFit = "contain";
- break;
- }
- case resizeModes.ScaleAspectFill: {
- this.videoElement.style.objectFit = "cover";
- break;
- }
- }
- }
-
- set seek(value: number) {
- this.videoElement.currentTime = value;
- }
-
- set source(value: VideoSource) {
- let uri = value.uri;
-
- if (uri.startsWith("blob:")) {
- let blob = this.bridge.blobManager.resolveURL(uri);
- if (blob.type === "text/xml") {
- blob = new Blob([blob], { type: "video/mp4" });
- }
- uri = URL.createObjectURL(blob);
- }
-
- if (!shaka.Player.isBrowserSupported()) { // primarily iOS WebKit
- this.videoElement.setAttribute("src", uri);
- if (!this._paused) {
- this.requestPlay();
- }
- } else {
- this.player.load(uri)
- .then(() => {
- if (!this._paused) {
- this.requestPlay();
- }
- })
- .catch(this.onError);
- }
- }
-
- set volume(value: number) {
- if (value === 0) {
- this.muted = true;
- } else {
- this.videoElement.volume = value;
- this.muted = false;
- }
- }
-
- onEnd = () => {
- this.onProgress();
- this.sendEvent("topVideoEnd", null);
- this.stopProgressTimer();
- }
-
- onError = error => {
- console.warn("topVideoError", error);
- }
-
- onLoad = () => {
- // height & width are safe with audio, will be 0
- const height = this.videoElement.videoHeight;
- const width = this.videoElement.videoWidth;
- const payload = {
- currentPosition: this.videoElement.currentTime,
- duration: this.videoElement.duration,
- naturalSize: {
- width,
- height,
- orientation: width >= height ? "landscape" : "portrait"
- }
- };
- this.sendEvent("topVideoLoad", payload);
- }
-
- onReadyForDisplay = () => {
- this.sendEvent("onReadyForDisplay");
- }
-
- onLoadStart = () => {
- const src = this.videoElement.currentSrc;
- const payload = {
- isNetwork: !src.match(/^https?:\/\/localhost/), // require is served from localhost
- uri: this.videoElement.currentSrc
- };
- this.sendEvent("topVideoLoadStart", payload);
- }
-
- onPause = () => {
- this.stopProgressTimer();
- }
-
- onPlay = () => {
- this.startProgressTimer();
- }
-
- onProgress = () => {
- const payload = {
- currentTime: this.videoElement.currentTime,
- seekableDuration: this.videoElement.duration
- };
- this.sendEvent("topVideoProgress", payload);
- }
-
- onRejectedAutoplay = () => {
- this.sendEvent("topVideoRejectedAutoplay", null);
- }
-
- requestPlay() {
- const playPromise = this.videoElement.play();
- if (playPromise) {
- playPromise
- .then(() => {})
- .catch(e => {
- /* This is likely one of:
- * name: NotAllowedError - autoplay is not supported
- * name: NotSupportedError - format is not supported
- */
- this.onError({ code: e.name, message: e.message });
- });
- }
- }
-
- sendEvent(eventName, payload) {
- const event = new RCTVideoEvent(eventName, this.reactTag, 0, payload);
- this.eventDispatcher.sendEvent(event);
- }
-
- startProgressTimer() {
- if (!this.progressTimer && this._progressUpdateInterval) {
- this.onProgress();
- this.progressTimer = setInterval(this.onProgress, this._progressUpdateInterval);
- }
- }
-
- stopProgressTimer() {
- if (this.progressTimer) {
- clearInterval(this.progressTimer);
- this.progressTimer = null;
- }
- }
-}
-
-customElements.define("rct-video", RCTVideo);
-
-export default RCTVideo;
diff --git a/dom/RCTVideoEvent.js b/dom/RCTVideoEvent.js
deleted file mode 100644
index 699a88c4a2..0000000000
--- a/dom/RCTVideoEvent.js
+++ /dev/null
@@ -1,56 +0,0 @@
-// import { RCTEvent } from "react-native-dom";
-
-interface RCTEvent {
- viewTag: number;
- eventName: string;
- coalescingKey: number;
-
- canCoalesce(): boolean;
- coalesceWithEvent(event: RCTEvent): RCTEvent;
-
- moduleDotMethod(): string;
- arguments(): Array;
- }
-
-export default class RCTVideoEvent implements RCTEvent {
- viewTag: number;
- eventName: string;
- coalescingKey: number;
-
- constructor(
- eventName: string,
- reactTag: number,
- coalescingKey: number,
- data: ?Object
- ) {
- this.viewTag = reactTag;
- this.eventName = eventName;
- this.coalescingKey = coalescingKey;
- this.data = data;
- }
-
- canCoalesce(): boolean {
- return false;
- }
-
- coalesceWithEvent(event: RCTEvent): RCTEvent {
- return;
- }
-
- moduleDotMethod(): string {
- return "RCTEventEmitter.receiveEvent";
- }
-
- arguments(): Array {
- const args = [
- this.viewTag,
- this.eventName,
- this.data
- ];
- return args;
- }
-
- coalescingKey(): number {
- return this.coalescingKey;
- }
-}
diff --git a/dom/RCTVideoManager.js b/dom/RCTVideoManager.js
deleted file mode 100644
index 2eb5309b98..0000000000
--- a/dom/RCTVideoManager.js
+++ /dev/null
@@ -1,87 +0,0 @@
-// @flow
-
-import { RCTViewManager } from "react-native-dom";
-
-import RCTVideo from "./RCTVideo";
-import resizeModes from "./resizeModes";
-
-import type { VideoSource } from "./types";
-
-class RCTVideoManager extends RCTViewManager {
- static moduleName = "RCTVideoManager";
-
- view() {
- return new RCTVideo(this.bridge);
- }
-
- describeProps() {
- return super
- .describeProps()
- .addBooleanProp("controls", this.setControls)
- .addStringProp("id", this.setId)
- .addBooleanProp("muted", this.setMuted)
- .addBooleanProp("paused", this.setPaused)
- .addNumberProp("progressUpdateInterval", this.setProgressUpdateInterval)
- .addBooleanProp("rate", this.setRate)
- .addBooleanProp("repeat", this.setRepeat)
- .addNumberProp("resizeMode", this.setResizeMode)
- .addNumberProp("seek", this.setSeek)
- .addObjectProp("src", this.setSource)
- .addNumberProp("volume", this.setVolume)
- .addDirectEvent("onVideoEnd")
- .addDirectEvent("onVideoError")
- .addDirectEvent("onVideoLoad")
- .addDirectEvent("onVideoLoadStart")
- .addDirectEvent("onVideoProgress");
- }
-
- dismissFullscreenPlayer() {
- // not currently working
- }
-
- presentFullscreenPlayer() {
- // not currently working
- }
-
- setControls(view: RCTVideo, value: boolean) {
- view.controls = value;
- }
-
- setId(view: RCTVideo, value: string) {
- view.id = value;
- }
-
- setMuted(view: RCTVideo, value: boolean) {
- view.muted = value;
- }
-
- setPaused(view: RCTVideo, value: boolean) {
- view.paused = value;
- }
-
- setRate(view: RCTVideo, value: number) {
- view.rate = value;
- }
-
- setRepeat(view: RCTVideo, value: boolean) {
- view.repeat = value;
- }
-
- setResizeMode(view: RCTVideo, value: number) {
- view.resizeMode = value;
- }
-
- setSeek(view: RCTVideo, value: number) {
- view.seek = value;
- }
-
- setSource(view: RCTVideo, value: VideoSource) {
- view.source = value;
- }
-
- constantsToExport() {
- return { ...resizeModes };
- }
-}
-
-export default RCTVideoManager;
diff --git a/dom/index.js b/dom/index.js
deleted file mode 100644
index d68d5bba8b..0000000000
--- a/dom/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-// @flow
-
-module.exports = require("./RCTVideoManager");
diff --git a/dom/resizeModes.js b/dom/resizeModes.js
deleted file mode 100644
index 5f569e0dee..0000000000
--- a/dom/resizeModes.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// @flow
-
-export default {
- ScaleNone: 0,
- ScaleToFill: 1,
- ScaleAspectFit: 2,
- ScaleAspectFill: 3,
-};
diff --git a/dom/types.js b/dom/types.js
deleted file mode 100644
index fe1d1a155e..0000000000
--- a/dom/types.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// @flow
-
-export type VideoSource = {
- uri: string,
- type: string,
- mainVer: number,
- patchVer: number,
- isNetwork: boolean,
- isAsset: boolean,
-};
diff --git a/examples/basic/.eslintrc.js b/examples/basic/.eslintrc.js
new file mode 100644
index 0000000000..40c6dcd05f
--- /dev/null
+++ b/examples/basic/.eslintrc.js
@@ -0,0 +1,4 @@
+module.exports = {
+ root: true,
+ extends: '@react-native-community',
+};
diff --git a/examples/basic/.flowconfig b/examples/basic/.flowconfig
index bb65654de3..3782e447cd 100644
--- a/examples/basic/.flowconfig
+++ b/examples/basic/.flowconfig
@@ -2,57 +2,65 @@
; We fork some components by platform
.*/*[.]android.js
-# We fork some components by platform.
-.*/*[.]android.js
-
-# Ignore templates with `@flow` in header
-.*/local-cli/generator.*
-
-# Ignore malformed json
-.*/node_modules/y18n/test/.*\.json
-
-# Ignore the website subdir
-/website/.*
+; Ignore "BUCK" generated dirs
+/\.buckd/
-# Ignore BUCK generated dirs
+; Ignore polyfills
+node_modules/react-native/Libraries/polyfills/.*
-/\.buckd/
+; Flow doesn't support platforms
+.*/Libraries/Utilities/LoadingView.js
-; Ignore unexpected extra "@providesModule"
-.*/node_modules/.*/node_modules/fbjs/.*
+.*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$
-; Ignore duplicate module providers
-; For RN Apps installed via npm, "Libraries" folder is inside
-; "node_modules/react-native" but in the source repo it is in the root
-.*/Libraries/react-native/React.js
-.*/Libraries/react-native/ReactNative.js
+[untyped]
+.*/node_modules/@react-native-community/cli/.*/.*
[include]
[libs]
-node_modules/react-native/Libraries/react-native/react-native-interface.js
-node_modules/react-native/flow
-flow/
+node_modules/react-native/interface.js
+node_modules/react-native/flow/
[options]
-module.system=haste
+emoji=true
-experimental.strict_type_args=true
+exact_by_default=true
+
+format.bracket_spacing=false
+
+module.file_ext=.js
+module.file_ext=.json
+module.file_ext=.ios.js
munge_underscores=true
-module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
+module.name_mapper='^react-native/\(.*\)$' -> '/node_modules/react-native/\1'
+module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '/node_modules/react-native/Libraries/Image/RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
-suppress_type=$FixMe
-
-suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-6]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
-suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-6]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
-suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
-
-unsafe.enable_getters_and_setters=true
+suppress_type=$FlowFixMeProps
+suppress_type=$FlowFixMeState
+
+[lints]
+sketchy-null-number=warn
+sketchy-null-mixed=warn
+sketchy-number=warn
+untyped-type-import=warn
+nonstrict-import=warn
+deprecated-type=warn
+unsafe-getters-setters=warn
+unnecessary-invariant=warn
+
+[strict]
+deprecated-type
+nonstrict-import
+sketchy-null
+unclear-type
+unsafe-getters-setters
+untyped-import
+untyped-type-import
[version]
-
-^0.36.0
+^0.182.0
diff --git a/examples/basic/.gitattributes b/examples/basic/.gitattributes
deleted file mode 100644
index d42ff18354..0000000000
--- a/examples/basic/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-*.pbxproj -text
diff --git a/examples/basic/.gitignore b/examples/basic/.gitignore
index e9ec10b5f7..2423126f72 100644
--- a/examples/basic/.gitignore
+++ b/examples/basic/.gitignore
@@ -20,7 +20,7 @@ DerivedData
*.hmap
*.ipa
*.xcuserstate
-project.xcworkspace
+ios/.xcode.env.local
# Android/IntelliJ
#
@@ -29,27 +29,36 @@ build/
.gradle
local.properties
*.iml
+*.hprof
+.cxx/
# node.js
#
node_modules/
npm-debug.log
+yarn-error.log
# BUCK
buck-out/
\.buckd/
-android/app/libs
*.keystore
+!debug.keystore
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
-# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
+# https://docs.fastlane.tools/best-practices/source-control/
-fastlane/report.xml
-fastlane/Preview.html
-fastlane/screenshots
+**/fastlane/report.xml
+**/fastlane/Preview.html
+**/fastlane/screenshots
+**/fastlane/test_output
-*.binlog
+# Bundle artifact
+*.jsbundle
+
+# Ruby / CocoaPods
+/ios/Pods/
+/vendor/bundle/
diff --git a/examples/basic/.prettierrc.js b/examples/basic/.prettierrc.js
new file mode 100644
index 0000000000..2b540746a7
--- /dev/null
+++ b/examples/basic/.prettierrc.js
@@ -0,0 +1,7 @@
+module.exports = {
+ arrowParens: 'avoid',
+ bracketSameLine: true,
+ bracketSpacing: false,
+ singleQuote: true,
+ trailingComma: 'all',
+};
diff --git a/examples/basic/Gemfile b/examples/basic/Gemfile
new file mode 100644
index 0000000000..5efda89f45
--- /dev/null
+++ b/examples/basic/Gemfile
@@ -0,0 +1,6 @@
+source 'https://rubygems.org'
+
+# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
+ruby '2.7.5'
+
+gem 'cocoapods', '~> 1.11', '>= 1.11.2'
diff --git a/examples/basic/__tests__/App-test.js b/examples/basic/__tests__/App-test.js
new file mode 100644
index 0000000000..178476699b
--- /dev/null
+++ b/examples/basic/__tests__/App-test.js
@@ -0,0 +1,14 @@
+/**
+ * @format
+ */
+
+import 'react-native';
+import React from 'react';
+import App from '../App';
+
+// Note: test renderer must be required after react-native.
+import renderer from 'react-test-renderer';
+
+it('renders correctly', () => {
+ renderer.create( );
+});
diff --git a/examples/basic/_bundle/config b/examples/basic/_bundle/config
new file mode 100644
index 0000000000..848943bb52
--- /dev/null
+++ b/examples/basic/_bundle/config
@@ -0,0 +1,2 @@
+BUNDLE_PATH: "vendor/bundle"
+BUNDLE_FORCE_RUBY_PLATFORM: 1
diff --git a/examples/basic/_node-version b/examples/basic/_node-version
new file mode 100644
index 0000000000..b6a7d89c68
--- /dev/null
+++ b/examples/basic/_node-version
@@ -0,0 +1 @@
+16
diff --git a/examples/basic/_ruby-version b/examples/basic/_ruby-version
new file mode 100644
index 0000000000..a603bb50a2
--- /dev/null
+++ b/examples/basic/_ruby-version
@@ -0,0 +1 @@
+2.7.5
diff --git a/examples/basic/android/app/BUCK b/examples/basic/android/app/BUCK
deleted file mode 100644
index 41c624bb38..0000000000
--- a/examples/basic/android/app/BUCK
+++ /dev/null
@@ -1,66 +0,0 @@
-import re
-
-# To learn about Buck see [Docs](https://buckbuild.com/).
-# To run your application with Buck:
-# - install Buck
-# - `npm start` - to start the packager
-# - `cd android`
-# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
-# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
-# - `buck install -r android/app` - compile, install and run application
-#
-
-lib_deps = []
-for jarfile in glob(['libs/*.jar']):
- name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile)
- lib_deps.append(':' + name)
- prebuilt_jar(
- name = name,
- binary_jar = jarfile,
- )
-
-for aarfile in glob(['libs/*.aar']):
- name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile)
- lib_deps.append(':' + name)
- android_prebuilt_aar(
- name = name,
- aar = aarfile,
- )
-
-android_library(
- name = 'all-libs',
- exported_deps = lib_deps
-)
-
-android_library(
- name = 'app-code',
- srcs = glob([
- 'src/main/java/**/*.java',
- ]),
- deps = [
- ':all-libs',
- ':build_config',
- ':res',
- ],
-)
-
-android_build_config(
- name = 'build_config',
- package = 'com.videoplayer',
-)
-
-android_resource(
- name = 'res',
- res = 'src/main/res',
- package = 'com.videoplayer',
-)
-
-android_binary(
- name = 'app',
- package_type = 'debug',
- manifest = 'src/main/AndroidManifest.xml',
- keystore = '//android/keystores:debug',
- deps = [
- ':app-code',
- ],
-)
diff --git a/examples/basic/android/app/_BUCK b/examples/basic/android/app/_BUCK
new file mode 100644
index 0000000000..96fa2d6893
--- /dev/null
+++ b/examples/basic/android/app/_BUCK
@@ -0,0 +1,55 @@
+# To learn about Buck see [Docs](https://buckbuild.com/).
+# To run your application with Buck:
+# - install Buck
+# - `npm start` - to start the packager
+# - `cd android`
+# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
+# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
+# - `buck install -r android/app` - compile, install and run application
+#
+
+load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
+
+lib_deps = []
+
+create_aar_targets(glob(["libs/*.aar"]))
+
+create_jar_targets(glob(["libs/*.jar"]))
+
+android_library(
+ name = "all-libs",
+ exported_deps = lib_deps,
+)
+
+android_library(
+ name = "app-code",
+ srcs = glob([
+ "src/main/java/**/*.java",
+ ]),
+ deps = [
+ ":all-libs",
+ ":build_config",
+ ":res",
+ ],
+)
+
+android_build_config(
+ name = "build_config",
+ package = "com.videoplayer",
+)
+
+android_resource(
+ name = "res",
+ package = "com.videoplayer",
+ res = "src/main/res",
+)
+
+android_binary(
+ name = "app",
+ keystore = "//android/keystores:debug",
+ manifest = "src/main/AndroidManifest.xml",
+ package_type = "debug",
+ deps = [
+ ":app-code",
+ ],
+)
diff --git a/examples/basic/android/app/build.gradle b/examples/basic/android/app/build.gradle
index 082873d4b0..a9d5222144 100644
--- a/examples/basic/android/app/build.gradle
+++ b/examples/basic/android/app/build.gradle
@@ -1,6 +1,7 @@
apply plugin: "com.android.application"
import com.android.build.OutputFile
+import org.apache.tools.ant.taskdefs.condition.Os
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
@@ -15,9 +16,14 @@ import com.android.build.OutputFile
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
- * // the entry file for bundle generation
+ * // the entry file for bundle generation. If none specified and
+ * // "index.android.js" exists, it will be used. Otherwise "index.js" is
+ * // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
+ * // https://reactnative.dev/docs/performance#enable-the-ram-format
+ * bundleCommand: "ram-bundle",
+ *
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
@@ -33,6 +39,13 @@ import com.android.build.OutputFile
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
+ * // whether to disable dev mode in custom build variants (by default only disabled in release)
+ * // for example: to disable dev mode in the staging build type (if configured)
+ * devDisabledInStaging: true,
+ * // The configuration property can be in the following formats
+ * // 'devDisabledIn${productFlavor}${buildType}'
+ * // 'devDisabledIn${buildType}'
+ *
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
@@ -58,7 +71,7 @@ import com.android.build.OutputFile
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
- * nodeExecutableAndArgs: ["node"]
+ * nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
@@ -66,9 +79,9 @@ import com.android.build.OutputFile
*/
project.ext.react = [
- entryFile: "index.android.js",
- enableHermes: false,
+ enableHermes: true, // clean and rebuild if changing
]
+
apply from: "../../node_modules/react-native/react.gradle"
/**
@@ -86,63 +99,216 @@ def enableSeparateBuildPerCPUArchitecture = false
*/
def enableProguardInReleaseBuilds = false
+/**
+ * The preferred build flavor of JavaScriptCore.
+ *
+ * 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:+'
+
+/**
+ * Whether to enable the Hermes VM.
+ *
+ * This should be set on project.ext.react and that value will be read here. If it is not set
+ * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
+ * and the benefits of using Hermes will therefore be sharply reduced.
+ */
+def enableHermes = project.ext.react.get("enableHermes", false);
+
+/**
+ * Architectures to build native code for.
+ */
+def reactNativeArchitectures() {
+ def value = project.getProperties().get("reactNativeArchitectures")
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
+}
+
android {
+ ndkVersion rootProject.ext.ndkVersion
+
compileSdkVersion rootProject.ext.compileSdkVersion
- buildToolsVersion rootProject.ext.buildToolsVersion
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
+
defaultConfig {
applicationId "com.videoplayer"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
- ndk {
- abiFilters "armeabi-v7a", "x86"
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
+ multiDexEnabled true
+
+ if (isNewArchitectureEnabled()) {
+ // We configure the CMake build only if you decide to opt-in for the New Architecture.
+ externalNativeBuild {
+ cmake {
+ arguments "-DPROJECT_BUILD_DIR=$buildDir",
+ "-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
+ "-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
+ "-DNODE_MODULES_DIR=$rootDir/../node_modules",
+ "-DANDROID_STL=c++_shared"
+ }
+ }
+ if (!enableSeparateBuildPerCPUArchitecture) {
+ ndk {
+ abiFilters (*reactNativeArchitectures())
+ }
+ }
}
}
+
+ if (isNewArchitectureEnabled()) {
+ // We configure the NDK build only if you decide to opt-in for the New Architecture.
+ externalNativeBuild {
+ cmake {
+ path "$projectDir/src/main/jni/CMakeLists.txt"
+ }
+ }
+ def reactAndroidProjectDir = project(':ReactAndroid').projectDir
+ def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
+ dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
+ from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
+ into("$buildDir/react-ndk/exported")
+ }
+ def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
+ dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
+ from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
+ into("$buildDir/react-ndk/exported")
+ }
+ afterEvaluate {
+ // If you wish to add a custom TurboModule or component locally,
+ // you should uncomment this line.
+ // preBuild.dependsOn("generateCodegenArtifactsFromSchema")
+ preDebugBuild.dependsOn(packageReactNdkDebugLibs)
+ preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
+
+ // Due to a bug inside AGP, we have to explicitly set a dependency
+ // between configureCMakeDebug* tasks and the preBuild tasks.
+ // This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
+ configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild)
+ configureCMakeDebug.dependsOn(preDebugBuild)
+ reactNativeArchitectures().each { architecture ->
+ tasks.findByName("configureCMakeDebug[${architecture}]")?.configure {
+ dependsOn("preDebugBuild")
+ }
+ tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
+ dependsOn("preReleaseBuild")
+ }
+ }
+ }
+ }
+
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
- include "armeabi-v7a", "x86"
+ include (*reactNativeArchitectures())
+ }
+ }
+ 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
minifyEnabled enableProguardInReleaseBuilds
- matchingFallbacks = ['release', 'debug']
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
+
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
- // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
- def versionCodes = ["armeabi-v7a":1, "x86":2]
+ // https://developer.android.com/studio/build/configure-apk-splits.html
+ // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
+ def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
- versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
+ defaultConfig.versionCode * 1000 + versionCodes.get(abi)
}
+
}
}
}
dependencies {
- implementation project(':react-native-video')
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+
+ //noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
- implementation 'org.webkit:android-jsc:+'
+
+ implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
+
+ debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
+ exclude group:'com.facebook.fbjni'
+ }
+
+ debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
+ exclude group:'com.facebook.flipper'
+ exclude group:'com.squareup.okhttp3', module:'okhttp'
+ }
+
+ debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
+ exclude group:'com.facebook.flipper'
+ }
+
+ if (enableHermes) {
+ //noinspection GradleDynamicVersion
+ implementation("com.facebook.react:hermes-engine:+") { // From node_modules
+ exclude group:'com.facebook.fbjni'
+ }
+ } else {
+ implementation jscFlavor
+ }
+}
+
+if (isNewArchitectureEnabled()) {
+ // If new architecture is enabled, we let you build RN from source
+ // Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
+ // This will be applied to all the imported transtitive dependency.
+ configurations.all {
+ resolutionStrategy.dependencySubstitution {
+ substitute(module("com.facebook.react:react-native"))
+ .using(project(":ReactAndroid"))
+ .because("On New Architecture we're building React Native from source")
+ substitute(module("com.facebook.react:hermes-engine"))
+ .using(project(":ReactAndroid:hermes-engine"))
+ .because("On New Architecture we're building Hermes from source")
+ }
+ }
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
- from configurations.compile
+ from configurations.implementation
into 'libs'
}
+
+apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
+
+def isNewArchitectureEnabled() {
+ // To opt-in for the New Architecture, you can either:
+ // - Set `newArchEnabled` to true inside the `gradle.properties` file
+ // - Invoke gradle with `-newArchEnabled=true`
+ // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
+ return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
+}
diff --git a/examples/basic/android/app/build_defs.bzl b/examples/basic/android/app/build_defs.bzl
new file mode 100644
index 0000000000..fff270f8d1
--- /dev/null
+++ b/examples/basic/android/app/build_defs.bzl
@@ -0,0 +1,19 @@
+"""Helper definitions to glob .aar and .jar targets"""
+
+def create_aar_targets(aarfiles):
+ for aarfile in aarfiles:
+ name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
+ lib_deps.append(":" + name)
+ android_prebuilt_aar(
+ name = name,
+ aar = aarfile,
+ )
+
+def create_jar_targets(jarfiles):
+ for jarfile in jarfiles:
+ name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
+ lib_deps.append(":" + name)
+ prebuilt_jar(
+ name = name,
+ binary_jar = jarfile,
+ )
diff --git a/examples/basic/android/app/debug.keystore b/examples/basic/android/app/debug.keystore
new file mode 100644
index 0000000000..364e105ed3
Binary files /dev/null and b/examples/basic/android/app/debug.keystore differ
diff --git a/examples/basic/android/app/proguard-rules.pro b/examples/basic/android/app/proguard-rules.pro
index 48361a9015..11b025724a 100644
--- a/examples/basic/android/app/proguard-rules.pro
+++ b/examples/basic/android/app/proguard-rules.pro
@@ -8,59 +8,3 @@
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Disabling obfuscation is useful if you collect stack traces from production crashes
-# (unless you are using a system that supports de-obfuscate the stack traces).
--dontobfuscate
-
-# React Native
-
-# Keep our interfaces so they can be used by other ProGuard rules.
-# See http://sourceforge.net/p/proguard/bugs/466/
--keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
--keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
--keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
-
-# Do not strip any method/class that is annotated with @DoNotStrip
--keep @com.facebook.proguard.annotations.DoNotStrip class *
--keep @com.facebook.common.internal.DoNotStrip class *
--keepclassmembers class * {
- @com.facebook.proguard.annotations.DoNotStrip *;
- @com.facebook.common.internal.DoNotStrip *;
-}
-
--keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
- void set*(***);
- *** get*();
-}
-
--keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
--keep class * extends com.facebook.react.bridge.NativeModule { *; }
--keepclassmembers,includedescriptorclasses class * { native ; }
--keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; }
--keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; }
--keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; }
-
--dontwarn com.facebook.react.**
-
-# okhttp
-
--keepattributes Signature
--keepattributes *Annotation*
--keep class okhttp3.** { *; }
--keep interface okhttp3.** { *; }
--dontwarn okhttp3.**
-
-# okio
-
--keep class sun.misc.Unsafe { *; }
--dontwarn java.nio.file.*
--dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
--dontwarn okio.**
diff --git a/examples/basic/android/app/src/debug/AndroidManifest.xml b/examples/basic/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000000..4b185bc159
--- /dev/null
+++ b/examples/basic/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/examples/basic/android/app/src/debug/java/com/videoplayer/ReactNativeFlipper.java b/examples/basic/android/app/src/debug/java/com/videoplayer/ReactNativeFlipper.java
new file mode 100644
index 0000000000..543c821f00
--- /dev/null
+++ b/examples/basic/android/app/src/debug/java/com/videoplayer/ReactNativeFlipper.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root
+ * directory of this source tree.
+ */
+package com.videoplayer;
+
+import android.content.Context;
+import com.facebook.flipper.android.AndroidFlipperClient;
+import com.facebook.flipper.android.utils.FlipperUtils;
+import com.facebook.flipper.core.FlipperClient;
+import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
+import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
+import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
+import com.facebook.flipper.plugins.inspector.DescriptorMapping;
+import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
+import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
+import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
+import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
+import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
+import com.facebook.react.ReactInstanceEventListener;
+import com.facebook.react.ReactInstanceManager;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.modules.network.NetworkingModule;
+import okhttp3.OkHttpClient;
+
+public class ReactNativeFlipper {
+ public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
+ if (FlipperUtils.shouldEnableFlipper(context)) {
+ final FlipperClient client = AndroidFlipperClient.getInstance(context);
+
+ client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
+ client.addPlugin(new ReactFlipperPlugin());
+ client.addPlugin(new DatabasesFlipperPlugin(context));
+ client.addPlugin(new SharedPreferencesFlipperPlugin(context));
+ client.addPlugin(CrashReporterPlugin.getInstance());
+
+ NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
+ NetworkingModule.setCustomClientBuilder(
+ new NetworkingModule.CustomClientBuilder() {
+ @Override
+ public void apply(OkHttpClient.Builder builder) {
+ builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
+ }
+ });
+ client.addPlugin(networkFlipperPlugin);
+ client.start();
+
+ // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
+ // Hence we run if after all native modules have been initialized
+ ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
+ if (reactContext == null) {
+ reactInstanceManager.addReactInstanceEventListener(
+ new ReactInstanceEventListener() {
+ @Override
+ public void onReactContextInitialized(ReactContext reactContext) {
+ reactInstanceManager.removeReactInstanceEventListener(this);
+ reactContext.runOnNativeModulesQueueThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ client.addPlugin(new FrescoFlipperPlugin());
+ }
+ });
+ }
+ });
+ } else {
+ client.addPlugin(new FrescoFlipperPlugin());
+ }
+ }
+ }
+}
diff --git a/examples/basic/android/app/src/main/AndroidManifest.xml b/examples/basic/android/app/src/main/AndroidManifest.xml
index 96fc88287f..090af5526c 100644
--- a/examples/basic/android/app/src/main/AndroidManifest.xml
+++ b/examples/basic/android/app/src/main/AndroidManifest.xml
@@ -1,27 +1,37 @@
+ package="com.videoplayer">
+
+
+
-
+ android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
+ android:launchMode="singleTask"
+ android:windowSoftInputMode="adjustResize"
+ android:exported="true">
+
-
-
diff --git a/examples/basic/android/app/src/main/java/com/videoplayer/MainActivity.java b/examples/basic/android/app/src/main/java/com/videoplayer/MainActivity.java
index 7045aa125f..9036cb8bd3 100644
--- a/examples/basic/android/app/src/main/java/com/videoplayer/MainActivity.java
+++ b/examples/basic/android/app/src/main/java/com/videoplayer/MainActivity.java
@@ -1,15 +1,48 @@
package com.videoplayer;
import com.facebook.react.ReactActivity;
+import com.facebook.react.ReactActivityDelegate;
+import com.facebook.react.ReactRootView;
public class MainActivity extends ReactActivity {
- /**
- * Returns the name of the main component registered from JavaScript.
- * This is used to schedule rendering of the component.
- */
+ /**
+ * Returns the name of the main component registered from JavaScript. This is used to schedule
+ * rendering of the component.
+ */
+ @Override
+ protected String getMainComponentName() {
+ return "VideoPlayer";
+ }
+
+ /**
+ * Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
+ * you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer
+ * (Paper).
+ */
+ @Override
+ protected ReactActivityDelegate createReactActivityDelegate() {
+ return new MainActivityDelegate(this, getMainComponentName());
+ }
+
+ public static class MainActivityDelegate extends ReactActivityDelegate {
+ public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
+ super(activity, mainComponentName);
+ }
+
+ @Override
+ protected ReactRootView createRootView() {
+ ReactRootView reactRootView = new ReactRootView(getContext());
+ // If you opted-in for the New Architecture, we enable the Fabric Renderer.
+ reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
+ return reactRootView;
+ }
+
@Override
- protected String getMainComponentName() {
- return "VideoPlayer";
+ protected boolean isConcurrentRootEnabled() {
+ // If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
+ // More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
+ return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
}
+ }
}
diff --git a/examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java b/examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java
index a269841570..2d5a45e636 100644
--- a/examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java
+++ b/examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java
@@ -1,42 +1,91 @@
package com.videoplayer;
import android.app.Application;
-
+import android.content.Context;
+import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
-import com.brentvatne.react.ReactVideoPackage;
+import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
-import com.facebook.react.shell.MainReactPackage;
+import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.soloader.SoLoader;
-
-import java.util.Arrays;
+import com.videoplayer.newarchitecture.MainApplicationReactNativeHost;
+import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
- private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
- @Override
- public boolean getUseDeveloperSupport() {
- return BuildConfig.DEBUG;
- }
+ private final ReactNativeHost mReactNativeHost =
+ new ReactNativeHost(this) {
+ @Override
+ public boolean getUseDeveloperSupport() {
+ return BuildConfig.DEBUG;
+ }
- @Override
- protected List getPackages() {
- return Arrays.asList(
- new MainReactPackage(),
- new ReactVideoPackage()
- );
- }
- };
+ @Override
+ protected List getPackages() {
+ @SuppressWarnings("UnnecessaryLocalVariable")
+ List packages = new PackageList(this).getPackages();
+ // Packages that cannot be autolinked yet can be added manually here, for example:
+ // packages.add(new MyReactNativePackage());
+ return packages;
+ }
+
+ @Override
+ protected String getJSMainModuleName() {
+ return "src/index";
+ }
+ };
+
+ private final ReactNativeHost mNewArchitectureNativeHost =
+ new MainApplicationReactNativeHost(this);
@Override
public ReactNativeHost getReactNativeHost() {
- return mReactNativeHost;
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
+ return mNewArchitectureNativeHost;
+ } else {
+ return mReactNativeHost;
+ }
}
@Override
public void onCreate() {
super.onCreate();
+ // If you opted-in for the New Architecture, we enable the TurboModule system
+ ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
SoLoader.init(this, /* native exopackage */ false);
+ initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
+ }
+
+ /**
+ * Loads Flipper in React Native templates. Call this in the onCreate method with something like
+ * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
+ *
+ * @param context
+ * @param reactInstanceManager
+ */
+ private static void initializeFlipper(
+ Context context, ReactInstanceManager reactInstanceManager) {
+ if (BuildConfig.DEBUG) {
+ try {
+ /*
+ We use reflection here to pick up the class that initializes Flipper,
+ since Flipper library is not available in release mode
+ */
+ Class> aClass = Class.forName("com.videoplayer.ReactNativeFlipper");
+ aClass
+ .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
+ .invoke(null, context, reactInstanceManager);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ }
}
}
diff --git a/examples/basic/android/app/src/main/java/com/videoplayer/newarchitecture/MainApplicationReactNativeHost.java b/examples/basic/android/app/src/main/java/com/videoplayer/newarchitecture/MainApplicationReactNativeHost.java
new file mode 100644
index 0000000000..0271102180
--- /dev/null
+++ b/examples/basic/android/app/src/main/java/com/videoplayer/newarchitecture/MainApplicationReactNativeHost.java
@@ -0,0 +1,116 @@
+package com.videoplayer.newarchitecture;
+
+import android.app.Application;
+import androidx.annotation.NonNull;
+import com.facebook.react.PackageList;
+import com.facebook.react.ReactInstanceManager;
+import com.facebook.react.ReactNativeHost;
+import com.facebook.react.ReactPackage;
+import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
+import com.facebook.react.bridge.JSIModulePackage;
+import com.facebook.react.bridge.JSIModuleProvider;
+import com.facebook.react.bridge.JSIModuleSpec;
+import com.facebook.react.bridge.JSIModuleType;
+import com.facebook.react.bridge.JavaScriptContextHolder;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.UIManager;
+import com.facebook.react.fabric.ComponentFactory;
+import com.facebook.react.fabric.CoreComponentsRegistry;
+import com.facebook.react.fabric.FabricJSIModuleProvider;
+import com.facebook.react.fabric.ReactNativeConfig;
+import com.facebook.react.uimanager.ViewManagerRegistry;
+import com.videoplayer.BuildConfig;
+import com.videoplayer.newarchitecture.components.MainComponentsRegistry;
+import com.videoplayer.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
+ * TurboModule delegates and the Fabric Renderer.
+ *
+ * Please note that this class is used ONLY if you opt-in for the New Architecture (see the
+ * `newArchEnabled` property). Is ignored otherwise.
+ */
+public class MainApplicationReactNativeHost extends ReactNativeHost {
+ public MainApplicationReactNativeHost(Application application) {
+ super(application);
+ }
+
+ @Override
+ public boolean getUseDeveloperSupport() {
+ return BuildConfig.DEBUG;
+ }
+
+ @Override
+ protected List getPackages() {
+ List packages = new PackageList(this).getPackages();
+ // Packages that cannot be autolinked yet can be added manually here, for example:
+ // packages.add(new MyReactNativePackage());
+ // TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
+ // packages.add(new TurboReactPackage() { ... });
+ // If you have custom Fabric Components, their ViewManagers should also be loaded here
+ // inside a ReactPackage.
+ return packages;
+ }
+
+ @Override
+ protected String getJSMainModuleName() {
+ return "src/index";
+ }
+
+ @NonNull
+ @Override
+ protected ReactPackageTurboModuleManagerDelegate.Builder
+ getReactPackageTurboModuleManagerDelegateBuilder() {
+ // Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
+ // for the new architecture and to use TurboModules correctly.
+ return new MainApplicationTurboModuleManagerDelegate.Builder();
+ }
+
+ @Override
+ protected JSIModulePackage getJSIModulePackage() {
+ return new JSIModulePackage() {
+ @Override
+ public List getJSIModules(
+ final ReactApplicationContext reactApplicationContext,
+ final JavaScriptContextHolder jsContext) {
+ final List specs = new ArrayList<>();
+
+ // Here we provide a new JSIModuleSpec that will be responsible of providing the
+ // custom Fabric Components.
+ specs.add(
+ new JSIModuleSpec() {
+ @Override
+ public JSIModuleType getJSIModuleType() {
+ return JSIModuleType.UIManager;
+ }
+
+ @Override
+ public JSIModuleProvider getJSIModuleProvider() {
+ final ComponentFactory componentFactory = new ComponentFactory();
+ CoreComponentsRegistry.register(componentFactory);
+
+ // Here we register a Components Registry.
+ // The one that is generated with the template contains no components
+ // and just provides you the one from React Native core.
+ MainComponentsRegistry.register(componentFactory);
+
+ final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
+
+ ViewManagerRegistry viewManagerRegistry =
+ new ViewManagerRegistry(
+ reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
+
+ return new FabricJSIModuleProvider(
+ reactApplicationContext,
+ componentFactory,
+ ReactNativeConfig.DEFAULT_CONFIG,
+ viewManagerRegistry);
+ }
+ });
+ return specs;
+ }
+ };
+ }
+}
diff --git a/examples/basic/android/app/src/main/java/com/videoplayer/newarchitecture/components/MainComponentsRegistry.java b/examples/basic/android/app/src/main/java/com/videoplayer/newarchitecture/components/MainComponentsRegistry.java
new file mode 100644
index 0000000000..78e765fe7c
--- /dev/null
+++ b/examples/basic/android/app/src/main/java/com/videoplayer/newarchitecture/components/MainComponentsRegistry.java
@@ -0,0 +1,36 @@
+package com.videoplayer.newarchitecture.components;
+
+import com.facebook.jni.HybridData;
+import com.facebook.proguard.annotations.DoNotStrip;
+import com.facebook.react.fabric.ComponentFactory;
+import com.facebook.soloader.SoLoader;
+
+/**
+ * Class responsible to load the custom Fabric Components. This class has native methods and needs a
+ * corresponding C++ implementation/header file to work correctly (already placed inside the jni/
+ * folder for you).
+ *
+ * Please note that this class is used ONLY if you opt-in for the New Architecture (see the
+ * `newArchEnabled` property). Is ignored otherwise.
+ */
+@DoNotStrip
+public class MainComponentsRegistry {
+ static {
+ SoLoader.loadLibrary("fabricjni");
+ }
+
+ @DoNotStrip private final HybridData mHybridData;
+
+ @DoNotStrip
+ private native HybridData initHybrid(ComponentFactory componentFactory);
+
+ @DoNotStrip
+ private MainComponentsRegistry(ComponentFactory componentFactory) {
+ mHybridData = initHybrid(componentFactory);
+ }
+
+ @DoNotStrip
+ public static MainComponentsRegistry register(ComponentFactory componentFactory) {
+ return new MainComponentsRegistry(componentFactory);
+ }
+}
diff --git a/examples/basic/android/app/src/main/java/com/videoplayer/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java b/examples/basic/android/app/src/main/java/com/videoplayer/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java
new file mode 100644
index 0000000000..a86686222e
--- /dev/null
+++ b/examples/basic/android/app/src/main/java/com/videoplayer/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java
@@ -0,0 +1,48 @@
+package com.videoplayer.newarchitecture.modules;
+
+import com.facebook.jni.HybridData;
+import com.facebook.react.ReactPackage;
+import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.soloader.SoLoader;
+import java.util.List;
+
+/**
+ * Class responsible to load the TurboModules. This class has native methods and needs a
+ * corresponding C++ implementation/header file to work correctly (already placed inside the jni/
+ * folder for you).
+ *
+ *
Please note that this class is used ONLY if you opt-in for the New Architecture (see the
+ * `newArchEnabled` property). Is ignored otherwise.
+ */
+public class MainApplicationTurboModuleManagerDelegate
+ extends ReactPackageTurboModuleManagerDelegate {
+
+ private static volatile boolean sIsSoLibraryLoaded;
+
+ protected MainApplicationTurboModuleManagerDelegate(
+ ReactApplicationContext reactApplicationContext, List packages) {
+ super(reactApplicationContext, packages);
+ }
+
+ protected native HybridData initHybrid();
+
+ native boolean canCreateTurboModule(String moduleName);
+
+ public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
+ protected MainApplicationTurboModuleManagerDelegate build(
+ ReactApplicationContext context, List packages) {
+ return new MainApplicationTurboModuleManagerDelegate(context, packages);
+ }
+ }
+
+ @Override
+ protected synchronized void maybeLoadOtherSoLibraries() {
+ if (!sIsSoLibraryLoaded) {
+ // If you change the name of your application .so file in the Android.mk file,
+ // make sure you update the name here as well.
+ SoLoader.loadLibrary("videoplayer_appmodules");
+ sIsSoLibraryLoaded = true;
+ }
+ }
+}
diff --git a/examples/basic/android/app/src/main/jni/CMakeLists.txt b/examples/basic/android/app/src/main/jni/CMakeLists.txt
new file mode 100644
index 0000000000..44273b891a
--- /dev/null
+++ b/examples/basic/android/app/src/main/jni/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 3.13)
+
+# Define the library name here.
+project(videoplayer_appmodules)
+
+# This file includes all the necessary to let you build your application with the New Architecture.
+include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)
diff --git a/examples/basic/android/app/src/main/jni/MainApplicationModuleProvider.cpp b/examples/basic/android/app/src/main/jni/MainApplicationModuleProvider.cpp
new file mode 100644
index 0000000000..26162dd872
--- /dev/null
+++ b/examples/basic/android/app/src/main/jni/MainApplicationModuleProvider.cpp
@@ -0,0 +1,32 @@
+#include "MainApplicationModuleProvider.h"
+
+#include
+#include
+
+namespace facebook {
+namespace react {
+
+std::shared_ptr MainApplicationModuleProvider(
+ const std::string &moduleName,
+ const JavaTurboModule::InitParams ¶ms) {
+ // Here you can provide your own module provider for TurboModules coming from
+ // either your application or from external libraries. The approach to follow
+ // is similar to the following (for a library called `samplelibrary`:
+ //
+ // auto module = samplelibrary_ModuleProvider(moduleName, params);
+ // if (module != nullptr) {
+ // return module;
+ // }
+ // return rncore_ModuleProvider(moduleName, params);
+
+ // Module providers autolinked by RN CLI
+ auto rncli_module = rncli_ModuleProvider(moduleName, params);
+ if (rncli_module != nullptr) {
+ return rncli_module;
+ }
+
+ return rncore_ModuleProvider(moduleName, params);
+}
+
+} // namespace react
+} // namespace facebook
diff --git a/examples/basic/android/app/src/main/jni/MainApplicationModuleProvider.h b/examples/basic/android/app/src/main/jni/MainApplicationModuleProvider.h
new file mode 100644
index 0000000000..b38ccf53fd
--- /dev/null
+++ b/examples/basic/android/app/src/main/jni/MainApplicationModuleProvider.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include
+#include
+
+#include
+
+namespace facebook {
+namespace react {
+
+std::shared_ptr MainApplicationModuleProvider(
+ const std::string &moduleName,
+ const JavaTurboModule::InitParams ¶ms);
+
+} // namespace react
+} // namespace facebook
diff --git a/examples/basic/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp b/examples/basic/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp
new file mode 100644
index 0000000000..5fd688c509
--- /dev/null
+++ b/examples/basic/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp
@@ -0,0 +1,45 @@
+#include "MainApplicationTurboModuleManagerDelegate.h"
+#include "MainApplicationModuleProvider.h"
+
+namespace facebook {
+namespace react {
+
+jni::local_ref
+MainApplicationTurboModuleManagerDelegate::initHybrid(
+ jni::alias_ref) {
+ return makeCxxInstance();
+}
+
+void MainApplicationTurboModuleManagerDelegate::registerNatives() {
+ registerHybrid({
+ makeNativeMethod(
+ "initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
+ makeNativeMethod(
+ "canCreateTurboModule",
+ MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
+ });
+}
+
+std::shared_ptr
+MainApplicationTurboModuleManagerDelegate::getTurboModule(
+ const std::string &name,
+ const std::shared_ptr &jsInvoker) {
+ // Not implemented yet: provide pure-C++ NativeModules here.
+ return nullptr;
+}
+
+std::shared_ptr
+MainApplicationTurboModuleManagerDelegate::getTurboModule(
+ const std::string &name,
+ const JavaTurboModule::InitParams ¶ms) {
+ return MainApplicationModuleProvider(name, params);
+}
+
+bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
+ const std::string &name) {
+ return getTurboModule(name, nullptr) != nullptr ||
+ getTurboModule(name, {.moduleName = name}) != nullptr;
+}
+
+} // namespace react
+} // namespace facebook
diff --git a/examples/basic/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h b/examples/basic/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h
new file mode 100644
index 0000000000..59bd649f40
--- /dev/null
+++ b/examples/basic/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h
@@ -0,0 +1,38 @@
+#include
+#include
+
+#include
+#include
+
+namespace facebook {
+namespace react {
+
+class MainApplicationTurboModuleManagerDelegate
+ : public jni::HybridClass<
+ MainApplicationTurboModuleManagerDelegate,
+ TurboModuleManagerDelegate> {
+ public:
+ // Adapt it to the package you used for your Java class.
+ static constexpr auto kJavaDescriptor =
+ "Lcom/videoplayer/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
+
+ static jni::local_ref initHybrid(jni::alias_ref);
+
+ static void registerNatives();
+
+ std::shared_ptr getTurboModule(
+ const std::string &name,
+ const std::shared_ptr &jsInvoker) override;
+ std::shared_ptr getTurboModule(
+ const std::string &name,
+ const JavaTurboModule::InitParams ¶ms) override;
+
+ /**
+ * Test-only method. Allows user to verify whether a TurboModule can be
+ * created by instances of this class.
+ */
+ bool canCreateTurboModule(const std::string &name);
+};
+
+} // namespace react
+} // namespace facebook
diff --git a/examples/basic/android/app/src/main/jni/MainComponentsRegistry.cpp b/examples/basic/android/app/src/main/jni/MainComponentsRegistry.cpp
new file mode 100644
index 0000000000..54f598a486
--- /dev/null
+++ b/examples/basic/android/app/src/main/jni/MainComponentsRegistry.cpp
@@ -0,0 +1,65 @@
+#include "MainComponentsRegistry.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace facebook {
+namespace react {
+
+MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
+
+std::shared_ptr
+MainComponentsRegistry::sharedProviderRegistry() {
+ auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
+
+ // Autolinked providers registered by RN CLI
+ rncli_registerProviders(providerRegistry);
+
+ // Custom Fabric Components go here. You can register custom
+ // components coming from your App or from 3rd party libraries here.
+ //
+ // providerRegistry->add(concreteComponentDescriptorProvider<
+ // AocViewerComponentDescriptor>());
+ return providerRegistry;
+}
+
+jni::local_ref
+MainComponentsRegistry::initHybrid(
+ jni::alias_ref,
+ ComponentFactory *delegate) {
+ auto instance = makeCxxInstance(delegate);
+
+ auto buildRegistryFunction =
+ [](EventDispatcher::Weak const &eventDispatcher,
+ ContextContainer::Shared const &contextContainer)
+ -> ComponentDescriptorRegistry::Shared {
+ auto registry = MainComponentsRegistry::sharedProviderRegistry()
+ ->createComponentDescriptorRegistry(
+ {eventDispatcher, contextContainer});
+
+ auto mutableRegistry =
+ std::const_pointer_cast(registry);
+
+ mutableRegistry->setFallbackComponentDescriptor(
+ std::make_shared(
+ ComponentDescriptorParameters{
+ eventDispatcher, contextContainer, nullptr}));
+
+ return registry;
+ };
+
+ delegate->buildRegistryFunction = buildRegistryFunction;
+ return instance;
+}
+
+void MainComponentsRegistry::registerNatives() {
+ registerHybrid({
+ makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
+ });
+}
+
+} // namespace react
+} // namespace facebook
diff --git a/examples/basic/android/app/src/main/jni/MainComponentsRegistry.h b/examples/basic/android/app/src/main/jni/MainComponentsRegistry.h
new file mode 100644
index 0000000000..2daf359ad9
--- /dev/null
+++ b/examples/basic/android/app/src/main/jni/MainComponentsRegistry.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace facebook {
+namespace react {
+
+class MainComponentsRegistry
+ : public facebook::jni::HybridClass {
+ public:
+ // Adapt it to the package you used for your Java class.
+ constexpr static auto kJavaDescriptor =
+ "Lcom/videoplayer/newarchitecture/components/MainComponentsRegistry;";
+
+ static void registerNatives();
+
+ MainComponentsRegistry(ComponentFactory *delegate);
+
+ private:
+ static std::shared_ptr
+ sharedProviderRegistry();
+
+ static jni::local_ref initHybrid(
+ jni::alias_ref,
+ ComponentFactory *delegate);
+};
+
+} // namespace react
+} // namespace facebook
diff --git a/examples/basic/android/app/src/main/jni/OnLoad.cpp b/examples/basic/android/app/src/main/jni/OnLoad.cpp
new file mode 100644
index 0000000000..c569b6e865
--- /dev/null
+++ b/examples/basic/android/app/src/main/jni/OnLoad.cpp
@@ -0,0 +1,11 @@
+#include
+#include "MainApplicationTurboModuleManagerDelegate.h"
+#include "MainComponentsRegistry.h"
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
+ return facebook::jni::initialize(vm, [] {
+ facebook::react::MainApplicationTurboModuleManagerDelegate::
+ registerNatives();
+ facebook::react::MainComponentsRegistry::registerNatives();
+ });
+}
diff --git a/examples/basic/android/app/src/main/res/drawable/rn_edit_text_material.xml b/examples/basic/android/app/src/main/res/drawable/rn_edit_text_material.xml
new file mode 100644
index 0000000000..f35d996202
--- /dev/null
+++ b/examples/basic/android/app/src/main/res/drawable/rn_edit_text_material.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
index cde69bccce..a2f5908281 100644
Binary files a/examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..1b52399808
Binary files /dev/null and b/examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
index c133a0cbd3..ff10afd6e1 100644
Binary files a/examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..115a4c768a
Binary files /dev/null and b/examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index bfa42f0e7b..dcd3cd8083 100644
Binary files a/examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..459ca609d3
Binary files /dev/null and b/examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index 324e72cdd7..8ca12fe024 100644
Binary files a/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..8e19b410a1
Binary files /dev/null and b/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/examples/basic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..b824ebdd48
Binary files /dev/null and b/examples/basic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/examples/basic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/examples/basic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..4c19a13c23
Binary files /dev/null and b/examples/basic/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/examples/basic/android/app/src/main/res/values/styles.xml b/examples/basic/android/app/src/main/res/values/styles.xml
index 319eb0ca10..7ba83a2ad5 100644
--- a/examples/basic/android/app/src/main/res/values/styles.xml
+++ b/examples/basic/android/app/src/main/res/values/styles.xml
@@ -1,8 +1,9 @@
-
diff --git a/examples/basic/android/build.gradle b/examples/basic/android/build.gradle
index c219127827..533ca601ff 100644
--- a/examples/basic/android/build.gradle
+++ b/examples/basic/android/build.gradle
@@ -2,20 +2,29 @@
buildscript {
ext {
- buildToolsVersion = "28.0.3"
- minSdkVersion = 16
- compileSdkVersion = 28
- targetSdkVersion = 28
- supportLibVersion = "28.0.0"
+ buildToolsVersion = "31.0.0"
+ minSdkVersion = 21
+ compileSdkVersion = 31
+ targetSdkVersion = 31
-}
+ if (System.properties['os.arch'] == "aarch64") {
+ // For M1 Users we need to use the NDK 24 which added support for aarch64
+ ndkVersion = "24.0.8215888"
+ } else {
+ // Otherwise we default to the side-by-side NDK version from AGP.
+ ndkVersion = "21.4.7075529"
+ }
+
+ RNVUseExoplayerIMA = true
+ }
repositories {
- jcenter()
google()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.1'
-
+ classpath("com.android.tools.build:gradle:7.2.1")
+ classpath("com.facebook.react:react-native-gradle-plugin")
+ classpath("de.undercouch:gradle-download-task:5.0.1")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -23,19 +32,22 @@ buildscript {
allprojects {
repositories {
- mavenLocal()
- google()
- maven {
- url "https://jitpack.io"
- }
- jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
- url "$rootDir/../node_modules/react-native/android"
+ url("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
+ mavenCentral {
+ // We don't want to fetch react-native from Maven Central as there are
+ // older versions over there.
+ content {
+ excludeGroup "com.facebook.react"
+ }
+ }
+ google()
+ maven { url 'https://www.jitpack.io' }
}
}
diff --git a/examples/basic/android/gradle.properties b/examples/basic/android/gradle.properties
index ccb748f58c..fa4feae5f1 100644
--- a/examples/basic/android/gradle.properties
+++ b/examples/basic/android/gradle.properties
@@ -9,13 +9,32 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+# 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
-android.enableJetifier=true
\ No newline at end of file
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+
+# Version of flipper SDK to use with React Native
+FLIPPER_VERSION=0.125.0
+
+# Use this property to specify which architecture you want to build.
+# You can also override it from the CLI using
+# ./gradlew -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
diff --git a/examples/basic/android/gradle/wrapper/gradle-wrapper.jar b/examples/basic/android/gradle/wrapper/gradle-wrapper.jar
index b5166dad4d..41d9927a4d 100644
Binary files a/examples/basic/android/gradle/wrapper/gradle-wrapper.jar and b/examples/basic/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/examples/basic/android/gradle/wrapper/gradle-wrapper.properties b/examples/basic/android/gradle/wrapper/gradle-wrapper.properties
index 364205fc1d..8fad3f5a98 100644
--- a/examples/basic/android/gradle/wrapper/gradle-wrapper.properties
+++ b/examples/basic/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Thu Mar 09 08:03:03 PST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/examples/basic/android/gradlew b/examples/basic/android/gradlew
index 91a7e269e1..1b6c787337 100755
--- a/examples/basic/android/gradlew
+++ b/examples/basic/android/gradlew
@@ -1,79 +1,129 @@
-#!/usr/bin/env bash
+#!/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 UN*X
-##
+#
+# 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/master/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/.
+#
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# 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
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
+
+# 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"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
-warn ( ) {
+warn () {
echo "$*"
-}
+} >&2
-die ( ) {
+die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
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"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -82,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || 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
@@ -90,75 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-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" )
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ 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
- i=$((i+1))
+ # 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
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
-
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# 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" "$@"
diff --git a/examples/basic/android/gradlew.bat b/examples/basic/android/gradlew.bat
index aec99730b4..ac1b06f938 100644
--- a/examples/basic/android/gradlew.bat
+++ b/examples/basic/android/gradlew.bat
@@ -1,3 +1,19 @@
+@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
@@ -8,20 +24,23 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@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=
-
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
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%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -45,34 +64,14 @@ echo location of your Java installation.
goto fail
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
: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 %CMD_LINE_ARGS%
+"%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
diff --git a/examples/basic/android/keystores/BUCK b/examples/basic/android/keystores/BUCK
deleted file mode 100644
index 15da20e6b9..0000000000
--- a/examples/basic/android/keystores/BUCK
+++ /dev/null
@@ -1,8 +0,0 @@
-keystore(
- name = 'debug',
- store = 'debug.keystore',
- properties = 'debug.keystore.properties',
- visibility = [
- 'PUBLIC',
- ],
-)
diff --git a/examples/basic/android/keystores/debug.keystore.properties b/examples/basic/android/keystores/debug.keystore.properties
deleted file mode 100644
index 121bfb49f0..0000000000
--- a/examples/basic/android/keystores/debug.keystore.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-key.store=debug.keystore
-key.alias=androiddebugkey
-key.store.password=android
-key.alias.password=android
diff --git a/examples/basic/android/settings.gradle b/examples/basic/android/settings.gradle
index 5fc7fca440..75a3903339 100644
--- a/examples/basic/android/settings.gradle
+++ b/examples/basic/android/settings.gradle
@@ -1,6 +1,11 @@
-rootProject.name = 'VideoPlayer'
-
-include ':react-native-video'
-project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android-exoplayer')
-
+rootProject.name = 'videoplayer'
+apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
+includeBuild('../node_modules/react-native-gradle-plugin')
+
+if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
+ include(":ReactAndroid")
+ project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
+ include(":ReactAndroid:hermes-engine")
+ project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine')
+}
diff --git a/examples/basic/app.json b/examples/basic/app.json
new file mode 100644
index 0000000000..6cffdb8f40
--- /dev/null
+++ b/examples/basic/app.json
@@ -0,0 +1,4 @@
+{
+ "name": "VideoPlayer",
+ "displayName": "VideoPlayer"
+}
diff --git a/examples/basic/babel.config.js b/examples/basic/babel.config.js
index f842b77fcf..c5b2615385 100644
--- a/examples/basic/babel.config.js
+++ b/examples/basic/babel.config.js
@@ -1,3 +1,16 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
+ plugins: [
+ [
+ 'module-resolver',
+ {
+ extensions: ['.js', '.tsx', '.ts'],
+ root: ['./src'],
+
+ alias: {
+ src: './src',
+ },
+ },
+ ],
+ ],
};
diff --git a/examples/basic/index.android.js b/examples/basic/index.android.js
deleted file mode 100644
index 1242ddd011..0000000000
--- a/examples/basic/index.android.js
+++ /dev/null
@@ -1,230 +0,0 @@
-'use strict';
-
-import React, {
- Component
-} from 'react';
-
-import {
- AppRegistry,
- StyleSheet,
- Text,
- TouchableOpacity,
- View,
-} from 'react-native';
-
-import Video from 'react-native-video';
-
-class VideoPlayer extends Component {
-
- state = {
- rate: 1,
- volume: 1,
- muted: false,
- resizeMode: 'contain',
- duration: 0.0,
- currentTime: 0.0,
- paused: true,
- };
-
- video: Video;
-
- onLoad = (data) => {
- this.setState({ duration: data.duration });
- };
-
- onProgress = (data) => {
- this.setState({ currentTime: data.currentTime });
- };
-
- onEnd = () => {
- this.setState({ paused: true })
- this.video.seek(0)
- };
-
- onAudioBecomingNoisy = () => {
- this.setState({ paused: true })
- };
-
- onAudioFocusChanged = (event: { hasAudioFocus: boolean }) => {
- this.setState({ paused: !event.hasAudioFocus })
- };
-
- getCurrentTimePercentage() {
- if (this.state.currentTime > 0) {
- return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
- }
- return 0;
- };
-
- renderRateControl(rate) {
- const isSelected = (this.state.rate === rate);
-
- return (
- { this.setState({ rate }) }}>
-
- {rate}x
-
-
- );
- }
-
- renderResizeModeControl(resizeMode) {
- const isSelected = (this.state.resizeMode === resizeMode);
-
- return (
- { this.setState({ resizeMode }) }}>
-
- {resizeMode}
-
-
- )
- }
-
- renderVolumeControl(volume) {
- const isSelected = (this.state.volume === volume);
-
- return (
- { this.setState({ volume }) }}>
-
- {volume * 100}%
-
-
- )
- }
-
- render() {
- const flexCompleted = this.getCurrentTimePercentage() * 100;
- const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
-
- return (
-
- this.setState({ paused: !this.state.paused })}
- >
- { this.video = ref }}
- /* For ExoPlayer */
- /* source={{ 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' }} */
- source={require('./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={this.onEnd}
- onAudioBecomingNoisy={this.onAudioBecomingNoisy}
- onAudioFocusChanged={this.onAudioFocusChanged}
- repeat={false}
- />
-
-
-
-
-
- {this.renderRateControl(0.25)}
- {this.renderRateControl(0.5)}
- {this.renderRateControl(1.0)}
- {this.renderRateControl(1.5)}
- {this.renderRateControl(2.0)}
-
-
-
- {this.renderVolumeControl(0.5)}
- {this.renderVolumeControl(1)}
- {this.renderVolumeControl(1.5)}
-
-
-
- {this.renderResizeModeControl('cover')}
- {this.renderResizeModeControl('contain')}
- {this.renderResizeModeControl('stretch')}
-
-
-
-
-
-
-
-
-
-
-
- );
- }
-}
-
-
-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,
- },
-});
-
-AppRegistry.registerComponent('VideoPlayer', () => VideoPlayer);
diff --git a/examples/basic/ios/AppDelegate.h b/examples/basic/ios/AppDelegate.h
deleted file mode 100644
index a9654d5e01..0000000000
--- a/examples/basic/ios/AppDelegate.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-
-#import
-
-@interface AppDelegate : UIResponder
-
-@property (nonatomic, strong) UIWindow *window;
-
-@end
diff --git a/examples/basic/ios/AppDelegate.m b/examples/basic/ios/AppDelegate.m
deleted file mode 100644
index 74dbce6d11..0000000000
--- a/examples/basic/ios/AppDelegate.m
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-
-#import "AppDelegate.h"
-
-#import "RCTRootView.h"
-
-@implementation AppDelegate
-
-- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
-{
- NSURL *jsCodeLocation;
-
- // Loading JavaScript code - uncomment the one you want.
-
- // OPTION 1
- // Load from development server. Start the server from the repository root:
- //
- // $ npm start
- //
- // To run on device, change `localhost` to the IP address of your computer, and make sure your computer and
- // iOS device are on the same Wi-Fi network.
- jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
-
- // OPTION 2
- // Load from pre-bundled file on disk. To re-generate the static bundle, run
- //
- // $ curl 'http://localhost:8081/index.ios.bundle?dev=false&minify=true' -o iOS/main.jsbundle
- //
- // and uncomment the next following line
- // jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
-
- RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
- moduleName:@"VideoPlayer"
- initialProperties: nil
- launchOptions:launchOptions];
-
- self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
- UIViewController *rootViewController = [[UIViewController alloc] init];
- rootViewController.view = rootView;
- self.window.rootViewController = rootViewController;
- [self.window makeKeyAndVisible];
- return YES;
-}
-
-@end
diff --git a/examples/basic/ios/Base.lproj/LaunchScreen.xib b/examples/basic/ios/Base.lproj/LaunchScreen.xib
deleted file mode 100644
index 187346b09f..0000000000
--- a/examples/basic/ios/Base.lproj/LaunchScreen.xib
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/examples/basic/ios/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/basic/ios/Images.xcassets/AppIcon.appiconset/Contents.json
deleted file mode 100644
index 118c98f746..0000000000
--- a/examples/basic/ios/Images.xcassets/AppIcon.appiconset/Contents.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "images" : [
- {
- "idiom" : "iphone",
- "size" : "29x29",
- "scale" : "2x"
- },
- {
- "idiom" : "iphone",
- "size" : "29x29",
- "scale" : "3x"
- },
- {
- "idiom" : "iphone",
- "size" : "40x40",
- "scale" : "2x"
- },
- {
- "idiom" : "iphone",
- "size" : "40x40",
- "scale" : "3x"
- },
- {
- "idiom" : "iphone",
- "size" : "60x60",
- "scale" : "2x"
- },
- {
- "idiom" : "iphone",
- "size" : "60x60",
- "scale" : "3x"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
\ No newline at end of file
diff --git a/examples/basic/ios/Info.plist b/examples/basic/ios/Info.plist
deleted file mode 100644
index 45532a3add..0000000000
--- a/examples/basic/ios/Info.plist
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
- CFBundleDevelopmentRegion
- en
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $(PRODUCT_NAME)
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- 1.0.0
- CFBundleSignature
- ????
- CFBundleVersion
- 2
- LSApplicationCategoryType
-
- LSRequiresIPhoneOS
-
- NSAppTransportSecurity
-
- NSAllowsArbitraryLoads
-
-
- UILaunchStoryboardName
- LaunchScreen
- UIRequiredDeviceCapabilities
-
- armv7
-
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UIViewControllerBasedStatusBarAppearance
-
-
-
diff --git a/examples/basic/ios/Podfile b/examples/basic/ios/Podfile
new file mode 100644
index 0000000000..9438f885ea
--- /dev/null
+++ b/examples/basic/ios/Podfile
@@ -0,0 +1,45 @@
+require_relative '../node_modules/react-native/scripts/react_native_pods'
+require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
+
+platform :ios, '12.4'
+install! 'cocoapods', :deterministic_uuids => false
+
+target 'videoplayer' do
+ config = use_native_modules!
+
+ # $RNVideoUseGoogleIMA = true
+
+ # Flags change depending on the env values.
+ flags = get_default_flags()
+
+ use_react_native!(
+ :path => config[:reactNativePath],
+ # Hermes is now enabled by default. Disable by setting this flag to false.
+ # Upcoming versions of React Native may rely on get_default_flags(), but
+ # we make it explicit here to aid in the React Native upgrade process.
+ :hermes_enabled => true,
+ :fabric_enabled => flags[:fabric_enabled],
+ # Enables Flipper.
+ #
+ # Note that if you have use_frameworks! enabled, Flipper will not work and
+ # you should disable the next line.
+ :flipper_configuration => FlipperConfiguration.enabled,
+ # An absolute path to your application root.
+ :app_path => "#{Pod::Config.instance.installation_root}/.."
+ )
+
+ target 'videoplayerTests' do
+ inherit! :complete
+ # Pods for testing
+ end
+
+ post_install do |installer|
+ react_native_post_install(
+ installer,
+ # Set `mac_catalyst_enabled` to `true` in order to apply patches
+ # necessary for Mac Catalyst builds
+ :mac_catalyst_enabled => false
+ )
+ __apply_Xcode_12_5_M1_post_install_workaround(installer)
+ end
+end
diff --git a/examples/basic/ios/VideoPlayer.xcodeproj/project.pbxproj b/examples/basic/ios/VideoPlayer.xcodeproj/project.pbxproj
deleted file mode 100644
index db5f9a3933..0000000000
--- a/examples/basic/ios/VideoPlayer.xcodeproj/project.pbxproj
+++ /dev/null
@@ -1,1214 +0,0 @@
-// !$*UTF8*$!
-{
- archiveVersion = 1;
- classes = {
- };
- objectVersion = 46;
- objects = {
-
-/* Begin PBXBuildFile section */
- 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
- 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
- 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
- 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
- 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
- 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
- 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
- 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
- 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
- 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
- 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
- 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
- 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
- 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
- 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
- 8C2A0F841E2560A100E31596 /* libRCTVideo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C2A0F791E25608300E31596 /* libRCTVideo.a */; };
- FA3566AB216D5D7000E01ABD /* libRCTImage-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */; };
- FA3566AC216D5D7000E01ABD /* libRCTLinking-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */; };
- FA3566AD216D5D7000E01ABD /* libRCTNetwork-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */; };
- FA3566AE216D5D7000E01ABD /* libRCTSettings-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */; };
- FA3566AF216D5D7000E01ABD /* libRCTText-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */; };
- FA3566B0216D5D7000E01ABD /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */; };
- FA3566C8216D5DA900E01ABD /* libRCTVideo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D1107C542111145500073188 /* libRCTVideo.a */; };
- FA8681B8216D5C6D0010C92A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
- FA8681B9216D5C6D0010C92A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
- FA8681C8216D5C6D0010C92A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
- FA8B47A5216D777200AB07CF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXContainerItemProxy section */
- 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 134814201AA4EA6300B7C361;
- remoteInfo = RCTActionSheet;
- };
- 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 134814201AA4EA6300B7C361;
- remoteInfo = RCTGeolocation;
- };
- 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
- remoteInfo = RCTImage;
- };
- 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 58B511DB1A9E6C8500147676;
- remoteInfo = RCTNetwork;
- };
- 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 832C81801AAF6DEF007FA2F7;
- remoteInfo = RCTVibration;
- };
- 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 134814201AA4EA6300B7C361;
- remoteInfo = RCTSettings;
- };
- 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3C86DF461ADF2C930047B81A;
- remoteInfo = RCTWebSocket;
- };
- 146834031AC3E56700842450 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
- remoteInfo = React;
- };
- 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 2D2A283A1D9B042B00D4039D;
- remoteInfo = "RCTImage-tvOS";
- };
- 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 2D2A28471D9B043800D4039D;
- remoteInfo = "RCTLinking-tvOS";
- };
- 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 2D2A28541D9B044C00D4039D;
- remoteInfo = "RCTNetwork-tvOS";
- };
- 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 2D2A28611D9B046600D4039D;
- remoteInfo = "RCTSettings-tvOS";
- };
- 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 2D2A287B1D9B048500D4039D;
- remoteInfo = "RCTText-tvOS";
- };
- 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 2D2A28881D9B049200D4039D;
- remoteInfo = "RCTWebSocket-tvOS";
- };
- 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 2D2A28131D9B038B00D4039D;
- remoteInfo = "React-tvOS";
- };
- 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3D3C059A1DE3340900C268FA;
- remoteInfo = yoga;
- };
- 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3D3C06751DE3340C00C268FA;
- remoteInfo = "yoga-tvOS";
- };
- 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4;
- remoteInfo = cxxreact;
- };
- 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4;
- remoteInfo = "cxxreact-tvOS";
- };
- 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4;
- remoteInfo = jschelpers;
- };
- 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4;
- remoteInfo = "jschelpers-tvOS";
- };
- 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 134814201AA4EA6300B7C361;
- remoteInfo = RCTAnimation;
- };
- 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 2D2A28201D9B03D100D4039D;
- remoteInfo = "RCTAnimation-tvOS";
- };
- 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 134814201AA4EA6300B7C361;
- remoteInfo = RCTLinking;
- };
- 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 58B5119B1A9E6C1200147676;
- remoteInfo = RCTText;
- };
- 8C2A0F781E25608300E31596 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 134814201AA4EA6300B7C361;
- remoteInfo = RCTVideo;
- };
- D1107C532111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 641E28441F0EEC8500443AF6;
- remoteInfo = "RCTVideo-tvOS";
- };
- D1107C592111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3DBE0D001F3B181A0099AA32;
- remoteInfo = fishhook;
- };
- D1107C5B2111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32;
- remoteInfo = "fishhook-tvOS";
- };
- D1107C6D2111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = EBF21BDC1FC498900052F4D5;
- remoteInfo = jsinspector;
- };
- D1107C6F2111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = EBF21BFA1FC4989A0052F4D5;
- remoteInfo = "jsinspector-tvOS";
- };
- D1107C712111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7;
- remoteInfo = "third-party";
- };
- D1107C732111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3D383D3C1EBD27B6005632C8;
- remoteInfo = "third-party-tvOS";
- };
- D1107C752111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 139D7E881E25C6D100323FB7;
- remoteInfo = "double-conversion";
- };
- D1107C772111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 3D383D621EBD27B9005632C8;
- remoteInfo = "double-conversion-tvOS";
- };
- D1107C792111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 9936F3131F5F2E4B0010BF04;
- remoteInfo = privatedata;
- };
- D1107C7B2111145500073188 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04;
- remoteInfo = "privatedata-tvOS";
- };
-/* End PBXContainerItemProxy section */
-
-/* Begin PBXFileReference section */
- 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; };
- 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; };
- 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; };
- 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; };
- 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; };
- 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; };
- 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; };
- 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; };
- 13B07F961A680F5B00A75B9A /* VideoPlayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VideoPlayer.app; sourceTree = BUILT_PRODUCTS_DIR; };
- 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = VideoPlayer/AppDelegate.h; sourceTree = ""; };
- 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = VideoPlayer/AppDelegate.m; sourceTree = ""; };
- 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
- 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = VideoPlayer/Images.xcassets; sourceTree = ""; };
- 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = VideoPlayer/Info.plist; sourceTree = ""; };
- 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = VideoPlayer/main.m; sourceTree = ""; };
- 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; };
- 39CBB10045CEBFA9BBB9645E /* libPods-VideoPlayer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VideoPlayer.a"; sourceTree = BUILT_PRODUCTS_DIR; };
- 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; };
- 627363E07276C06249D7CEBF /* libPods-VideoPlayer-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VideoPlayer-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
- 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; };
- 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; };
- 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVideo.xcodeproj; path = "../node_modules/react-native-video/ios/RCTVideo.xcodeproj"; sourceTree = ""; };
- FA8681CE216D5C6D0010C92A /* VideoPlayer-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "VideoPlayer-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
- FA8681D0216D5C6E0010C92A /* VideoPlayer-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "VideoPlayer-tvOS.plist"; path = "/Users/amishra/Development/react-native-video-nfb/examples/basic/ios/VideoPlayer-tvOS.plist"; sourceTree = ""; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
- 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 146834051AC3E58100842450 /* libReact.a in Frameworks */,
- 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */,
- 8C2A0F841E2560A100E31596 /* libRCTVideo.a in Frameworks */,
- 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
- 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
- 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */,
- 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */,
- 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,
- 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */,
- 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
- 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
- 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- FA8681BA216D5C6D0010C92A /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- FA8B47A5216D777200AB07CF /* libReact.a in Frameworks */,
- FA3566AB216D5D7000E01ABD /* libRCTImage-tvOS.a in Frameworks */,
- FA3566AC216D5D7000E01ABD /* libRCTLinking-tvOS.a in Frameworks */,
- FA3566AD216D5D7000E01ABD /* libRCTNetwork-tvOS.a in Frameworks */,
- FA3566AE216D5D7000E01ABD /* libRCTSettings-tvOS.a in Frameworks */,
- FA3566AF216D5D7000E01ABD /* libRCTText-tvOS.a in Frameworks */,
- FA3566B0216D5D7000E01ABD /* libRCTWebSocket-tvOS.a in Frameworks */,
- FA3566C8216D5DA900E01ABD /* libRCTVideo.a in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
- 00C302A81ABCB8CE00DB3ED1 /* Products */ = {
- isa = PBXGroup;
- children = (
- 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 00C302B61ABCB90400DB3ED1 /* Products */ = {
- isa = PBXGroup;
- children = (
- 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 00C302BC1ABCB91800DB3ED1 /* Products */ = {
- isa = PBXGroup;
- children = (
- 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */,
- 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 00C302D41ABCB9D200DB3ED1 /* Products */ = {
- isa = PBXGroup;
- children = (
- 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */,
- 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 00C302E01ABCB9EE00DB3ED1 /* Products */ = {
- isa = PBXGroup;
- children = (
- 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 139105B71AF99BAD00B5F7CC /* Products */ = {
- isa = PBXGroup;
- children = (
- 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */,
- 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 139FDEE71B06529A00C62182 /* Products */ = {
- isa = PBXGroup;
- children = (
- 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,
- 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */,
- D1107C5A2111145500073188 /* libfishhook.a */,
- D1107C5C2111145500073188 /* libfishhook-tvOS.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 13B07FAE1A68108700A75B9A /* VideoPlayer */ = {
- isa = PBXGroup;
- children = (
- 008F07F21AC5B25A0029DE68 /* main.jsbundle */,
- 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
- 13B07FB01A68108700A75B9A /* AppDelegate.m */,
- 13B07FB51A68108700A75B9A /* Images.xcassets */,
- 13B07FB61A68108700A75B9A /* Info.plist */,
- 13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
- 13B07FB71A68108700A75B9A /* main.m */,
- );
- name = VideoPlayer;
- sourceTree = "";
- };
- 146834001AC3E56700842450 /* Products */ = {
- isa = PBXGroup;
- children = (
- 146834041AC3E56700842450 /* libReact.a */,
- 3DAD3EA31DF850E9000B6D8A /* libReact.a */,
- 3DAD3EA51DF850E9000B6D8A /* libyoga.a */,
- 3DAD3EA71DF850E9000B6D8A /* libyoga.a */,
- 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */,
- 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */,
- 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */,
- 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */,
- D1107C6E2111145500073188 /* libjsinspector.a */,
- D1107C702111145500073188 /* libjsinspector-tvOS.a */,
- D1107C722111145500073188 /* libthird-party.a */,
- D1107C742111145500073188 /* libthird-party.a */,
- D1107C762111145500073188 /* libdouble-conversion.a */,
- D1107C782111145500073188 /* libdouble-conversion.a */,
- D1107C7A2111145500073188 /* libprivatedata.a */,
- D1107C7C2111145500073188 /* libprivatedata-tvOS.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 5E91572E1DD0AC6500FF2AA8 /* Products */ = {
- isa = PBXGroup;
- children = (
- 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */,
- 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 78C398B11ACF4ADC00677621 /* Products */ = {
- isa = PBXGroup;
- children = (
- 78C398B91ACF4ADC00677621 /* libRCTLinking.a */,
- 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
- isa = PBXGroup;
- children = (
- 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */,
- 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */,
- 146833FF1AC3E56700842450 /* React.xcodeproj */,
- 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
- 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
- 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
- 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
- 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,
- 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */,
- 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
- 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
- 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
- );
- name = Libraries;
- sourceTree = "";
- };
- 832341B11AAA6A8300B99B32 /* Products */ = {
- isa = PBXGroup;
- children = (
- 832341B51AAA6A8300B99B32 /* libRCTText.a */,
- 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 83CBB9F61A601CBA00E9B192 = {
- isa = PBXGroup;
- children = (
- FA8681CF216D5C6D0010C92A /* Resources-iPad */,
- 13B07FAE1A68108700A75B9A /* VideoPlayer */,
- 832341AE1AAA6A7D00B99B32 /* Libraries */,
- 83CBBA001A601CBA00E9B192 /* Products */,
- FA35669C216D5D7000E01ABD /* Frameworks */,
- );
- indentWidth = 2;
- sourceTree = "";
- tabWidth = 2;
- };
- 83CBBA001A601CBA00E9B192 /* Products */ = {
- isa = PBXGroup;
- children = (
- 13B07F961A680F5B00A75B9A /* VideoPlayer.app */,
- FA8681CE216D5C6D0010C92A /* VideoPlayer-tvOS.app */,
- );
- name = Products;
- sourceTree = "";
- };
- 8C2A0F661E25608300E31596 /* Products */ = {
- isa = PBXGroup;
- children = (
- 8C2A0F791E25608300E31596 /* libRCTVideo.a */,
- D1107C542111145500073188 /* libRCTVideo.a */,
- );
- name = Products;
- sourceTree = "";
- };
- FA35669C216D5D7000E01ABD /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- 39CBB10045CEBFA9BBB9645E /* libPods-VideoPlayer.a */,
- 627363E07276C06249D7CEBF /* libPods-VideoPlayer-tvOS.a */,
- );
- name = Frameworks;
- sourceTree = "";
- };
- FA8681CF216D5C6D0010C92A /* Resources-iPad */ = {
- isa = PBXGroup;
- children = (
- FA8681D0216D5C6E0010C92A /* VideoPlayer-tvOS.plist */,
- );
- name = "Resources-iPad";
- sourceTree = "";
- };
-/* End PBXGroup section */
-
-/* Begin PBXNativeTarget section */
- 13B07F861A680F5B00A75B9A /* VideoPlayer */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VideoPlayer" */;
- buildPhases = (
- 13B07F871A680F5B00A75B9A /* Sources */,
- 13B07F8C1A680F5B00A75B9A /* Frameworks */,
- 13B07F8E1A680F5B00A75B9A /* Resources */,
- 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = VideoPlayer;
- productName = "Hello World";
- productReference = 13B07F961A680F5B00A75B9A /* VideoPlayer.app */;
- productType = "com.apple.product-type.application";
- };
- FA8681B6216D5C6D0010C92A /* VideoPlayer-tvOS */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = FA8681CB216D5C6D0010C92A /* Build configuration list for PBXNativeTarget "VideoPlayer-tvOS" */;
- buildPhases = (
- FA8681B7216D5C6D0010C92A /* Sources */,
- FA8681BA216D5C6D0010C92A /* Frameworks */,
- FA8681C7216D5C6D0010C92A /* Resources */,
- FA8681CA216D5C6D0010C92A /* Bundle React Native code and images */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = "VideoPlayer-tvOS";
- productName = "Hello World";
- productReference = FA8681CE216D5C6D0010C92A /* VideoPlayer-tvOS.app */;
- productType = "com.apple.product-type.application";
- };
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
- 83CBB9F71A601CBA00E9B192 /* Project object */ = {
- isa = PBXProject;
- attributes = {
- LastUpgradeCheck = 0610;
- ORGANIZATIONNAME = Facebook;
- };
- buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "VideoPlayer" */;
- compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
- hasScannedForEncodings = 0;
- knownRegions = (
- en,
- Base,
- );
- mainGroup = 83CBB9F61A601CBA00E9B192;
- productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
- projectDirPath = "";
- projectReferences = (
- {
- ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */;
- ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */;
- },
- {
- ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */;
- ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */;
- },
- {
- ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */;
- ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */;
- },
- {
- ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */;
- ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
- },
- {
- ProductGroup = 78C398B11ACF4ADC00677621 /* Products */;
- ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
- },
- {
- ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */;
- ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
- },
- {
- ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */;
- ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
- },
- {
- ProductGroup = 832341B11AAA6A8300B99B32 /* Products */;
- ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
- },
- {
- ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */;
- ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
- },
- {
- ProductGroup = 8C2A0F661E25608300E31596 /* Products */;
- ProjectRef = 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */;
- },
- {
- ProductGroup = 139FDEE71B06529A00C62182 /* Products */;
- ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
- },
- {
- ProductGroup = 146834001AC3E56700842450 /* Products */;
- ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
- },
- );
- projectRoot = "";
- targets = (
- 13B07F861A680F5B00A75B9A /* VideoPlayer */,
- FA8681B6216D5C6D0010C92A /* VideoPlayer-tvOS */,
- );
- };
-/* End PBXProject section */
-
-/* Begin PBXReferenceProxy section */
- 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTActionSheet.a;
- remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTGeolocation.a;
- remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTImage.a;
- remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTNetwork.a;
- remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTVibration.a;
- remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTSettings.a;
- remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTWebSocket.a;
- remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 146834041AC3E56700842450 /* libReact.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libReact.a;
- remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libRCTImage-tvOS.a";
- remoteRef = 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libRCTLinking-tvOS.a";
- remoteRef = 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libRCTNetwork-tvOS.a";
- remoteRef = 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libRCTSettings-tvOS.a";
- remoteRef = 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libRCTText-tvOS.a";
- remoteRef = 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libRCTWebSocket-tvOS.a";
- remoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3EA31DF850E9000B6D8A /* libReact.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libReact.a;
- remoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3EA51DF850E9000B6D8A /* libyoga.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libyoga.a;
- remoteRef = 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3EA71DF850E9000B6D8A /* libyoga.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libyoga.a;
- remoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libcxxreact.a;
- remoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libcxxreact.a;
- remoteRef = 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libjschelpers.a;
- remoteRef = 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libjschelpers.a;
- remoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTAnimation.a;
- remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTAnimation.a;
- remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTLinking.a;
- remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTText.a;
- remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 8C2A0F791E25608300E31596 /* libRCTVideo.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTVideo.a;
- remoteRef = 8C2A0F781E25608300E31596 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C542111145500073188 /* libRCTVideo.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRCTVideo.a;
- remoteRef = D1107C532111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C5A2111145500073188 /* libfishhook.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libfishhook.a;
- remoteRef = D1107C592111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C5C2111145500073188 /* libfishhook-tvOS.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libfishhook-tvOS.a";
- remoteRef = D1107C5B2111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C6E2111145500073188 /* libjsinspector.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libjsinspector.a;
- remoteRef = D1107C6D2111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C702111145500073188 /* libjsinspector-tvOS.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libjsinspector-tvOS.a";
- remoteRef = D1107C6F2111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C722111145500073188 /* libthird-party.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libthird-party.a";
- remoteRef = D1107C712111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C742111145500073188 /* libthird-party.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libthird-party.a";
- remoteRef = D1107C732111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C762111145500073188 /* libdouble-conversion.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libdouble-conversion.a";
- remoteRef = D1107C752111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C782111145500073188 /* libdouble-conversion.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libdouble-conversion.a";
- remoteRef = D1107C772111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C7A2111145500073188 /* libprivatedata.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libprivatedata.a;
- remoteRef = D1107C792111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- D1107C7C2111145500073188 /* libprivatedata-tvOS.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = "libprivatedata-tvOS.a";
- remoteRef = D1107C7B2111145500073188 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
-/* End PBXReferenceProxy section */
-
-/* Begin PBXResourcesBuildPhase section */
- 13B07F8E1A680F5B00A75B9A /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
- 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- FA8681C7216D5C6D0010C92A /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- FA8681C8216D5C6D0010C92A /* Images.xcassets in Resources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXResourcesBuildPhase section */
-
-/* Begin PBXShellScriptBuildPhase section */
- 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "Bundle React Native code and images";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
- };
- FA8681CA216D5C6D0010C92A /* Bundle React Native code and images */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- );
- name = "Bundle React Native code and images";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
- };
-/* End PBXShellScriptBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
- 13B07F871A680F5B00A75B9A /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
- 13B07FC11A68108700A75B9A /* main.m in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- FA8681B7216D5C6D0010C92A /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- FA8681B8216D5C6D0010C92A /* AppDelegate.m in Sources */,
- FA8681B9216D5C6D0010C92A /* main.m in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXSourcesBuildPhase section */
-
-/* Begin PBXVariantGroup section */
- 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
- isa = PBXVariantGroup;
- children = (
- 13B07FB21A68108700A75B9A /* Base */,
- );
- name = LaunchScreen.xib;
- path = VideoPlayer;
- sourceTree = "";
- };
-/* End PBXVariantGroup section */
-
-/* Begin XCBuildConfiguration section */
- 13B07F941A680F5B00A75B9A /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CURRENT_PROJECT_VERSION = 1;
- DEAD_CODE_STRIPPING = NO;
- HEADER_SEARCH_PATHS = "$(inherited)";
- INFOPLIST_FILE = VideoPlayer/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- OTHER_LDFLAGS = (
- "$(inherited)",
- "-ObjC",
- "-lc++",
- );
- PRODUCT_NAME = VideoPlayer;
- VERSIONING_SYSTEM = "apple-generic";
- };
- name = Debug;
- };
- 13B07F951A680F5B00A75B9A /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CURRENT_PROJECT_VERSION = 1;
- HEADER_SEARCH_PATHS = "$(inherited)";
- INFOPLIST_FILE = VideoPlayer/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- OTHER_LDFLAGS = (
- "$(inherited)",
- "-ObjC",
- "-lc++",
- );
- PRODUCT_NAME = VideoPlayer;
- VERSIONING_SYSTEM = "apple-generic";
- };
- name = Release;
- };
- 83CBBA201A601CBA00E9B192 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- 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;
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_DYNAMIC_NO_PIC = NO;
- 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 = 8.0;
- MTL_ENABLE_DEBUG_INFO = YES;
- ONLY_ACTIVE_ARCH = YES;
- SDKROOT = iphoneos;
- };
- name = Debug;
- };
- 83CBBA211A601CBA00E9B192 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- 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_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 = 8.0;
- MTL_ENABLE_DEBUG_INFO = NO;
- SDKROOT = iphoneos;
- VALIDATE_PRODUCT = YES;
- };
- name = Release;
- };
- FA8681CC216D5C6D0010C92A /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CURRENT_PROJECT_VERSION = 1;
- DEAD_CODE_STRIPPING = NO;
- INFOPLIST_FILE = "VideoPlayer-tvOS.plist";
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- OTHER_LDFLAGS = (
- "$(inherited)",
- "-ObjC",
- "-lc++",
- );
- PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.VideoPlayer-tvOS";
- PRODUCT_NAME = "VideoPlayer-tvOS";
- SDKROOT = appletvos;
- SUPPORTED_PLATFORMS = "appletvsimulator appletvos";
- TARGETED_DEVICE_FAMILY = 3;
- VERSIONING_SYSTEM = "apple-generic";
- };
- name = Debug;
- };
- FA8681CD216D5C6D0010C92A /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CURRENT_PROJECT_VERSION = 1;
- INFOPLIST_FILE = "VideoPlayer-tvOS.plist";
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
- LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- OTHER_LDFLAGS = (
- "$(inherited)",
- "-ObjC",
- "-lc++",
- );
- PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.VideoPlayer-tvOS";
- PRODUCT_NAME = "VideoPlayer-tvOS";
- SDKROOT = appletvos;
- SUPPORTED_PLATFORMS = "appletvsimulator appletvos";
- TARGETED_DEVICE_FAMILY = 3;
- VERSIONING_SYSTEM = "apple-generic";
- };
- name = Release;
- };
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
- 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VideoPlayer" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 13B07F941A680F5B00A75B9A /* Debug */,
- 13B07F951A680F5B00A75B9A /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "VideoPlayer" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 83CBBA201A601CBA00E9B192 /* Debug */,
- 83CBBA211A601CBA00E9B192 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- FA8681CB216D5C6D0010C92A /* Build configuration list for PBXNativeTarget "VideoPlayer-tvOS" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- FA8681CC216D5C6D0010C92A /* Debug */,
- FA8681CD216D5C6D0010C92A /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
-/* End XCConfigurationList section */
- };
- rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
-}
diff --git a/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer-tvOS.xcscheme b/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer-tvOS.xcscheme
deleted file mode 100644
index 75b3d2ebf1..0000000000
--- a/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer-tvOS.xcscheme
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/examples/basic/ios/VideoPlayer/AppDelegate.h b/examples/basic/ios/VideoPlayer/AppDelegate.h
deleted file mode 100644
index a9654d5e01..0000000000
--- a/examples/basic/ios/VideoPlayer/AppDelegate.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-
-#import
-
-@interface AppDelegate : UIResponder
-
-@property (nonatomic, strong) UIWindow *window;
-
-@end
diff --git a/examples/basic/ios/VideoPlayer/AppDelegate.m b/examples/basic/ios/VideoPlayer/AppDelegate.m
deleted file mode 100644
index 6c27cb4165..0000000000
--- a/examples/basic/ios/VideoPlayer/AppDelegate.m
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-
-#import "AppDelegate.h"
-
-#import
-#import
-
-@implementation AppDelegate
-
-- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
-{
- NSURL *jsCodeLocation;
-
- jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
-
- RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
- moduleName:@"VideoPlayer"
- initialProperties:nil
- launchOptions:launchOptions];
- rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
-
- self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
- UIViewController *rootViewController = [UIViewController new];
- rootViewController.view = rootView;
- self.window.rootViewController = rootViewController;
- [self.window makeKeyAndVisible];
- return YES;
-}
-
-@end
diff --git a/examples/basic/ios/VideoPlayer/Base.lproj/LaunchScreen.xib b/examples/basic/ios/VideoPlayer/Base.lproj/LaunchScreen.xib
deleted file mode 100644
index 9d8b0c8923..0000000000
--- a/examples/basic/ios/VideoPlayer/Base.lproj/LaunchScreen.xib
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/examples/basic/ios/VideoPlayer/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/basic/ios/VideoPlayer/Images.xcassets/AppIcon.appiconset/Contents.json
deleted file mode 100644
index 118c98f746..0000000000
--- a/examples/basic/ios/VideoPlayer/Images.xcassets/AppIcon.appiconset/Contents.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "images" : [
- {
- "idiom" : "iphone",
- "size" : "29x29",
- "scale" : "2x"
- },
- {
- "idiom" : "iphone",
- "size" : "29x29",
- "scale" : "3x"
- },
- {
- "idiom" : "iphone",
- "size" : "40x40",
- "scale" : "2x"
- },
- {
- "idiom" : "iphone",
- "size" : "40x40",
- "scale" : "3x"
- },
- {
- "idiom" : "iphone",
- "size" : "60x60",
- "scale" : "2x"
- },
- {
- "idiom" : "iphone",
- "size" : "60x60",
- "scale" : "3x"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
\ No newline at end of file
diff --git a/examples/basic/ios/VideoPlayer/Info.plist b/examples/basic/ios/VideoPlayer/Info.plist
deleted file mode 100644
index 2fb6a11c2c..0000000000
--- a/examples/basic/ios/VideoPlayer/Info.plist
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
- CFBundleDevelopmentRegion
- en
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $(PRODUCT_NAME)
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- 1.0
- CFBundleSignature
- ????
- CFBundleVersion
- 1
- LSRequiresIPhoneOS
-
- UILaunchStoryboardName
- LaunchScreen
- UIRequiredDeviceCapabilities
-
- armv7
-
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UIViewControllerBasedStatusBarAppearance
-
- NSLocationWhenInUseUsageDescription
-
- NSAppTransportSecurity
-
-
- NSExceptionDomains
-
- localhost
-
- NSExceptionAllowsInsecureHTTPLoads
-
-
-
-
-
-
diff --git a/examples/basic/ios/VideoPlayer/main.m b/examples/basic/ios/VideoPlayer/main.m
deleted file mode 100644
index 3d767fcbb9..0000000000
--- a/examples/basic/ios/VideoPlayer/main.m
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-
-#import
-
-#import "AppDelegate.h"
-
-int main(int argc, char * argv[]) {
- @autoreleasepool {
- return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
- }
-}
diff --git a/examples/basic/ios/_xcode.env b/examples/basic/ios/_xcode.env
new file mode 100644
index 0000000000..3d5782c715
--- /dev/null
+++ b/examples/basic/ios/_xcode.env
@@ -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)
diff --git a/examples/basic/ios/main.jsbundle b/examples/basic/ios/main.jsbundle
deleted file mode 100644
index 4f50ea3379..0000000000
--- a/examples/basic/ios/main.jsbundle
+++ /dev/null
@@ -1,5 +0,0 @@
-// Offline JS
-// To re-generate the offline bundle, run this from root of your project
-// $ curl 'http://localhost:8081/index.ios.bundle?dev=false&minify=true' -o iOS/main.jsbundle
-
-throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions');
diff --git a/examples/basic/ios/main.m b/examples/basic/ios/main.m
deleted file mode 100644
index cb502bc136..0000000000
--- a/examples/basic/ios/main.m
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-
-#import
-
-#import "AppDelegate.h"
-
-int main(int argc, char * argv[]) {
- @autoreleasepool {
- return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
- }
-}
-
diff --git a/examples/basic/ios/videoplayer.xcodeproj/project.pbxproj b/examples/basic/ios/videoplayer.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..57c5b1b7d4
--- /dev/null
+++ b/examples/basic/ios/videoplayer.xcodeproj/project.pbxproj
@@ -0,0 +1,700 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 00E356F31AD99517003FC87E /* videoplayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* videoplayerTests.m */; };
+ 0C80B921A6F3F58F76C31292 /* libPods-videoplayer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-videoplayer.a */; };
+ 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 */; };
+ 7699B88040F8A987B510C191 /* libPods-videoplayer-videoplayerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-videoplayer-videoplayerTests.a */; };
+ 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
+ remoteInfo = videoplayer;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 00E356EE1AD99517003FC87E /* videoplayerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = videoplayerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 00E356F21AD99517003FC87E /* videoplayerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = videoplayerTests.m; sourceTree = ""; };
+ 13B07F961A680F5B00A75B9A /* videoplayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = videoplayer.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = videoplayer/AppDelegate.h; sourceTree = ""; };
+ 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = videoplayer/AppDelegate.mm; sourceTree = ""; };
+ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = videoplayer/Images.xcassets; sourceTree = ""; };
+ 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = videoplayer/Info.plist; sourceTree = ""; };
+ 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = videoplayer/main.m; sourceTree = ""; };
+ 19F6CBCC0A4E27FBF8BF4A61 /* libPods-videoplayer-videoplayerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-videoplayer-videoplayerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3B4392A12AC88292D35C810B /* Pods-videoplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer.debug.xcconfig"; path = "Target Support Files/Pods-videoplayer/Pods-videoplayer.debug.xcconfig"; sourceTree = ""; };
+ 5709B34CF0A7D63546082F79 /* Pods-videoplayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer.release.xcconfig"; path = "Target Support Files/Pods-videoplayer/Pods-videoplayer.release.xcconfig"; sourceTree = ""; };
+ 5B7EB9410499542E8C5724F5 /* Pods-videoplayer-videoplayerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer-videoplayerTests.debug.xcconfig"; path = "Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests.debug.xcconfig"; sourceTree = ""; };
+ 5DCACB8F33CDC322A6C60F78 /* libPods-videoplayer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-videoplayer.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = videoplayer/LaunchScreen.storyboard; sourceTree = ""; };
+ 89C6BE57DB24E9ADA2F236DE /* Pods-videoplayer-videoplayerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer-videoplayerTests.release.xcconfig"; path = "Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests.release.xcconfig"; sourceTree = ""; };
+ ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 00E356EB1AD99517003FC87E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7699B88040F8A987B510C191 /* libPods-videoplayer-videoplayerTests.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 0C80B921A6F3F58F76C31292 /* libPods-videoplayer.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 00E356EF1AD99517003FC87E /* videoplayerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 00E356F21AD99517003FC87E /* videoplayerTests.m */,
+ 00E356F01AD99517003FC87E /* Supporting Files */,
+ );
+ path = videoplayerTests;
+ sourceTree = "";
+ };
+ 00E356F01AD99517003FC87E /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 00E356F11AD99517003FC87E /* Info.plist */,
+ );
+ name = "Supporting Files";
+ sourceTree = "";
+ };
+ 13B07FAE1A68108700A75B9A /* videoplayer */ = {
+ isa = PBXGroup;
+ children = (
+ 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
+ 13B07FB01A68108700A75B9A /* AppDelegate.mm */,
+ 13B07FB51A68108700A75B9A /* Images.xcassets */,
+ 13B07FB61A68108700A75B9A /* Info.plist */,
+ 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
+ 13B07FB71A68108700A75B9A /* main.m */,
+ );
+ name = videoplayer;
+ sourceTree = "";
+ };
+ 2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
+ 5DCACB8F33CDC322A6C60F78 /* libPods-videoplayer.a */,
+ 19F6CBCC0A4E27FBF8BF4A61 /* libPods-videoplayer-videoplayerTests.a */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Libraries;
+ sourceTree = "";
+ };
+ 83CBB9F61A601CBA00E9B192 = {
+ isa = PBXGroup;
+ children = (
+ 13B07FAE1A68108700A75B9A /* videoplayer */,
+ 832341AE1AAA6A7D00B99B32 /* Libraries */,
+ 00E356EF1AD99517003FC87E /* videoplayerTests */,
+ 83CBBA001A601CBA00E9B192 /* Products */,
+ 2D16E6871FA4F8E400B85C8A /* Frameworks */,
+ BBD78D7AC51CEA395F1C20DB /* Pods */,
+ );
+ indentWidth = 2;
+ sourceTree = "";
+ tabWidth = 2;
+ usesTabs = 0;
+ };
+ 83CBBA001A601CBA00E9B192 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 13B07F961A680F5B00A75B9A /* videoplayer.app */,
+ 00E356EE1AD99517003FC87E /* videoplayerTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ BBD78D7AC51CEA395F1C20DB /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 3B4392A12AC88292D35C810B /* Pods-videoplayer.debug.xcconfig */,
+ 5709B34CF0A7D63546082F79 /* Pods-videoplayer.release.xcconfig */,
+ 5B7EB9410499542E8C5724F5 /* Pods-videoplayer-videoplayerTests.debug.xcconfig */,
+ 89C6BE57DB24E9ADA2F236DE /* Pods-videoplayer-videoplayerTests.release.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 00E356ED1AD99517003FC87E /* videoplayerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "videoplayerTests" */;
+ buildPhases = (
+ A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */,
+ 00E356EA1AD99517003FC87E /* Sources */,
+ 00E356EB1AD99517003FC87E /* Frameworks */,
+ 00E356EC1AD99517003FC87E /* Resources */,
+ C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */,
+ F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 00E356F51AD99517003FC87E /* PBXTargetDependency */,
+ );
+ name = videoplayerTests;
+ productName = videoplayerTests;
+ productReference = 00E356EE1AD99517003FC87E /* videoplayerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 13B07F861A680F5B00A75B9A /* videoplayer */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "videoplayer" */;
+ buildPhases = (
+ C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */,
+ FD10A7F022414F080027D42C /* Start Packager */,
+ 13B07F871A680F5B00A75B9A /* Sources */,
+ 13B07F8C1A680F5B00A75B9A /* Frameworks */,
+ 13B07F8E1A680F5B00A75B9A /* Resources */,
+ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
+ 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
+ E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = videoplayer;
+ productName = videoplayer;
+ productReference = 13B07F961A680F5B00A75B9A /* videoplayer.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 83CBB9F71A601CBA00E9B192 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1210;
+ TargetAttributes = {
+ 00E356ED1AD99517003FC87E = {
+ CreatedOnToolsVersion = 6.2;
+ TestTargetID = 13B07F861A680F5B00A75B9A;
+ };
+ 13B07F861A680F5B00A75B9A = {
+ LastSwiftMigration = 1120;
+ };
+ };
+ };
+ buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "videoplayer" */;
+ compatibilityVersion = "Xcode 12.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 83CBB9F61A601CBA00E9B192;
+ productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 13B07F861A680F5B00A75B9A /* videoplayer */,
+ 00E356ED1AD99517003FC87E /* videoplayerTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 00E356EC1AD99517003FC87E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 13B07F8E1A680F5B00A75B9A /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
+ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "$(SRCROOT)/.xcode.env.local",
+ "$(SRCROOT)/.xcode.env",
+ );
+ name = "Bundle React Native code and images";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
+ };
+ 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ A55EABD7B0C7F3A422A6CC61 /* [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-videoplayer-videoplayerTests-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;
+ };
+ C38B50BA6285516D6DCD4F65 /* [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-videoplayer-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;
+ };
+ C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ FD10A7F022414F080027D42C /* Start Packager */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ name = "Start Packager";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 00E356EA1AD99517003FC87E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 00E356F31AD99517003FC87E /* videoplayerTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 13B07F871A680F5B00A75B9A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
+ 13B07FC11A68108700A75B9A /* main.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 13B07F861A680F5B00A75B9A /* videoplayer */;
+ targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 00E356F61AD99517003FC87E /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-videoplayer-videoplayerTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ INFOPLIST_FILE = videoplayerTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "-lc++",
+ "$(inherited)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/videoplayer.app/videoplayer";
+ };
+ name = Debug;
+ };
+ 00E356F71AD99517003FC87E /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-videoplayer-videoplayerTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ COPY_PHASE_STRIP = NO;
+ INFOPLIST_FILE = videoplayerTests/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.4;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "-ObjC",
+ "-lc++",
+ "$(inherited)",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/videoplayer.app/videoplayer";
+ };
+ name = Release;
+ };
+ 13B07F941A680F5B00A75B9A /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-videoplayer.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = videoplayer/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-ObjC",
+ "-lc++",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_NAME = videoplayer;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 13B07F951A680F5B00A75B9A /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-videoplayer.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ INFOPLIST_FILE = videoplayer/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-ObjC",
+ "-lc++",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_NAME = videoplayer;
+ SWIFT_VERSION = 5.0;
+ 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++17";
+ 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ 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;
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
+ 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 = 12.4;
+ LD_RUNPATH_SEARCH_PATHS = (
+ /usr/lib/swift,
+ "$(inherited)",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "\"$(SDKROOT)/usr/lib/swift\"",
+ "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
+ "\"$(inherited)\"",
+ );
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "-DFOLLY_NO_CONFIG",
+ "-DFOLLY_MOBILE=1",
+ "-DFOLLY_USE_LIBCPP=1",
+ );
+ SDKROOT = iphoneos;
+ };
+ name = Debug;
+ };
+ 83CBBA211A601CBA00E9B192 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "c++17";
+ 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ 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;
+ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
+ 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 = 12.4;
+ LD_RUNPATH_SEARCH_PATHS = (
+ /usr/lib/swift,
+ "$(inherited)",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "\"$(SDKROOT)/usr/lib/swift\"",
+ "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
+ "\"$(inherited)\"",
+ );
+ MTL_ENABLE_DEBUG_INFO = NO;
+ OTHER_CPLUSPLUSFLAGS = (
+ "$(OTHER_CFLAGS)",
+ "-DFOLLY_NO_CONFIG",
+ "-DFOLLY_MOBILE=1",
+ "-DFOLLY_USE_LIBCPP=1",
+ );
+ SDKROOT = iphoneos;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "videoplayerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 00E356F61AD99517003FC87E /* Debug */,
+ 00E356F71AD99517003FC87E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "videoplayer" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 13B07F941A680F5B00A75B9A /* Debug */,
+ 13B07F951A680F5B00A75B9A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "videoplayer" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 83CBBA201A601CBA00E9B192 /* Debug */,
+ 83CBBA211A601CBA00E9B192 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
+}
diff --git a/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme b/examples/basic/ios/videoplayer.xcodeproj/xcshareddata/xcschemes/videoplayer.xcscheme
similarity index 62%
rename from examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme
rename to examples/basic/ios/videoplayer.xcodeproj/xcshareddata/xcschemes/videoplayer.xcscheme
index 48a644677d..df1cbb7654 100644
--- a/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme
+++ b/examples/basic/ios/videoplayer.xcodeproj/xcshareddata/xcschemes/videoplayer.xcscheme
@@ -1,25 +1,11 @@
-
-
-
-
+ BuildableName = "videoplayer.app"
+ BlueprintName = "videoplayer"
+ ReferencedContainer = "container:videoplayer.xcodeproj">
@@ -42,18 +28,17 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
+
+
+
+
-
-
-
-
-
-
+ BuildableName = "videoplayer.app"
+ BlueprintName = "videoplayer"
+ ReferencedContainer = "container:videoplayer.xcodeproj">
-
-
+ BuildableName = "videoplayer.app"
+ BlueprintName = "videoplayer"
+ ReferencedContainer = "container:videoplayer.xcodeproj">
diff --git a/examples/basic/ios/videoplayer/AppDelegate.h b/examples/basic/ios/videoplayer/AppDelegate.h
new file mode 100644
index 0000000000..ef1de86a2a
--- /dev/null
+++ b/examples/basic/ios/videoplayer/AppDelegate.h
@@ -0,0 +1,8 @@
+#import
+#import
+
+@interface AppDelegate : UIResponder
+
+@property (nonatomic, strong) UIWindow *window;
+
+@end
diff --git a/examples/basic/ios/videoplayer/AppDelegate.mm b/examples/basic/ios/videoplayer/AppDelegate.mm
new file mode 100644
index 0000000000..e263372b4d
--- /dev/null
+++ b/examples/basic/ios/videoplayer/AppDelegate.mm
@@ -0,0 +1,133 @@
+#import "AppDelegate.h"
+
+#import
+#import
+#import
+
+#import
+
+#if RCT_NEW_ARCH_ENABLED
+#import
+#import
+#import
+#import
+#import
+#import
+
+#import
+
+static NSString *const kRNConcurrentRoot = @"concurrentRoot";
+
+@interface AppDelegate () {
+ RCTTurboModuleManager *_turboModuleManager;
+ RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
+ std::shared_ptr _reactNativeConfig;
+ facebook::react::ContextContainer::Shared _contextContainer;
+}
+@end
+#endif
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+ RCTAppSetupPrepareApp(application);
+
+ RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
+
+#if RCT_NEW_ARCH_ENABLED
+ _contextContainer = std::make_shared();
+ _reactNativeConfig = std::make_shared();
+ _contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
+ _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
+ bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
+#endif
+
+ NSDictionary *initProps = [self prepareInitialProps];
+ UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"VideoPlayer", initProps);
+
+ if (@available(iOS 13.0, *)) {
+ rootView.backgroundColor = [UIColor systemBackgroundColor];
+ } else {
+ rootView.backgroundColor = [UIColor whiteColor];
+ }
+
+ self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
+ UIViewController *rootViewController = [UIViewController new];
+ rootViewController.view = rootView;
+ self.window.rootViewController = rootViewController;
+ [self.window makeKeyAndVisible];
+ return YES;
+}
+
+/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
+///
+/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
+/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
+/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
+- (BOOL)concurrentRootEnabled
+{
+ // Switch this bool to turn on and off the concurrent root
+ return true;
+}
+
+- (NSDictionary *)prepareInitialProps
+{
+ NSMutableDictionary *initProps = [NSMutableDictionary new];
+
+#ifdef RCT_NEW_ARCH_ENABLED
+ initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
+#endif
+
+ return initProps;
+}
+
+- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
+{
+#if DEBUG
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"src/index"];
+#else
+ return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
+#endif
+}
+
+#if RCT_NEW_ARCH_ENABLED
+
+#pragma mark - RCTCxxBridgeDelegate
+
+- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge
+{
+ _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
+ delegate:self
+ jsInvoker:bridge.jsCallInvoker];
+ return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
+}
+
+#pragma mark RCTTurboModuleManagerDelegate
+
+- (Class)getModuleClassFromName:(const char *)name
+{
+ return RCTCoreModulesClassProvider(name);
+}
+
+- (std::shared_ptr)getTurboModule:(const std::string &)name
+ jsInvoker:(std::shared_ptr)jsInvoker
+{
+ return nullptr;
+}
+
+- (std::shared_ptr)getTurboModule:(const std::string &)name
+ initParams:
+ (const facebook::react::ObjCTurboModule::InitParams &)params
+{
+ return nullptr;
+}
+
+- (id)getModuleInstanceFromClass:(Class)moduleClass
+{
+ return RCTAppSetupDefaultModuleFromClass(moduleClass);
+}
+
+#endif
+
+@end
diff --git a/examples/basic/ios/videoplayer/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/basic/ios/videoplayer/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..81213230de
--- /dev/null
+++ b/examples/basic/ios/videoplayer/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,53 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/examples/basic/ios/videoplayer/Images.xcassets/Contents.json b/examples/basic/ios/videoplayer/Images.xcassets/Contents.json
new file mode 100644
index 0000000000..2d92bd53fd
--- /dev/null
+++ b/examples/basic/ios/videoplayer/Images.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/examples/basic/ios/VideoPlayer-tvOS.plist b/examples/basic/ios/videoplayer/Info.plist
similarity index 96%
rename from examples/basic/ios/VideoPlayer-tvOS.plist
rename to examples/basic/ios/videoplayer/Info.plist
index ecbd496be7..2b2d6f407f 100644
--- a/examples/basic/ios/VideoPlayer-tvOS.plist
+++ b/examples/basic/ios/videoplayer/Info.plist
@@ -4,6 +4,8 @@
CFBundleDevelopmentRegion
en
+ CFBundleDisplayName
+ videoplayer
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
diff --git a/examples/basic/ios/videoplayer/LaunchScreen.storyboard b/examples/basic/ios/videoplayer/LaunchScreen.storyboard
new file mode 100644
index 0000000000..263a998e31
--- /dev/null
+++ b/examples/basic/ios/videoplayer/LaunchScreen.storyboard
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/basic/ios/videoplayer/main.m b/examples/basic/ios/videoplayer/main.m
new file mode 100644
index 0000000000..d645c7246c
--- /dev/null
+++ b/examples/basic/ios/videoplayer/main.m
@@ -0,0 +1,10 @@
+#import
+
+#import "AppDelegate.h"
+
+int main(int argc, char *argv[])
+{
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/examples/basic/ios/videoplayerTests/Info.plist b/examples/basic/ios/videoplayerTests/Info.plist
new file mode 100644
index 0000000000..ba72822e87
--- /dev/null
+++ b/examples/basic/ios/videoplayerTests/Info.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ BNDL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+
+
diff --git a/examples/basic/ios/videoplayerTests/videoplayerTests.m b/examples/basic/ios/videoplayerTests/videoplayerTests.m
new file mode 100644
index 0000000000..f3bf36a697
--- /dev/null
+++ b/examples/basic/ios/videoplayerTests/videoplayerTests.m
@@ -0,0 +1,66 @@
+#import
+#import
+
+#import
+#import
+
+#define TIMEOUT_SECONDS 600
+#define TEXT_TO_LOOK_FOR @"Welcome to React"
+
+@interface videoplayerTests : XCTestCase
+
+@end
+
+@implementation videoplayerTests
+
+- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
+{
+ if (test(view)) {
+ return YES;
+ }
+ for (UIView *subview in [view subviews]) {
+ if ([self findSubviewInView:subview matching:test]) {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (void)testRendersWelcomeScreen
+{
+ UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
+ NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
+ BOOL foundElement = NO;
+
+ __block NSString *redboxError = nil;
+#ifdef DEBUG
+ RCTSetLogFunction(
+ ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
+ if (level >= RCTLogLevelError) {
+ redboxError = message;
+ }
+ });
+#endif
+
+ while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
+ [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+ [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+
+ foundElement = [self findSubviewInView:vc.view
+ matching:^BOOL(UIView *view) {
+ if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
+ return YES;
+ }
+ return NO;
+ }];
+ }
+
+#ifdef DEBUG
+ RCTSetLogFunction(RCTDefaultLogFunction);
+#endif
+
+ XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
+ XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
+}
+
+@end
diff --git a/examples/basic/metro.config.js b/examples/basic/metro.config.js
index 59a9b33a9e..fa377047dd 100644
--- a/examples/basic/metro.config.js
+++ b/examples/basic/metro.config.js
@@ -4,52 +4,26 @@
*
* @format
*/
-const fs = require('fs');
const path = require('path');
-const blacklist = require('metro-config/src/defaults/blacklist');
-
-const rnPath = fs.realpathSync(
- path.resolve(require.resolve('react-native/package.json'), '..'),
-);
-const rnwPath = fs.realpathSync(
- path.resolve(require.resolve('react-native-windows/package.json'), '..'),
-);
+const blacklist = require('metro-config/src/defaults/exclusionList');
module.exports = {
resolver: {
- extraNodeModules: {
- // Redirect react-native to react-native-windows
- 'react-native': rnwPath,
- 'react-native-windows': rnwPath,
- },
- // Include the macos platform in addition to the defaults because the fork includes macos, but doesn't declare it
- platforms: ['ios', 'android', 'windesktop', 'windows', 'web', 'macos'],
- // Since there are multiple copies of react-native, we need to ensure that metro only sees one of them
- // This should go in RN 0.61 when haste is removed
blacklistRE: blacklist([
- new RegExp(
- `${(path.resolve(rnPath) + path.sep).replace(/[/\\]/g, '/')}.*`,
- ),
-
// This stops "react-native run-windows" from causing the metro server to crash if its already running
new RegExp(
`${path.resolve(__dirname, 'windows').replace(/[/\\]/g, '/')}.*`,
),
- // Prevent recursive node_modules from local react-native-video
- new RegExp(
- `${path.resolve(__dirname, 'node_modules/react-native-video/node_modules').replace(/[/\\]/g, '/')}.*`,
- ),
- // Prevent recursive examples from local react-native-video
- new RegExp(
- `${path.resolve(__dirname, 'node_modules/react-native-video/examples').replace(/[/\\]/g, '/')}.*`,
- ),
+ // This prevents "react-native run-windows" from hitting: EBUSY: resource busy or locked, open msbuild.ProjectImports.zip
+ /.*\.ProjectImports\.zip/,
+ /(.*\/react-native-video\/node_modules\/.*)$/,
]),
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
- inlineRequires: false,
+ inlineRequires: true,
},
}),
},
diff --git a/examples/basic/package.json b/examples/basic/package.json
index 0b6e62928e..07e1e890af 100644
--- a/examples/basic/package.json
+++ b/examples/basic/package.json
@@ -3,25 +3,34 @@
"version": "1.0.0",
"private": true,
"scripts": {
+ "android": "react-native run-android",
+ "ios": "react-native run-ios",
+ "windows": "react-native run-windows",
"start": "react-native start",
- "postinstall": "rm -rf node_modules/react-native-video/{examples,node_modules}",
"test": "jest",
"lint": "eslint ."
},
"dependencies": {
- "react": "^16.12.0",
- "react-native": "0.61.5",
- "react-native-windows": "^0.61.0-0",
- "react-native-video": "file:../.."
+ "@react-native-picker/picker": "^1.9.11",
+ "react": "18.1.0",
+ "react-native": "0.70.6",
+ "react-native-video": "../..",
+ "react-native-windows": "0.63.41"
},
"devDependencies": {
- "@babel/core": "^7.6.0",
- "@babel/runtime": "^7.6.0",
- "@react-native-community/eslint-config": "^0.0.5",
- "babel-jest": "^24.9.0",
- "eslint": "^6.4.0",
- "jest": "^24.9.0",
- "metro-react-native-babel-preset": "^0.56.0",
- "react-test-renderer": "16.8.6"
+ "@babel/core": "^7.12.9",
+ "@babel/runtime": "^7.12.5",
+ "@react-native-community/eslint-config": "^2.0.0",
+ "@types/react": "^18.0.6",
+ "@types/react-native": "^0.70.6",
+ "babel-plugin-module-resolver": "^4.1.0",
+ "babel-jest": "^26.6.3",
+ "eslint": "^7.32.0",
+ "jest": "^26.6.3",
+ "metro-react-native-babel-preset": "0.72.3",
+ "react-test-renderer": "18.1.0"
+ },
+ "jest": {
+ "preset": "react-native"
}
-}
\ No newline at end of file
+}
diff --git a/examples/basic/react-native.config.js b/examples/basic/react-native.config.js
deleted file mode 100644
index cedd9d380a..0000000000
--- a/examples/basic/react-native.config.js
+++ /dev/null
@@ -1,5 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-module.exports = {
- reactNativePath: fs.realpathSync(path.resolve(require.resolve('react-native-windows/package.json'), '..')),
-};
diff --git a/examples/basic/src/VideoPlayer.android.tsx b/examples/basic/src/VideoPlayer.android.tsx
new file mode 100644
index 0000000000..a3206a8476
--- /dev/null
+++ b/examples/basic/src/VideoPlayer.android.tsx
@@ -0,0 +1,893 @@
+'use strict';
+
+import React, {
+ Component
+} from 'react';
+
+import {
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+ ActivityIndicator,
+ PanResponder,
+ ToastAndroid,
+} from 'react-native';
+
+import { Picker } from '@react-native-picker/picker'
+
+import Video, { VideoDecoderProperties, TextTrackType } from 'react-native-video';
+
+class VideoPlayer extends Component {
+
+ state = {
+ rate: 1,
+ volume: 1,
+ muted: false,
+ resizeMode: 'contain',
+ duration: 0.0,
+ currentTime: 0.0,
+ videoWidth: 0,
+ videoHeight: 0,
+ paused: false,
+ fullscreen: true,
+ decoration: true,
+ isLoading: false,
+ seekerFillWidth: 0,
+ seekerPosition: 0,
+ seekerOffset: 0,
+ seeking: false,
+ audioTracks: [],
+ textTracks: [],
+ selectedAudioTrack: undefined,
+ selectedTextTrack: undefined,
+ srcListId: 0,
+ loop: false,
+ showRNVControls: false,
+ };
+
+ seekerWidth = 0
+
+ srcList = [
+ require('./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: '(hls|live) red bull tv',
+ uri: 'https://rbmn-live.akamaized.net/hls/live/590964/BoRB-AT/master_928.m3u8'
+ },
+ {
+ 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: 'invalid URL',
+ uri:
+ 'mmt://www.youtube.com',
+ type: 'mpd',
+ },
+ { description: '(no url) Stopped playback', uri: undefined },
+ {
+ description: '(no view) no View',
+ noView: true,
+ },
+ ]
+
+ video: Video;
+ seekPanResponder: PanResponder | undefined;
+
+ popupInfo = () => {
+ VideoDecoderProperties.getWidevineLevel().then((widevineLevel: number) => {
+ VideoDecoderProperties.isHEVCSupported().then((hevcSupported: boolean) => {
+ VideoDecoderProperties.isCodecSupported('video/avc', 1920, 1080).then(
+ (avcSupported: boolean) => {
+ this.toast(
+ true,
+ 'Widevine level: ' +
+ widevineLevel +
+ '\n hevc: ' +
+ (hevcSupported ? '' : 'NOT') +
+ 'supported' +
+ '\n avc: ' +
+ (avcSupported ? '' : 'NOT') +
+ 'supported',
+ )
+ },
+ )
+ })
+ })
+ }
+
+ onLoad = (data: any) => {
+ this.setState({ duration: data.duration, loading: false, });
+ this.onAudioTracks(data)
+ this.onTextTracks(data)
+ };
+
+ onProgress = (data: any) => {
+ if (!this.state.seeking) {
+ const position = this.calculateSeekerPosition()
+ this.setSeekerPosition(position)
+ }
+ this.setState({ currentTime: data.currentTime })
+ };
+
+
+ onVideoLoadStart = () => {
+ console.log('onVideoLoadStart')
+ this.setState({ isLoading: true })
+ }
+
+
+ onAudioTracks = (data: any) => {
+ const selectedTrack = data.audioTracks?.find((x: any) => {
+ return x.selected
+ })
+ this.setState({
+ audioTracks: data.audioTracks,
+ })
+ if (selectedTrack?.language) {
+ this.setState({
+ selectedAudioTrack: {
+ type: 'language',
+ value: selectedTrack?.language,
+ },
+ })
+
+ }
+ }
+
+ onTextTracks = (data: any) => {
+ const selectedTrack = data.textTracks?.find((x: any) => {
+ return x.selected
+ })
+
+ this.setState({
+ textTracks: data.textTracks,
+ })
+ if (selectedTrack?.language) {
+ this.setState({
+ textTracks: data,
+ selectedTextTrack: {
+ type: 'language',
+ value: selectedTrack?.language,
+ },
+ })
+ }
+ }
+
+ onAspectRatio = (data: any) => {
+ console.log('onAspectRadio called ' + JSON.stringify(data))
+ this.setState({
+ videoWidth: data.width,
+ videoHeight: data.height,
+ })
+ }
+
+ onVideoBuffer = (param: any) => {
+ console.log('onVideoBuffer')
+
+ this.setState({ isLoading: param.isBuffering })
+ }
+
+
+ onReadyForDisplay = () => {
+ console.log('onReadyForDisplay')
+
+ this.setState({ isLoading: false })
+ }
+
+
+ onAudioBecomingNoisy = () => {
+ this.setState({ paused: true })
+ };
+
+ onAudioFocusChanged = (event: { hasAudioFocus: boolean }) => {
+ this.setState({ paused: !event.hasAudioFocus })
+ };
+
+ getCurrentTimePercentage = () => {
+ if (this.state.currentTime > 0 && this.state.duration !== 0) {
+ return this.state.currentTime / this.state.duration;
+ }
+ return 0;
+ };
+
+ renderRateControl(rate: number) {
+ const isSelected = (this.state.rate === rate);
+
+ return (
+ { this.setState({ rate }) }}>
+
+ {rate}
+
+
+ );
+ }
+
+ renderResizeModeControl(resizeMode: string) {
+ const isSelected = (this.state.resizeMode === resizeMode);
+
+ return (
+ { this.setState({ resizeMode }) }}>
+
+ {resizeMode}
+
+
+ )
+ }
+
+ renderVolumeControl(volume: number) {
+ const isSelected = (this.state.volume === volume);
+
+ return (
+ { this.setState({ volume }) }}>
+
+ {volume * 100}%
+
+
+ )
+ }
+
+
+ toast = (visible: boolean, message: string) => {
+ if (visible) {
+ ToastAndroid.showWithGravityAndOffset(
+ message,
+ ToastAndroid.LONG,
+ ToastAndroid.BOTTOM,
+ 25,
+ 50,
+ )
+ return null
+ }
+ return null
+ }
+
+ onError = (err: any) => {
+ console.log(JSON.stringify(err?.error.errorCode))
+ this.toast(true, 'error: ' + err?.error.errorCode)
+ }
+
+ onEnd = () => {
+ this.channelUp()
+ };
+
+
+ toggleFullscreen() {
+ this.setState({ fullscreen: !this.state.fullscreen })
+ }
+ toggleControls() {
+ this.setState({ showRNVControls: !this.state.showRNVControls })
+ }
+
+ toggleDecoration() {
+ this.setState({ decoration: !this.state.decoration })
+ if (this.state.decoration) {
+ this.video.dismissFullscreenPlayer()
+ } else {
+ this.video.presentFullscreenPlayer()
+ }
+ }
+
+ goToChannel(channel: any) {
+ this.setState({
+ srcListId: channel,
+ duration: 0.0,
+ currentTime: 0.0,
+ videoWidth: 0,
+ videoHeight: 0,
+ isLoading: false,
+ audioTracks: [],
+ textTracks: [],
+ selectedAudioTrack: undefined,
+ selectedTextTrack: undefined,
+ })
+ }
+
+
+ channelUp() {
+ console.log('channel up')
+ this.goToChannel((this.state.srcListId + 1) % this.srcList.length)
+ }
+
+ channelDown() {
+ console.log('channel down')
+ this.goToChannel((this.state.srcListId + this.srcList.length - 1) % this.srcList.length)
+ }
+
+ componentDidMount() {
+ this.initSeekPanResponder()
+ }
+
+ renderDecorationsControl() {
+ return (
+ {
+ this.toggleDecoration()
+ }}
+ >
+ {'decoration'}
+
+ )
+ }
+
+ renderInfoControl() {
+ return (
+ {
+ this.popupInfo()
+ }}
+ >
+ {'decoderInfo'}
+
+ )
+ }
+
+ renderFullScreenControl() {
+ return (
+ {
+ this.toggleFullscreen()
+ }}
+ >
+ {'fullscreen'}
+
+ )
+ }
+
+ renderPause() {
+ return (
+ {
+ this.setState({ paused: !this.state.paused })
+ }}
+ >
+
+ {this.state.paused ? 'pause' : 'playing'}
+
+
+ )
+ }
+
+ renderRepeatModeControl() {
+ return (
+ {
+ this.setState({ loop: !this.state.loop })
+ }}
+ >
+
+ {this.state.loop ? 'loop enable' : 'loop disable'}
+
+
+ )
+ }
+
+ renderLeftControl() {
+ return (
+
+ {
+ this.channelDown()
+ }}
+ >
+ {'ChDown'}
+
+
+ // onTimelineUpdated
+ )
+ }
+
+ renderRightControl() {
+ return (
+
+ {
+ this.channelUp()
+ }}
+ >
+ {'ChUp'}
+
+
+ )
+ }
+
+ /**
+ * Render the seekbar and attach its handlers
+ */
+
+ /**
+ * Constrain the location of the seeker to the
+ * min/max value based on how big the
+ * seeker is.
+ *
+ * @param {float} val position of seeker handle in px
+ * @return {float} constrained position of seeker handle in px
+ */
+ constrainToSeekerMinMax(val = 0) {
+ if (val <= 0) {
+ return 0
+ } else if (val >= this.seekerWidth) {
+ return this.seekerWidth
+ }
+ return val
+ }
+
+ /**
+ * 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}
+ */
+ setSeekerPosition(position = 0) {
+ const state = this.state
+ position = this.constrainToSeekerMinMax(position)
+
+ state.seekerFillWidth = position
+ state.seekerPosition = position
+
+ if (!state.seeking) {
+ state.seekerOffset = position
+ }
+
+ this.setState(state)
+ }
+
+ /**
+ * Calculate the position that the seeker should be
+ * at along its track.
+ *
+ * @return {float} position of seeker handle in px based on currentTime
+ */
+ calculateSeekerPosition() {
+ const percent = this.state.currentTime / this.state.duration
+ return this.seekerWidth * percent
+ }
+
+ /**
+ * Return the time that the video should be at
+ * based on where the seeker handle is.
+ *
+ * @return {float} time in ms based on seekerPosition.
+ */
+ calculateTimeFromSeekerPosition() {
+ const percent = this.state.seekerPosition / this.seekerWidth
+ return this.state.duration * percent
+ }
+
+ /**
+ * Get our seekbar responder going
+ */
+ initSeekPanResponder() {
+ this.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 state = this.state
+ // this.clearControlTimeout()
+ const position = evt.nativeEvent.locationX
+ this.setSeekerPosition(position)
+ state.seeking = true
+ this.setState(state)
+ },
+
+ /**
+ * When panning, update the seekbar position, duh.
+ */
+ onPanResponderMove: (evt, gestureState) => {
+ const position = this.state.seekerOffset + gestureState.dx
+ this.setSeekerPosition(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 = this.calculateTimeFromSeekerPosition()
+ const state = this.state
+ if (time >= state.duration && !state.isLoading) {
+ state.paused = true
+ this.onEnd()
+ } else {
+ this.video?.seek(time)
+ state.seeking = false
+ }
+ this.setState(state)
+ },
+ })
+ }
+
+ renderSeekBar() {
+ if (!this.seekPanResponder) {
+ return null
+ }
+ return (
+
+ (this.seekerWidth = event.nativeEvent.layout.width)}
+ pointerEvents={'none'}
+ >
+ 0 ? this.state.seekerFillWidth : 0,
+ backgroundColor: '#FFF',
+ },
+ ]}
+ pointerEvents={'none'}
+ />
+
+ 0 ? this.state.seekerPosition : 0 },
+ ]}
+ pointerEvents={'none'}
+ >
+
+
+
+ )
+ }
+
+ IndicatorLoadingView() {
+ if (this.state.isLoading)
+ return
+ else return
+ }
+
+ renderTopControl() {
+ return (<>
+
+ {this.srcList[this.state.srcListId]?.description || 'local file'}
+
+
+ {
+ this.toggleControls()
+ }}
+ >
+ {this.state.showRNVControls ? 'Hide controls' : 'Show controls'}
+
+
+ >)
+ }
+
+
+ renderOverlay() {
+ return (
+ <>
+ {this.IndicatorLoadingView()}
+
+ {this.renderTopControl()}
+
+ {!this.state.showRNVControls ? (
+ <>
+
+ {this.renderLeftControl()}
+
+ {this.renderRightControl()}
+
+
+
+ {this.renderInfoControl()}
+
+ {this.renderPause()}
+
+ {this.renderRepeatModeControl()}
+
+
+ {this.renderFullScreenControl()}
+
+
+ {this.renderDecorationsControl()}
+
+
+
+
+ {this.renderRateControl(0.25)}
+ {this.renderRateControl(0.5)}
+ {this.renderRateControl(1.0)}
+ {this.renderRateControl(1.5)}
+ {this.renderRateControl(2.0)}
+
+
+
+ {this.renderVolumeControl(0.5)}
+ {this.renderVolumeControl(1)}
+ {this.renderVolumeControl(1.5)}
+
+
+
+ {this.renderResizeModeControl('cover')}
+ {this.renderResizeModeControl('contain')}
+ {this.renderResizeModeControl('stretch')}
+
+
+ {this.renderSeekBar()}
+
+ AudioTrack
+ {this.state.audioTracks?.length <= 0 ? (
+ empty
+ ) : (
+ {
+ console.log('on audio value change ' + itemValue);
+ this.setState({
+ selectedAudioTrack: {
+ type: 'language',
+ value: itemValue,
+ },
+ });
+ }}
+ >
+ {this.state.audioTracks.map((track) => {
+ return (
+
+ );
+ })}
+
+ )}
+ TextTrack
+ {this.state.textTracks?.length <= 0 ? (
+ empty
+ ) : (
+ {
+ console.log('on value change ' + itemValue);
+ this.setState({
+ selectedTextTrack: {
+ type: 'language',
+ value: itemValue,
+ },
+ });
+ }}
+ >
+
+ {this.state.textTracks.map((track) => (
+
+ ))}
+
+ )}
+
+ >
+ ) : null
+ }
+ >
+ )
+ }
+
+ renderVideoView() {
+ const viewStyle = this.state.fullscreen ? styles.fullScreen : styles.halfScreen
+
+ return (
+
+ {
+ this.video = ref
+ }}
+ source={this.srcList[this.state.srcListId]}
+ style={viewStyle}
+ rate={this.state.rate}
+ paused={this.state.paused}
+ volume={this.state.volume}
+ muted={this.state.muted}
+ fullscreen={this.state.fullscreen}
+ controls={this.state.showRNVControls}
+ resizeMode={this.state.resizeMode}
+ onLoad={this.onLoad}
+ onAudioTracks={this.onAudioTracks}
+ onTextTracks={this.onTextTracks}
+ onProgress={this.onProgress}
+ onEnd={this.onEnd}
+ progressUpdateInterval={1000}
+ onError={this.onError}
+ onAudioBecomingNoisy={this.onAudioBecomingNoisy}
+ onAudioFocusChanged={this.onAudioFocusChanged}
+ onLoadStart={this.onVideoLoadStart}
+ onVideoAspectRatio={this.onAspectRatio}
+ onReadyForDisplay={this.onReadyForDisplay}
+ onBuffer={this.onVideoBuffer}
+ repeat={this.state.loop}
+ selectedTextTrack={this.state.selectedTextTrack}
+ selectedAudioTrack={this.state.selectedAudioTrack}
+ playInBackground={false}
+ />
+
+ )
+ }
+
+ render() {
+ return (
+
+ {this.srcList[this.state.srcListId]?.noView ? null : this.renderVideoView()}
+ {this.renderOverlay()}
+
+ )
+ }
+
+}
+
+
+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,
+ },
+ IndicatorStyle: {
+ flex: 1,
+ justifyContent: 'center',
+ },
+ 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: {
+ color: 'white',
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'center',
+ },
+});
+
+export default VideoPlayer
\ No newline at end of file
diff --git a/examples/basic/index.ios.js b/examples/basic/src/VideoPlayer.ios.tsx
similarity index 94%
rename from examples/basic/index.ios.js
rename to examples/basic/src/VideoPlayer.ios.tsx
index c4c21c3ddb..740251047a 100644
--- a/examples/basic/index.ios.js
+++ b/examples/basic/src/VideoPlayer.ios.tsx
@@ -4,8 +4,7 @@ import React, {
} from 'react';
import {
- AlertIOS,
- AppRegistry,
+ Alert,
Platform,
StyleSheet,
Text,
@@ -35,7 +34,7 @@ const filterTypes = [
];
class VideoPlayer extends Component {
- constructor(props) {
+ constructor(props: any) {
super(props);
this.onLoad = this.onLoad.bind(this);
this.onProgress = this.onProgress.bind(this);
@@ -58,12 +57,12 @@ class VideoPlayer extends Component {
filterEnabled: true
};
- onLoad(data) {
+ onLoad(data: any) {
console.log('On load fired!');
this.setState({duration: data.duration});
}
- onProgress(data) {
+ onProgress(data: any) {
this.setState({currentTime: data.currentTime});
}
@@ -72,14 +71,14 @@ class VideoPlayer extends Component {
}
getCurrentTimePercentage() {
- if (this.state.currentTime > 0) {
- return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
+ if (this.state.currentTime > 0 && this.state.duration !== 0) {
+ return this.state.currentTime / this.state.duration;
} else {
return 0;
}
}
- setFilter(step) {
+ setFilter(step: number) {
let index = filterTypes.indexOf(this.state.filter) + step;
if (index === filterTypes.length) {
@@ -108,7 +107,7 @@ class VideoPlayer extends Component {
);
}
- renderRateControl(rate) {
+ renderRateControl(rate: number) {
const isSelected = (this.state.rate == rate);
return (
@@ -120,7 +119,7 @@ class VideoPlayer extends Component {
)
}
- renderResizeModeControl(resizeMode) {
+ renderResizeModeControl(resizeMode: string) {
const isSelected = (this.state.resizeMode == resizeMode);
return (
@@ -132,7 +131,7 @@ class VideoPlayer extends Component {
)
}
- renderVolumeControl(volume) {
+ renderVolumeControl(volume: number) {
const isSelected = (this.state.volume == volume);
return (
@@ -144,7 +143,7 @@ class VideoPlayer extends Component {
)
}
- renderIgnoreSilentSwitchControl(ignoreSilentSwitch) {
+ renderIgnoreSilentSwitchControl(ignoreSilentSwitch: string) {
const isSelected = (this.state.ignoreSilentSwitch == ignoreSilentSwitch);
return (
@@ -156,7 +155,7 @@ class VideoPlayer extends Component {
)
}
- renderMixWithOthersControl(mixWithOthers) {
+ renderMixWithOthersControl(mixWithOthers: string) {
const isSelected = (this.state.mixWithOthers == mixWithOthers);
return (
@@ -188,7 +187,7 @@ class VideoPlayer extends Component {
onLoad={this.onLoad}
onBuffer={this.onBuffer}
onProgress={this.onProgress}
- onEnd={() => { AlertIOS.alert('Done!') }}
+ onEnd={() => { Alert.alert('Done!') }}
repeat={true}
filter={this.state.filter}
filterEnabled={this.state.filterEnabled}
@@ -282,7 +281,7 @@ class VideoPlayer extends Component {
onLoad={this.onLoad}
onBuffer={this.onBuffer}
onProgress={this.onProgress}
- onEnd={() => { AlertIOS.alert('Done!') }}
+ onEnd={() => { Alert.alert('Done!') }}
repeat={true}
controls={this.state.controls}
filter={this.state.filter}
@@ -442,8 +441,13 @@ const styles = StyleSheet.create({
},
nativeVideoControls: {
top: 184,
- height: 300
- }
+ height: 300,
+ },
+ trackingControls: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
});
-
-AppRegistry.registerComponent('VideoPlayer', () => VideoPlayer);
+export default VideoPlayer
\ No newline at end of file
diff --git a/examples/basic/index.windows.js b/examples/basic/src/VideoPlayer.windows.tsx
similarity index 90%
rename from examples/basic/index.windows.js
rename to examples/basic/src/VideoPlayer.windows.tsx
index a3d21c6faa..217fcc6e2c 100644
--- a/examples/basic/index.windows.js
+++ b/examples/basic/src/VideoPlayer.windows.tsx
@@ -5,7 +5,6 @@ import React, {
} from 'react';
import {
- AppRegistry,
StyleSheet,
Text,
TouchableOpacity,
@@ -15,7 +14,7 @@ import {
import Video from 'react-native-video';
class VideoPlayer extends Component {
- constructor(props) {
+ constructor(props: any) {
super(props);
this.onLoad = this.onLoad.bind(this);
this.onProgress = this.onProgress.bind(this);
@@ -28,25 +27,26 @@ class VideoPlayer extends Component {
resizeMode: 'contain',
duration: 0.0,
currentTime: 0.0,
+ paused: 0,
};
- onLoad(data) {
+ onLoad(data: any) {
this.setState({duration: data.duration});
}
- onProgress(data) {
+ onProgress(data: any) {
this.setState({currentTime: data.currentTime});
}
getCurrentTimePercentage() {
- if (this.state.currentTime > 0) {
- return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
+ if (this.state.currentTime > 0 && this.state.duration !== 0) {
+ return this.state.currentTime / this.state.duration;
} else {
return 0;
}
}
- renderRateControl(rate) {
+ renderRateControl(rate: number) {
const isSelected = (this.state.rate == rate);
return (
@@ -58,7 +58,7 @@ class VideoPlayer extends Component {
)
}
- renderResizeModeControl(resizeMode) {
+ renderResizeModeControl(resizeMode: string) {
const isSelected = (this.state.resizeMode == resizeMode);
return (
@@ -70,7 +70,7 @@ class VideoPlayer extends Component {
)
}
- renderVolumeControl(volume) {
+ renderVolumeControl(volume: number) {
const isSelected = (this.state.volume == volume);
return (
@@ -205,6 +205,11 @@ const styles = StyleSheet.create({
paddingRight: 2,
lineHeight: 12,
},
+ trackingControls: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
});
-
-AppRegistry.registerComponent('VideoPlayer', () => VideoPlayer);
+export default VideoPlayer
\ No newline at end of file
diff --git a/examples/basic/broadchurch.mp4 b/examples/basic/src/broadchurch.mp4
similarity index 100%
rename from examples/basic/broadchurch.mp4
rename to examples/basic/src/broadchurch.mp4
diff --git a/examples/basic/src/index.js b/examples/basic/src/index.js
new file mode 100644
index 0000000000..add8ab1640
--- /dev/null
+++ b/examples/basic/src/index.js
@@ -0,0 +1,5 @@
+import {AppRegistry} from 'react-native';
+import VideoPlayer from './VideoPlayer';
+import {name as appName} from '../app.json';
+
+AppRegistry.registerComponent(appName, () => VideoPlayer);
diff --git a/examples/basic/tsconfig.json b/examples/basic/tsconfig.json
new file mode 100644
index 0000000000..a415e49cc6
--- /dev/null
+++ b/examples/basic/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "allowSyntheticDefaultImports": true,
+ "esModuleInterop": true,
+ "isolatedModules": true,
+ "jsx": "react-native",
+ "lib": ["es2017"],
+ "moduleResolution": "node",
+ "noEmit": true,
+ "strict": true,
+ "target": "esnext"
+ },
+ "exclude": [
+ "node_modules",
+ "babel.config.js",
+ "metro.config.js",
+ "jest.config.js"
+ ]
+}
\ No newline at end of file
diff --git a/examples/basic/windows/VideoPlayer.sln b/examples/basic/windows/VideoPlayer.sln
index c3c7265be4..9c6c57cd15 100644
--- a/examples/basic/windows/VideoPlayer.sln
+++ b/examples/basic/windows/VideoPlayer.sln
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29215.179
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoPlayer", "VideoPlayer\VideoPlayer.vcxproj", "{ADF1CF02-8224-4167-A737-8CBE1A0D5208}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "videoplayer", "videoplayer\videoplayer.vcxproj", "{BF9FF035-B976-4990-86FE-09D015E91C20}"
ProjectSection(ProjectDependencies) = postProject
{F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {F7D32BD0-2749-483E-9A0D-1635EF7E3136}
EndProjectSection
@@ -15,11 +15,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactCommon", "..\node_modu
{A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {A990658C-CE31-4BCC-976F-0FC6B1AF693D}
EndProjectSection
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactWindowsCore", "..\node_modules\react-native-windows\ReactWindowsCore\ReactWindowsCore.vcxproj", "{11C084A3-A57C-4296-A679-CAC17B603144}"
- ProjectSection(ProjectDependencies) = postProject
- {A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {A990658C-CE31-4BCC-976F-0FC6B1AF693D}
- EndProjectSection
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Chakra", "..\node_modules\react-native-windows\Chakra\Chakra.vcxitems", "{C38970C0-5FBF-4D69-90D8-CBAC225AE895}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative", "..\node_modules\react-native-windows\Microsoft.ReactNative\Microsoft.ReactNative.vcxproj", "{F7D32BD0-2749-483E-9A0D-1635EF7E3136}"
@@ -30,31 +25,29 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JSI.Universal", "..\node_mo
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx", "..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems", "{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B}"
EndProject
-Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.ReactNative.SharedManaged", "..\node_modules\react-native-windows\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.shproj", "{67A1076F-7790-4203-86EA-4402CCB5E782}"
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\node_modules\react-native-windows\Common\Common.vcxproj", "{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReactNative", "ReactNative", "{5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shared", "..\node_modules\react-native-windows\Shared\Shared.vcxitems", "{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Shared", "..\node_modules\react-native-windows\Shared\Shared.vcxitems", "{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mso", "..\node_modules\react-native-windows\Mso\Mso.vcxitems", "{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactNativeVideoCPP61", "..\node_modules\react-native-video\windows\ReactNativeVideoCPP61\ReactNativeVideoCPP61.vcxproj", "{765365E4-9553-4900-9F69-E26D4309C8DA}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Include", "..\node_modules\react-native-windows\include\Include.vcxitems", "{EF074BA1-2D54-4D49-A28E-5E040B47CD2E}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactNativeVideoCPP", "..\node_modules\react-native-video\windows\ReactNativeVideoCPP\ReactNativeVideoCPP.vcxproj", "{0D1E54D3-4BE1-4DAF-98BF-124C28C85014}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{0cc28589-39e4-4288-b162-97b959f8b843}*SharedItemsImports = 9
..\node_modules\react-native-windows\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9
- ..\node_modules\react-native-windows\Microsoft.ReactNative.SharedManaged\Microsoft.ReactNative.SharedManaged.projitems*{67a1076f-7790-4203-86ea-4402ccb5e782}*SharedItemsImports = 13
- ..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{765365e4-9553-4900-9f69-e26d4309c8da}*SharedItemsImports = 4
..\node_modules\react-native-windows\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9
..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4
- ..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{adf1cf02-8224-4167-a737-8cbe1a0d5208}*SharedItemsImports = 4
..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{c38970c0-5fbf-4d69-90d8-cbac225ae895}*SharedItemsImports = 9
..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9
+ ..\node_modules\react-native-windows\include\Include.vcxitems*{ef074ba1-2d54-4d49-a28e-5e040b47cd2e}*SharedItemsImports = 9
..\node_modules\react-native-windows\Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
- ..\node_modules\react-native-windows\JSI\Shared\JSI.Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
+ ..\node_modules\react-native-windows\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
..\node_modules\react-native-windows\Mso\Mso.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
..\node_modules\react-native-windows\Shared\Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4
EndGlobalSection
@@ -69,30 +62,30 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|ARM.ActiveCfg = Debug|ARM
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|ARM.Build.0 = Debug|ARM
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|ARM.Deploy.0 = Debug|ARM
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|ARM64.ActiveCfg = Debug|ARM64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|ARM64.Build.0 = Debug|ARM64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|ARM64.Deploy.0 = Debug|ARM64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x64.ActiveCfg = Debug|x64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x64.Build.0 = Debug|x64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x64.Deploy.0 = Debug|x64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x86.ActiveCfg = Debug|Win32
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x86.Build.0 = Debug|Win32
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Debug|x86.Deploy.0 = Debug|Win32
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|ARM.ActiveCfg = Release|ARM
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|ARM.Build.0 = Release|ARM
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|ARM.Deploy.0 = Release|ARM
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|ARM64.ActiveCfg = Release|ARM64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|ARM64.Build.0 = Release|ARM64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|ARM64.Deploy.0 = Release|ARM64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x64.ActiveCfg = Release|x64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x64.Build.0 = Release|x64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x64.Deploy.0 = Release|x64
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x86.ActiveCfg = Release|Win32
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x86.Build.0 = Release|Win32
- {ADF1CF02-8224-4167-A737-8CBE1A0D5208}.Release|x86.Deploy.0 = Release|Win32
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM.ActiveCfg = Debug|ARM
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM.Build.0 = Debug|ARM
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM.Deploy.0 = Debug|ARM
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM64.Build.0 = Debug|ARM64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x64.ActiveCfg = Debug|x64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x64.Build.0 = Debug|x64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x64.Deploy.0 = Debug|x64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x86.ActiveCfg = Debug|Win32
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x86.Build.0 = Debug|Win32
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Debug|x86.Deploy.0 = Debug|Win32
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM.ActiveCfg = Release|ARM
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM.Build.0 = Release|ARM
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM.Deploy.0 = Release|ARM
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM64.ActiveCfg = Release|ARM64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM64.Build.0 = Release|ARM64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|ARM64.Deploy.0 = Release|ARM64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x64.ActiveCfg = Release|x64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x64.Build.0 = Release|x64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x64.Deploy.0 = Release|x64
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x86.ActiveCfg = Release|Win32
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x86.Build.0 = Release|Win32
+ {BF9FF035-B976-4990-86FE-09D015E91C20}.Release|x86.Deploy.0 = Release|Win32
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.ActiveCfg = Debug|ARM
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM.Build.0 = Debug|ARM
{A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.ActiveCfg = Debug|ARM64
@@ -125,22 +118,6 @@ Global
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x64.Build.0 = Release|x64
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.ActiveCfg = Release|Win32
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Release|x86.Build.0 = Release|Win32
- {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.ActiveCfg = Debug|ARM
- {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM.Build.0 = Debug|ARM
- {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.ActiveCfg = Debug|ARM64
- {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|ARM64.Build.0 = Debug|ARM64
- {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.ActiveCfg = Debug|x64
- {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x64.Build.0 = Debug|x64
- {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.ActiveCfg = Debug|Win32
- {11C084A3-A57C-4296-A679-CAC17B603144}.Debug|x86.Build.0 = Debug|Win32
- {11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.ActiveCfg = Release|ARM
- {11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM.Build.0 = Release|ARM
- {11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.ActiveCfg = Release|ARM64
- {11C084A3-A57C-4296-A679-CAC17B603144}.Release|ARM64.Build.0 = Release|ARM64
- {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.ActiveCfg = Release|x64
- {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x64.Build.0 = Release|x64
- {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.ActiveCfg = Release|Win32
- {11C084A3-A57C-4296-A679-CAC17B603144}.Release|x86.Build.0 = Release|Win32
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.ActiveCfg = Debug|ARM
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM.Build.0 = Debug|ARM
{F7D32BD0-2749-483E-9A0D-1635EF7E3136}.Debug|ARM64.ActiveCfg = Debug|ARM64
@@ -189,22 +166,22 @@ Global
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.Build.0 = Release|x64
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.ActiveCfg = Release|Win32
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.Build.0 = Release|Win32
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|ARM.ActiveCfg = Debug|ARM
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|ARM.Build.0 = Debug|ARM
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|ARM64.ActiveCfg = Debug|ARM64
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|ARM64.Build.0 = Debug|ARM64
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|x64.ActiveCfg = Debug|x64
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|x64.Build.0 = Debug|x64
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|x86.ActiveCfg = Debug|Win32
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Debug|x86.Build.0 = Debug|Win32
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|ARM.ActiveCfg = Release|ARM
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|ARM.Build.0 = Release|ARM
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|ARM64.ActiveCfg = Release|ARM64
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|ARM64.Build.0 = Release|ARM64
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|x64.ActiveCfg = Release|x64
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|x64.Build.0 = Release|x64
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|x86.ActiveCfg = Release|Win32
- {765365E4-9553-4900-9F69-E26D4309C8DA}.Release|x86.Build.0 = Release|Win32
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|ARM.ActiveCfg = Debug|ARM
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|ARM.Build.0 = Debug|ARM
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|ARM64.Build.0 = Debug|ARM64
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|x64.ActiveCfg = Debug|x64
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|x64.Build.0 = Debug|x64
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|x86.ActiveCfg = Debug|Win32
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Debug|x86.Build.0 = Debug|Win32
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|ARM.ActiveCfg = Release|ARM
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|ARM.Build.0 = Release|ARM
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|ARM64.ActiveCfg = Release|ARM64
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|ARM64.Build.0 = Release|ARM64
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|x64.ActiveCfg = Release|x64
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|x64.Build.0 = Release|x64
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|x86.ActiveCfg = Release|Win32
+ {0D1E54D3-4BE1-4DAF-98BF-124C28C85014}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -212,16 +189,15 @@ Global
GlobalSection(NestedProjects) = preSolution
{A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
- {11C084A3-A57C-4296-A679-CAC17B603144} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{C38970C0-5FBF-4D69-90D8-CBAC225AE895} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{0CC28589-39E4-4288-B162-97B959F8B843} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{A62D504A-16B8-41D2-9F19-E2E86019E5E4} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
- {67A1076F-7790-4203-86EA-4402CCB5E782} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{FCA38F3C-7C73-4C47-BE4E-32F77FA8538D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{2049DBE9-8D13-42C9-AE4B-413AE38FFFD0} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
+ {EF074BA1-2D54-4D49-A28E-5E040B47CD2E} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D43FAD39-F619-437D-BB40-04A3982ACB6A}
diff --git a/examples/basic/windows/VideoPlayer/.gitignore b/examples/basic/windows/VideoPlayer/.gitignore
new file mode 100644
index 0000000000..917243bd7a
--- /dev/null
+++ b/examples/basic/windows/VideoPlayer/.gitignore
@@ -0,0 +1 @@
+/Bundle
diff --git a/examples/basic/windows/VideoPlayer/App.cpp b/examples/basic/windows/VideoPlayer/App.cpp
index 9bc4bf46c2..2379674eb3 100644
--- a/examples/basic/windows/VideoPlayer/App.cpp
+++ b/examples/basic/windows/VideoPlayer/App.cpp
@@ -1,50 +1,79 @@
-#include "pch.h"
-
-#include "App.h"
-#include "ReactPackageProvider.h"
-
-#include "winrt/ReactNativeVideoCPP.h"
-
-using namespace winrt::VideoPlayer;
-using namespace winrt::VideoPlayer::implementation;
-
-///
-/// Initializes the singleton application object. This is the first line of
-/// authored code executed, and as such is the logical equivalent of main() or
-/// WinMain().
-///
-App::App() noexcept
-{
- MainComponentName(L"VideoPlayer");
-
-#if BUNDLE
- JavaScriptBundleFile(L"index.windows");
- InstanceSettings().UseWebDebugger(false);
- InstanceSettings().UseFastRefresh(false);
-#else
- JavaScriptMainModuleName(L"index");
- InstanceSettings().UseWebDebugger(true);
- InstanceSettings().UseFastRefresh(true);
-#endif
-
-#if _DEBUG
- InstanceSettings().EnableDeveloperMenu(true);
-#else
- InstanceSettings().EnableDeveloperMenu(false);
-#endif
-
- PackageProviders().Append(make()); // Includes all modules in this project
-
- PackageProviders().Append(winrt::ReactNativeVideoCPP::ReactPackageProvider());
-
- REACT_REGISTER_NATIVE_MODULE_PACKAGES(); //code-gen macro from autolink
-
- InitializeComponent();
-
- // This works around a cpp/winrt bug with composable/aggregable types tracked
- // by 22116519
- AddRef();
- m_inner.as<::IUnknown>()->Release();
-}
-
-
+#include "pch.h"
+
+#include "App.h"
+
+#include "AutolinkedNativeModules.g.h"
+#include "ReactPackageProvider.h"
+
+using namespace winrt::videoplayer;
+using namespace winrt::videoplayer::implementation;
+using namespace winrt;
+using namespace Windows::UI::Xaml;
+using namespace Windows::UI::Xaml::Controls;
+using namespace Windows::UI::Xaml::Navigation;
+using namespace Windows::ApplicationModel;
+
+///
+/// Initializes the singleton application object. This is the first line of
+/// authored code executed, and as such is the logical equivalent of main() or
+/// WinMain().
+///
+App::App() noexcept
+{
+#if BUNDLE
+ JavaScriptBundleFile(L"src/index.windows");
+ InstanceSettings().UseWebDebugger(false);
+ InstanceSettings().UseFastRefresh(false);
+#else
+ JavaScriptMainModuleName(L"src/index");
+ InstanceSettings().UseWebDebugger(true);
+ InstanceSettings().UseFastRefresh(true);
+#endif
+
+#if _DEBUG
+ InstanceSettings().UseDeveloperSupport(true);
+#else
+ InstanceSettings().UseDeveloperSupport(false);
+#endif
+
+ RegisterAutolinkedNativeModulePackages(PackageProviders()); // Includes any autolinked modules
+
+ PackageProviders().Append(make()); // Includes all modules in this project
+
+ InitializeComponent();
+}
+
+///
+/// Invoked when the application is launched normally by the end user. Other entry points
+/// will be used such as when the application is launched to open a specific file.
+///
+/// Details about the launch request and process.
+void App::OnLaunched(activation::LaunchActivatedEventArgs const& e)
+{
+ super::OnLaunched(e);
+
+ Frame rootFrame = Window::Current().Content().as ();
+ rootFrame.Navigate(xaml_typename(), box_value(e.Arguments()));
+}
+
+///
+/// Invoked when application execution is being suspended. Application state is saved
+/// without knowing whether the application will be terminated or resumed with the contents
+/// of memory still intact.
+///
+/// The source of the suspend request.
+/// Details about the suspend request.
+void App::OnSuspending([[maybe_unused]] IInspectable const& sender, [[maybe_unused]] SuspendingEventArgs const& e)
+{
+ // Save application state and stop any background activity
+}
+
+///
+/// Invoked when Navigation to a certain page fails
+///
+/// The Frame which failed navigation
+/// Details about the navigation failure
+void App::OnNavigationFailed(IInspectable const&, NavigationFailedEventArgs const& e)
+{
+ throw hresult_error(E_FAIL, hstring(L"Failed to load Page ") + e.SourcePageType().Name);
+}
diff --git a/examples/basic/windows/VideoPlayer/App.h b/examples/basic/windows/VideoPlayer/App.h
index f2dc98d381..16a5339336 100644
--- a/examples/basic/windows/VideoPlayer/App.h
+++ b/examples/basic/windows/VideoPlayer/App.h
@@ -1,15 +1,18 @@
-#pragma once
-
-#include "App.xaml.g.h"
-
-
-
-namespace winrt::VideoPlayer::implementation
-{
- struct App : AppT
- {
- App() noexcept;
- };
-} // namespace winrt::VideoPlayer::implementation
-
-
+#pragma once
+
+#include "App.xaml.g.h"
+
+namespace activation = winrt::Windows::ApplicationModel::Activation;
+
+namespace winrt::videoplayer::implementation
+{
+ struct App : AppT
+ {
+ App() noexcept;
+ void OnLaunched(activation::LaunchActivatedEventArgs const&);
+ void OnSuspending(IInspectable const&, Windows::ApplicationModel::SuspendingEventArgs const&);
+ void OnNavigationFailed(IInspectable const&, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs const&);
+ private:
+ using super = AppT;
+ };
+} // namespace winrt::videoplayer::implementation
diff --git a/examples/basic/windows/VideoPlayer/App.idl b/examples/basic/windows/VideoPlayer/App.idl
index fca0646c39..e327b433ee 100644
--- a/examples/basic/windows/VideoPlayer/App.idl
+++ b/examples/basic/windows/VideoPlayer/App.idl
@@ -1,3 +1,3 @@
-namespace VideoPlayer
-{
-}
+namespace videoplayer
+{
+}
diff --git a/examples/basic/windows/VideoPlayer/App.xaml b/examples/basic/windows/VideoPlayer/App.xaml
index 1cf8c5422b..f1d79c8ce8 100644
--- a/examples/basic/windows/VideoPlayer/App.xaml
+++ b/examples/basic/windows/VideoPlayer/App.xaml
@@ -1,8 +1,8 @@
diff --git a/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.cpp b/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.cpp
new file mode 100644
index 0000000000..440fd52f74
--- /dev/null
+++ b/examples/basic/windows/VideoPlayer/AutolinkedNativeModules.g.cpp
@@ -0,0 +1,18 @@
+// AutolinkedNativeModules.g.cpp contents generated by "react-native autolink-windows"
+// clang-format off
+#include "pch.h"
+#include "AutolinkedNativeModules.g.h"
+
+// Includes from react-native-video
+#include
+
+namespace winrt::Microsoft::ReactNative
+{
+
+void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector