diff --git a/.github/workflows/pr_ios.yml b/.github/workflows/pr_ios.yml index 8912f231..e4754129 100644 --- a/.github/workflows/pr_ios.yml +++ b/.github/workflows/pr_ios.yml @@ -69,7 +69,7 @@ jobs: - name: Run pod install & update dependencies working-directory: apps/e2e/ios run: | - RCT_NEW_ARCH_ENABLED=${{ matrix.newArchEnabled }} bundle exec pod install + RCT_NEW_ARCH_ENABLED=${{ matrix.newArchEnabled }} bundle exec pod update - name: Start iOS simulator uses: futureware-tech/simulator-action@v4 diff --git a/README.md b/README.md index 6f10153a..9d6722b1 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,10 @@ the official THEOplayer React Native video player. | Content protection (DRM) | [![%40theoplayer/react-native-drm](https://img.shields.io/npm/v/%40theoplayer%2Freact-native-drm?label=%40theoplayer/react-native-drm)](https://www.npmjs.com/package/%40theoplayer%2Freact-native-drm) | [`DRM`](https://github.com/THEOplayer/react-native-connectors/tree/main/drm) | | Conviva analytics | [![%40theoplayer/react-native-analytics-conviva](https://img.shields.io/npm/v/%40theoplayer%2Freact-native-analytics-conviva?label=%40theoplayer/react-native-analytics-conviva)](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-conviva) | [`Conviva`](https://github.com/THEOplayer/react-native-connectors/tree/main/conviva) | | Engage | [![%40theoplayer/react-native-engage](https://img.shields.io/npm/v/%40theoplayer%2Freact-native-engage?label=%40theoplayer/react-native-engage)](https://www.npmjs.com/package/%40theoplayer%2Freact-native-engage) | [`Engage`](https://github.com/THEOplayer/react-native-connectors/tree/main/engage) | +| Gemius | [![%40theoplayer/react-native-analytics-gemius](https://img.shields.io/npm/v/%40theoplayer%2Freact-native-analytics-gemius?label=%40theoplayer/react-native-analytics-gemius)](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-engage) | [`Gemius`](https://github.com/THEOplayer/react-native-connectors/tree/main/engage) | | Mux analytics | [![%40theoplayer/react-native-analytics-mux](https://img.shields.io/npm/v/%40theoplayer%2Freact-native-analytics-mux?label=%40theoplayer/react-native-analytics-mux)](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-mux) | [`Mux`](https://github.com/THEOplayer/react-native-connectors/tree/main/mux) | | Nielsen analytics | [![%40theoplayer/react-native-analytics-nielsen](https://img.shields.io/npm/v/%40theoplayer%2Freact-native-analytics-nielsen?label=%40theoplayer/react-native-analytics-nielsen)](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-nielsen) | [`Nielsen`](https://github.com/THEOplayer/react-native-connectors/tree/main/nielsen) | +| Nielsen AdScript analytics | [![%40theoplayer/react-native-analytics-adscript](https://img.shields.io/npm/v/%40theoplayer%2Freact-native-analytics-adscript?label=%40theoplayer/react-native-analytics-adscript)](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-adscript) | [`AdScript`](https://github.com/THEOplayer/react-native-connectors/tree/main/adscript) | | Yospace SSAI | [![%40theoplayer/react-native-yospace](https://img.shields.io/npm/v/%40theoplayer%2Freact-native-yospace?label=%40theoplayer/react-native-yospace=)](https://www.npmjs.com/package/%40theoplayer%2Freact-native-yospace) | [`Yospace`](https://github.com/THEOplayer/react-native-connectors/tree/main/yospace) | | Youbora analytics | [![%40theoplayer/react-native-analytics-youbora](https://img.shields.io/npm/v/%40theoplayer%2Freact-native-analytics-youbora?label=%40theoplayer/react-native-analytics-youbora=)](https://www.npmjs.com/package/%40theoplayer%2Freact-native-analytics-youbora) | [`Youbora`](https://github.com/THEOplayer/react-native-connectors/tree/main/youbora) | diff --git a/adscript/.gitignore b/adscript/.gitignore new file mode 100644 index 00000000..75356714 --- /dev/null +++ b/adscript/.gitignore @@ -0,0 +1,70 @@ +# OSX +# +.DS_Store + +# XDE +.expo/ + +# VSCode +.vscode/ +jsconfig.json + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IJ +# +.classpath +.cxx +.gradle +.idea +.project +.settings +local.properties +android.iml + +# Cocoapods +# +example/ios/Pods + +# Ruby +example/vendor/ + +# node.js +# +node_modules/ +npm-debug.log +yarn-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +android/app/libs +android/keystores/debug.keystore + +# Expo +.expo/ + +# Turborepo +.turbo/ + +# generated by bob +lib/ diff --git a/adscript/CHANGELOG.md b/adscript/CHANGELOG.md new file mode 100644 index 00000000..b6f31266 --- /dev/null +++ b/adscript/CHANGELOG.md @@ -0,0 +1,7 @@ +# @theoplayer/react-native-analytics-adscript + +## 1.0.0 + +### ✨ Features + +- Initial release diff --git a/adscript/README.md b/adscript/README.md new file mode 100644 index 00000000..783d77f5 --- /dev/null +++ b/adscript/README.md @@ -0,0 +1,123 @@ +# THEOplayer React-Native AdScript Connector + +A [Nielsen AdScript](https://adscript.admosphere.cz/) analytics connector for `@theoplayer/react-native`. + +## Installation + +```sh +npm install @theoplayer/react-native-analytics-adscript +``` + +[//]: # (npm install @theoplayer/react-native-analytics-adscript) + +## Prerequisites + +### Android + +On Android, the connector requires downloading the private +[AdScript client library](https://adscript.admosphere.cz/download/AdScriptApiClient_v1.0.10.aar.gz) +module into the app's `libs/` folder. Pass the SDK location to the connector +by setting the `adScriptSdkDir` in your app's `gradle.properties` file: + +```bash +# Location of the AdScript SDK +adScriptSdkDir=./app/libs/ +``` + +### iOS & tvOS + +On Apple platforms, the connector requires downloading the private `AdScriptAPIClient` archive. + +Create a `Frameworks` folder in your app's `ios` folder, copy the Adscript SDK XCFramework in it +and add this `AdScriptApiClient.podspec` file inside, which describes the structure and metadata of Adscript's CocoaPod: + +```ruby +Pod::Spec.new do |spec| + spec.name = "AdScriptApiClient" + spec.version = "1.0.8" + spec.summary = "The Adscript Api Client for iOS" + + spec.homepage = 'https://github.com/THEOplayer/iOS-Connector' + spec.license = { :type => 'MIT', :file => 'LICENSE' } + spec.author = "THEO technologies" + spec.source = { :git => 'https://github.com/THEOplayer/iOS-Connector.git', :tag => spec.version.to_s } + + spec.source_files = "Classes", "Classes/**/*.{h,m}" + spec.ios.vendored_frameworks = "AdScriptApiClient.xcframework" +end +``` + +To enable IDFA (Identifier for Advertisers), replace the framework reference: + +``` +spec.ios.vendored_frameworks = "AdScriptApiClient.xcframework" +``` + +with + +``` +spec.ios.vendored_frameworks = "AdScriptApiClient_withidfa.xcframework" +``` + +Finally, include the AdScript SDK as dependency in your app's `Podfile`: + +```ruby + pod 'AdScriptApiClient', :path => 'Frameworks/' +``` + +## Usage + +### Configuring the connector + +Create the connector by providing the `THEOplayer` instance, an `implementationId`, and a `metadata` object. + +```tsx +import { useAdScript, AdScriptMetadata } from '@theoplayer/react-native-analytics-adscript'; + +const adscriptImplementationId = "myImplementionId"; +const adscriptContentMetadata: AdScriptMetadata = { + assetId: "abc98731568435405", + type: "content" +}; + +const App = () => { + const [adscript, initAdScript] = useAdScript(adscriptImplementationId, adscriptContentMetadata, true /*debug*/); + + const onPlayerReady = (player: THEOplayer) => { + // Initialize AdScript connector + initAdScript(player); + }; + + return ; +}; +``` + +### Passing metadata dynamically + +The connector allows passing or updating the current asset's metadata at any time: + +```typescript +const onUpdateMetadata = () => { + adscript.current?.updateMetadata({ + assetId: 'updatedId', + type: "content", + // ... + }); +} +``` + +### Passing additional information + +Additional information about the logged-in user from the client database: + +```typescript +const onUpdateUser = () => { + adscript.current?.updateUser([ + "866d3a3c-aa87-4fe7-9066-8286641edd17", // client-side user identifier (customerId) + "cf895a00-a987-4501-ac00-6adb57014129", // client-side user device identifier (Android_ID) + "9530321a-d3d0-4bda-8704-1eb94a5940c3", // client-side profile identifier of the logged-in user (profileId) + "jsg75682-k276t-kw82-k8d5-8926sh6528j2", // optional - SW device identifier for a situation where there are multiple device IDs in the client DB (typically a) HW ID - fill in i2, b) SW ID - fill in i4). + "ef3c6dc72a26912f07f0e733d51b46c771d807bf" // fingerprint of the user's email address + ]); +} +``` diff --git a/adscript/android/build.gradle b/adscript/android/build.gradle new file mode 100644 index 00000000..969bfdd4 --- /dev/null +++ b/adscript/android/build.gradle @@ -0,0 +1,125 @@ +import java.util.zip.GZIPInputStream + +buildscript { + // Buildscript is evaluated before everything else so we can't use getExtOrDefault + def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : '1.9.10' + + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "com.android.tools.build:gradle:7.2.2" + // noinspection DifferentKotlinGradleVersion + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +def isNewArchitectureEnabled() { + return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} + +def safeExtGet(prop, fallback) { + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback +} + +android { + namespace = "com.theoplayer.reactnative.adscript" + compileSdkVersion safeExtGet("THEOplayerAdScript_compileSdkVersion", 35) + + defaultConfig { + minSdkVersion safeExtGet("THEOplayerAdScript_minSdkVersion", 21) + targetSdkVersion safeExtGet("THEOplayerAdScript_targetSdkVersion", 35) + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + } + } + + lintOptions { + disable "GradleCompatible" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +repositories { + mavenLocal() + google() + mavenCentral() + // Local Maven repo + maven { url("local") } + maven { url "https://maven.theoplayer.com/releases" } +} + +rootProject.allprojects { + repositories { + maven { url("$rootDir/../node_modules/@theoplayer/react-native-analytics-adscript/android/local") } + } +} + +// The AdScript connector requires at least THEOplayer SDK v5.11.0. +def theoplayer_sdk_version = safeExtGet('THEOplayer_sdk', '[5.11.0, 10.0.0)') +def kotlin_version = safeExtGet("THEOplayerAdScript_kotlinVersion", "1.9.10") +def adscript_version = safeExtGet("THEOplayerAdScript_adscriptVersion", "1.0.10") + +dependencies { + // For < 0.71, this will be from the local maven repo + // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin + //noinspection GradleDynamicVersion + implementation "com.facebook.react:react-native" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.lifecycle:lifecycle-process:2.5.1' + + // THEOplayer dependencies + compileOnly "com.theoplayer.theoplayer-sdk-android:core:$theoplayer_sdk_version" + compileOnly "com.theoplayer.theoplayer-sdk-android:integration-ads-ima:$theoplayer_sdk_version" + implementation project(':react-native-theoplayer') + + // TEMP: replace this once the native connector has been published +// implementation "com.theoplayer.android-connector:adscript:$adscript_version" + implementation files("./libs/adscript.aar") + def aarFileName = "AdScriptApiClient_v${adscript_version}.aar" + def adScriptSdkDir = rootProject.properties['adScriptSdkDir'] + if (!adScriptSdkDir) { + logger.warn("⚠️ WARNING: adScriptSdkDir not set.") + } else if (!rootProject.file(adScriptSdkDir).exists()) { + logger.warn("⚠️ WARNING: adScriptSdkDir does not exist at: ${adScriptSdkDir}") + } else { + def aarFile = new File(adScriptSdkDir, aarFileName) + + // The AdScript aar file is typically provided as .gz, check if we still need to extract. + def gzFile = new File("${aarFile}.gz") + if (!aarFile.exists() && gzFile.exists()) { + new FileInputStream(gzFile).withCloseable { fis -> + new GZIPInputStream(fis).withCloseable { gis -> + new FileOutputStream(aarFile).withCloseable { fos -> + fos << gis + } + } + } + } + dependencies { + implementation fileTree(dir: "${rootProject.file(adScriptSdkDir)}/$aarFileName", include: ['*.aar', '*.jar'], exclude: []) + } + } + + // Align the Kotlin SDK libraries with the same version. + implementation(platform("org.jetbrains.kotlin:kotlin-bom:$kotlin_version")) +} diff --git a/adscript/android/gradle/wrapper/gradle-wrapper.jar b/adscript/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..41d9927a Binary files /dev/null and b/adscript/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/adscript/android/gradle/wrapper/gradle-wrapper.properties b/adscript/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..2733ed5d --- /dev/null +++ b/adscript/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/adscript/android/gradlew b/adscript/android/gradlew new file mode 100755 index 00000000..1b6c7873 --- /dev/null +++ b/adscript/android/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/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/. +# +############################################################################## + +# 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=${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 + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + 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 +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +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 + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# 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/adscript/android/gradlew.bat b/adscript/android/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/adscript/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +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 execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/adscript/android/libs/adscript.aar b/adscript/android/libs/adscript.aar new file mode 100644 index 00000000..ff3cffb7 Binary files /dev/null and b/adscript/android/libs/adscript.aar differ diff --git a/adscript/android/src/main/AndroidManifest.xml b/adscript/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..cc947c56 --- /dev/null +++ b/adscript/android/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/adscript/android/src/main/java/com/theoplayer/reactnative/adscript/ReactTHEOplayerAdScriptModule.kt b/adscript/android/src/main/java/com/theoplayer/reactnative/adscript/ReactTHEOplayerAdScriptModule.kt new file mode 100644 index 00000000..d826269f --- /dev/null +++ b/adscript/android/src/main/java/com/theoplayer/reactnative/adscript/ReactTHEOplayerAdScriptModule.kt @@ -0,0 +1,94 @@ +package com.theoplayer.reactnative.adscript + +import android.util.Log +import com.facebook.react.bridge.* +import com.theoplayer.ReactTHEOplayerView +import com.theoplayer.android.api.THEOplayerView +import com.theoplayer.android.connector.analytics.adscript.AdscriptConfiguration +import com.theoplayer.android.connector.analytics.adscript.AdscriptConnector +import com.theoplayer.util.ViewResolver +import com.nad.adscriptapiclient.AdScriptDataObject +import com.nad.adscriptapiclient.AdScriptI12n +import org.json.JSONException + +private const val TAG = "AdScriptModule" + +class ReactTHEOplayerAdScriptModule(context: ReactApplicationContext) : + ReactContextBaseJavaModule(context) { + + private val viewResolver: ViewResolver = ViewResolver(context) + + private var adscriptConnectors: HashMap = HashMap() + + override fun getName(): String { + return TAG + } + + @ReactMethod + fun initialize(tag: Int, implementationId: String, contentMetadata: ReadableMap, debug: Boolean) { + viewResolver.resolveViewByTag(tag) { view: ReactTHEOplayerView? -> + view?.playerContext?.playerView?.let { playerView -> + adscriptConnectors[tag] = + initializeAdScript(playerView, implementationId, contentMetadata, debug) + } + } + } + + private fun initializeAdScript( + view: THEOplayerView, + implementationId: String, + contentMetadata: ReadableMap, + debug: Boolean + ): AdscriptConnector { + return AdscriptConnector( + activity = reactApplicationContext.currentActivity!!, + playerView = view, + configuration = AdscriptConfiguration(implementationId, debug), + contentMetadata = buildContentMetadata(contentMetadata), + adProcessor = null) + } + + private fun buildContentMetadata(contentMetadata: ReadableMap): AdScriptDataObject { + return AdScriptDataObject().apply { + for (entry in contentMetadata.toHashMap().entries) { + try { + when (entry.value) { + is String -> set(entry.key, entry.value as String) + is Boolean -> set(entry.key, entry.value as Boolean) + is Int -> set(entry.key, entry.value as Int) + is Long -> set(entry.key, entry.value as Long) + } + } catch (e: Exception) { + Log.e(TAG, "Failed to add contentMetadata value ${entry.key}: ${e.message}") + } + } + } + } + + private fun buildUser(user: ReadableArray): AdScriptI12n { + return AdScriptI12n().apply { + try { + for (i in 0 until user.size()) { + set(i, user.getString(i)) + } + } catch (e: JSONException) { + Log.e(TAG, "Failed to build user: ${e.message}") + } + } + } + + @ReactMethod + fun updateMetadata(tag: Int, contentMetadata: ReadableMap) { + adscriptConnectors[tag]?.updateMetadata(buildContentMetadata(contentMetadata)) + } + + @ReactMethod + fun updateUser(tag: Int, user: ReadableArray) { + adscriptConnectors[tag]?.updateUser(buildUser(user)) + } + + @ReactMethod + fun destroy(tag: Int) { + adscriptConnectors[tag]?.destroy() + } +} diff --git a/adscript/android/src/main/java/com/theoplayer/reactnative/adscript/ReactTHEOplayerAdScriptPackage.kt b/adscript/android/src/main/java/com/theoplayer/reactnative/adscript/ReactTHEOplayerAdScriptPackage.kt new file mode 100644 index 00000000..1ced5e35 --- /dev/null +++ b/adscript/android/src/main/java/com/theoplayer/reactnative/adscript/ReactTHEOplayerAdScriptPackage.kt @@ -0,0 +1,16 @@ +package com.theoplayer.reactnative.adscript + +import com.facebook.react.ReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.uimanager.ViewManager + +class ReactTHEOplayerAdScriptPackage : ReactPackage { + override fun createNativeModules(reactContext: ReactApplicationContext): List { + return listOf(ReactTHEOplayerAdScriptModule(reactContext)) + } + + override fun createViewManagers(reactContext: ReactApplicationContext): List> { + return emptyList() + } +} diff --git a/adscript/babel.config.js b/adscript/babel.config.js new file mode 100644 index 00000000..f842b77f --- /dev/null +++ b/adscript/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['module:metro-react-native-babel-preset'], +}; diff --git a/adscript/ios/Source/AdScriptConnector.swift b/adscript/ios/Source/AdScriptConnector.swift new file mode 100644 index 00000000..d29e694d --- /dev/null +++ b/adscript/ios/Source/AdScriptConnector.swift @@ -0,0 +1,37 @@ +import THEOplayerSDK +#if canImport(AdScriptApiClient) +import AdScriptApiClient +#endif + +public struct AdScriptConfiguration { + let implementationId: String + let debug: Bool + + public init(implementationId: String, debug: Bool) { + self.implementationId = implementationId + self.debug = debug + } +} + +public struct AdScriptConnector { + private let adapter: AdScriptAdapter + private let player: THEOplayer + + + public init(configuration: AdScriptConfiguration, player: THEOplayer, metadata: AdScriptDataObject) { + self.player = player + self.adapter = AdScriptAdapter(configuration: configuration, player: player, metadata: metadata) + } + + public func sessionStart() { + self.adapter.sessionStart() + } + + public func update(metadata: AdScriptDataObject) { + self.adapter.update(metadata: metadata) + } + + public func updateUser(i12n: AdScriptI12n) { + self.adapter.updateUser(i12n: i12n) + } +} diff --git a/adscript/ios/Source/integration/AdScriptAdapter.swift b/adscript/ios/Source/integration/AdScriptAdapter.swift new file mode 100644 index 00000000..1b8f7e99 --- /dev/null +++ b/adscript/ios/Source/integration/AdScriptAdapter.swift @@ -0,0 +1,450 @@ +import THEOplayerSDK +import Collections +#if canImport(AdScriptApiClient) +import AdScriptApiClient +#endif + +public struct LogPoint { + let name: AdScriptEventName; + let cue: Double; +} + +let LOG_PLAYER_EVENTS = false +let LOG_ADSCRIPT_EVENTS = false + +public class AdScriptAdapter { + private let player: THEOplayer + private var contentMetadata: AdScriptDataObject + private var contentLogPoints: Deque = Deque() + private var adMetadata: AdScriptDataObject? = nil + private var waitingForFirstSecondOfAd = false + private var waitingForFirstSecondOfSsaiAdSince: Double? = nil + private let configuration: AdScriptConfiguration + private let adscriptCollector: AdScriptCollector + + private var playEventListener: EventListener? + private var playingEventListener: EventListener? + private var errorEventListener: EventListener? + private var sourceChangeEventListener: EventListener? + private var endedEventListener: EventListener? + private var durationChangeEventListener: EventListener? + private var timeUpdateEventListener: EventListener? + private var volumeChangeEventListener: EventListener? + private var rateChangeEventListener: EventListener? + private var presentationModeChangeEventListener: EventListener? + + private var adBreakBeginListener: EventListener? + private var adBeginListener: EventListener? + private var adFirstQuartileListener: EventListener? + private var adMidpointListener: EventListener? + private var adThirdQuartileListener: EventListener? + private var adCompletedListener: EventListener? + private var adBreakEndedListener: EventListener? + + + public init(configuration: AdScriptConfiguration, player: THEOplayer, metadata: AdScriptDataObject) { + self.player = player + self.contentMetadata = metadata + self.configuration = configuration + + self.adscriptCollector = AdScriptCollector(implementationId: configuration.implementationId, isDebug: configuration.debug && LOG_ADSCRIPT_EVENTS) + + reportPlayerState() + addEventListeners() + } + + public func sessionStart() { + self.adscriptCollector.sessionStart() + } + + public func update(metadata: AdScriptDataObject) { + self.contentMetadata = metadata + } + + public func updateUser(i12n: AdScriptI12n) { + self.adscriptCollector.i12n = i12n + } + + private func reportPlayerState() { + reportFullscreen(isFullscreen: player.presentationMode == PresentationMode.fullscreen) + reportDimensions(width: player.videoWidth, height: player.videoHeight) + reportPlaybackSpeed(playbackRate: player.playbackRate) + reportVolumeAndMuted(isMuted: player.muted, volume: player.volume) + reportTriggeredByUser(autoplayEnabled: player.autoplay) + reportVisibility() + } + + private func reportFullscreen(isFullscreen: Bool) { + _ = self.adscriptCollector.playerState.set(key: AdScriptPlayerStateKey.fullscreen, value: isFullscreen ? 1 : 0) + } + + private func reportDimensions(width: Int, height: Int) { + _ = self.adscriptCollector.playerState.set(key: AdScriptPlayerStateKey.width, value: width) + _ = self.adscriptCollector.playerState.set(key: AdScriptPlayerStateKey.height, value: height) + } + + private func reportPlaybackSpeed(playbackRate: Double) { + _ = self.adscriptCollector.playerState.set(key: AdScriptPlayerStateKey.normalSpeed, value: playbackRate == 1 ? 1 : 0) + } + + private func reportVolumeAndMuted(isMuted: Bool, volume: Float) { + _ = self.adscriptCollector.playerState.set(key: AdScriptPlayerStateKey.muted, value: (isMuted || volume == 0) ? 1 : 0) + _ = self.adscriptCollector.playerState.set(key: AdScriptPlayerStateKey.volume, value: Int(volume)) + } + private func reportTriggeredByUser(autoplayEnabled: Bool) { + _ = self.adscriptCollector.playerState.set(key: AdScriptPlayerStateKey.triggeredByUser, value: autoplayEnabled ? 1 : 0) + } + + private func reportVisibility() { + // TODO + } + + private func handlePlaying(event: PlayingEvent) { + if (self.configuration.debug && LOG_PLAYER_EVENTS) { + print("[AdScriptConnector] Player Event: \(event.type) : currentTime = \(event.currentTime)") + } + if (self.player.ads.playing) { + if let adMetadata = self.adMetadata { + if (self.configuration.debug) { + print("[AdScriptConnector] Push .start event with adMetadata \(adMetadata.toJsonString())") + } + self.adscriptCollector.push(event: .start, data: adMetadata) + } + } else { + if (self.configuration.debug) { + print("[AdScriptConnector] Push .start event with contentMetadata \(contentMetadata.toJsonString())") + } + self.adscriptCollector.push(event: .start, data: self.contentMetadata) + // TODO check if flag is needed or just one playing event is dispatched on iOS + } + if let playingEventListener: THEOplayerSDK.EventListener = self.playingEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: playingEventListener) + } + } + + private func addLogPoints(duration: Double?) { + if let duration = duration { + if (duration.isFinite) { + self.contentLogPoints.append(LogPoint(name: .progress1 , cue: 1.0)) + self.contentLogPoints.append(LogPoint(name: .firstQuartile , cue: 0.25 * duration)) + self.contentLogPoints.append(LogPoint(name: .midpoint , cue: 0.5 * duration)) + self.contentLogPoints.append(LogPoint(name: .thirdQuartile , cue: 0.75 * duration)) + } else { + self.contentLogPoints.append(LogPoint(name: .progress1, cue: 1.0)) + } + if (self.configuration.debug) { + print("[AdScriptConnector] Stored \(self.contentLogPoints.count) logPoints : \(self.contentLogPoints.debugDescription)") + } + } + + } + + private func reportLogPoint(name: AdScriptEventName) { + if (self.configuration.debug) { + print("[AdScriptConnector] Push .\(name.rawValue) event with contentMetadata \(contentMetadata.toJsonString())") + } + self.adscriptCollector.push(event: name, data: self.contentMetadata) + } + + private func maybeReportAdProgress(currentTime: Double) { + if (!self.waitingForFirstSecondOfAd) { return } + if let currentAd = player.ads.currentAds.first { + switch currentAd.integration { + case .google_ima: + if let adMetadata = self.adMetadata, currentTime >= 1 { + if (self.configuration.debug) { + print("[AdScriptConnector] Push .progress1 event with adMetadata \(adMetadata.toJsonString())") + } + self.adscriptCollector.push(event: .progress1, data: adMetadata) + self.waitingForFirstSecondOfAd = false + } + case .google_dai: + if let waitingSince = self.waitingForFirstSecondOfSsaiAdSince, + let adMetadata = self.adMetadata, + currentTime >= waitingSince + 1.0 { + if (self.configuration.debug) { + print("[AdScriptConnector] Push .progress1 event with adMetadata \(adMetadata.toJsonString())") + } + self.adscriptCollector.push(event: .progress1, data: adMetadata) + self.waitingForFirstSecondOfAd = false + self.waitingForFirstSecondOfSsaiAdSince = nil + } + case .theoads: + if (self.configuration.debug) { print("[AdScriptConnector] Ad Integration is not supported (maybeReportAdProgress)") } + case .custom: + if (self.configuration.debug) { print("[AdScriptConnector] Ad Integration is not supported (maybeReportAdProgress)") } + @unknown default: + if (self.configuration.debug) { print("[AdScriptConnector] Ad Integration is not supported (maybeReportAdProgress)") } + } + } + } + + private func maybeReportProgress(currentTime: Double) { + if (player.ads.playing) { + maybeReportAdProgress(currentTime: currentTime) + return + } + + let nextLogPoint = contentLogPoints.first + if (nextLogPoint != nil && currentTime >= nextLogPoint!.cue) { + reportLogPoint(name: nextLogPoint!.name) + contentLogPoints.removeFirst() + } + } + + private func getAdType(offset: Int) -> AdScriptDataValueType { + if (offset == 0) { + return .preroll + } else if (offset == -1) { + return .postroll + } else if let duration = player.duration, offset >= Int(floor(duration)) { + return .postroll + } else { + return .midroll + } + } + + private func buildAdMetadata(ad: THEOplayerSDK.Ad) { + if (false) { + // TODO provide adProcessor + } else { + var currentAdMetadata = AdScriptDataObject() + if let id = ad.id { + _ = currentAdMetadata.set(key: .assetId, value: id) + _ = currentAdMetadata.set(key: .title, value: id) + + } +// _ = currentAdMetadata.set(key: .asmea, value: "TODO") + _ = currentAdMetadata.set(key: .attribute, value: .commercial) + _ = currentAdMetadata.set(key: .type, value: self.getAdType(offset: ad.adBreak.timeOffset)) + if let duration = ad.duration { + _ = currentAdMetadata.set(key: .length, value: duration) + } + + adMetadata = currentAdMetadata + } + } + + + private func addEventListeners() { + self.playEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAY, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[AdScriptConnector] Player Event: \(event.type) : currentTime = \(event.currentTime)") + } + }) + self.playingEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: { [weak self] event in self?.handlePlaying(event: event) }) + self.errorEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.ERROR, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if let code = event.errorObject?.code, let cause = event.errorObject?.cause, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[AdScriptConnector] Player Event: \(event.type) : code = \(code) ; cause = \(cause)") + } + }) + self.sourceChangeEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.SOURCE_CHANGE, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[AdScriptConnector] Player Event: \(event.type) : source = \(event.source.debugDescription)") + } + + if let playingEventListener: THEOplayerSDK.EventListener = welf.playingEventListener { + welf.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: playingEventListener) + } + welf.contentLogPoints = Deque() + welf.playingEventListener = welf.player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: { [weak self] event in self?.handlePlaying(event: event) }) + }) + self.endedEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.ENDED, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[AdScriptConnector] Player Event: \(event.type) : currentTime = \(event.currentTime)") + } + if (welf.configuration.debug) { + print("[AdScriptConnector] Push .complete event with contentMetadata \(welf.contentMetadata.toJsonString())") + } + welf.adscriptCollector.push(event: .complete, data: welf.contentMetadata) + }) + self.durationChangeEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.DURATION_CHANGE, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if let duration = event.duration, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[AdScriptConnector] Player Event: \(event.type) : duration = \(duration)") + } + welf.addLogPoints(duration: event.duration) + }) + self.timeUpdateEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.TIME_UPDATE, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[AdScriptConnector] Player Event: \(event.type) : currentTime = \(event.currentTime)") + } + welf.maybeReportProgress(currentTime: event.currentTime) + }) + self.volumeChangeEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.VOLUME_CHANGE, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[AdScriptConnector] Player Event: \(event.type) : volume = \(event.volume)") + } + welf.reportVolumeAndMuted(isMuted: welf.player.muted, volume: event.volume) + }) + self.rateChangeEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.RATE_CHANGE, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[AdScriptConnector] Player Event: \(event.type) : playbackRate = \(event.playbackRate)") + } + welf.reportPlaybackSpeed(playbackRate: event.playbackRate) + }) + self.presentationModeChangeEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PRESENTATION_MODE_CHANGE, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[AdScriptConnector] Player Event: \(event.type) : presentationMode = \(event.presentationMode._rawValue)") + } + welf.reportFullscreen(isFullscreen: event.presentationMode == PresentationMode.fullscreen) + }) + + + if (hasAdIntegration()) { + self.adBreakBeginListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BREAK_BEGIN, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if let offset = event.ad?.timeOffset, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[AdScriptConnector] Player Event: \(event.type) : offset = \(offset)") + } + }) + self.adBeginListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BEGIN, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if let id = event.ad?.id, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[AdScriptConnector] Player Event: \(event.type) : id = \(id)") + } + guard let ad = event.ad else { return } + welf.buildAdMetadata(ad: ad) + welf.waitingForFirstSecondOfAd = true + if (event.ad?.integration == .google_dai) { + welf.waitingForFirstSecondOfSsaiAdSince = welf.player.currentTime + } + guard let adMetadata = self?.adMetadata else { return } + if (welf.configuration.debug) { + print("[AdScriptConnector] Push .start event with adMetadata \(adMetadata.toJsonString())") + } + welf.adscriptCollector.push(event: .start, data: adMetadata) + }) + self.adFirstQuartileListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_FIRST_QUARTILE, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if let id = event.ad?.id, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[AdScriptConnector] Player Event: \(event.type) : id = \(id)") + } + guard let adMetadata = welf.adMetadata else { return } + if (welf.configuration.debug) { + print("[AdScriptConnector] Push .firstQuartile event with adMetadata \(adMetadata.toJsonString())") + } + welf.adscriptCollector.push(event: .firstQuartile, data: adMetadata) + }) + self.adMidpointListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_MIDPOINT, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if let id = event.ad?.id, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[AdScriptConnector] Player Event: \(event.type) : id = \(id)") + } + guard let adMetadata = welf.adMetadata else { return } + if (welf.configuration.debug) { + print("[AdScriptConnector] Push .midpoint event with adMetadata \(adMetadata.toJsonString())") + } + welf.adscriptCollector.push(event: .midpoint, data: adMetadata) + }) + self.adThirdQuartileListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_THIRD_QUARTILE, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if let id = event.ad?.id, welf.configuration.debug { + print("[AdScriptConnector] Player Event: \(event.type) : id = \(id)") + } + guard let adMetadata = welf.adMetadata else { return } + if (welf.configuration.debug) { + print("[AdScriptConnector] Push .thirdQuartile event with adMetadata \(adMetadata.toJsonString())") + } + welf.adscriptCollector.push(event: .thirdQuartile, data: adMetadata) + }) + self.adCompletedListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_END, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if let id = event.ad?.id, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[AdScriptConnector] Player Event: \(event.type) : id = \(id)") + } + guard let adMetadata = welf.adMetadata else { return } + if (welf.configuration.debug) { + print("[AdScriptConnector] Push .complete event with adMetadata \(adMetadata.toJsonString())") + } + welf.adscriptCollector.push(event: .complete, data: adMetadata) + }) + self.adBreakEndedListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BREAK_END, listener: { [weak self] event in + guard let welf: AdScriptAdapter = self else { return } + if let offset = event.ad?.timeOffset, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[AdScriptConnector] Player Event: \(event.type) : offset = \(offset)") + } + if (event.ad?.timeOffset == 0) { + welf.playingEventListener = welf.player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: { [weak self] event in self?.handlePlaying(event: event) }) + } + }) + } + } + + private func removeEventListeners() { + if let playEventListener: THEOplayerSDK.EventListener = self.playEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.TIME_UPDATE, listener: playEventListener) + } + if let playingEventListener: THEOplayerSDK.EventListener = self.playingEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: playingEventListener) + } + if let errorEventListener: THEOplayerSDK.EventListener = self.errorEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.ERROR, listener: errorEventListener) + } + if let sourceChangeEventListener: THEOplayerSDK.EventListener = self.sourceChangeEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.SOURCE_CHANGE, listener: sourceChangeEventListener) + } + if let endedEventListener: THEOplayerSDK.EventListener = self.endedEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.ENDED, listener: endedEventListener) + } + if let durationChangeEventListener: THEOplayerSDK.EventListener = self.durationChangeEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.DURATION_CHANGE, listener: durationChangeEventListener) + } + if let timeUpdateEventListener: THEOplayerSDK.EventListener = self.timeUpdateEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.TIME_UPDATE, listener: timeUpdateEventListener) + } + if let volumeChangeEventListener: THEOplayerSDK.EventListener = self.volumeChangeEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.VOLUME_CHANGE, listener: volumeChangeEventListener) + } + if let rateChangeEventListener: THEOplayerSDK.EventListener = self.rateChangeEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.RATE_CHANGE, listener: rateChangeEventListener) + } + if let presentationModeChangeEventListener: THEOplayerSDK.EventListener = self.presentationModeChangeEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PRESENTATION_MODE_CHANGE, listener: presentationModeChangeEventListener) + } + if let adBreakBeginListener: THEOplayerSDK.EventListener = self.adBreakBeginListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BREAK_BEGIN, listener: adBreakBeginListener) + } + if let adBeginListener: THEOplayerSDK.EventListener = self.adBeginListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BEGIN, listener: adBeginListener) + } + if let adFirstQuartileListener: THEOplayerSDK.EventListener = self.adFirstQuartileListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_FIRST_QUARTILE, listener: adFirstQuartileListener) + } + if let adMidpointListener: THEOplayerSDK.EventListener = self.adMidpointListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_MIDPOINT, listener: adMidpointListener) + } + if let adThirdQuartileListener: THEOplayerSDK.EventListener = self.adThirdQuartileListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_THIRD_QUARTILE, listener: adThirdQuartileListener) + } + if let adCompletedListener: THEOplayerSDK.EventListener = self.adCompletedListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_END, listener: adCompletedListener) + } + if let adBreakEndedListener: THEOplayerSDK.EventListener = self.adBreakEndedListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BREAK_END, listener: adBreakEndedListener) + } + } + + private func hasAdIntegration() -> Bool { + let hasAdIntegration = player.getAllIntegrations().contains { integration in + switch integration.kind { + case IntegrationKind.GOOGLE_DAI: + return true + case IntegrationKind.GOOGLE_IMA: + return true + default: + print("[AdScriptConnector] no supported ad integration was found") + return false + } + } + return hasAdIntegration + } +} diff --git a/adscript/ios/THEOplayerAdScriptRCTAdScriptAPI.swift b/adscript/ios/THEOplayerAdScriptRCTAdScriptAPI.swift new file mode 100644 index 00000000..852be794 --- /dev/null +++ b/adscript/ios/THEOplayerAdScriptRCTAdScriptAPI.swift @@ -0,0 +1,116 @@ +import Foundation +import THEOplayerSDK +import UIKit +import react_native_theoplayer +import AdScriptApiClient + +@objc(THEOplayerAdScriptRCTAdScriptAPI) +class THEOplayerAdScriptRCTAdScriptAPI: NSObject, RCTBridgeModule { + @objc var bridge: RCTBridge! + + var connectors = [NSNumber: AdScriptConnector]() + + static func moduleName() -> String! { + return "AdScriptModule" + } + + static func requiresMainQueueSetup() -> Bool { + return false + } + + @objc(initialize:implementationId:contentMetadata:debug:) + func initialize( + _ node: NSNumber, implementationId: NSString, contentMetadata: NSDictionary, debug: Bool + ) { + log("initialize triggered.") + + DispatchQueue.main.async { + if let view = self.view(for: node), let player = view.player { + self.connectors[node] = AdScriptConnector( + configuration: AdScriptConfiguration(implementationId: implementationId as String, debug: debug), + player: player, + metadata: self.buildMetadata(contentMetadata) + ) + log("Added connector to view \(node)") + } else { + log("Cannot find THEOPlayer for node \(node)") + } + } + } + + @objc(updateMetadata:metadata:) + func updateMetadata(_ node: NSNumber, metadata: NSDictionary) { + log("updateMetadata triggered.") + DispatchQueue.main.async { + if let connector = self.connectors[node] { + connector + .update( + metadata: self.buildMetadata(metadata) + ) + } + } + } + + @objc(updateUser:user:) + func updateUser(_ node: NSNumber, user: NSArray) { + log("updateUser triggered.") + DispatchQueue.main.async { + if let connector = self.connectors[node] { + connector + .updateUser( + i12n: self.buildUser(user) + ) + } + } + } + + private func buildMetadata(_ metadata: NSDictionary) -> AdScriptDataObject { + let data = AdScriptDataObject() + + metadata.forEach { key, value in + guard let keyString = key as? String, + let stringValue = value as? String else { return } + + if let dataKey = AdScriptDataKey(rawValue: keyString) { + _ = data.set(key: dataKey, value: stringValue) + } else { + // custom param + _ = data.set(key: keyString, value: stringValue) + } + } + return data + } + + private func buildUser(_ user: NSArray) -> AdScriptI12n { + let data = AdScriptI12n() + for i in 0.. THEOplayerRCTView? { + self.bridge.uiManager.view(forReactTag: node) as? THEOplayerRCTView + } + + func player(for node: NSNumber) -> THEOplayer? { + view(for: node)?.player + } +} + +func log(_ text: String) { +#if DEBUG + print("[react-native-theoplayer-adscript bridge]", text) +#endif +} diff --git a/adscript/ios/THEOplayerAdScriptRCTBridge.m b/adscript/ios/THEOplayerAdScriptRCTBridge.m new file mode 100644 index 00000000..ba7bb74b --- /dev/null +++ b/adscript/ios/THEOplayerAdScriptRCTBridge.m @@ -0,0 +1,23 @@ +#import +#import +#import + +// ---------------------------------------------------------------------------- +// AdScript Module +// ---------------------------------------------------------------------------- +@interface RCT_EXTERN_REMAP_MODULE(AdScriptModule, THEOplayerAdScriptRCTAdScriptAPI, NSObject) + +RCT_EXTERN_METHOD(initialize:(nonnull NSNumber *)node + implementationId:(nonnull NSString *)implementationId + contentMetadata:(nonnull NSString *)contentMetadata + debug:(BOOL)debug) + +RCT_EXTERN_METHOD(updateMetadata:(nonnull NSNumber *)node + metadata:(NSDictionary)metadata) + +RCT_EXTERN_METHOD(updateUser:(nonnull NSNumber *)node + user:(NSArray)user) + +RCT_EXTERN_METHOD(destroy:(nonnull NSNumber *)node) + +@end diff --git a/adscript/ios/TheoplayerAdScript-Bridging-Header.h b/adscript/ios/TheoplayerAdScript-Bridging-Header.h new file mode 100644 index 00000000..7ecf415d --- /dev/null +++ b/adscript/ios/TheoplayerAdScript-Bridging-Header.h @@ -0,0 +1,4 @@ +#import +#import +#import +#import diff --git a/adscript/ios/TheoplayerAdScript.xcodeproj/project.pbxproj b/adscript/ios/TheoplayerAdScript.xcodeproj/project.pbxproj new file mode 100644 index 00000000..4b879855 --- /dev/null +++ b/adscript/ios/TheoplayerAdScript.xcodeproj/project.pbxproj @@ -0,0 +1,283 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5E555C0D2413F4C50049A1A2 /* TheoplayerAdScript.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* TheoplayerAdScript.m */; }; + F4FF95D7245B92E800C19C63 /* TheoplayerAdScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* TheoplayerAdScript.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 58B511D91A9E6C8500147676 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 134814201AA4EA6300B7C361 /* libTheoplayerAdScript.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTheoplayerAdScript.a; sourceTree = BUILT_PRODUCTS_DIR; }; + B3E7B5891CC2AC0600A0062D /* TheoplayerAdScript.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TheoplayerAdScript.m; sourceTree = ""; }; + F4FF95D5245B92E700C19C63 /* TheoplayerAdScript-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TheoplayerAdScript-Bridging-Header.h"; sourceTree = ""; }; + F4FF95D6245B92E800C19C63 /* TheoplayerAdScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TheoplayerAdScript.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 58B511D81A9E6C8500147676 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 134814211AA4EA7D00B7C361 /* Products */ = { + isa = PBXGroup; + children = ( + 134814201AA4EA6300B7C361 /* libTheoplayerAdScript.a */, + ); + name = Products; + sourceTree = ""; + }; + 58B511D21A9E6C8500147676 = { + isa = PBXGroup; + children = ( + F4FF95D6245B92E800C19C63 /* TheoplayerAdScript.swift */, + B3E7B5891CC2AC0600A0062D /* TheoplayerAdScript.m */, + F4FF95D5245B92E700C19C63 /* TheoplayerAdScript-Bridging-Header.h */, + 134814211AA4EA7D00B7C361 /* Products */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 58B511DA1A9E6C8500147676 /* TheoplayerAdScript */ = { + isa = PBXNativeTarget; + buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "TheoplayerAdScript" */; + buildPhases = ( + 58B511D71A9E6C8500147676 /* Sources */, + 58B511D81A9E6C8500147676 /* Frameworks */, + 58B511D91A9E6C8500147676 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TheoplayerAdScript; + productName = RCTDataManager; + productReference = 134814201AA4EA6300B7C361 /* libTheoplayerAdScript.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 58B511D31A9E6C8500147676 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 58B511DA1A9E6C8500147676 = { + CreatedOnToolsVersion = 6.1.1; + }; + }; + }; + buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "TheoplayerAdScript" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + English, + en, + ); + mainGroup = 58B511D21A9E6C8500147676; + productRefGroup = 58B511D21A9E6C8500147676; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 58B511DA1A9E6C8500147676 /* TheoplayerAdScript */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 58B511D71A9E6C8500147676 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F4FF95D7245B92E800C19C63 /* TheoplayerAdScript.swift in Sources */, + B3E7B58A1CC2AC0600A0062D /* TheoplayerAdScript.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 58B511ED1A9E6C8500147676 /* 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_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = 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_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=*]" = arm64; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 58B511EE1A9E6C8500147676 /* 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_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = 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_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=*]" = arm64; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 58B511F01A9E6C8500147676 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../../React/**", + "$(SRCROOT)/../../react-native/React/**", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = TheoplayerAdScript; + SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "TheoplayerAdScript-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 58B511F11A9E6C8500147676 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../../React/**", + "$(SRCROOT)/../../react-native/React/**", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = TheoplayerAdScript; + SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "TheoplayerAdScript-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "TheoplayerAdScript" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511ED1A9E6C8500147676 /* Debug */, + 58B511EE1A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "TheoplayerAdScript" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511F01A9E6C8500147676 /* Debug */, + 58B511F11A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 58B511D31A9E6C8500147676 /* Project object */; +} diff --git a/adscript/manifest.js b/adscript/manifest.js new file mode 100755 index 00000000..e7629f28 --- /dev/null +++ b/adscript/manifest.js @@ -0,0 +1,6 @@ +const manifest = { + version: process.env.npm_package_version, + buildDate: new Date().toISOString(), +}; + +console.log(JSON.stringify(manifest)); diff --git a/adscript/package.json b/adscript/package.json new file mode 100644 index 00000000..28239582 --- /dev/null +++ b/adscript/package.json @@ -0,0 +1,86 @@ +{ + "name": "@theoplayer/react-native-analytics-adscript", + "version": "1.0.0", + "description": "Nielsen AdScript analytics connector for @theoplayer/react-native", + "main": "lib/commonjs/index", + "module": "lib/module/index", + "types": "lib/typescript/src/index.d.ts", + "react-native": "src/index", + "source": "src/index", + "files": [ + "src", + "lib", + "android", + "ios", + "cpp", + "*.podspec", + "*.tgz", + "CHANGELOG.md", + "manifest.json", + "!lib/typescript/example", + "!ios/build", + "!android/build", + "!android/gradle", + "!android/gradlew", + "!android/gradlew.bat", + "!android/local.properties", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__", + "!**/.*" + ], + "scripts": { + "typescript": "tsc --noEmit", + "build": "npm run manifest && bob build", + "manifest": "node manifest.js > src/manifest.json", + "prepare": "npm run manifest && npm run build", + "clean": "rimraf lib android/build example/android/build example/android/app/build example/ios/build" + }, + "keywords": [ + "react-native", + "THEOplayer", + "ios", + "android", + "nielsen", + "adscript" + ], + "repository": { + "type": "git", + "url": "git@github.com:THEOplayer/react-native-connectors.git" + }, + "author": "THEO Technologies", + "license": "SEE LICENSE AT https://www.theoplayer.com/terms", + "homepage": "https://github.com/THEOplayer/react-native-connectors/tree/main/adscript#readme", + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-theoplayer": "^3 || ^7 || ^8 || ^9", + "theoplayer": "^5 || ^6 || ^7 || ^8 || ^9" + }, + "peerDependenciesMeta": { + "theoplayer": { + "optional": true + } + }, + "eslintIgnore": [ + "node_modules/", + "lib/" + ], + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "commonjs", + "module", + [ + "typescript", + { + "project": "tsconfig.build.json" + } + ] + ] + } +} diff --git a/adscript/react-native-theoplayer-adscript.podspec b/adscript/react-native-theoplayer-adscript.podspec new file mode 100644 index 00000000..c794c2e6 --- /dev/null +++ b/adscript/react-native-theoplayer-adscript.podspec @@ -0,0 +1,45 @@ +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "package.json"))) +folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' + +Pod::Spec.new do |s| + s.name = "react-native-theoplayer-adscript" + s.version = package["version"] + s.summary = package["description"] + s.homepage = package["homepage"] + s.license = package["license"] + s.authors = package["author"] + + s.platforms = { :ios => "13.4", :tvos => "13.4" } + s.source = { :git => "https://github.com/THEOplayer/react-native-connectors.git", :tag => "adscript_v#{s.version}" } + + s.source_files = "ios/**/*.{h,m,mm,swift}" + + s.dependency "react-native-theoplayer" + s.dependency "AdScriptApiClient" + s.dependency 'swift-collections', '~> 1.1' + + # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. + # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. + if respond_to?(:install_modules_dependencies, true) + install_modules_dependencies(s) + else + s.dependency "React-Core" + + # Don't install the dependencies when we run `pod install` in the old architecture. + if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then + s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" + s.pod_target_xcconfig = { + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", + "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" + } + s.dependency "React-Codegen" + s.dependency "RCT-Folly" + s.dependency "RCTRequired" + s.dependency "RCTTypeSafety" + s.dependency "ReactCommon/turbomodule/core" + end + end +end diff --git a/adscript/src/__tests__/index.test.tsx b/adscript/src/__tests__/index.test.tsx new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/adscript/src/__tests__/index.test.tsx @@ -0,0 +1 @@ +export {}; diff --git a/adscript/src/api/AdScriptConnector.ts b/adscript/src/api/AdScriptConnector.ts new file mode 100644 index 00000000..6a318b81 --- /dev/null +++ b/adscript/src/api/AdScriptConnector.ts @@ -0,0 +1,44 @@ +import type { THEOplayer } from 'react-native-theoplayer'; +import { AdScriptConnectorAdapter } from '../internal/AdScriptConnectorAdapter'; +import { AdScriptMetadata } from './AdScriptMetadata'; +import { AdScriptI12n } from './AdScriptI12n'; + +export class AdScriptConnector { + private connectorAdapter: AdScriptConnectorAdapter; + + /** + * Create AdScriptConnector + * + * @param player A React Native THEOplayer instance. + * @param implementationId The implementation ID you will receive from your Nielsen Admosphere representative. + * @param contentMetadata Content metadata. + * @param debug Whether to show debug logging. + * @public + */ + constructor(player: THEOplayer, implementationId: string, contentMetadata: AdScriptMetadata, debug?: boolean) { + this.connectorAdapter = new AdScriptConnectorAdapter(player, implementationId, contentMetadata, debug); + } + + /** + * Update metadata. + */ + updateMetadata(metadata: AdScriptMetadata) { + this.connectorAdapter.updateMetadata(metadata); + } + + /** + * Update logged-in user info. + */ + updateUser(user: AdScriptI12n) { + this.connectorAdapter.updateUser(user); + } + + /** + * Stops video and ad analytics and closes all sessions. + * + * @public + */ + destroy(): void { + this.connectorAdapter.destroy(); + } +} diff --git a/adscript/src/api/AdScriptI12n.ts b/adscript/src/api/AdScriptI12n.ts new file mode 100644 index 00000000..5902d31a --- /dev/null +++ b/adscript/src/api/AdScriptI12n.ts @@ -0,0 +1,12 @@ +/** + * Additional information about the logged-in user from the client database. + * + * @example + * ["866d3a3c-aa87-4fe7-9066-8286641edd17", // client-side user identifier (customerId) + * "cf895a00-a987-4501-ac00-6adb57014129", // client-side user device identifier (Android_ID) + * "9530321a-d3d0-4bda-8704-1eb94a5940c3", // client-side profile identifier of the logged-in user (profileId) + * "jsg75682-k276t-kw82-k8d5-8926sh6528j2", // optional - SW device identifier for a situation where there are multiple device IDs in the client DB (typically a) HW ID - fill in i2, b) SW ID - fill in i4). + * "ef3c6dc72a26912f07f0e733d51b46c771d807bf" // fingerprint of the user's email address + * ] + */ +export type AdScriptI12n = string[]; diff --git a/adscript/src/api/AdScriptMetadata.ts b/adscript/src/api/AdScriptMetadata.ts new file mode 100644 index 00000000..e87d4206 --- /dev/null +++ b/adscript/src/api/AdScriptMetadata.ts @@ -0,0 +1,115 @@ +export type AdScriptMetadata = { + /** + * Unique video content identifier. Any designation according to the needs of the TV company, + * which ensures identification of the same content across platforms. + * + * @example "abc98731568435405". + */ + assetId: string; + + /** + * Specifies the type of content being measured. For main video content, it always has value "content". + * + * @example "content". + */ + type: string; + + /** + * Program name. + * + * @example "Welcome to freedom". + */ + program?: string; + + /** + * Detailed description of the content, episode title. + * + * @example "Part 2 - 20.6.2018 20:00". + */ + title?: string; + + /** + * Length of video content in seconds. + * + * @example For VOD: "3359", for LIVE broadcast: "86400". + */ + length?: string; + + /** + * The designation of the original program from which the online video content was created. + * It may happen that, for example, three separate video files with unique assetIds are inserted into the + * TV Internet Archive, all three of which were created from one specific program. + * + * @example "915 954 39504". + */ + crossId?: string; + + /** + * Indication of whether this is a live broadcast. + * "1" - yes + * "0" - no + */ + livestream?: string; + + /** + * TV station code for live broadcast. (If the live broadcast does not correspond to any TV station, + * use the code "9999"). + * + * @example "4". + */ + channelId?: string; + + /** + * Item in the CMS tag reserved for an identifier allowing the attachment of an advertisement description from + * the RTVK system similarly to PEM TV data (ASMEA code) + * + * @example "1488725615". + */ + asmea?: string; + + /** + * More detailed categorization of the main video content. The value is the code according to the TV code list. + * + * {@link https://adscript.admosphere.cz/cz_metadata.html#line3_1} + * + * @example "1". + */ + attribute?: string; + + /** + * Name of the HbbTV application or 1st level website. + * + * @example "Homepage". + */ + sec1?: string; + + /** + * Categorization of static content within HbbTV or a website. Level 2 title. + * + * @example "Subpage2". + */ + sec2?: string; + + /** + * Categorization of static content within HbbTV or a website. Level 3 title. + * + * @example "Subpage3". + */ + sec3?: string; + + /** + * Categorization of static content within HbbTV or a website. Level 4 title. + * + * @example "Subpage4". + */ + sec4?: string; + + /** + * Static content referrer + * + * @example "redButton". + */ + ref?: string; + + [id: string]: string; +}; diff --git a/adscript/src/api/hooks/useAdScript.ts b/adscript/src/api/hooks/useAdScript.ts new file mode 100644 index 00000000..a2353358 --- /dev/null +++ b/adscript/src/api/hooks/useAdScript.ts @@ -0,0 +1,43 @@ +import { PlayerEventType, THEOplayer } from 'react-native-theoplayer'; +import { RefObject, useEffect, useRef } from 'react'; +import { AdScriptMetadata } from '../AdScriptMetadata'; +import { AdScriptConnector } from '../AdScriptConnector'; + +export function useAdScript( + implementationId: string, + contentMetadata: AdScriptMetadata, + debug?: boolean, +): [RefObject, (player: THEOplayer | undefined) => void] { + const connector = useRef(); + const theoPlayer = useRef(); + + const initialize = (player: THEOplayer | undefined) => { + // Optionally destroy existent connector + onDestroy(); + + theoPlayer.current = player; + if (player) { + connector.current = new AdScriptConnector(player, implementationId, contentMetadata, debug); + player.addEventListener(PlayerEventType.DESTROY, onDestroy); + } else { + throw new Error('Invalid THEOplayer instance'); + } + }; + + const onDestroy = () => { + if (connector.current) { + if (!theoPlayer.current) { + throw new Error('Invalid THEOplayer instance'); + } + theoPlayer.current.removeEventListener(PlayerEventType.DESTROY, onDestroy); + connector.current.destroy(); + connector.current = undefined; + } + }; + + useEffect(() => { + return onDestroy; + }, []); + + return [connector, initialize]; +} diff --git a/adscript/src/index.ts b/adscript/src/index.ts new file mode 100644 index 00000000..d06978bb --- /dev/null +++ b/adscript/src/index.ts @@ -0,0 +1,4 @@ +export * from './api/AdScriptConnector'; +export * from './api/AdScriptMetadata'; +export * from './api/AdScriptI12n'; +export { useAdScript } from './api/hooks/useAdScript'; diff --git a/adscript/src/internal/AdScriptConnectorAdapter.ts b/adscript/src/internal/AdScriptConnectorAdapter.ts new file mode 100644 index 00000000..1ba949e5 --- /dev/null +++ b/adscript/src/internal/AdScriptConnectorAdapter.ts @@ -0,0 +1,46 @@ +import type { THEOplayer } from 'react-native-theoplayer'; +import { NativeModules } from 'react-native'; +import { AdScriptMetadata } from '../api/AdScriptMetadata'; +import { AdScriptI12n } from '../api/AdScriptI12n'; + +const TAG = 'AdScriptConnector'; +const ERROR_MSG = 'AdScriptConnectorAdapter Error'; + +export class AdScriptConnectorAdapter { + constructor( + private player: THEOplayer, + implementationId: string, + contentMetadata: AdScriptMetadata, + debug?: boolean, + ) { + try { + NativeModules.AdScriptModule.initialize(this.player.nativeHandle || -1, implementationId, contentMetadata, debug ?? false); + } catch (error: unknown) { + console.error(TAG, `${ERROR_MSG}: ${error}`); + } + } + + updateMetadata(metadata: AdScriptMetadata) { + try { + NativeModules.AdScriptModule.updateMetadata(this.player.nativeHandle || -1, metadata); + } catch (error: unknown) { + console.error(TAG, `${ERROR_MSG}: ${error}`); + } + } + + updateUser(user: AdScriptI12n) { + try { + NativeModules.AdScriptModule.updateUser(this.player.nativeHandle || -1, user); + } catch (error: unknown) { + console.error(TAG, `${ERROR_MSG}: ${error}`); + } + } + + destroy(): void { + try { + NativeModules.AdScriptModule.destroy(this.player.nativeHandle || -1); + } catch (error: unknown) { + console.error(TAG, `${ERROR_MSG}: ${error}`); + } + } +} diff --git a/adscript/src/internal/AdScriptConnectorAdapter.web.ts b/adscript/src/internal/AdScriptConnectorAdapter.web.ts new file mode 100644 index 00000000..d87e329b --- /dev/null +++ b/adscript/src/internal/AdScriptConnectorAdapter.web.ts @@ -0,0 +1,17 @@ +import type { THEOplayer } from 'react-native-theoplayer'; +import { AdScriptMetadata } from '../api/AdScriptMetadata'; + +export class AdScriptConnectorAdapter { + constructor( + private player: THEOplayer, + implementationId: string, + contentMetadata: AdScriptMetadata, + debug?: boolean, + ) { + throw new Error('AdScript connector is not supported for Web.'); + } + + updateMetadata(metadata: AdScriptMetadata) {} + + destroy() {} +} diff --git a/adscript/src/manifest.json b/adscript/src/manifest.json new file mode 100644 index 00000000..4efadef6 --- /dev/null +++ b/adscript/src/manifest.json @@ -0,0 +1 @@ +{"version":"1.3.0","buildDate":"2025-08-04T09:46:38.897Z"} diff --git a/adscript/tsconfig.build.json b/adscript/tsconfig.build.json new file mode 100644 index 00000000..f929194b --- /dev/null +++ b/adscript/tsconfig.build.json @@ -0,0 +1,5 @@ + +{ + "extends": "./tsconfig", + "exclude": ["example", "ios", "android"] +} diff --git a/adscript/tsconfig.json b/adscript/tsconfig.json new file mode 100644 index 00000000..7ae3f792 --- /dev/null +++ b/adscript/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@theoplayer/react-native-analytics-adscript": [ + "./src/index" + ] + } + } +} diff --git a/adscript/typedoc.json b/adscript/typedoc.json new file mode 100644 index 00000000..0d320987 --- /dev/null +++ b/adscript/typedoc.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": [ + "../typedoc.base.json" + ], + "entryPoints": [ + "src/index.ts" + ], + "tsconfig": "tsconfig.json", + "readme": "README.md", + "name": "AdScript Connector" +} diff --git a/apps/e2e/android/app/build.gradle b/apps/e2e/android/app/build.gradle index 5fa17271..ac7611e3 100644 --- a/apps/e2e/android/app/build.gradle +++ b/apps/e2e/android/app/build.gradle @@ -116,6 +116,8 @@ dependencies { implementation project(path: ':react-native-theoplayer-analytics-conviva') implementation project(path: ':react-native-theoplayer-analytics-nielsen') implementation project(path: ':react-native-theoplayer-yospace') +// implementation project(path: ':react-native-theoplayer-analytics-adscript') +// implementation project(path: ':react-native-theoplayer-analytics-gemius') implementation "com.google.android.gms:play-services-cast-framework:${safeExtGet('castFrameworkVersion', '+')}" diff --git a/apps/e2e/android/app/libs/.gitignore b/apps/e2e/android/app/libs/.gitignore new file mode 100644 index 00000000..94b126ff --- /dev/null +++ b/apps/e2e/android/app/libs/.gitignore @@ -0,0 +1,2 @@ +*.aar +*.aar.gz diff --git a/apps/e2e/android/build.gradle b/apps/e2e/android/build.gradle index f2622cc3..767a6acb 100644 --- a/apps/e2e/android/build.gradle +++ b/apps/e2e/android/build.gradle @@ -2,7 +2,7 @@ buildscript { ext { - buildToolsVersion = "34.0.0" + buildToolsVersion = "35.0.0" // React Native 0.74+ needs minSdkVersion 23+ // supportsPictureInPicture and networkSecurityConfig need minSdkVersion 24+ minSdkVersion = 24 @@ -10,7 +10,7 @@ buildscript { targetSdkVersion = 35 ndkVersion = "26.1.10909125" castFrameworkVersion = "21.4.0" - kotlinVersion = "1.9.24" + kotlinVersion = "2.0.21" } repositories { google() @@ -19,7 +19,7 @@ buildscript { dependencies { classpath("com.android.tools.build:gradle") classpath("com.facebook.react:react-native-gradle-plugin") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") } } diff --git a/apps/e2e/android/gradle.properties b/apps/e2e/android/gradle.properties index 0f561443..f3009869 100644 --- a/apps/e2e/android/gradle.properties +++ b/apps/e2e/android/gradle.properties @@ -73,3 +73,9 @@ THEOplayer_timeUpdateRate = com.theoplayer.TimeUpdateRate.LIMITED_TWO_HZ # Toggle whether to use a media service for background playback #THEOplayer_usePlaybackService = true + +# Location of the adscript SDK +adscriptSdkDir=./app/libs/ + +# Location of the gemius SDK +gemiusSdkDir=./app/libs/ diff --git a/apps/e2e/android/settings.gradle b/apps/e2e/android/settings.gradle index bc5ec84d..3eb5cf14 100644 --- a/apps/e2e/android/settings.gradle +++ b/apps/e2e/android/settings.gradle @@ -14,3 +14,7 @@ include ':react-native-theoplayer-analytics-nielsen' project(':react-native-theoplayer-analytics-nielsen').projectDir = new File(rootProject.projectDir, '../../../nielsen/android') include ':react-native-theoplayer-yospace' project(':react-native-theoplayer-yospace').projectDir = new File(rootProject.projectDir, '../../../yospace/android') +// include ':react-native-theoplayer-analytics-adscript' +// project(':react-native-theoplayer-analytics-adscript').projectDir = new File(rootProject.projectDir, '../../../adscript/android') +// include ':react-native-theoplayer-analytics-gemius' +// project(':react-native-theoplayer-analytics-gemius').projectDir = new File(rootProject.projectDir, '../../../gemius/android') diff --git a/apps/e2e/ios/Podfile.lock b/apps/e2e/ios/Podfile.lock index b9b02fa5..f2a0bbfd 100644 --- a/apps/e2e/ios/Podfile.lock +++ b/apps/e2e/ios/Podfile.lock @@ -1277,13 +1277,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-theoplayer (9.3.0): + - react-native-theoplayer (9.9.1): - React-Core - - THEOplayer-Connector-SideloadedSubtitle (~> 9.3) - - THEOplayer-Integration-GoogleCast (~> 9.3) - - THEOplayer-Integration-GoogleIMA (~> 9.3) - - THEOplayer-Integration-THEOlive (~> 9.3) - - THEOplayerSDK-core (~> 9.3) + - THEOplayer-Connector-SideloadedSubtitle (~> 9.9) + - THEOplayer-Integration-GoogleCast (~> 9.9) + - THEOplayer-Integration-GoogleIMA (~> 9.9) + - THEOplayer-Integration-THEOlive (~> 9.9) + - THEOplayerSDK-core (~> 9.9) - react-native-theoplayer-comscore (1.10.0): - DoubleConversion - glog @@ -1622,41 +1622,41 @@ PODS: - SwiftSubtitles (0.9.1): - DSFRegex (~> 3.3.1) - TinyCSV (~> 0.6.1) - - THEOplayer-Connector-Comscore (9.5.0): + - THEOplayer-Connector-Comscore (9.10.1): - ComScore (~> 6.10.0) - THEOplayerSDK-core (~> 9) - - THEOplayer-Connector-Conviva (9.5.0): + - THEOplayer-Connector-Conviva (9.10.1): - ConvivaSDK (= 4.0.51) - - THEOplayer-Connector-Utilities (>= 9.5.0, ~> 9.5) + - THEOplayer-Connector-Utilities (>= 9.10.1, ~> 9.10) - THEOplayerSDK-core (~> 9) - THEOplayer-Connector-Nielsen (9.0.0): - NielsenAppSDK-XC (~> 9) - THEOplayer-Connector-Utilities (>= 9.0.0, ~> 9.0) - THEOplayerSDK-core (~> 9) - - THEOplayer-Connector-SideloadedSubtitle (9.5.0): + - THEOplayer-Connector-SideloadedSubtitle (9.10.1): - Swifter (= 1.5.0) - SwiftSubtitles (= 0.9.1) - THEOplayerSDK-core (~> 9) - - THEOplayer-Connector-Utilities (9.5.0): + - THEOplayer-Connector-Utilities (9.10.1): - ConvivaSDK (= 4.0.51) - THEOplayerSDK-core (~> 9) - - THEOplayer-Integration-GoogleCast (9.5.0): - - THEOplayer-Integration-GoogleCast/Base (= 9.5.0) - - THEOplayer-Integration-GoogleCast/Dependencies (= 9.5.0) - - THEOplayer-Integration-GoogleCast/Base (9.5.0) - - THEOplayer-Integration-GoogleCast/Dependencies (9.5.0): + - THEOplayer-Integration-GoogleCast (9.10.1): + - THEOplayer-Integration-GoogleCast/Base (= 9.10.1) + - THEOplayer-Integration-GoogleCast/Dependencies (= 9.10.1) + - THEOplayer-Integration-GoogleCast/Base (9.10.1) + - THEOplayer-Integration-GoogleCast/Dependencies (9.10.1): - google-cast-sdk-dynamic-xcframework (~> 4.8) - - THEOplayer-Integration-GoogleIMA (9.5.0): - - THEOplayer-Integration-GoogleIMA/Base (= 9.5.0) - - THEOplayer-Integration-GoogleIMA/Dependencies (= 9.5.0) - - THEOplayer-Integration-GoogleIMA/Base (9.5.0) - - THEOplayer-Integration-GoogleIMA/Dependencies (9.5.0): + - THEOplayer-Integration-GoogleIMA (9.10.1): + - THEOplayer-Integration-GoogleIMA/Base (= 9.10.1) + - THEOplayer-Integration-GoogleIMA/Dependencies (= 9.10.1) + - THEOplayer-Integration-GoogleIMA/Base (9.10.1) + - THEOplayer-Integration-GoogleIMA/Dependencies (9.10.1): - GoogleAds-IMA-iOS-SDK (~> 3.18) - GoogleAds-IMA-tvOS-SDK (~> 4.8) - - THEOplayer-Integration-THEOlive (9.5.0): - - THEOplayer-Integration-THEOlive/Base (= 9.5.0) - - THEOplayer-Integration-THEOlive/Base (9.5.0) - - THEOplayerSDK-core (9.5.0) + - THEOplayer-Integration-THEOlive (9.10.1): + - THEOplayer-Integration-THEOlive/Base (= 9.10.1) + - THEOplayer-Integration-THEOlive/Base (9.10.1) + - THEOplayerSDK-core (9.10.1) - TinyCSV (0.6.1) - Yoga (0.0.0) @@ -1923,83 +1923,83 @@ SPEC CHECKSUMS: hermes-engine: ac68d6c3169772a7a7f9eeb25dbb5ff87930034f NielsenAppSDK-XC: a32c6b55d158dde2366152fe13406dea588929d6 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 - RCT-Folly: 2d9ee9f24278cbaf99cfdd0720a87c3a8ff152a6 + RCT-Folly: 4728c42e04357ad445c7048e7c542b59f3ee6b4f RCTDeprecation: 3d8708756d84c6f6bce6b0e538bbc109ada51385 RCTRequired: 13a733ef574d5b7a99cbb6ed9d2e729aa0980383 RCTTypeSafety: 541082d481bd123e7a985ac75e04536a6b2fc591 React: af752f9ddaffdd4835b5522014db0b2bc15c7780 React-callinvoker: 196463beafe3a3db7a9382124d57d0e1bc2fafbf - React-Core: c394313d359e3175899ef8889ff06a9d94be9f8f - React-CoreModules: 9f4f3d996af44ff509454f87942605aae793f4f0 - React-cxxreact: bea5c7ce2c63d5a005621dbe21075afb5699246b + React-Core: dbd0eb507eb92ba8ccdbe9e995a253382f0d0659 + React-CoreModules: 73750929751f3f9d6acb7dc76f73d72b2ffb736e + React-cxxreact: 766380b9459a5f4e1d2f9b31832e8fcff6b7be6d React-debug: 2f519ade026b776867eca46fe4581dfe9b62e5b5 - React-defaultsnativemodule: bf7b67f632601aa3039fe8ad5adaf5675ab2207c - React-domnativemodule: ff2731cf778ac5e0753a8341c6a9fbe5d436ee7e - React-Fabric: b70ca05937b2d5027f1110e8b0bd799733332db4 - React-FabricComponents: 0f26d2799f7f414395d961d5b9367263ead359b9 - React-FabricImage: 6605feaddeac81ce252b68256ed4e2e5d8ac49b7 + React-defaultsnativemodule: 06238b62515b2f45ccac59796e2be6f744e07cc4 + React-domnativemodule: 548687bd939068141b5982dfcee11839db38c6fc + React-Fabric: ca8ae5c40e322a2682dc606a168547e202c0edda + React-FabricComponents: b0331e740462e515a3cc32ed48230ccda083ae98 + React-FabricImage: 722eb881d0040c622f949a2a924d5d2c7e0ae374 React-featureflags: ca676d9c7d20b0dbc5f781bac2c0b3ee16619263 - React-featureflagsnativemodule: 5a8df629249ab10c6e765a41efe9ccc75e20f7d5 - React-graphics: 2df3b86d47f16e6c863defc1d9cb220c2a46111e - React-hermes: bfb3df16e4d67a4e58d437dd07ddeab3ee8c68d1 - React-idlecallbacksnativemodule: 2796b26e146c11908bd20d3fb6c99faf4aaff0a2 - React-ImageManager: cfc12df8c28cabaf9917937ed6844d3ded0c68e1 - React-jserrorhandler: e03ceddee09d5d57ade8cf439d65a14b6cddce46 - React-jsi: e5d4658e2fe1df30ffd7be89660f997c9187ab4f - React-jsiexecutor: 75b92bbfdeee55cc1b758574b5641c40d3e1d724 - React-jsinspector: a8266b659d73df806d8dc57d928796a8c2eb8ab1 - React-jsitracing: 17b8fe5f99910033d30c155812544bca9f2a2512 - React-logger: 42594c81767ea1f0c5341d971e8c754d3517b11f - React-Mapbuffer: a17da7731000c79ef66ee89d9f888426d5e01635 - React-microtasksnativemodule: fbe3567e51a8c22e374e77638d681b65a5119f0a - react-native-google-cast: 872b05bf12f7db32c2f66818fdd5406474af25f1 - react-native-slider: fc7f35c082abec47e341dfe43657a1c26f38db2f - react-native-theoplayer: 9fb718c5f2bfe73d93d6ac4d3db9b79769f72b6f - react-native-theoplayer-comscore: b02ae6fc53965ade5f7a6bd54170d442bd1addda - react-native-theoplayer-conviva: 8747c7f866c71cda89b16861c64e690689fb3416 - react-native-theoplayer-nielsen: 71609b83a43224e9cd10fed334b6c1c91e6c6157 + React-featureflagsnativemodule: 3951f061e4d16d59ffc294fdb935b0c9c52ef1e2 + React-graphics: 582a67f03ec691d7e06a0601567c2131c977d985 + React-hermes: f28a9afe7951bac61a9a6daa7b76c6416d3461bc + React-idlecallbacksnativemodule: a14afed4c635ec0f685dece2580c72719098afce + React-ImageManager: 08319519439571ad007ebb5592663b8bb01e442e + React-jserrorhandler: cff5ddef8ce3c67098b0c58ed9d6258d67e99f91 + React-jsi: e57747fd7928672d87537a39bf1ffd031610ba3a + React-jsiexecutor: 6c6de186f89a5511a21c47594d173d19565768e3 + React-jsinspector: 638786af723d5758d6ca973955b9e7ba0f1da20d + React-jsitracing: 43fe823d1df0218131e2f46355ba24e1a895cc8b + React-logger: 0d08bfa620586b8dc472f58dfbd6217b0d228f2c + React-Mapbuffer: d9594fa247e7866b44249d5e04a68ace76af2841 + React-microtasksnativemodule: a3489ca37b515f6f685ec1a86c8df364343ac578 + react-native-google-cast: d7bdfd1a0eeba84afde03b9722351ec29543e74c + react-native-slider: 4a0f3386a38fc3d2d955efc515aef7096f7d1ee4 + react-native-theoplayer: ce0550e68da3288301d6292457b92adfcd730c6c + react-native-theoplayer-comscore: ebaa08bd40b87f0cb1e1849d94c4113db7f9861a + react-native-theoplayer-conviva: 064d394532404ed35ca7d1ccd689fc1de208f73a + react-native-theoplayer-nielsen: a8c8b98ba57e03dc662a747b4750c48a3a3f80f5 React-nativeconfig: ea22f0ab525feb865d2e0ed5d7aad156c36abe6b - React-NativeModulesApple: eb4740b113517d23b482a5bf45ec887561fde447 + React-NativeModulesApple: 5efee2e69aaa7ff47f40a2918f2b48534a2e431b React-perflogger: f31660a8693c3444e1832c237ba25a13f613436e - React-performancetimeline: 4a440850e67b0bbd3466d07f43b27cd36cbd7564 + React-performancetimeline: dcd29523710add9e90a4f1166d67b78b955a21ce React-RCTActionSheet: 8c6af175df9c6714ce7a5eba1d5283c0d4b3d084 - React-RCTAnimation: f5c8c43860a8669db69976bfc79e6140d5032d17 - React-RCTAppDelegate: 81c05e40089272a81d13e2c00b4dc0e43d930079 - React-RCTBlob: 5633368a4c5544e0e81f705d5c3176e774b4bc45 - React-RCTFabric: 607efda657391f6e70de8d9d767da5f0f5e82d6a - React-RCTImage: 8d8866a5c4db6182ecc492e24800115c61882ee1 - React-RCTLinking: f4ad99fd13735cc914224c1015c5abdf7de2c295 - React-RCTNetwork: c31ad41b5485a4ef800ba0ebc56898f8da267c53 - React-RCTSettings: a47ef8bf8f0d112b994fa6e0cfc038e778cbd070 - React-RCTText: b520e0b6254f4ecd362adb9e0baa223ee817dbcc - React-RCTVibration: 181264593f8b6fc3efa4eeb6267400ee55fa0001 + React-RCTAnimation: 7eef1a8c9f9159e73f1c6a4c1aee2161213aabc6 + React-RCTAppDelegate: 8f6d5bd90c1460195a945621ca6db446727327eb + React-RCTBlob: 454a83cc3928a4e2ec9543388fc0ad191a2fcb52 + React-RCTFabric: f0a35f40363698cca6a4da8cc53919cb328a84d4 + React-RCTImage: d84301136ae487b5db286d6367bb2c477d3a63b2 + React-RCTLinking: d51889e9321851c0c1432b935495d125a2e2dbbd + React-RCTNetwork: 74779cdac22264f1a5d21bb5a98c47dc94d7b2ca + React-RCTSettings: 6e07373e02f58f0af009545245b5ada398fb50bc + React-RCTText: 5feef8ba6849fb2589616a6c68193a979707ae60 + React-RCTVibration: e3f84af731628e6b48093c1edf7214449e6917e8 React-rendererconsistency: 041d8ba3309a91bf456063e649e1988eac21810c - React-rendererdebug: b2059115d1cecb509f9b04e9eafef96d81176f56 + React-rendererdebug: 4eaba2da7bcb7ce439098a6dfa5c411411f8d4d2 React-rncore: 81e6dabad294ad1f33c88baa760a6b5298213c73 - React-RuntimeApple: fecc3cbae6cf8e19824bc01776be318a62121aca - React-RuntimeCore: 23a6488c6955b859512830d8a3b309e337817db4 + React-RuntimeApple: 1e598aa4a4f64a86ec50e3487799c2c9bc1749d5 + React-RuntimeCore: e09f9b7fa60eda245ab35c823e90f7463a901b8a React-runtimeexecutor: afa7158e5b848205048c3afc47e828b2ebf09f7d - React-RuntimeHermes: 46f7d8eb93d4db1263a1056f4f9fbe30d82cbe61 - React-runtimescheduler: bce052173a90c0036e2b7bed44fe6716b5fa3bf7 - React-utils: e7bf11fa100826bb5b011ded0062eccda5067a2a - ReactCodegen: 20d036906cfefa5f9b6bb2ea5d1808aafe9201c9 - ReactCommon: 944ff65876eb80b58b039f13b5ad8b3e2ad7ef27 - RNDeviceInfo: 98bb51ba1519cd3f19f14e7236b5bb1c312c780f - RNSVG: 46769c92d1609e617dbf9326ad8a0cff912d0982 + React-RuntimeHermes: 6d3cbb37c6a99520b89e8f26f51de2d4cf7e053e + React-runtimescheduler: 2285ca64c8fa06f6ad5d8af92309d658d9ae9622 + React-utils: 8289642e20d751967cb4e685956e5197cae2188c + ReactCodegen: 873a3cfea4daf5b7f86d49361cc049831a45d840 + ReactCommon: a1cd388360d4e9545ab8e571ff293606c9ea6687 + RNDeviceInfo: 59344c19152c4b2b32283005f9737c5c64b42fba + RNSVG: 669ed128ab9005090c612a0d627dbecb6ab5c76f SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Swifter: e71dd674404923d7f03ebb03f3f222d1c570bc8e SwiftSubtitles: c659af19d710a2946779015464c0577d07fe4666 - THEOplayer-Connector-Comscore: 7cdab7db28ebb7396bd6f2d11a8e7d33e26abd05 - THEOplayer-Connector-Conviva: 8fe3747ff439f40deaf6a527484bb96101d665a8 + THEOplayer-Connector-Comscore: aa8dfa0384555041d3a8c02fd1789598e5d56fbd + THEOplayer-Connector-Conviva: 9e4bf09269065da5559b154f8d6104c26617a16e THEOplayer-Connector-Nielsen: dc6d2b2c0701283fe60c1c804d3403b3d8d66181 - THEOplayer-Connector-SideloadedSubtitle: 4e03b39ac277d590fe8fcbe174a00c98635f2a9c - THEOplayer-Connector-Utilities: 19d73a93fde9e91b165475d647c513e5e9083114 - THEOplayer-Integration-GoogleCast: 05e141d974a3c77e9fe977154354e3d2f51fe012 - THEOplayer-Integration-GoogleIMA: 90010535d28e0de15bb4c1c68977ade2f613a46c - THEOplayer-Integration-THEOlive: 10256a337e2a249f5cac066fbe6097af5a33c70d - THEOplayerSDK-core: 86b39a98dbcab0dd5e8ce02a4d741dbdb49551d5 + THEOplayer-Connector-SideloadedSubtitle: f5a1edab54372c8e3b96b2ccea6fac85db3d6a92 + THEOplayer-Connector-Utilities: 0fa21bc82f84b4523510dd16ca9cb1786d1ad307 + THEOplayer-Integration-GoogleCast: 44a44e356f2f8d87e62da127b274d5747d7dafae + THEOplayer-Integration-GoogleIMA: 6b74abcebc73aab0c3dea5b2ecb2d5e44ab95b45 + THEOplayer-Integration-THEOlive: 6ccaaf6b99f3b399b6aadb38ce3b08d539dcc982 + THEOplayerSDK-core: 65edd54e1b40b9c82cebec53f47b2b7777d81ed9 TinyCSV: fd6228edbcf1c07466ac34b76dac5e052143eaba - Yoga: 1eb8c4882b3018c344a2ff61c5f2e5c6b1711d82 + Yoga: 07ebe50bd234e51e5e3e07befa14a3078a0fcbbd PODFILE CHECKSUM: baa77347c1627215315b6f99b250aab99cddbb2e diff --git a/apps/e2e/ios/ReactNativeTHEOplayer.xcodeproj/project.pbxproj b/apps/e2e/ios/ReactNativeTHEOplayer.xcodeproj/project.pbxproj index 44cb66dc..6347aa58 100644 --- a/apps/e2e/ios/ReactNativeTHEOplayer.xcodeproj/project.pbxproj +++ b/apps/e2e/ios/ReactNativeTHEOplayer.xcodeproj/project.pbxproj @@ -11,17 +11,17 @@ 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 1D9FB4F5244ADB9A9476E40F /* libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 23E00601FCEB429652C6408F /* libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a */; }; 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 2DCD954D1E0B4F2C00145EB5 /* ReactNativeTHEOplayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ReactNativeTHEOplayerTests.m */; }; + 2EE1497105BEF36614E65375 /* libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE2DA74F86530FCAA0F76894 /* libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a */; }; 442BFC3895EC4E39B762450A /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = E13BF0204B80026411EC76D2 /* PrivacyInfo.xcprivacy */; }; - 7D9BA408F65A91301883B2AE /* libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A616D4268BA584D7B7DD45 /* libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a */; }; + 4FB1DB1E9987979DEBCC4FC4 /* libPods-ReactNativeTHEOplayer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B1D161D72CC81D6F34B818B /* libPods-ReactNativeTHEOplayer.a */; }; + 70CE312D16DEB809B138E59C /* libPods-ReactNativeTHEOplayer-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 86A3F444751038514D315CC6 /* libPods-ReactNativeTHEOplayer-tvOS.a */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; - D7D6759FBE424E107C798C00 /* libPods-ReactNativeTHEOplayer-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B1F82D8697B6422C62C08B4 /* libPods-ReactNativeTHEOplayer-tvOS.a */; }; DC6595BC1C39D39B157BFA59 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 7740B1311B6C85C9C1C70CE2 /* PrivacyInfo.xcprivacy */; }; - EA3FB8B3CECC8EC0D507A5E6 /* libPods-ReactNativeTHEOplayer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E6461124ED8CD324BEC84C8F /* libPods-ReactNativeTHEOplayer.a */; }; + E9DF7D274A28A97A957224C7 /* libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C574172A94A8AACB015539FC /* libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -46,33 +46,33 @@ 00E356EE1AD99517003FC87E /* ReactNativeTHEOplayerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeTHEOplayerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* ReactNativeTHEOplayerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeTHEOplayerTests.m; sourceTree = ""; }; + 06D65152529DA3A7B6EC728D /* Pods-ReactNativeTHEOplayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer.release.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* ReactNativeTHEOplayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReactNativeTHEOplayer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeTHEOplayer/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ReactNativeTHEOplayer/AppDelegate.m; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ReactNativeTHEOplayer/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeTHEOplayer/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeTHEOplayer/main.m; sourceTree = ""; }; - 212158D9C4053BB12B721822 /* Pods-ReactNativeTHEOplayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer.release.xcconfig"; sourceTree = ""; }; - 23E00601FCEB429652C6408F /* libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 26849ED127A3FF7900FFFB02 /* ReactNativeTHEOplayer-tvOS-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ReactNativeTHEOplayer-tvOS-Bridging-Header.h"; sourceTree = ""; }; 26849ED227A3FF8800FFFB02 /* ReactNativeTHEOplayer-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ReactNativeTHEOplayer-Bridging-Header.h"; sourceTree = ""; }; + 2859932C44A7266BC9A26016 /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.release.xcconfig"; sourceTree = ""; }; + 2B1869737A6D3574FB9FCB91 /* Pods-ReactNativeTHEOplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer.debug.xcconfig"; sourceTree = ""; }; + 2B1D161D72CC81D6F34B818B /* libPods-ReactNativeTHEOplayer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeTHEOplayer.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 2D02E47B1E0B4A5D006451C7 /* ReactNativeTHEOplayer-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ReactNativeTHEOplayer-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 2D02E4901E0B4A5D006451C7 /* ReactNativeTHEOplayer-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ReactNativeTHEOplayer-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 37F2476D4FFC19937969BA39 /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.release.xcconfig"; sourceTree = ""; }; - 53B450B3183AC1FCE970C242 /* Pods-ReactNativeTHEOplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer.debug.xcconfig"; sourceTree = ""; }; - 586FDDD06EFF4AC6D6026609 /* Pods-ReactNativeTHEOplayer-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS.debug.xcconfig"; sourceTree = ""; }; + 67508AB782AB1AEF1725FF10 /* Pods-ReactNativeTHEOplayer-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-tvOS.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS.release.xcconfig"; sourceTree = ""; }; 7740B1311B6C85C9C1C70CE2 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ReactNativeTHEOplayer/PrivacyInfo.xcprivacy; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeTHEOplayer/LaunchScreen.storyboard; sourceTree = ""; }; - 90DCD6370FFDC116ADAF7756 /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.debug.xcconfig"; sourceTree = ""; }; - 9B1F82D8697B6422C62C08B4 /* libPods-ReactNativeTHEOplayer-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeTHEOplayer-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - A1A616D4268BA584D7B7DD45 /* libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - D726618BE2FF02B74D4B3B33 /* Pods-ReactNativeTHEOplayer-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-tvOS.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS.release.xcconfig"; sourceTree = ""; }; + 86A3F444751038514D315CC6 /* libPods-ReactNativeTHEOplayer-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeTHEOplayer-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 8C4B9785AAB059DC6B233E3B /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.debug.xcconfig"; sourceTree = ""; }; + C574172A94A8AACB015539FC /* libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + CE2DA74F86530FCAA0F76894 /* libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + CEEC6FD978D0E9BD7B92494E /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.release.xcconfig"; sourceTree = ""; }; + D466E8BC201BE02656CD9B59 /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.debug.xcconfig"; sourceTree = ""; }; E13BF0204B80026411EC76D2 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = ReactNativeTHEOplayer/PrivacyInfo.xcprivacy; sourceTree = ""; }; - E6461124ED8CD324BEC84C8F /* libPods-ReactNativeTHEOplayer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeTHEOplayer.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - E94FE6E5DE0E478F2A505A3B /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.debug.xcconfig"; sourceTree = ""; }; + E69795734BECC7F9A40F1181 /* Pods-ReactNativeTHEOplayer-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS.debug.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; - FC92E903EC76001BC932F05F /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -80,7 +80,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7D9BA408F65A91301883B2AE /* libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a in Frameworks */, + 2EE1497105BEF36614E65375 /* libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -88,7 +88,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EA3FB8B3CECC8EC0D507A5E6 /* libPods-ReactNativeTHEOplayer.a in Frameworks */, + 4FB1DB1E9987979DEBCC4FC4 /* libPods-ReactNativeTHEOplayer.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -96,7 +96,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D7D6759FBE424E107C798C00 /* libPods-ReactNativeTHEOplayer-tvOS.a in Frameworks */, + 70CE312D16DEB809B138E59C /* libPods-ReactNativeTHEOplayer-tvOS.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -104,7 +104,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1D9FB4F5244ADB9A9476E40F /* libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a in Frameworks */, + E9DF7D274A28A97A957224C7 /* libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -150,10 +150,10 @@ children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED2971642150620600B7C4FE /* JavaScriptCore.framework */, - E6461124ED8CD324BEC84C8F /* libPods-ReactNativeTHEOplayer.a */, - A1A616D4268BA584D7B7DD45 /* libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a */, - 9B1F82D8697B6422C62C08B4 /* libPods-ReactNativeTHEOplayer-tvOS.a */, - 23E00601FCEB429652C6408F /* libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a */, + 2B1D161D72CC81D6F34B818B /* libPods-ReactNativeTHEOplayer.a */, + CE2DA74F86530FCAA0F76894 /* libPods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.a */, + 86A3F444751038514D315CC6 /* libPods-ReactNativeTHEOplayer-tvOS.a */, + C574172A94A8AACB015539FC /* libPods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.a */, ); name = Frameworks; sourceTree = ""; @@ -195,14 +195,14 @@ 9B1644CBAE2FABCCC6E29A69 /* Pods */ = { isa = PBXGroup; children = ( - 53B450B3183AC1FCE970C242 /* Pods-ReactNativeTHEOplayer.debug.xcconfig */, - 212158D9C4053BB12B721822 /* Pods-ReactNativeTHEOplayer.release.xcconfig */, - E94FE6E5DE0E478F2A505A3B /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.debug.xcconfig */, - 37F2476D4FFC19937969BA39 /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.release.xcconfig */, - 586FDDD06EFF4AC6D6026609 /* Pods-ReactNativeTHEOplayer-tvOS.debug.xcconfig */, - D726618BE2FF02B74D4B3B33 /* Pods-ReactNativeTHEOplayer-tvOS.release.xcconfig */, - 90DCD6370FFDC116ADAF7756 /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.debug.xcconfig */, - FC92E903EC76001BC932F05F /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.release.xcconfig */, + 2B1869737A6D3574FB9FCB91 /* Pods-ReactNativeTHEOplayer.debug.xcconfig */, + 06D65152529DA3A7B6EC728D /* Pods-ReactNativeTHEOplayer.release.xcconfig */, + D466E8BC201BE02656CD9B59 /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.debug.xcconfig */, + 2859932C44A7266BC9A26016 /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.release.xcconfig */, + E69795734BECC7F9A40F1181 /* Pods-ReactNativeTHEOplayer-tvOS.debug.xcconfig */, + 67508AB782AB1AEF1725FF10 /* Pods-ReactNativeTHEOplayer-tvOS.release.xcconfig */, + 8C4B9785AAB059DC6B233E3B /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.debug.xcconfig */, + CEEC6FD978D0E9BD7B92494E /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -214,12 +214,12 @@ isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeTHEOplayerTests" */; buildPhases = ( - BF7D9DA46C8DD41F30E94E14 /* [CP] Check Pods Manifest.lock */, + 5526C01D810116E99F3A4679 /* [CP] Check Pods Manifest.lock */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, - 5BA2B0560E7D7CE9AF0D0818 /* [CP] Embed Pods Frameworks */, - 0B8CC744116E1B5E5B20EA04 /* [CP] Copy Pods Resources */, + AF252140E32770FE8BFE8D3F /* [CP] Embed Pods Frameworks */, + 086C153BFE3C5915E73DC4C0 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -235,14 +235,14 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeTHEOplayer" */; buildPhases = ( - B9E191269F06C2C6DE0B7BEE /* [CP] Check Pods Manifest.lock */, + 7BBA710A33E470D2F583B9A6 /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - FFBBB750AB49D2A12D4B9411 /* [CP] Embed Pods Frameworks */, - 748F9736B5DB84741462615C /* [CP] Copy Pods Resources */, + 1D15609091359AD4D3ABA416 /* [CP] Embed Pods Frameworks */, + 0CB8C49FAC1F771517CA3173 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -257,14 +257,14 @@ isa = PBXNativeTarget; buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "ReactNativeTHEOplayer-tvOS" */; buildPhases = ( - 46AD3B3302DE9D5FAFA699CC /* [CP] Check Pods Manifest.lock */, + 4B57619928400C79996728F6 /* [CP] Check Pods Manifest.lock */, FD10A7F122414F3F0027D42C /* Start Packager */, 2D02E4771E0B4A5D006451C7 /* Sources */, 2D02E4781E0B4A5D006451C7 /* Frameworks */, 2D02E4791E0B4A5D006451C7 /* Resources */, 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */, - 3EAE09C2B98A8448EAB946E9 /* [CP] Embed Pods Frameworks */, - 19B54E206FAAF7D707797670 /* [CP] Copy Pods Resources */, + B8D57C04307543F132F63F30 /* [CP] Embed Pods Frameworks */, + A898285FBD4906492CE0B89C /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -279,12 +279,12 @@ isa = PBXNativeTarget; buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "ReactNativeTHEOplayer-tvOSTests" */; buildPhases = ( - AB3A88D1CB03C39A24F1F209 /* [CP] Check Pods Manifest.lock */, + B5D3C1C3CFB215CE85008475 /* [CP] Check Pods Manifest.lock */, 2D02E48C1E0B4A5D006451C7 /* Sources */, 2D02E48D1E0B4A5D006451C7 /* Frameworks */, 2D02E48E1E0B4A5D006451C7 /* Resources */, - 683118538B5068DC1C841822 /* [CP] Embed Pods Frameworks */, - 28E824AD327E0F20519619D8 /* [CP] Copy Pods Resources */, + AA539643D5EC434F1B2CA0F5 /* [CP] Embed Pods Frameworks */, + 16EBC9494DF6037754DC5E54 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -395,7 +395,7 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; - 0B8CC744116E1B5E5B20EA04 /* [CP] Copy Pods Resources */ = { + 086C153BFE3C5915E73DC4C0 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -431,26 +431,28 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 19B54E206FAAF7D707797670 /* [CP] Copy Pods Resources */ = { + 0CB8C49FAC1F771517CA3173 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/ComScore-tvOS/SCORBundle.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly-tvOS/RCT-Folly_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo-tvOS/RNDeviceInfoPrivacyInfo.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNSVG-tvOS/RNSVGFilters.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core-tvOS/React-Core_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact-tvOS/React-cxxreact_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/boost-tvOS/boost_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/glog-tvOS/glog_privacy.bundle", + "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/ComScore-iOS/SCORBundle.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly-iOS/RCT-Folly_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo-iOS/RNDeviceInfoPrivacyInfo.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNSVG-iOS/RNSVGFilters.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core-iOS/React-Core_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact-iOS/React-cxxreact_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/boost-iOS/boost_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/glog-iOS/glog_privacy.bundle", "${PODS_ROOT}/../../node_modules/react-native-theoplayer/ios/style.css", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SCORBundle.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle", @@ -462,10 +464,10 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 28E824AD327E0F20519619D8 /* [CP] Copy Pods Resources */ = { + 16EBC9494DF6037754DC5E54 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -499,51 +501,55 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { + 1D15609091359AD4D3ABA416 /* [CP] Embed Pods Frameworks */ = { 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"; - }; - 3EAE09C2B98A8448EAB946E9 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer-frameworks.sh", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ComScore/Dynamic/ComScore.framework/ComScore", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-iOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds", "${PODS_XCFRAMEWORKS_BUILD_DIR}/NielsenAppSDK-XC/NielsenAppApi.framework/NielsenAppApi", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleCast/Base/THEOplayerGoogleCastIntegration.framework/THEOplayerGoogleCastIntegration", "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration", "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-THEOlive/Base/THEOplayerTHEOliveIntegration.framework/THEOplayerTHEOliveIntegration", "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/google-cast-sdk-dynamic-xcframework/GoogleCast.framework/GoogleCast", "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-tvOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ComScore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NielsenAppApi.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleCastIntegration.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerTHEOliveIntegration.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleCast.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 46AD3B3302DE9D5FAFA699CC /* [CP] Check Pods Manifest.lock */ = { + 2D02E4CB1E0B4B27006451C7 /* 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"; + }; + 4B57619928400C79996728F6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -565,92 +571,70 @@ 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; }; - 5BA2B0560E7D7CE9AF0D0818 /* [CP] Embed Pods Frameworks */ = { + 5526C01D810116E99F3A4679 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/ComScore/Dynamic/ComScore.framework/ComScore", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-iOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/NielsenAppSDK-XC/NielsenAppApi.framework/NielsenAppApi", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleCast/Base/THEOplayerGoogleCastIntegration.framework/THEOplayerGoogleCastIntegration", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-THEOlive/Base/THEOplayerTHEOliveIntegration.framework/THEOplayerTHEOliveIntegration", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/google-cast-sdk-dynamic-xcframework/GoogleCast.framework/GoogleCast", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ComScore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NielsenAppApi.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleCastIntegration.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerTHEOliveIntegration.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleCast.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + "$(DERIVED_FILE_DIR)/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests-frameworks.sh\"\n"; + 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; }; - 683118538B5068DC1C841822 /* [CP] Embed Pods Frameworks */ = { + 7BBA710A33E470D2F583B9A6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/ComScore/Dynamic/ComScore.framework/ComScore", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/NielsenAppSDK-XC/NielsenAppApi.framework/NielsenAppApi", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-THEOlive/Base/THEOplayerTHEOliveIntegration.framework/THEOplayerTHEOliveIntegration", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-tvOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ComScore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NielsenAppApi.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerTHEOliveIntegration.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework", + "$(DERIVED_FILE_DIR)/Pods-ReactNativeTHEOplayer-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests-frameworks.sh\"\n"; + 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; }; - 748F9736B5DB84741462615C /* [CP] Copy Pods Resources */ = { + A898285FBD4906492CE0B89C /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/ComScore-iOS/SCORBundle.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly-iOS/RCT-Folly_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo-iOS/RNDeviceInfoPrivacyInfo.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNSVG-iOS/RNSVGFilters.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core-iOS/React-Core_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact-iOS/React-cxxreact_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/boost-iOS/boost_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/glog-iOS/glog_privacy.bundle", + "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/ComScore-tvOS/SCORBundle.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly-tvOS/RCT-Folly_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo-tvOS/RNDeviceInfoPrivacyInfo.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNSVG-tvOS/RNSVGFilters.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core-tvOS/React-Core_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact-tvOS/React-cxxreact_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/boost-tvOS/boost_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/glog-tvOS/glog_privacy.bundle", "${PODS_ROOT}/../../node_modules/react-native-theoplayer/ios/style.css", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SCORBundle.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle", @@ -662,54 +646,74 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS-resources.sh\"\n"; showEnvVarsInLog = 0; }; - AB3A88D1CB03C39A24F1F209 /* [CP] Check Pods Manifest.lock */ = { + AA539643D5EC434F1B2CA0F5 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/ComScore/Dynamic/ComScore.framework/ComScore", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/NielsenAppSDK-XC/NielsenAppApi.framework/NielsenAppApi", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-THEOlive/Base/THEOplayerTHEOliveIntegration.framework/THEOplayerTHEOliveIntegration", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-tvOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds", ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ComScore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NielsenAppApi.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerTHEOliveIntegration.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework", ); 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"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - B9E191269F06C2C6DE0B7BEE /* [CP] Check Pods Manifest.lock */ = { + AF252140E32770FE8BFE8D3F /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/ComScore/Dynamic/ComScore.framework/ComScore", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-iOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/NielsenAppSDK-XC/NielsenAppApi.framework/NielsenAppApi", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleCast/Base/THEOplayerGoogleCastIntegration.framework/THEOplayerGoogleCastIntegration", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-THEOlive/Base/THEOplayerTHEOliveIntegration.framework/THEOplayerTHEOliveIntegration", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/google-cast-sdk-dynamic-xcframework/GoogleCast.framework/GoogleCast", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ReactNativeTHEOplayer-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ComScore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NielsenAppApi.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleCastIntegration.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerTHEOliveIntegration.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleCast.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); 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"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - BF7D9DA46C8DD41F30E94E14 /* [CP] Check Pods Manifest.lock */ = { + B5D3C1C3CFB215CE85008475 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -724,33 +728,44 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests-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; }; - FD10A7F022414F080027D42C /* Start Packager */ = { + B8D57C04307543F132F63F30 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/ComScore/Dynamic/ComScore.framework/ComScore", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/NielsenAppSDK-XC/NielsenAppApi.framework/NielsenAppApi", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-THEOlive/Base/THEOplayerTHEOliveIntegration.framework/THEOplayerTHEOliveIntegration", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-tvOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds", ); - name = "Start Packager"; - outputFileListPaths = ( - ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ComScore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NielsenAppApi.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerTHEOliveIntegration.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework", ); 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"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - FD10A7F122414F3F0027D42C /* Start Packager */ = { + FD10A7F022414F080027D42C /* Start Packager */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -769,38 +784,23 @@ 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; }; - FFBBB750AB49D2A12D4B9411 /* [CP] Embed Pods Frameworks */ = { + FD10A7F122414F3F0027D42C /* Start Packager */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/ComScore/Dynamic/ComScore.framework/ComScore", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-iOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/NielsenAppSDK-XC/NielsenAppApi.framework/NielsenAppApi", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleCast/Base/THEOplayerGoogleCastIntegration.framework/THEOplayerGoogleCastIntegration", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-THEOlive/Base/THEOplayerTHEOliveIntegration.framework/THEOplayerTHEOliveIntegration", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/google-cast-sdk-dynamic-xcframework/GoogleCast.framework/GoogleCast", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); - name = "[CP] Embed Pods Frameworks"; + name = "Start Packager"; + outputFileListPaths = ( + ); outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ComScore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NielsenAppApi.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleCastIntegration.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerTHEOliveIntegration.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleCast.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer-frameworks.sh\"\n"; + 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 */ @@ -858,7 +858,7 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E94FE6E5DE0E478F2A505A3B /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.debug.xcconfig */; + baseConfigurationReference = D466E8BC201BE02656CD9B59 /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -881,7 +881,7 @@ }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 37F2476D4FFC19937969BA39 /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.release.xcconfig */; + baseConfigurationReference = 2859932C44A7266BC9A26016 /* Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COPY_PHASE_STRIP = NO; @@ -901,7 +901,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 53B450B3183AC1FCE970C242 /* Pods-ReactNativeTHEOplayer.debug.xcconfig */; + baseConfigurationReference = 2B1869737A6D3574FB9FCB91 /* Pods-ReactNativeTHEOplayer.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -933,7 +933,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 212158D9C4053BB12B721822 /* Pods-ReactNativeTHEOplayer.release.xcconfig */; + baseConfigurationReference = 06D65152529DA3A7B6EC728D /* Pods-ReactNativeTHEOplayer.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -957,7 +957,7 @@ }; 2D02E4971E0B4A5E006451C7 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 586FDDD06EFF4AC6D6026609 /* Pods-ReactNativeTHEOplayer-tvOS.debug.xcconfig */; + baseConfigurationReference = E69795734BECC7F9A40F1181 /* Pods-ReactNativeTHEOplayer-tvOS.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; @@ -990,7 +990,7 @@ }; 2D02E4981E0B4A5E006451C7 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D726618BE2FF02B74D4B3B33 /* Pods-ReactNativeTHEOplayer-tvOS.release.xcconfig */; + baseConfigurationReference = 67508AB782AB1AEF1725FF10 /* Pods-ReactNativeTHEOplayer-tvOS.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; @@ -1022,7 +1022,7 @@ }; 2D02E4991E0B4A5E006451C7 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 90DCD6370FFDC116ADAF7756 /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.debug.xcconfig */; + baseConfigurationReference = 8C4B9785AAB059DC6B233E3B /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1049,7 +1049,7 @@ }; 2D02E49A1E0B4A5E006451C7 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FC92E903EC76001BC932F05F /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.release.xcconfig */; + baseConfigurationReference = CEEC6FD978D0E9BD7B92494E /* Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; diff --git a/apps/e2e/package-lock.json b/apps/e2e/package-lock.json index 94cd9704..9b15de0d 100644 --- a/apps/e2e/package-lock.json +++ b/apps/e2e/package-lock.json @@ -17,7 +17,7 @@ "react-native-device-info": "^10.14.0", "react-native-status-bar-height": "^2.6.0", "react-native-svg": "^15.8.0", - "react-native-theoplayer": "^9.3.0", + "react-native-theoplayer": "^9.9.1", "react-native-web": "^0.19.13", "react-native-web-image-loader": "^0.1.1" }, @@ -11916,9 +11916,9 @@ } }, "node_modules/react-native-theoplayer": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/react-native-theoplayer/-/react-native-theoplayer-9.3.0.tgz", - "integrity": "sha512-K8Lvaxpwq8lhSKTy49BcRWEzO2OsR7KMdINJiFmOEOdCuSASSAcXL9s0bqB5MF777q/dr+d3LQQc5ss1mNGpWQ==", + "version": "9.9.1", + "resolved": "https://registry.npmjs.org/react-native-theoplayer/-/react-native-theoplayer-9.9.1.tgz", + "integrity": "sha512-yJjbaIR4Vor9UlfXH11kT9csedKKlQBe+LDM0Urg4lVX2PY526qQA9JWpILJ9zFruAWpt/1ym7OP9Gguuksg0A==", "license": "SEE LICENSE AT https://www.theoplayer.com/terms", "dependencies": { "@theoplayer/cmcd-connector-web": "^1.3.0", diff --git a/apps/e2e/package.json b/apps/e2e/package.json index 4eb492f6..ad6be81a 100644 --- a/apps/e2e/package.json +++ b/apps/e2e/package.json @@ -22,7 +22,7 @@ "react-native-device-info": "^10.14.0", "react-native-status-bar-height": "^2.6.0", "react-native-svg": "^15.8.0", - "react-native-theoplayer": "^9.3.0", + "react-native-theoplayer": "^9.9.1", "react-native-web": "^0.19.13", "react-native-web-image-loader": "^0.1.1" }, diff --git a/apps/e2e/tsconfig.json b/apps/e2e/tsconfig.json index 8c0856b0..14e5d466 100644 --- a/apps/e2e/tsconfig.json +++ b/apps/e2e/tsconfig.json @@ -17,6 +17,12 @@ ], "@theoplayer/react-native-yospace": [ "../../yospace/src/index" + ], + "@theoplayer/react-native-adscript": [ + "../../adscript/src/index" + ], + "@theoplayer/react-native-gemius": [ + "../../gemius/src/index" ] } } diff --git a/gemius/.gitignore b/gemius/.gitignore new file mode 100644 index 00000000..028d7364 --- /dev/null +++ b/gemius/.gitignore @@ -0,0 +1,72 @@ +# OSX +# +.DS_Store + +# XDE +.expo/ + +# VSCode +.vscode/ +jsconfig.json + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IJ +# +.classpath +.cxx +.gradle +.idea +.project +.settings +local.properties +android.iml + +# Cocoapods +# +example/ios/Pods + +# Ruby +example/vendor/ + +# node.js +# +node_modules/ +npm-debug.log +yarn-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +android/app/libs +android/keystores/debug.keystore + +# Expo +.expo/ + +# Turborepo +.turbo/ + +# generated by bob +lib/ + +src/manifest.json diff --git a/gemius/CHANGELOG.md b/gemius/CHANGELOG.md new file mode 100644 index 00000000..47637602 --- /dev/null +++ b/gemius/CHANGELOG.md @@ -0,0 +1,7 @@ +# @theoplayer/react-native-analytics-gemius + +## 1.0.0 + +### ✨ Features + +- Initial release diff --git a/gemius/README.md b/gemius/README.md new file mode 100644 index 00000000..527df05f --- /dev/null +++ b/gemius/README.md @@ -0,0 +1,101 @@ +# THEOplayer React-Native Gemius Connector + +A Gemius analytics connector for `@theoplayer/react-native`. + +## Installation + +```sh +npm install @theoplayer/react-native-analytics-gemius +``` + +[//]: # (npm install @theoplayer/react-native-analytics-gemius) + +## Prerequisites + +### Android + +The Gemius Android SDK is not publicly available as a Maven module, so additional work is needed to include it as +connector dependency. +It requires downloading the private `GemiusSDK_2.0.8.aar` module into the app's `libs/` folder. +The SDK location needs to be passed to the connector by setting the `gemiusSdkDir` in your app's `gradle.properties` file: + +```bash +# Location of the Gemius SDK +gemiusSdkDir=./app/libs/ +``` + +### iOS & tvOS + +On Apple platforms, the connector requires downloading the private `GemiusSDK_iOS` archive, which contains +a multiplatform binary platform module for both iOS and tvOS. + +Create a `Frameworks` folder in your app's `ios` folder, copy the Gemius SDK XCFrameworks in it +and add this `GemiusSDK.podspec` file inside, which describes the structure and metadata of Gemius's CocoaPod: + +```ruby +Pod::Spec.new do |spec| + spec.name = "GemiusSDK" + spec.version = "2.0.6" + spec.summary = "The Gemius SDK for iOS" + + spec.homepage = 'https://github.com/THEOplayer/iOS-Connector' + spec.license = { :type => 'MIT', :file => 'LICENSE' } + spec.author = "THEO technologies" + spec.source = { :git => 'https://github.com/THEOplayer/iOS-Connector.git', :tag => spec.version.to_s } + + spec.source_files = "Classes", "Classes/**/*.{h,m}" + spec.ios.vendored_frameworks = "GemiusSDK.xcframework" + spec.tvos.vendored_frameworks = "GemiusSDKtvOS.xcframework" +end +``` + +Finally, include the Gemius SDK as dependency in your app's `Podfile`: + +```ruby + pod 'GemiusSDK', :path => 'Frameworks/' +``` + +## Usage + +### Configuring the connector + +Create the connector by providing the `THEOplayer` instance and a `GemiusConfiguration` object. + +```tsx +import { useGemius, GemiusConfiguration, ProgramType } from '@theoplayer/react-native-analytics-gemius'; + +const gemiusConfig: GemiusConfiguration = { + applicationName: "Demo", + applicationVersion: "1.0", + hitCollectorHost: "your_hit_collector_host", + gemiusId: "your_gemius_id", + debug: true +} + +const App = () => { + const [gemius, initGemius] = useGemius(gemiusConfig); + + const onPlayerReady = (player: THEOplayer) => { + // Initialize Gemius connector + initGemius(player); + }; + + return ; +}; +``` + +### Passing metadata dynamically + +The connector allows passing or updating the current asset's metadata at any time: + +```typescript +const onUpdateMetadata = () => { + gemius.current?.update('programId', { + name: 'Demo asset', + duration: 1200, + programType: ProgramType.VIDEO, + customKey: 'customValue', + // ... + }); +} +``` diff --git a/gemius/android/build.gradle b/gemius/android/build.gradle new file mode 100644 index 00000000..2b709091 --- /dev/null +++ b/gemius/android/build.gradle @@ -0,0 +1,117 @@ +import java.util.zip.GZIPInputStream + +buildscript { + // Buildscript is evaluated before everything else so we can't use getExtOrDefault + def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : '1.9.10' + + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "com.android.tools.build:gradle:7.2.2" + // noinspection DifferentKotlinGradleVersion + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +def isNewArchitectureEnabled() { + return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} + +def safeExtGet(prop, fallback) { + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback +} + +android { + namespace = "com.theoplayer.reactnative.gemius" + compileSdkVersion safeExtGet("THEOplayerGemius_compileSdkVersion", 35) + + defaultConfig { + minSdkVersion safeExtGet("THEOplayerGemius_minSdkVersion", 21) + targetSdkVersion safeExtGet("THEOplayerGemius_targetSdkVersion", 35) + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + versionCode 1 + versionName "1.0" + } + + buildTypes { + release { + minifyEnabled false + } + } + + lintOptions { + disable "GradleCompatible" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +repositories { + mavenLocal() + google() + mavenCentral() + // Local Maven repo + maven { url("local") } + maven { url "https://maven.theoplayer.com/releases" } +} + +rootProject.allprojects { + repositories { + maven { url("$rootDir/../node_modules/@theoplayer/react-native-analytics-gemius/android/local") } + } +} + +// The Gemius connector requires at least THEOplayer SDK v9.0.0. +def theoplayer_sdk_version = safeExtGet('THEOplayer_sdk', '[9.0.0, 10.0.0)') +def kotlin_version = safeExtGet("THEOplayerGemius_kotlinVersion", "1.9.10") +def gemius_version = safeExtGet("THEOplayerGemius_gemiusVersion", "2.0.8") + +// ACRA 5.8+ uses Kotlin; GemiusSDK is pure java (for now) +def acra_version = safeExtGet("THEOplayerGemius_acraVersion", '5.7.0') + +dependencies { + // For < 0.71, this will be from the local maven repo + // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin + //noinspection GradleDynamicVersion + implementation "com.facebook.react:react-native" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.lifecycle:lifecycle-process:2.5.1' + + // THEOplayer dependencies + compileOnly "com.theoplayer.theoplayer-sdk-android:core:$theoplayer_sdk_version" + compileOnly "com.theoplayer.theoplayer-sdk-android:integration-ads-ima:$theoplayer_sdk_version" + implementation project(':react-native-theoplayer') + + // TEMP: replace this once the native connector has been published + // implementation "com.theoplayer.android-connector:gemius:$gemius_version" + implementation files("./libs/gemius.aar") + def aarFileName = "GemiusSDK_${gemius_version}.aar" + def gemiusSdkDir = rootProject.properties['gemiusSdkDir'] + if (!gemiusSdkDir) { + logger.warn("⚠️ WARNING: gemiusSdkDir not set.") + } else if (!rootProject.file(gemiusSdkDir).exists()) { + logger.warn("⚠️ WARNING: gemiusSdkDir does not exist at: ${gemiusSdkDir}") + } else { + dependencies { + implementation fileTree(dir: "${rootProject.file(gemiusSdkDir)}/$aarFileName", include: ['*.aar', '*.jar'], exclude: []) + } + } + + implementation "ch.acra:acra-http:$acra_version" + + // Align the Kotlin SDK libraries with the same version. + implementation(platform("org.jetbrains.kotlin:kotlin-bom:$kotlin_version")) +} diff --git a/gemius/android/gradle/wrapper/gradle-wrapper.jar b/gemius/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..41d9927a Binary files /dev/null and b/gemius/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gemius/android/gradle/wrapper/gradle-wrapper.properties b/gemius/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..2733ed5d --- /dev/null +++ b/gemius/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gemius/android/gradlew b/gemius/android/gradlew new file mode 100755 index 00000000..1b6c7873 --- /dev/null +++ b/gemius/android/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/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/. +# +############################################################################## + +# 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=${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 + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + 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 +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +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 + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# 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/gemius/android/gradlew.bat b/gemius/android/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/gemius/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +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 execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/gemius/android/libs/gemius.aar b/gemius/android/libs/gemius.aar new file mode 100644 index 00000000..8d9c06d6 Binary files /dev/null and b/gemius/android/libs/gemius.aar differ diff --git a/gemius/android/src/main/AndroidManifest.xml b/gemius/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..cc947c56 --- /dev/null +++ b/gemius/android/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/gemius/android/src/main/java/com/theoplayer/reactnative/gemius/ReactTHEOplayerGemiusModule.kt b/gemius/android/src/main/java/com/theoplayer/reactnative/gemius/ReactTHEOplayerGemiusModule.kt new file mode 100644 index 00000000..99aef23c --- /dev/null +++ b/gemius/android/src/main/java/com/theoplayer/reactnative/gemius/ReactTHEOplayerGemiusModule.kt @@ -0,0 +1,126 @@ +package com.theoplayer.reactnative.gemius + +import com.facebook.react.bridge.* +import com.gemius.sdk.stream.ProgramData +import com.theoplayer.ReactTHEOplayerView +import com.theoplayer.android.api.THEOplayerView +import com.theoplayer.android.connector.analytics.gemius.GemiusConfiguration +import com.theoplayer.android.connector.analytics.gemius.GemiusConnector +import com.theoplayer.util.ViewResolver + +private const val TAG = "GemiusModule" + +private const val PROP_APP_NAME = "applicationName" +private const val PROP_APP_VERSION = "applicationVersion" +private const val PROP_HIT_COLLECTOR_HOST = "hitCollectorHost" +private const val PROP_GEMIUS_ID = "gemiusId" +private const val PROP_DEBUG = "debug" + +private const val PROP_NAME = "name" +private const val PROP_DURATION = "duration" +private const val PROP_TRANSMISSION = "transmission" +private const val PROP_QUALITY = "quality" +private const val PROP_RESOLUTION = "resolution" +private const val PROP_VOLUME = "volume" +private const val PROP_EXTERNAL_PREMIERE_DATE = "externalPremiereDate" +private const val PROP_PREMIERE_DATE = "premiereDate" +private const val PROP_SERIES = "series" +private const val PROP_PROGRAM_TYPE = "programType" +private const val PROP_TYPOLOGY = "typology" +private const val PROP_PROGRAM_GENRE = "programGenre" +private const val PROP_PROGRAM_PARTIAL_NAME = "programPartialName" +private const val PROP_PROGRAM_PRODUCER = "programProducer" +private const val PROP_PROGRAM_THEMATIC_CATEGORY = "programThematicCategory" +private const val PROP_PROGRAM_SEASON = "programSeason" +private const val PROP_TRANSMISSION_CHANNEL = "transmissionChannel" +private const val PROP_TRANSMISSION_START_TIME = "transmissionStartTime" +private const val PROP_TRANSMISSION_TYPE = "transmissionType" + +class ReactTHEOplayerGemiusModule(context: ReactApplicationContext) : + ReactContextBaseJavaModule(context) { + + private val viewResolver: ViewResolver = ViewResolver(context) + + private var gemiusConnectors: HashMap = HashMap() + + override fun getName(): String { + return TAG + } + + @ReactMethod + fun initialize(tag: Int, configuration: ReadableMap) { + viewResolver.resolveViewByTag(tag) { view: ReactTHEOplayerView? -> + view?.playerContext?.playerView?.let { playerView -> + gemiusConnectors[tag] = + initializeGemius(playerView, configuration) + } + } + } + + private fun initializeGemius( + view: THEOplayerView, + configuration: ReadableMap, + ): GemiusConnector { + return GemiusConnector( + context = reactApplicationContext, + playerView = view, + configuration = buildConfiguration(configuration)) + } + + private fun buildConfiguration(contentMetadata: ReadableMap): GemiusConfiguration { + return GemiusConfiguration( + applicationName = contentMetadata.getString(PROP_APP_NAME) ?: "NA", + applicationVersion = contentMetadata.getString(PROP_APP_VERSION) ?: "NA", + hitCollectorHost = contentMetadata.getString(PROP_HIT_COLLECTOR_HOST) ?: "NA", + gemiusId = contentMetadata.getString(PROP_GEMIUS_ID) ?: "NA", + debug = if (contentMetadata.hasKey(PROP_DEBUG)) contentMetadata.getBoolean(PROP_DEBUG) else false, + adProcessor = null + ) + } + + private fun buildProgramData(data: ReadableMap): ProgramData { + return ProgramData().apply { + data.toHashMap().entries.forEach { entry -> + when (entry.key) { + PROP_NAME -> name = data.getString(entry.key) + PROP_DURATION -> duration = data.takeIf { data.hasKey(entry.key) }?.getInt(entry.key) + PROP_TRANSMISSION -> transmission = data.getString(entry.key) + PROP_QUALITY -> quality = data.getString(entry.key) + PROP_RESOLUTION -> resolution = data.getString(entry.key) + PROP_VOLUME -> volume = data.takeIf { data.hasKey(entry.key) }?.getInt(entry.key) + PROP_EXTERNAL_PREMIERE_DATE -> externalPremiereDate = data.getString(entry.key) + PROP_PREMIERE_DATE -> premiereDate = data.getString(entry.key) + PROP_SERIES -> series = data.getString(entry.key) + PROP_PROGRAM_TYPE -> programType = data.takeIf { data.hasKey(entry.key) }?.getInt(entry.key)?.let { + when (it) { + 0 -> ProgramData.ProgramType.AUDIO + else -> ProgramData.ProgramType.VIDEO + } + } + PROP_TYPOLOGY -> typology = data.getString(entry.key) + PROP_PROGRAM_GENRE -> programGenre = data.takeIf { data.hasKey(entry.key) }?.getInt(entry.key) + PROP_PROGRAM_PARTIAL_NAME -> programPartialName = data.getString(entry.key) + PROP_PROGRAM_PRODUCER -> programProducer = data.getString(entry.key) + PROP_PROGRAM_THEMATIC_CATEGORY -> programThematicCategory = data.getString(entry.key) + PROP_PROGRAM_SEASON -> programSeason = data.getString(entry.key) + PROP_TRANSMISSION_CHANNEL -> transmissionChannel = data.getString(entry.key) + PROP_TRANSMISSION_START_TIME -> transmissionStartTime = data.getString(entry.key) + PROP_TRANSMISSION_TYPE -> transmissionType = data.takeIf { data.hasKey(entry.key) }?.getInt(entry.key) + + // Custom parameter, just add + else -> addCustomParameter(entry.key, entry.value as? String) + } + } + } + } + + @ReactMethod + fun update(tag: Int, programId: String, programData: ReadableMap) { + gemiusConnectors[tag]?.update(programId, buildProgramData(programData)) + } + + @ReactMethod + fun destroy(tag: Int) { +// gemiusConnectors[tag]?.destroy() + } +} diff --git a/gemius/android/src/main/java/com/theoplayer/reactnative/gemius/ReactTHEOplayerGemiusPackage.kt b/gemius/android/src/main/java/com/theoplayer/reactnative/gemius/ReactTHEOplayerGemiusPackage.kt new file mode 100644 index 00000000..9ac6f5c6 --- /dev/null +++ b/gemius/android/src/main/java/com/theoplayer/reactnative/gemius/ReactTHEOplayerGemiusPackage.kt @@ -0,0 +1,16 @@ +package com.theoplayer.reactnative.gemius + +import com.facebook.react.ReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.uimanager.ViewManager + +class ReactTHEOplayerGemiusPackage : ReactPackage { + override fun createNativeModules(reactContext: ReactApplicationContext): List { + return listOf(ReactTHEOplayerGemiusModule(reactContext)) + } + + override fun createViewManagers(reactContext: ReactApplicationContext): List> { + return emptyList() + } +} diff --git a/gemius/babel.config.js b/gemius/babel.config.js new file mode 100644 index 00000000..f842b77f --- /dev/null +++ b/gemius/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['module:metro-react-native-babel-preset'], +}; diff --git a/gemius/ios/Source/GemiusAdapter.swift b/gemius/ios/Source/GemiusAdapter.swift new file mode 100644 index 00000000..be61f109 --- /dev/null +++ b/gemius/ios/Source/GemiusAdapter.swift @@ -0,0 +1,398 @@ +import THEOplayerSDK +#if canImport(GemiusSDK) +import GemiusSDK +#endif + +let LOG_PLAYER_EVENTS = false +let LOG_GEMIUS_EVENTS = false + +public class GemiusAdapter { + private let player: THEOplayer + private let configuration: GemiusConfiguration + private var gsmPlayer: GSMPlayer + private let adProcessor: ((THEOplayerSDK.Ad) -> GemiusSDK.GSMAdData)? + + private var programId: String? + private var programData: GemiusSDK.GSMProgramData? + + private var partCount = 1 + private var adCount = 1 + private var currentAd: Ad? = nil + + private var sourceChangeEventListener: EventListener? + private var playingEventListener: EventListener? + private var playEventListener: EventListener? + private var pauseEventListener: EventListener? + private var waitingEventListener: EventListener? + private var seekingEventListener: EventListener? + private var errorEventListener: EventListener? + private var endedEventListener: EventListener? + private var volumeChangeEventListener: EventListener? + + private var addVideoTrackEventListener: EventListener? + private var removeVideoTrackEventListener: EventListener? + private var videoQualityChangedEventListener: EventListener? + + private var adBreakBeginListener: EventListener? + private var adBeginListener: EventListener? + private var adEndListener: EventListener? + private var adSkipListener: EventListener? + private var adBreakEndedListener: EventListener? + + public init(configuration: GemiusConfiguration, player: THEOplayer, adProcessor: ((THEOplayerSDK.Ad) -> GemiusSDK.GSMAdData)? = nil) { + self.player = player + self.configuration = configuration + self.adProcessor = adProcessor + let playerData = GemiusSDK.GSMPlayerData() + playerData.resolution = "\(player.frame.width)x\(player.frame.height)" + if (player.muted) { + playerData.volume = -1 + } else { + playerData.volume = NSNumber(value: player.volume * 100) + } + GemiusSDK.GEMConfig.sharedInstance().loggingEnabled = configuration.debug + GemiusSDK.GEMConfig.sharedInstance().setAppInfo(configuration.applicationName, version: configuration.applicationVersion) + self.gsmPlayer = GemiusSDK.GSMPlayer(id: "THEOplayer", withHost: configuration.hitCollectorHost, withGemiusID: configuration.gemiusId, with: playerData) + + addEventListeners() + } + + deinit { + destroy() + } + + public func destroy() { + removeEventListeners() + } + + public func update(programId: String, programData: GemiusSDK.GSMProgramData) { + self.programId = programId + self.programData = programData + } + + private func addEventListeners() { + self.sourceChangeEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.SOURCE_CHANGE, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[GemiusConnector] Player Event: \(event.type) : source = \(event.source.debugDescription)") + } + welf.partCount = 1 + welf.currentAd = nil + if let programData = welf.programData, let programId = welf.programId { + welf.gsmPlayer.newProgram(programId, with: programData) + } else { + print("[GemiusConnector] No program parameters were provided") + } + + if let playingEventListener: THEOplayerSDK.EventListener = welf.playingEventListener { + welf.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: playingEventListener) + } + welf.playingEventListener = welf.player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: { [weak self] event in self?.handleFirstPlaying(event: event) }) + }) + self.playingEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: { [weak self] event in self?.handleFirstPlaying(event: event) }) + self.playEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAY, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[GemiusConnector] Player Event: \(event.type) : currentTime = \(event.currentTime)") + } + let computedVolume = welf.player.muted ? -1 : Int(welf.player.volume * 100) + if let currentAd = welf.currentAd, let id = currentAd.id { + let adBreak = currentAd.adBreak + let offset = adBreak.timeOffset + let adEventData = GSMEventAdData() + adEventData.adDuration = NSNumber(value: currentAd.duration ?? 0) + adEventData.autoPlay = welf.player.autoplay ? 1 : 0 + adEventData.adPosition = NSNumber(value: welf.adCount) + adEventData.breakSize = NSNumber(value: adBreak.ads.count) + adEventData.volume = NSNumber(value: computedVolume) + welf.gsmPlayer.adEvent(.PLAY, forProgram: welf.programId, forAd: id, atOffset: NSNumber(value: offset), with: adEventData) + } else { + if (welf.hasPrerollScheduled()) { return } + let programEventData = GSMEventProgramData() + programEventData.autoPlay = welf.player.autoplay ? 1 : 0 + programEventData.volume = NSNumber(value: computedVolume) + programEventData.partID = NSNumber(value: welf.partCount) + programEventData.programDuration = NSNumber(value: welf.player.duration ?? 0) + welf.gsmPlayer.program(.PLAY, forProgram: welf.programId, atOffset: NSNumber(value: welf.player.currentTime), with: programEventData) + } + }) + self.pauseEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PAUSE, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[GemiusConnector] Player Event: \(event.type) : currentTime = \(event.currentTime)") + } + welf.reportBasicEvent(event: .PAUSE) + }) + self.waitingEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.WAITING, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[GemiusConnector] Player Event: \(event.type) : currentTime = \(event.currentTime)") + } + welf.reportBasicEvent(event: .BUFFER) + }) + self.seekingEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.SEEKING, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[GemiusConnector] Player Event: \(event.type) : currentTime = \(event.currentTime)") + } + welf.reportBasicEvent(event: .SEEK) + }) + self.errorEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.ERROR, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if let code = event.errorObject?.code, let cause = event.errorObject?.cause, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[GemiusConnector] Player Event: \(event.type) : code = \(code) ; cause = \(cause)") + } + welf.reportBasicEvent(event: .COMPLETE) + }) + self.endedEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.ENDED, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[GemiusConnector] Player Event: \(event.type) : currentTime = \(event.currentTime)") + } + welf.reportBasicEvent(event: .COMPLETE) + }) + self.volumeChangeEventListener = player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.VOLUME_CHANGE, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if (welf.configuration.debug && LOG_PLAYER_EVENTS) { + print("[GemiusConnector] Player Event: \(event.type) : volume = \(event.volume)") + } + let computedVolume = welf.player.muted ? -1 : Int(welf.player.volume * 100) + let programId = welf.programId + if let currentAd = welf.currentAd, let id = currentAd.id { + let adBreak = currentAd.adBreak + let adEventData = GSMEventAdData() + adEventData.volume = NSNumber(value: computedVolume) + welf.gsmPlayer.adEvent(.CHANGE_VOL, forProgram: programId, forAd: id, atOffset: NSNumber(value: adBreak.timeOffset), with: adEventData) + } else { + let programEventData = GSMEventProgramData() + programEventData.volume = NSNumber(value: computedVolume) + welf.gsmPlayer.program(.CHANGE_VOL, forProgram: programId, atOffset: NSNumber( value: welf.player.currentTime), with: programEventData) + } + }) + + self.addVideoTrackEventListener = player.videoTracks.addEventListener(type: VideoTrackListEventTypes.ADD_TRACK, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + let videoTrack = event.track + welf.videoQualityChangedEventListener = videoTrack.addEventListener(type: MediaTrackEventTypes.ACTIVE_QUALITY_CHANGED) { [weak self] event in + let programId = welf.programId + let height = welf.player.videoHeight + let width = welf.player.videoWidth + if let currentAd = welf.currentAd, let id = currentAd.id { + let adBreak = currentAd.adBreak + let adEventData = GSMEventAdData() + adEventData.quality = "\(width)x\(height)" + welf.gsmPlayer.adEvent(.CHANGE_QUAL, forProgram: programId, forAd: id, atOffset: NSNumber(value: adBreak.timeOffset), with: adEventData) + } else { + let programEventData = GSMEventProgramData() + programEventData.quality = "\(width)x\(height)" + welf.gsmPlayer.program(.CHANGE_QUAL, forProgram: programId, atOffset: NSNumber(value: welf.player.currentTime), with: programEventData) + } + } + }) + self.removeVideoTrackEventListener = player.videoTracks.addEventListener(type: VideoTrackListEventTypes.REMOVE_TRACK, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + let videoTrack = event.track + if let videoQualityChangedEventListener: THEOplayerSDK.EventListener = welf.videoQualityChangedEventListener { + videoTrack.removeEventListener(type: THEOplayerSDK.MediaTrackEventTypes.ACTIVE_QUALITY_CHANGED, listener: videoQualityChangedEventListener) + } + }) + + if (hasAdIntegration()) { + self.adBreakBeginListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BREAK_BEGIN, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if let offset = event.ad?.timeOffset, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[GemiusConnector] Player Event: \(event.type) : offset = \(offset)") + } + welf.reportBasicEvent(event: .BREAK) + if let playingEventListener: THEOplayerSDK.EventListener = welf.playingEventListener { + welf.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: playingEventListener) + } + welf.playingEventListener = welf.player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: { [weak self] event in self?.handleFirstPlaying(event: event) }) + }) + self.adBeginListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BEGIN, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if let id = event.ad?.id, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[GemiusConnector] Player Event: \(event.type) : id = \(id)") + } + guard let ad = event.ad, let id = ad.id else { return } + welf.currentAd = ad + let adData = welf.buildAdData(ad: ad) + welf.gsmPlayer.newAd(id, with: adData) + }) + self.adEndListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_END, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if let id = event.ad?.id, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[GemiusConnector] Player Event: \(event.type) : id = \(id)") + } + guard let ad = event.ad, let id = ad.id else { return } + welf.reportBasicEvent(event: .COMPLETE) + welf.reportBasicEvent(event: .CLOSE) + welf.adCount += 1 + welf.currentAd = nil + if let playingEventListener: THEOplayerSDK.EventListener = welf.playingEventListener { + welf.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: playingEventListener) + } + welf.playingEventListener = welf.player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: { [weak self] event in self?.handleFirstPlaying(event: event) }) + + }) + self.adSkipListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_SKIP, listener: { [weak self] event in + guard let welf: GemiusAdapter = self else { return } + if let id = event.ad?.id, welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[GemiusConnector] Player Event: \(event.type) : id = \(id)") + } + welf.reportBasicEvent(event: .SKIP) + }) + self.adBreakEndedListener = player.ads.addEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BREAK_END, listener: { [weak self] event in + + guard let welf: GemiusAdapter = self else { return } + welf.adCount = 1 + guard let offset = event.ad?.timeOffset else { return } + if welf.configuration.debug && LOG_PLAYER_EVENTS { + print("[GemiusConnector] Player Event: \(event.type) : offset = \(offset)") + } + if (offset > 0) { + welf.partCount += 1 + } + welf.gsmPlayer.newProgram(welf.programId, with: welf.programData) + if let playingEventListener: THEOplayerSDK.EventListener = welf.playingEventListener { + welf.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: playingEventListener) + } + if (event.ad?.timeOffset == 0) { + welf.playingEventListener = welf.player.addEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: { [weak self] event in self?.handleFirstPlaying(event: event) }) + } + }) + } + } + + private func removeEventListeners() { + if let sourceChangeEventListener: THEOplayerSDK.EventListener = self.sourceChangeEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.SOURCE_CHANGE, listener: sourceChangeEventListener) + } + if let playingEventListener: THEOplayerSDK.EventListener = self.playingEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: playingEventListener) + } + if let playEventListener: THEOplayerSDK.EventListener = self.playEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAY, listener: playEventListener) + } + if let pauseEventListener: THEOplayerSDK.EventListener = self.pauseEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PAUSE, listener: pauseEventListener) + } + if let waitingEventListener: THEOplayerSDK.EventListener = self.waitingEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.WAITING, listener: waitingEventListener) + } + if let seekingEventListener: THEOplayerSDK.EventListener = self.seekingEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.SEEKING, listener: seekingEventListener) + } + if let errorEventListener: THEOplayerSDK.EventListener = self.errorEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.ERROR, listener: errorEventListener) + } + if let endedEventListener: THEOplayerSDK.EventListener = self.endedEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.ENDED, listener: endedEventListener) + } + if let volumeChangeEventListener: THEOplayerSDK.EventListener = self.volumeChangeEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.VOLUME_CHANGE, listener: volumeChangeEventListener) + } + if let adBreakBeginListener: THEOplayerSDK.EventListener = self.adBreakBeginListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BREAK_BEGIN, listener: adBreakBeginListener) + } + if let adBeginListener: THEOplayerSDK.EventListener = self.adBeginListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BEGIN, listener: adBeginListener) + } + if let adEndListener: THEOplayerSDK.EventListener = self.adEndListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_END, listener: adEndListener) + } + if let adSkipListener: THEOplayerSDK.EventListener = self.adSkipListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_SKIP, listener: adSkipListener) + } + if let adBreakEndedListener: THEOplayerSDK.EventListener = self.adBreakEndedListener { + self.player.removeEventListener(type: THEOplayerSDK.AdsEventTypes.AD_BREAK_END, listener: adBreakEndedListener) + } + if let addVideoTrackEventListener: THEOplayerSDK.EventListener = self.addVideoTrackEventListener { + self.player.videoTracks.removeEventListener(type: THEOplayerSDK.VideoTrackListEventTypes.ADD_TRACK, listener: addVideoTrackEventListener) + } + if let removeVideoTrackEventListener: THEOplayerSDK.EventListener = self.removeVideoTrackEventListener { + self.player.videoTracks.removeEventListener(type: THEOplayerSDK.VideoTrackListEventTypes.REMOVE_TRACK, listener: removeVideoTrackEventListener) + } + } + + private func handleFirstPlaying(event: PlayingEvent) { + if (self.configuration.debug && LOG_PLAYER_EVENTS) { + print("[GemiusConnector] Player Event: \(event.type) : currentTime = \(event.currentTime)") + } + let computedVolume = player.muted ? -1 : Int(player.volume * 100) + if let currentAd = self.currentAd, let id = currentAd.id { + let adBreak = currentAd.adBreak + let offset = adBreak.timeOffset + let adEventData = GSMEventAdData() + adEventData.adDuration = NSNumber(value: currentAd.duration ?? 0) + adEventData.autoPlay = self.player.autoplay ? 1 : 0 + adEventData.adPosition = NSNumber(value: self.adCount) + adEventData.breakSize = NSNumber(value: adBreak.ads.count) + adEventData.volume = NSNumber(value: computedVolume) + self.gsmPlayer.adEvent(.PLAY, forProgram: self.programId, forAd: id, atOffset: NSNumber(value: offset), with: adEventData) + } else { + if (hasPrerollScheduled()) { return } + let programEventData = GSMEventProgramData() + programEventData.autoPlay = self.player.autoplay ? 1 : 0 + programEventData.volume = NSNumber(value: computedVolume) + programEventData.partID = NSNumber(value: self.partCount) + programEventData.programDuration = NSNumber(value: self.player.duration ?? 0) + self.gsmPlayer.program(.PLAY, forProgram: self.programId, atOffset: NSNumber(value: self.player.currentTime), with: programEventData) + } + + if let playingEventListener: THEOplayerSDK.EventListener = self.playingEventListener { + self.player.removeEventListener(type: THEOplayerSDK.PlayerEventTypes.PLAYING, listener: playingEventListener) + } + } + + private func hasAdIntegration() -> Bool { + let hasAdIntegration = player.getAllIntegrations().contains { integration in + switch integration.kind { + case IntegrationKind.GOOGLE_DAI: + return true + case IntegrationKind.GOOGLE_IMA: + return true + default: + print("[GemiusConnector] no supported ad integration was found") + return false + } + } + return hasAdIntegration + } + + private func buildAdData(ad: THEOplayerSDK.Ad) -> GemiusSDK.GSMAdData { + if let adProcessor = self.adProcessor { + return adProcessor(ad) + } + let adData = GemiusSDK.GSMAdData() + if [AdIntegrationKind.google_ima, AdIntegrationKind.google_dai].contains(ad.integration), + let imaAd = ad as? GoogleImaAd { + adData.name = imaAd.creativeId + } + + adData.adFormat = .video + adData.adType = .AD_BREAK + if let duration = ad.duration { + adData.duration = NSNumber(value: duration) + } + adData.name = ad.id + adData.quality = "\(ad.width)x\(ad.height)" + adData.resolution = "\(player.frame.width)x\(player.frame.height)" + return adData + } + + private func reportBasicEvent(event: GemiusSDK.GSMEventType) { + guard let programId = self.programId else { return } + if let currentAd = self.currentAd { + self.gsmPlayer.adEvent(event, forProgram: programId, forAd: currentAd.id, atOffset: NSNumber(value: currentAd.adBreak.timeOffset), with: nil) + + } else { + self.gsmPlayer.program(event, forProgram: programId, atOffset: NSNumber(value: player.currentTime), with: nil) + + } + } + + private func hasPrerollScheduled() -> Bool { + player.ads.scheduledAdBreaks.contains(where: {adBreak in adBreak.timeOffset == 0}) + } +} diff --git a/gemius/ios/Source/GemiusConnector.swift b/gemius/ios/Source/GemiusConnector.swift new file mode 100644 index 00000000..0fdc501d --- /dev/null +++ b/gemius/ios/Source/GemiusConnector.swift @@ -0,0 +1,41 @@ +import THEOplayerSDK +#if canImport(GemiusSDK) +import GemiusSDK +#endif + +public struct GemiusConfiguration { + let applicationName: String + let applicationVersion: String + let hitCollectorHost: String + let gemiusId: String + let debug: Bool + let adProcessor: ((THEOplayerSDK.Ad) -> GemiusSDK.GSMAdData)? + + public init(applicationName: String, applicationVersion: String, hitCollectorHost: String, gemiusId: String, debug: Bool, adProcessor: ((THEOplayerSDK.Ad) -> GemiusSDK.GSMAdData)? = nil) { + self.applicationName = applicationName + self.applicationVersion = applicationVersion + self.hitCollectorHost = hitCollectorHost + self.gemiusId = gemiusId + self.debug = debug + self.adProcessor = adProcessor + } +} + +public struct GemiusConnector { + private let adapter: GemiusAdapter + private let player: THEOplayer + + + public init(configuration: GemiusConfiguration, player: THEOplayer) { + self.player = player + self.adapter = GemiusAdapter(configuration: configuration, player: player, adProcessor: configuration.adProcessor) + } + + public func update(programId: String, programData: GemiusSDK.GSMProgramData) { + self.adapter.update(programId: programId, programData: programData) + } + + public func destroy() { + self.adapter.destroy() + } +} diff --git a/gemius/ios/THEOplayerGemiusRCTBridge.m b/gemius/ios/THEOplayerGemiusRCTBridge.m new file mode 100644 index 00000000..6a1e49cf --- /dev/null +++ b/gemius/ios/THEOplayerGemiusRCTBridge.m @@ -0,0 +1,19 @@ +#import +#import +#import + +// ---------------------------------------------------------------------------- +// Gemius Module +// ---------------------------------------------------------------------------- +@interface RCT_EXTERN_REMAP_MODULE(GemiusModule, THEOplayerGemiusRCTGemiusAPI, NSObject) + +RCT_EXTERN_METHOD(initialize:(nonnull NSNumber *)node + gemiusConfig:(NSDictionary)gemiusConfig) + +RCT_EXTERN_METHOD(update:(nonnull NSNumber *)node + programId:(nonnull NSString *)programId + metadata:(NSDictionary)metadata) + +RCT_EXTERN_METHOD(destroy:(nonnull NSNumber *)node) + +@end diff --git a/gemius/ios/THEOplayerGemiusRCTGemiusAPI.swift b/gemius/ios/THEOplayerGemiusRCTGemiusAPI.swift new file mode 100644 index 00000000..08d43fde --- /dev/null +++ b/gemius/ios/THEOplayerGemiusRCTGemiusAPI.swift @@ -0,0 +1,158 @@ +import Foundation +import THEOplayerSDK +import UIKit +import react_native_theoplayer +import GemiusSDK + +@objc(THEOplayerGemiusRCTGemiusAPI) +class THEOplayerGemiusRCTGemiusAPI: NSObject, RCTBridgeModule { + @objc var bridge: RCTBridge! + + var connectors = [NSNumber: GemiusConnector]() + + static func moduleName() -> String! { + return "GemiusModule" + } + + static func requiresMainQueueSetup() -> Bool { + return false + } + + @objc(initialize:gemiusConfig:) + func initialize( + _ node: NSNumber, gemiusConfig: NSDictionary + ) { + log("initialize triggered.") + + DispatchQueue.main.async { + if let view = self.view(for: node), let player = view.player { + let configuration = GemiusConfiguration( + applicationName: gemiusConfig["applicationName"] as! String, + applicationVersion: gemiusConfig["applicationVersion"] as! String, + hitCollectorHost: gemiusConfig["hitCollectorHost"] as! String, + gemiusId: gemiusConfig["gemiusId"] as! String, + debug: gemiusConfig["debug"] as? Bool ?? false + ) + self.connectors[node] = GemiusConnector( + configuration: configuration, + player: player + ) + log("Added connector to view \(node)") + } else { + log("Cannot find THEOPlayer for node \(node)") + } + } + } + + @objc(update:programId:metadata:) + func update(_ node: NSNumber, programId: NSString, metadata: NSDictionary) { + log("update triggered.") + DispatchQueue.main.async { + if let connector = self.connectors[node] { + connector + .update( + programId: programId as String, + programData: self.buildProgramData(metadata) + ) + } + } + } + + private func buildProgramData(_ metadata: NSDictionary) -> GemiusSDK.GSMProgramData { + let data = GemiusSDK.GSMProgramData() + + metadata.forEach { key, value in + guard let key = key as? String else { return } + + switch key { + case "name": + data.name = value as? String + case "duration": + data.duration = value as? NSNumber + case "transmission": + data.transmission = value as? String + case "quality": + data.quality = value as? String + case "resolution": + data.resolution = value as? String + case "volume": + data.volume = value as? NSNumber + case "externalPremiereDate": + data.externalPremiereDate = value as? String + case "premiereDate": + data.premiereDate = value as? String + case "series": + data.series = value as? String + case "programType": + if let num = value as? NSNumber { + data.programType = buildProgramType(num) + } + case "typology": + data.typology = value as? String + case "programGenre": + if let num = value as? NSNumber { + data.programGenre = buildProgramGenre(num) + } + case "programPartialName": + data.programPartialName = value as? String + case "programProducer": + data.programProducer = value as? String + case "programThematicCategory": + data.programThematicCategory = value as? String + case "programSeason": + data.programSeason = value as? String + case "transmissionChannel": + data.transmissionChannel = value as? String + case "transmissionStartTime": + data.transmissionStartTime = value as? String + case "transmissionType": + if let num = value as? NSNumber { + data.transmissionType = buildTransmissionType(num) + } + default: + // Assume any other field is a custom parameter + if let strVal = value as? String { + data.addCustomParameter(key, value: strVal) + } + } + } + + return data + } + + + private func buildProgramType(_ type: NSNumber) -> GemiusSDK.GSMProgramType { + return GemiusSDK.GSMProgramType(rawValue: type.intValue) ?? .VIDEO + } + + private func buildProgramGenre(_ genre: NSNumber) -> GemiusSDK.GSMProgramGenre { + return GemiusSDK.GSMProgramGenre(rawValue: genre.intValue) ?? .undefined + } + + private func buildTransmissionType(_ type: NSNumber) -> GemiusSDK.GSMTransmissionType { + return GemiusSDK.GSMTransmissionType(rawValue: type.intValue) ?? .undefined + } + + @objc(destroy:) + func destroy(_ node: NSNumber) { + log("destroy triggered.") + DispatchQueue.main.async { + // self.connectors[node]?.destroy() + self.connectors.removeValue(forKey: node) + } + } + + func view(for node: NSNumber) -> THEOplayerRCTView? { + self.bridge.uiManager.view(forReactTag: node) as? THEOplayerRCTView + } + + func player(for node: NSNumber) -> THEOplayer? { + view(for: node)?.player + } +} + +func log(_ text: String) { +#if DEBUG + print("[react-native-theoplayer-gemius bridge]", text) +#endif +} diff --git a/gemius/ios/TheoplayerGemius-Bridging-Header.h b/gemius/ios/TheoplayerGemius-Bridging-Header.h new file mode 100644 index 00000000..7ecf415d --- /dev/null +++ b/gemius/ios/TheoplayerGemius-Bridging-Header.h @@ -0,0 +1,4 @@ +#import +#import +#import +#import diff --git a/gemius/ios/TheoplayerGemius.xcodeproj/project.pbxproj b/gemius/ios/TheoplayerGemius.xcodeproj/project.pbxproj new file mode 100644 index 00000000..0ab9f5b8 --- /dev/null +++ b/gemius/ios/TheoplayerGemius.xcodeproj/project.pbxproj @@ -0,0 +1,283 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5E555C0D2413F4C50049A1A2 /* TheoplayerGemius.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* TheoplayerGemius.m */; }; + F4FF95D7245B92E800C19C63 /* TheoplayerGemius.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* TheoplayerGemius.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 58B511D91A9E6C8500147676 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 134814201AA4EA6300B7C361 /* libTheoplayerGemius.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTheoplayerGemius.a; sourceTree = BUILT_PRODUCTS_DIR; }; + B3E7B5891CC2AC0600A0062D /* TheoplayerGemius.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TheoplayerGemius.m; sourceTree = ""; }; + F4FF95D5245B92E700C19C63 /* TheoplayerGemius-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TheoplayerGemius-Bridging-Header.h"; sourceTree = ""; }; + F4FF95D6245B92E800C19C63 /* TheoplayerGemius.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TheoplayerGemius.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 58B511D81A9E6C8500147676 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 134814211AA4EA7D00B7C361 /* Products */ = { + isa = PBXGroup; + children = ( + 134814201AA4EA6300B7C361 /* libTheoplayerGemius.a */, + ); + name = Products; + sourceTree = ""; + }; + 58B511D21A9E6C8500147676 = { + isa = PBXGroup; + children = ( + F4FF95D6245B92E800C19C63 /* TheoplayerGemius.swift */, + B3E7B5891CC2AC0600A0062D /* TheoplayerGemius.m */, + F4FF95D5245B92E700C19C63 /* TheoplayerGemius-Bridging-Header.h */, + 134814211AA4EA7D00B7C361 /* Products */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 58B511DA1A9E6C8500147676 /* TheoplayerGemius */ = { + isa = PBXNativeTarget; + buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "TheoplayerGemius" */; + buildPhases = ( + 58B511D71A9E6C8500147676 /* Sources */, + 58B511D81A9E6C8500147676 /* Frameworks */, + 58B511D91A9E6C8500147676 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TheoplayerGemius; + productName = RCTDataManager; + productReference = 134814201AA4EA6300B7C361 /* libTheoplayerGemius.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 58B511D31A9E6C8500147676 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 58B511DA1A9E6C8500147676 = { + CreatedOnToolsVersion = 6.1.1; + }; + }; + }; + buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "TheoplayerGemius" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + English, + en, + ); + mainGroup = 58B511D21A9E6C8500147676; + productRefGroup = 58B511D21A9E6C8500147676; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 58B511DA1A9E6C8500147676 /* TheoplayerGemius */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 58B511D71A9E6C8500147676 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F4FF95D7245B92E800C19C63 /* TheoplayerGemius.swift in Sources */, + B3E7B58A1CC2AC0600A0062D /* TheoplayerGemius.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 58B511ED1A9E6C8500147676 /* 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_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = 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_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=*]" = arm64; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 58B511EE1A9E6C8500147676 /* 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_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = 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_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=*]" = arm64; + 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 = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 58B511F01A9E6C8500147676 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../../React/**", + "$(SRCROOT)/../../react-native/React/**", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = TheoplayerGemius; + SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "TheoplayerGemius-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 58B511F11A9E6C8500147676 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../../../React/**", + "$(SRCROOT)/../../react-native/React/**", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = TheoplayerGemius; + SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "TheoplayerGemius-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "TheoplayerGemius" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511ED1A9E6C8500147676 /* Debug */, + 58B511EE1A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "TheoplayerGemius" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511F01A9E6C8500147676 /* Debug */, + 58B511F11A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 58B511D31A9E6C8500147676 /* Project object */; +} diff --git a/gemius/manifest.js b/gemius/manifest.js new file mode 100755 index 00000000..e7629f28 --- /dev/null +++ b/gemius/manifest.js @@ -0,0 +1,6 @@ +const manifest = { + version: process.env.npm_package_version, + buildDate: new Date().toISOString(), +}; + +console.log(JSON.stringify(manifest)); diff --git a/gemius/package.json b/gemius/package.json new file mode 100644 index 00000000..2e5bd986 --- /dev/null +++ b/gemius/package.json @@ -0,0 +1,85 @@ +{ + "name": "@theoplayer/react-native-analytics-gemius", + "version": "1.0.0", + "description": "Nielsen Gemius analytics connector for @theoplayer/react-native", + "main": "lib/commonjs/index", + "module": "lib/module/index", + "types": "lib/typescript/src/index.d.ts", + "react-native": "src/index", + "source": "src/index", + "files": [ + "src", + "lib", + "android", + "ios", + "cpp", + "*.podspec", + "*.tgz", + "CHANGELOG.md", + "manifest.json", + "!lib/typescript/example", + "!ios/build", + "!android/build", + "!android/gradle", + "!android/gradlew", + "!android/gradlew.bat", + "!android/local.properties", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__", + "!**/.*" + ], + "scripts": { + "typescript": "tsc --noEmit", + "build": "npm run manifest && bob build", + "manifest": "node manifest.js > src/manifest.json", + "prepare": "npm run manifest && npm run build", + "clean": "rimraf lib android/build example/android/build example/android/app/build example/ios/build" + }, + "keywords": [ + "react-native", + "THEOplayer", + "ios", + "android", + "gemius" + ], + "repository": { + "type": "git", + "url": "git@github.com:THEOplayer/react-native-connectors.git" + }, + "author": "THEO Technologies", + "license": "SEE LICENSE AT https://www.theoplayer.com/terms", + "homepage": "https://github.com/THEOplayer/react-native-connectors/tree/main/gemius#readme", + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-theoplayer": "^3 || ^7 || ^8 || ^9", + "theoplayer": "^5 || ^6 || ^7 || ^8 || ^9" + }, + "peerDependenciesMeta": { + "theoplayer": { + "optional": true + } + }, + "eslintIgnore": [ + "node_modules/", + "lib/" + ], + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "commonjs", + "module", + [ + "typescript", + { + "project": "tsconfig.build.json" + } + ] + ] + } +} diff --git a/gemius/react-native-theoplayer-gemius.podspec b/gemius/react-native-theoplayer-gemius.podspec new file mode 100644 index 00000000..3c0c8f18 --- /dev/null +++ b/gemius/react-native-theoplayer-gemius.podspec @@ -0,0 +1,45 @@ +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "package.json"))) +folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' + +Pod::Spec.new do |s| + s.name = "react-native-theoplayer-gemius" + s.version = package["version"] + s.summary = package["description"] + s.homepage = package["homepage"] + s.license = package["license"] + s.authors = package["author"] + + s.platforms = { :ios => "13.4", :tvos => "13.4" } + s.source = { :git => "https://github.com/THEOplayer/react-native-connectors.git", :tag => "gemius_v#{s.version}" } + + s.source_files = "ios/**/*.{h,m,mm,swift}" + + s.dependency "react-native-theoplayer" + s.dependency 'GemiusSDK' + + # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. + # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. + if respond_to?(:install_modules_dependencies, true) + install_modules_dependencies(s) + else + s.dependency "React-Core" + + # Don't install the dependencies when we run `pod install` in the old architecture. + if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then + s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" + s.pod_target_xcconfig = { + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", + "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" + } + s.dependency "React-Codegen" + s.dependency "RCT-Folly" + s.dependency "RCTRequired" + s.dependency "RCTTypeSafety" + s.dependency "ReactCommon/turbomodule/core" + end + end +end + diff --git a/gemius/src/__tests__/index.test.tsx b/gemius/src/__tests__/index.test.tsx new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/gemius/src/__tests__/index.test.tsx @@ -0,0 +1 @@ +export {}; diff --git a/gemius/src/api/GemiusConfiguration.ts b/gemius/src/api/GemiusConfiguration.ts new file mode 100644 index 00000000..aeda8fcd --- /dev/null +++ b/gemius/src/api/GemiusConfiguration.ts @@ -0,0 +1,7 @@ +export type GemiusConfiguration = { + applicationName: string; + applicationVersion: string; + hitCollectorHost: string; + gemiusId: string; + debug?: boolean; +}; diff --git a/gemius/src/api/GemiusConnector.ts b/gemius/src/api/GemiusConnector.ts new file mode 100644 index 00000000..87a47477 --- /dev/null +++ b/gemius/src/api/GemiusConnector.ts @@ -0,0 +1,36 @@ +import type { THEOplayer } from 'react-native-theoplayer'; +import { GemiusConfiguration } from './GemiusConfiguration'; +import { GemiusConnectorAdapter } from '../internal/GemiusConnectorAdapter'; +import { ProgramData } from './ProgramData'; + +export class GemiusConnector { + private connectorAdapter: GemiusConnectorAdapter; + + /** + * Create GemiusConnector + * + * @param player A React Native THEOplayer instance. + * @param configuration The connector configuration + * + * @public + */ + constructor(player: THEOplayer, configuration: GemiusConfiguration) { + this.connectorAdapter = new GemiusConnectorAdapter(player, configuration); + } + + /** + * Update the current program data. + */ + update(programId: string, programData: ProgramData) { + this.connectorAdapter.update(programId, programData); + } + + /** + * Stops video and ad analytics and closes all sessions. + * + * @public + */ + destroy(): void { + this.connectorAdapter.destroy(); + } +} diff --git a/gemius/src/api/ProgramData.ts b/gemius/src/api/ProgramData.ts new file mode 100644 index 00000000..dd097f69 --- /dev/null +++ b/gemius/src/api/ProgramData.ts @@ -0,0 +1,27 @@ +export enum ProgramType { + AUDIO = 0, + VIDEO = 1, +} + +export class ProgramData { + name?: string; + duration?: number; + transmission?: string; + quality?: string; + resolution?: string; + volume?: number; + externalPremiereDate?: string; + premiereDate?: string; + series?: string; + programType?: ProgramType; + typology?: string; + programGenre?: number; + programPartialName?: string; + programProducer?: string; + programThematicCategory?: string; + programSeason?: string; + transmissionChannel?: string; + transmissionStartTime?: string; + transmissionType?: number; + [key: string | number | ProgramType]: string | number; +} diff --git a/gemius/src/api/hooks/useGemius.ts b/gemius/src/api/hooks/useGemius.ts new file mode 100644 index 00000000..89c823bf --- /dev/null +++ b/gemius/src/api/hooks/useGemius.ts @@ -0,0 +1,39 @@ +import { PlayerEventType, THEOplayer } from 'react-native-theoplayer'; +import { RefObject, useEffect, useRef } from 'react'; +import { GemiusConnector } from '../GemiusConnector'; +import { GemiusConfiguration } from '../GemiusConfiguration'; + +export function useGemius(configuration: GemiusConfiguration): [RefObject, (player: THEOplayer | undefined) => void] { + const connector = useRef(); + const theoPlayer = useRef(); + + const initialize = (player: THEOplayer | undefined) => { + // Optionally destroy existent connector + onDestroy(); + + theoPlayer.current = player; + if (player) { + connector.current = new GemiusConnector(player, configuration); + player.addEventListener(PlayerEventType.DESTROY, onDestroy); + } else { + throw new Error('Invalid THEOplayer instance'); + } + }; + + const onDestroy = () => { + if (connector.current) { + if (!theoPlayer.current) { + throw new Error('Invalid THEOplayer instance'); + } + theoPlayer.current.removeEventListener(PlayerEventType.DESTROY, onDestroy); + connector.current.destroy(); + connector.current = undefined; + } + }; + + useEffect(() => { + return onDestroy; + }, []); + + return [connector, initialize]; +} diff --git a/gemius/src/index.ts b/gemius/src/index.ts new file mode 100644 index 00000000..e624b5dc --- /dev/null +++ b/gemius/src/index.ts @@ -0,0 +1,4 @@ +export * from './api/GemiusConnector'; +export * from './api/GemiusConfiguration'; +export * from './api/ProgramData'; +export { useGemius } from './api/hooks/useGemius'; diff --git a/gemius/src/internal/GemiusConnectorAdapter.ts b/gemius/src/internal/GemiusConnectorAdapter.ts new file mode 100644 index 00000000..ccf3857f --- /dev/null +++ b/gemius/src/internal/GemiusConnectorAdapter.ts @@ -0,0 +1,36 @@ +import type { THEOplayer } from 'react-native-theoplayer'; +import { NativeModules } from 'react-native'; +import { GemiusConfiguration } from '../api/GemiusConfiguration'; +import { ProgramData } from '../api/ProgramData'; + +const TAG = 'GemiusConnector'; +const ERROR_MSG = 'GemiusConnectorAdapter Error'; + +export class GemiusConnectorAdapter { + constructor( + private player: THEOplayer, + configuration: GemiusConfiguration, + ) { + try { + NativeModules.GemiusModule.initialize(this.player.nativeHandle || -1, configuration); + } catch (error: unknown) { + console.error(TAG, `${ERROR_MSG}: ${error}`); + } + } + + update(programId: string, programData: ProgramData) { + try { + NativeModules.GemiusModule.update(this.player.nativeHandle || -1, programId, programData); + } catch (error: unknown) { + console.error(TAG, `${ERROR_MSG}: ${error}`); + } + } + + destroy(): void { + try { + NativeModules.GemiusModule.destroy(this.player.nativeHandle || -1); + } catch (error: unknown) { + console.error(TAG, `${ERROR_MSG}: ${error}`); + } + } +} diff --git a/gemius/src/internal/GemiusConnectorAdapter.web.ts b/gemius/src/internal/GemiusConnectorAdapter.web.ts new file mode 100644 index 00000000..e375c23e --- /dev/null +++ b/gemius/src/internal/GemiusConnectorAdapter.web.ts @@ -0,0 +1,16 @@ +import type { THEOplayer } from 'react-native-theoplayer'; +import { GemiusConfiguration } from '../api/GemiusConfiguration'; +import { ProgramData } from '../api/ProgramData'; + +export class GemiusConnectorAdapter { + constructor( + private player: THEOplayer, + configuration: GemiusConfiguration, + ) { + throw new Error('Gemius connector is not supported for Web.'); + } + + update(programId: string, programData: ProgramData) {} + + destroy() {} +} diff --git a/gemius/tsconfig.build.json b/gemius/tsconfig.build.json new file mode 100644 index 00000000..f929194b --- /dev/null +++ b/gemius/tsconfig.build.json @@ -0,0 +1,5 @@ + +{ + "extends": "./tsconfig", + "exclude": ["example", "ios", "android"] +} diff --git a/gemius/tsconfig.json b/gemius/tsconfig.json new file mode 100644 index 00000000..61ad2c8e --- /dev/null +++ b/gemius/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "@theoplayer/react-native-analytics-gemius": [ + "./src/index" + ] + } + } +} diff --git a/gemius/typedoc.json b/gemius/typedoc.json new file mode 100644 index 00000000..7cafc8ff --- /dev/null +++ b/gemius/typedoc.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": [ + "../typedoc.base.json" + ], + "entryPoints": [ + "src/index.ts" + ], + "tsconfig": "tsconfig.json", + "readme": "README.md", + "name": "Gemius Connector" +} diff --git a/package-lock.json b/package-lock.json index b515fb9b..ba4d4f84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,17 +9,19 @@ "version": "0.0.0", "license": "MIT", "workspaces": [ + "agama", "adobe", "adobe-edge", - "agama", + "adscript", "comscore", "conviva", + "drm", "engage", "mux", "nielsen", "yospace", "youbora", - "drm" + "gemius" ], "devDependencies": { "@changesets/cli": "^2.27.10", @@ -40,7 +42,7 @@ "react-native": "^0.74.5", "react-native-builder-bob": "^0.23.2", "react-native-device-info": "^14.0.4", - "react-native-theoplayer": "^9.0.0", + "react-native-theoplayer": "^9.9.1", "rimraf": "^5.0.10", "theoplayer": "^9.0.0", "typedoc": "^0.25.13", @@ -51,7 +53,7 @@ }, "adobe": { "name": "@theoplayer/react-native-analytics-adobe", - "version": "1.9.0", + "version": "1.10.0", "license": "SEE LICENSE AT https://www.theoplayer.com/terms", "peerDependencies": { "react": "*", @@ -68,7 +70,7 @@ }, "adobe-edge": { "name": "@theoplayer/react-native-analytics-adobe-edge", - "version": "0.4.0", + "version": "0.5.0", "license": "SEE LICENSE AT https://www.theoplayer.com/terms", "dependencies": { "openapi-fetch": "^0.9.8" @@ -86,9 +88,25 @@ } } }, + "adscript": { + "name": "@theoplayer/react-native-analytics-adscript", + "version": "1.0.0", + "license": "SEE LICENSE AT https://www.theoplayer.com/terms", + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-theoplayer": "^3 || ^7 || ^8 || ^9", + "theoplayer": "^5 || ^6 || ^7 || ^8 || ^9" + }, + "peerDependenciesMeta": { + "theoplayer": { + "optional": true + } + } + }, "agama": { "name": "@theoplayer/react-native-analytics-agama", - "version": "1.7.0", + "version": "1.8.0", "license": "SEE LICENSE AT https://www.theoplayer.com/terms", "dependencies": { "react-native-uuid": "^2.0.2", @@ -166,6 +184,22 @@ "react-native-mmkv": "^2.12.2" } }, + "gemius": { + "name": "@theoplayer/react-native-analytics-gemius", + "version": "1.0.0", + "license": "SEE LICENSE AT https://www.theoplayer.com/terms", + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-theoplayer": "^3 || ^7 || ^8 || ^9", + "theoplayer": "^5 || ^6 || ^7 || ^8 || ^9" + }, + "peerDependenciesMeta": { + "theoplayer": { + "optional": true + } + } + }, "mux": { "name": "@theoplayer/react-native-analytics-mux", "version": "1.7.0", @@ -4470,6 +4504,10 @@ "resolved": "adobe-edge", "link": true }, + "node_modules/@theoplayer/react-native-analytics-adscript": { + "resolved": "adscript", + "link": true + }, "node_modules/@theoplayer/react-native-analytics-agama": { "resolved": "agama", "link": true @@ -4482,6 +4520,10 @@ "resolved": "conviva", "link": true }, + "node_modules/@theoplayer/react-native-analytics-gemius": { + "resolved": "gemius", + "link": true + }, "node_modules/@theoplayer/react-native-analytics-mux": { "resolved": "mux", "link": true @@ -12042,9 +12084,10 @@ } }, "node_modules/react-native-theoplayer": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/react-native-theoplayer/-/react-native-theoplayer-9.0.0.tgz", - "integrity": "sha512-4rzr7445/AxKBTKdkNF6MKH5+w/EfH0FMNijL30DkY1lnhs5VIMr4IYeD+vU4YP73YU0y436kPceFRhEOBRt+g==", + "version": "9.9.1", + "resolved": "https://registry.npmjs.org/react-native-theoplayer/-/react-native-theoplayer-9.9.1.tgz", + "integrity": "sha512-yJjbaIR4Vor9UlfXH11kT9csedKKlQBe+LDM0Urg4lVX2PY526qQA9JWpILJ9zFruAWpt/1ym7OP9Gguuksg0A==", + "license": "SEE LICENSE AT https://www.theoplayer.com/terms", "dependencies": { "@theoplayer/cmcd-connector-web": "^1.3.0", "buffer": "^6.0.3" @@ -14030,7 +14073,7 @@ }, "yospace": { "name": "@theoplayer/react-native-yospace", - "version": "1.3.0", + "version": "1.3.1", "license": "SEE LICENSE AT https://www.theoplayer.com/terms", "dependencies": { "@theoplayer/yospace-connector-web": "^2.6.0" diff --git a/package.json b/package.json index 73971d0f..c07a1125 100644 --- a/package.json +++ b/package.json @@ -7,17 +7,19 @@ "author": "THEO Technologies NV", "license": "MIT", "workspaces": [ + "agama", "adobe", "adobe-edge", - "agama", + "adscript", "comscore", "conviva", + "drm", "engage", "mux", "nielsen", "yospace", "youbora", - "drm" + "gemius" ], "scripts": { "changeset:version": "changeset version && node .changeset/post-process.js", @@ -56,7 +58,7 @@ "react-native": "^0.74.5", "react-native-builder-bob": "^0.23.2", "react-native-device-info": "^14.0.4", - "react-native-theoplayer": "^9.0.0", + "react-native-theoplayer": "^9.9.1", "rimraf": "^5.0.10", "theoplayer": "^9.0.0", "typedoc": "^0.25.13",