diff --git a/native/android/app/CMakeLists.txt b/native/android/app/CMakeLists.txt
index 8de088a6a..97b68340c 100644
--- a/native/android/app/CMakeLists.txt
+++ b/native/android/app/CMakeLists.txt
@@ -1,246 +1,246 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
project(comm CXX C)
set(CMAKE_CXX_STANDARD 14)
# C0103 is a naming convention, but the variable names which need to be set
# are determined by the upstream project
# cmake-lint: disable=C0103
# Disable line length as some paths are hard to reduce without becoming cryptic
# cmake-lint: disable=C0301
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.18)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
set(PACKAGE_NAME "comm_jni_module")
find_package(fbjni REQUIRED CONFIG)
set(BUILD_TESTING OFF)
set(HAVE_SYMBOLIZE OFF)
set(WITH_GTEST OFF CACHE BOOL "Use googletest" FORCE)
set(WITH_GFLAGS OFF CACHE BOOL "Use gflags" FORCE)
# General
set(_third_party_dir ${CMAKE_CURRENT_SOURCE_DIR}/build/third-party-ndk)
set(_android_build_dir build/${CMAKE_ANDROID_ARCH_ABI})
include(FetchContent)
if(CMAKE_ANDROID_ARCH_ABI STREQUAL arm64-v8a)
set(Rust_CARGO_TARGET aarch64-linux-android)
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL x86)
set(Rust_CARGO_TARGET i686-linux-android)
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL x86_64)
set(Rust_CARGO_TARGET x86_64-linux-android)
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL armeabi-v7a)
set(Rust_CARGO_TARGET arm-linux-androideabi)
endif()
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} CMAKE_HOST_SYSTEM_NAME_LOWER)
set(_toolchain_path
"$ENV{ANDROID_HOME}/ndk/${NDK_VERSION}/toolchains/llvm/prebuilt/${CMAKE_HOST_SYSTEM_NAME_LOWER}-x86_64/bin"
)
if(EXISTS "${_toolchain_path}/${Rust_CARGO_TARGET}-ar")
set(AR "${_toolchain_path}/${Rust_CARGO_TARGET}-ar")
else()
set(AR "${_toolchain_path}/llvm-ar")
endif()
FetchContent_Declare(
Corrosion
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
GIT_TAG v0.2.1
)
FetchContent_MakeAvailable(Corrosion)
include(../../../shared/cmake/corrosion-cxx.cmake)
add_library_rust(PATH ../../native_rust_library NAMESPACE comm)
# We're updating parameters below for Cmake's find_OpenSSL() function
set(OPENSSL_ROOT_DIR
"${_third_party_dir}/openssl/openssl-${OPENSSL_VERSION}/${_android_build_dir}"
)
list(APPEND CMAKE_FIND_ROOT_PATH "${OPENSSL_ROOT_DIR}")
add_subdirectory(${_third_party_dir}/glog/glog-${GLOG_VERSION}/)
add_subdirectory(../../node_modules/olm ./build)
set(_node_modules_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../node_modules)
set(_react_native_dir ${_node_modules_dir}/react-native)
string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LOWER)
include_directories(
${_react_native_dir}/React
${_react_native_dir}/React/Base
${_react_native_dir}/ReactCommon
${_react_native_dir}/ReactCommon/jsi
${_react_native_dir}/ReactCommon/callinvoker
${_react_native_dir}/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon
# OpenSSL
${_third_party_dir}/openssl/openssl-${OPENSSL_VERSION}/build/${CMAKE_ANDROID_ARCH_ABI}/include
# SQLCipher amalgamation
${_node_modules_dir}/@commapp/sqlcipher-amalgamation/src
# SQLite ORM
../../cpp/third-party/sqlite_orm
# symlinked React Native headers
../headers
# external libs
${_third_party_dir}/folly
${_third_party_dir}/boost/boost
${_third_party_dir}/double-conversion
# comm android specific code
./src/cpp
# comm native mutual code
../../cpp/CommonCpp/
../../cpp/CommonCpp/NativeModules
../../cpp/CommonCpp/NativeModules/InternalModules
../../cpp/CommonCpp/NativeModules/PersistentStorageUtilities
../../cpp/CommonCpp/NativeModules/PersistentStorageUtilities/ThreadOperationsUtilities
../../cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities
../../cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs
../../cpp/CommonCpp/DatabaseManagers
../../cpp/CommonCpp/Tools
# native rust library
${native_rust_library_include_dir}
)
# search for all cpp files in this directory
file(GLOB SQLCIPHER
"${_node_modules_dir}/@commapp/sqlcipher-amalgamation/src/*.c"
)
file(GLOB_RECURSE COMMON_NATIVE_CODE "../../cpp/CommonCpp/**/*.cpp")
file(GLOB ANDROID_NATIVE_CODE "./src/cpp/*.cpp")
file(GLOB DOUBLE_CONVERSION_SOURCES
"${_third_party_dir}/double-conversion/double-conversion/*.cc"
)
add_library(
# Sets the name of the library.
${PACKAGE_NAME}
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
${_react_native_dir}/ReactCommon/jsi/jsi/jsi.cpp
${_react_native_dir}/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/CallInvokerHolder.cpp
${_react_native_dir}/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp
- ${_react_native_dir}/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.cpp
+ ${_react_native_dir}/ReactCommon/react/bridging/LongLivedObject.cpp
${_react_native_dir}/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.cpp
# SQLCipher
${SQLCIPHER}
# folly
${_third_party_dir}/folly/folly/detail/Futex.cpp
${_third_party_dir}/folly/folly/synchronization/ParkingLot.cpp
${_third_party_dir}/folly/folly/lang/SafeAssert.cpp
${_third_party_dir}/folly/folly/FileUtil.cpp
${_third_party_dir}/folly/folly/Subprocess.cpp
${_third_party_dir}/folly/folly/File.cpp
${_third_party_dir}/folly/folly/Format.cpp
${_third_party_dir}/folly/folly/Conv.cpp
${_third_party_dir}/folly/folly/io/IOBuf.cpp
${_third_party_dir}/folly/folly/memory/detail/MallocImpl.cpp
${_third_party_dir}/folly/folly/ScopeGuard.cpp
${_third_party_dir}/folly/folly/hash/SpookyHashV2.cpp
${_third_party_dir}/folly/folly/io/IOBufQueue.cpp
${_third_party_dir}/folly/folly/lang/Assume.cpp
${_third_party_dir}/folly/folly/String.cpp
${_third_party_dir}/folly/folly/portability/SysUio.cpp
${_third_party_dir}/folly/folly/net/NetOps.cpp
${_third_party_dir}/folly/folly/dynamic.cpp
${_third_party_dir}/folly/folly/json.cpp
${_third_party_dir}/folly/folly/json_pointer.cpp
${_third_party_dir}/folly/folly/Unicode.cpp
# double-conversion
${DOUBLE_CONVERSION_SOURCES}
# comm code
${ANDROID_NATIVE_CODE}
${COMMON_NATIVE_CODE}
)
add_definitions(
# Folly
-DFOLLY_NO_CONFIG=1
-DFOLLY_HAVE_CLOCK_GETTIME=1
-DFOLLY_HAVE_MEMRCHR=1
-DFOLLY_USE_LIBCPP=1
-DFOLLY_MOBILE=1
# SQLCipher
-DSQLITE_THREADSAFE=0
-DSQLITE_HAS_CODEC
-DSQLITE_TEMP_STORE=2
-DSQLCIPHER_CRYPTO_OPENSSL
)
find_library(log-lib log)
add_library(
# OpenSSL Crypto lib
openssl-crypto
STATIC
IMPORTED
)
set_target_properties(
# OpenSSL Crypto lib
openssl-crypto
PROPERTIES IMPORTED_LOCATION
"${_third_party_dir}/openssl/openssl-${OPENSSL_VERSION}/build/${CMAKE_ANDROID_ARCH_ABI}/lib/libcrypto.a"
)
add_library(
# OpenSSL SSL lib
openssl-ssl
STATIC
IMPORTED
)
set_target_properties(
# OpenSSL SSL lib
openssl-ssl
PROPERTIES IMPORTED_LOCATION
"${_third_party_dir}/openssl/openssl-${OPENSSL_VERSION}/build/${CMAKE_ANDROID_ARCH_ABI}/lib/libssl.a"
)
target_link_libraries(
${PACKAGE_NAME}
fbjni::fbjni
android
${log-lib}
glog::glog
olm
openssl-crypto
openssl-ssl
comm::native_rust_library
)
# add a dummy library which is required by CallInvokerHolderImpl.java
add_library(
turbomodulejsijni
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
./src/cpp/dummy.cpp
)
diff --git a/native/android/app/build.gradle b/native/android/app/build.gradle
index c6c8f5a92..03b1b12ef 100644
--- a/native/android/app/build.gradle
+++ b/native/android/app/build.gradle
@@ -1,582 +1,669 @@
apply plugin: "com.android.application"
import com.android.build.OutputFile
import de.undercouch.gradle.tasks.download.Download
import app.comm.gradle.tasks.GitModules
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
enableHermes: true, // clean and rebuild if changing
- hermesCommand: new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/%OS-BIN%/hermesc",
- cliPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/cli.js",
- composeSourceMapsPath: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/scripts/compose-source-maps.js",
+ cliPath: ["node", "-e", "console.log(require('react-native/cli').bin);"].execute([], projectDir).text.trim(),
]
apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../react.gradle")
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore.
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and that value will be read here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false)
/**
- * Architectures to build native code for in debug.
+ * Architectures to build native code for.
*/
-def nativeArchitectures = project.getProperties().get("reactNativeDebugArchitectures")
+def reactNativeArchitectures() {
+ def value = project.getProperties().get("reactNativeArchitectures")
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
+}
def customDownloadsDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR")
def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES")
def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File("$buildDir/downloads")
def thirdPartyNdkDir = new File("$buildDir/third-party-ndk")
// The Boost library is a very large download (>100MB).
// If Boost is already present on your system, define the REACT_NATIVE_BOOST_PATH env variable
// and the build will use that.
def boostPath = dependenciesPath ?: System.getenv("REACT_NATIVE_BOOST_PATH")
task createNativeDepsDirectories {
downloadsDir.mkdirs()
thirdPartyNdkDir.mkdirs()
}
// FOLLY
task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) {
src("https://github.com/facebook/folly/archive/v${FOLLY_VERSION}.tar.gz")
onlyIfNewer(true)
overwrite(false)
dest(new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz"))
}
def follyFindWrapNoInt = 'return int\\(wrapNoInt\\(open, name, flags, mode\\)\\);'
def follyReplaceWrapNoInt = '''
ssize_t r;
do {
r = open(name, flags, mode);
} while (r == -1 && errno == EINTR);
return r;
'''
def follyFindTableSize = 'for \\(int fd = getdtablesize\\(\\) - 1'
def follyReplaceTableSize = 'for (int fd = sysconf(_SC_OPEN_MAX) - 1'
task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy) {
inputs.properties([
'findWrapNoInt': follyFindWrapNoInt,
'replaceWrapNoInt': follyReplaceWrapNoInt,
'findTableSize': follyFindTableSize,
'replaceTableSize': follyReplaceTableSize,
])
from(dependenciesPath ?: tarTree(downloadFolly.dest))
include("folly-${FOLLY_VERSION}/folly/**/*")
eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") }
// Fixes problem with Folly failing to build on certain systems. See
// https://github.com/facebook/react-native/issues/28298
filter { line -> line.replaceAll(follyFindWrapNoInt, follyReplaceWrapNoInt) }
// fix undeclared identifier 'getdtablesize' in Subprocess.cpp
// https://stackoverflow.com/questions/8225186/portable-equivalent-of-open-max/8225235#8225235
filter { line -> line.replaceAll(follyFindTableSize, follyReplaceTableSize) }
includeEmptyDirs = false
into("$thirdPartyNdkDir/folly")
}
// GLOG
task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz")
onlyIfNewer(true)
overwrite(false)
dest(new File(downloadsDir, "glog-${GLOG_VERSION}.tar.gz"))
}
task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy) {
from(dependenciesPath ?: tarTree(downloadGlog.dest))
include("glog-${GLOG_VERSION}/**/*")
includeEmptyDirs = false
into("$thirdPartyNdkDir/glog")
}
// BOOST
task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
src("https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz")
onlyIfNewer(true)
overwrite(false)
dest(new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz"))
}
task prepareBoost(dependsOn: boostPath ? [] : [downloadBoost]) {
inputs.properties([
'boost.version': BOOST_VERSION
])
outputs.dir("$thirdPartyNdkDir/boost/boost/")
.withPropertyName('boost.output')
doFirst {
copy {
from(boostPath ?: tarTree(resources.gzip(downloadBoost.dest)))
include("boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp")
include("boost_${BOOST_VERSION}/boost/**/*.ipp", "boost/boost/**/*.ipp")
include("boost_${BOOST_VERSION}/boost/**/*.h", "boost/boost/**/*.h")
includeEmptyDirs = false
into("$thirdPartyNdkDir/boost")
}
}
doLast {
file("$thirdPartyNdkDir/boost/boost_${BOOST_VERSION}").renameTo("$thirdPartyNdkDir/boost/boost")
}
}
// DOUBLE-CONVERSION
task downloadDoubleConversion(dependsOn: createNativeDepsDirectories, type: Download) {
src("https://github.com/google/double-conversion/archive/v${DOUBLE_CONVERSION_VERSION}.tar.gz")
onlyIfNewer(true)
overwrite(false)
dest(new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz"))
}
task prepareDoubleConversion(dependsOn: dependenciesPath ? [] : [downloadDoubleConversion], type: Copy) {
from(dependenciesPath ?: tarTree(downloadDoubleConversion.dest))
from("src/main/jni/third-party/double-conversion/Android.mk")
include("double-conversion-${DOUBLE_CONVERSION_VERSION}/src/**/*", "Android.mk")
filesMatching("*/src/**/*", { fname -> fname.path = "double-conversion/${fname.name}" })
includeEmptyDirs = false
into("$thirdPartyNdkDir/double-conversion")
}
// OPENSSL
def hostSystem = System.getProperty('os.name').toLowerCase(Locale.ROOT)
def hostTag = hostSystem.contains('mac') ? 'darwin-x86_64' : 'linux-x86_64'
task downloadOpenSSL(dependsOn: createNativeDepsDirectories, type: Download) {
src("https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz")
onlyIfNewer(true)
overwrite(false)
dest(new File(downloadsDir, "openssl-${OPENSSL_VERSION}.tar.gz"))
}
task prepareOpenSSL(
dependsOn: dependenciesPath ? [] : [downloadOpenSSL],
type: Copy
) {
from(dependenciesPath ?: tarTree(downloadOpenSSL.dest))
include("openssl-${OPENSSL_VERSION}/**/*")
includeEmptyDirs = false
into("${thirdPartyNdkDir}/openssl")
}
task prebuildOpenSSL(dependsOn: dependenciesPath ? [] : [prepareOpenSSL]) {
inputs.properties([
'openssl.version': OPENSSL_VERSION,
- 'openssl.abis': getBuildTypeABIs(nativeArchitectures)
+ 'openssl.abis': getBuildTypeABIs(reactNativeArchitectures())
])
outputs.dir("${thirdPartyNdkDir}/openssl/openssl-${OPENSSL_VERSION}/build/")
.withPropertyName('openssl.output')
doFirst {
- getBuildTypeABIs(nativeArchitectures).each { buildABI ->
+ getBuildTypeABIs(reactNativeArchitectures()).each { buildABI ->
logger.info("Building OpenSSL library for the ${buildABI}")
exec {
commandLine './bash/build_openssl.sh',
// OPENSSL_SUBMODULE_PATH
"${thirdPartyNdkDir}/openssl/openssl-${OPENSSL_VERSION}/",
// HOST_TAG
hostTag,
// ANDROID_ARCH_ABI
"${buildABI}",
// MIN_SDK_VERSION
rootProject.ext.minSdkVersion,
// ANDROID_NDK_HOME
android.ndkDirectory.absolutePath,
// PARALLEL_THREADS
Runtime.getRuntime().availableProcessors()
}
}
}
}
// EXTERNAL LIBS
task prepareExternalLibs {
dependsOn prepareFolly
dependsOn prepareGlog
dependsOn prepareBoost
dependsOn prepareDoubleConversion
dependsOn prebuildOpenSSL
}
// Removes the '.cxx' directory to prevent running
// ninja clean when the 'clean' command is executed
task removeCXX(type: Exec) {
commandLine 'rm', '-rf', '.cxx'
}
// Add cross-compilation targets to Rust toolchain
task updateRustToolchain(type: Exec) {
commandLine "rustup", "target", "add",
"aarch64-linux-android",
"armv7-linux-androideabi",
"i686-linux-android",
"arm-linux-androideabi",
"x86_64-linux-android"
}
// Bind preBuild dependencies only if not 'clean' running
if (!isCleanRunning()) {
afterEvaluate {
preBuild.dependsOn(prepareExternalLibs, updateRustToolchain)
}
}
// Run removing CXX task before the clean execution
beforeEvaluate {
clean.dependsOn(removeCXX)
}
// Detects are we running the 'clean' commands
def isCleanRunning() {
gradle.startParameter.taskRequests.any {
!it.args.isEmpty() && it.args.first().startsWith('clean')
}
}
// Release keystore via macOS Keychain Access
def getPassword(String keyLabel) {
if (System.getenv('ANDROID_SIGNING_PASSWORD')) {
return System.getenv('ANDROID_SIGNING_PASSWORD')
}
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'security',
'find-generic-password',
'-wl', keyLabel,
'-a', System.properties['user.name']
standardOutput = stdout
ignoreExitValue true
}
return stdout.toString().strip()
}
// Returns all ABIs architectures for the 'bundleRelease'
// or only from 'adb devices' if running debug release.
def getBuildTypeABIs(nativeArchitectures) {
if (System.getenv("BUILDKITE") == "true") {
return ["arm64-v8a"]
}
def isBundleRelease = gradle.startParameter.taskRequests.any {
!it.args.isEmpty() && it.args.first().contains("bundleRelease")
}
if (isBundleRelease) {
// All of the supported ABIs
// https://developer.android.com/ndk/guides/abis.html#sa
final allAbis = ["armeabi-v7a", "x86", "arm64-v8a", "x86_64"]
logger.info("Using all architectures to build: ${allAbis}")
return allAbis
}
if (nativeArchitectures) {
- return nativeArchitectures.split(',')
+ return nativeArchitectures
}
// Get current 'adb devices' architectures
def commandOutput = new ByteArrayOutputStream()
exec {
commandLine "./bash/detect_abis.sh"
standardOutput = commandOutput
}
final detectedAbis = commandOutput.toString("UTF-8").trim().tokenize()
logger.info("Detected architectures to build: ${detectedAbis}")
return detectedAbis
}
android {
buildFeatures {
prefab true
}
configurations {
all*.exclude module: 'fbjni-java-only'
}
dependencies {
implementation 'com.facebook.fbjni:fbjni:0.1.0'
}
ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
applicationId 'app.comm.android'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 166
versionName '1.0.166'
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
+ if (isNewArchitectureEnabled()) {
+ // We configure the CMake build only if you decide to opt-in for the New Architecture.
+ externalNativeBuild {
+ cmake {
+ arguments "-DPROJECT_BUILD_DIR=$buildDir",
+ "-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
+ "-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
+ "-DNODE_MODULES_DIR=$rootDir/../node_modules",
+ "-DANDROID_STL=c++_shared"
+ }
+ }
+ if (!enableSeparateBuildPerCPUArchitecture) {
+ ndk {
+ abiFilters (*reactNativeArchitectures())
+ }
+ }
+ }
missingDimensionStrategy 'react-native-camera', 'general'
multiDexEnabled true
}
+
+ if (isNewArchitectureEnabled()) {
+ // We configure the NDK build only if you decide to opt-in for the New Architecture.
+ externalNativeBuild {
+ cmake {
+ path "$projectDir/src/main/jni/CMakeLists.txt"
+ }
+ }
+ def reactAndroidProjectDir = project(':ReactAndroid').projectDir
+ def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
+ dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
+ from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
+ into("$buildDir/react-ndk/exported")
+ }
+ def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
+ dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
+ from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
+ into("$buildDir/react-ndk/exported")
+ }
+ afterEvaluate {
+ // If you wish to add a custom TurboModule or component locally,
+ // you should uncomment this line.
+ // preBuild.dependsOn("generateCodegenArtifactsFromSchema")
+ preDebugBuild.dependsOn(packageReactNdkDebugLibs)
+ preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
+ // Due to a bug inside AGP, we have to explicitly set a dependency
+ // between configureCMakeDebug* tasks and the preBuild tasks.
+ // This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
+ configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild)
+ configureCMakeDebug.dependsOn(preDebugBuild)
+ reactNativeArchitectures().each { architecture ->
+ tasks.findByName("configureCMakeDebug[${architecture}]")?.configure {
+ dependsOn("preDebugBuild")
+ }
+ tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
+ dependsOn("preReleaseBuild")
+ }
+ }
+ }
+ }
+
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
- include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
+ include (*reactNativeArchitectures())
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
release {
if (project.hasProperty('COMM_UPLOAD_STORE_FILE')) {
def password = getPassword('CommAndroidKeyPassword')
storeFile file(COMM_UPLOAD_STORE_FILE)
storePassword password
keyAlias COMM_UPLOAD_KEY_ALIAS
keyPassword password
}
}
}
buildTypes {
- final buildABIs = getBuildTypeABIs(nativeArchitectures)
+ final buildABIs = getBuildTypeABIs(reactNativeArchitectures())
release {
if (project.hasProperty('COMM_UPLOAD_STORE_FILE')) {
signingConfig signingConfigs.release
} else {
signingConfig signingConfigs.debug
}
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
ndk {
abiFilters = []
abiFilters.addAll(buildABIs)
}
}
debug {
signingConfig signingConfigs.debug
ndk {
abiFilters = []
abiFilters.addAll(buildABIs)
}
}
}
packagingOptions {
pickFirst "**/libc++_shared.so"
pickFirst "**/libfbjni.so"
}
defaultConfig {
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_shared",
"-DGLOG_VERSION=" + GLOG_VERSION,
"-DOPENSSL_VERSION=" + OPENSSL_VERSION,
"-DNDK_VERSION=" + rootProject.ext.ndkVersion
targets "comm_jni_module", "turbomodulejsijni"
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
version "3.18.1"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21"
//noinspection GradleDynamicVersion
implementation("com.facebook.react:react-native:+")
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
// If your app supports Android versions before Ice Cream Sandwich (API level 14)
// All fresco packages should use the same version
if (isGifEnabled || isWebpEnabled) {
implementation 'com.facebook.fresco:fresco:2.0.0'
implementation 'com.facebook.fresco:imagepipeline-okhttp3:2.0.0'
}
if (isGifEnabled) {
// For animated gif support
implementation 'com.facebook.fresco:animated-gif:2.0.0'
}
if (isWebpEnabled) {
// For webp support
implementation 'com.facebook.fresco:webpsupport:2.0.0'
if (isWebpAnimatedEnabled) {
// Animated webp support
implementation 'com.facebook.fresco:animated-webp:2.0.0'
}
}
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.facebook.fresco', module:'stetho'
}
implementation "com.google.android.gms:play-services-base:16.1.0"
implementation "com.google.firebase:firebase-core:16.0.9"
implementation "com.google.firebase:firebase-messaging:18.0.0"
implementation "me.leolin:ShortcutBadger:1.1.21@aar"
implementation project(':reactnativekeyboardinput')
implementation "androidx.multidex:multidex:2.0.1"
implementation "androidx.lifecycle:lifecycle-runtime:2.2.0"
implementation "androidx.lifecycle:lifecycle-process:2.2.0"
implementation 'com.facebook.fresco:fresco:2.2.0'
implementation 'com.facebook.fresco:animated-gif:2.2.0'
implementation 'com.facebook.fresco:animated-webp:2.2.0'
implementation 'com.facebook.fresco:webpsupport:2.2.0'
implementation 'org.conscrypt:conscrypt-android:2.0.0'
if (enableHermes) {
- debugImplementation files(new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim(), "../android/hermes-debug.aar"))
- releaseImplementation files(new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim(), "../android/hermes-release.aar"))
+ //noinspection GradleDynamicVersion
+ implementation("com.facebook.react:hermes-engine:+") { // From node_modules
+ exclude group:'com.facebook.fbjni'
+ }
} else {
implementation jscFlavor
}
}
+if (isNewArchitectureEnabled()) {
+ // If new architecture is enabled, we let you build RN from source
+ // Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
+ // This will be applied to all the imported transtitive dependency.
+ configurations.all {
+ resolutionStrategy.dependencySubstitution {
+ substitute(module("com.facebook.react:react-native"))
+ .using(project(":ReactAndroid"))
+ .because("On New Architecture we're building React Native from source")
+ substitute(module("com.facebook.react:hermes-engine"))
+ .using(project(":ReactAndroid:hermes-engine"))
+ .because("On New Architecture we're building Hermes from source")
+ }
+ }
+}
+
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.implementation
into 'libs'
}
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
applyNativeModulesAppBuildGradle(project)
+
+def isNewArchitectureEnabled() {
+ // To opt-in for the New Architecture, you can either:
+ // - Set `newArchEnabled` to true inside the `gradle.properties` file
+ // - Invoke gradle with `-newArchEnabled=true`
+ // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
+ return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
+}
+
apply plugin: 'com.google.gms.google-services'
diff --git a/native/android/app/src/debug/AndroidManifest.xml b/native/android/app/src/debug/AndroidManifest.xml
index c9bd575e8..71977d943 100644
--- a/native/android/app/src/debug/AndroidManifest.xml
+++ b/native/android/app/src/debug/AndroidManifest.xml
@@ -1,14 +1,14 @@
This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree.
*/
package app.comm.android;
import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.android.utils.FlipperUtils;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
+import com.facebook.react.ReactInstanceEventListener;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import okhttp3.OkHttpClient;
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
if (FlipperUtils.shouldEnableFlipper(context)) {
final FlipperClient client = AndroidFlipperClient.getInstance(context);
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
client.addPlugin(new ReactFlipperPlugin());
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new NetworkingModule.CustomClientBuilder() {
@Override
public void apply(OkHttpClient.Builder builder) {
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
}
});
client.addPlugin(networkFlipperPlugin);
client.start();
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
// Hence we run if after all native modules have been initialized
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
reactInstanceManager.addReactInstanceEventListener(
- new ReactInstanceManager.ReactInstanceEventListener() {
+ new ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext reactContext) {
reactInstanceManager.removeReactInstanceEventListener(this);
reactContext.runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
client.addPlugin(new FrescoFlipperPlugin());
}
});
}
});
} else {
client.addPlugin(new FrescoFlipperPlugin());
}
}
}
}
diff --git a/native/android/app/src/main/AndroidManifest.xml b/native/android/app/src/main/AndroidManifest.xml
index 0d1e9d9af..dff351601 100644
--- a/native/android/app/src/main/AndroidManifest.xml
+++ b/native/android/app/src/main/AndroidManifest.xml
@@ -1,69 +1,69 @@
Please note that this class is used ONLY if you opt-in for the New
+ * Architecture (see the `newArchEnabled` property). Is ignored otherwise.
+ */
+public class MainApplicationReactNativeHost extends ReactNativeHost {
+ public MainApplicationReactNativeHost(Application application) {
+ super(application);
+ }
+
+ @Override
+ public boolean getUseDeveloperSupport() {
+ return BuildConfig.DEBUG;
+ }
+
+ @Override
+ protected List Please note that this class is used ONLY if you opt-in for the New
+ * Architecture (see the `newArchEnabled` property). Is ignored otherwise.
+ */
+@DoNotStrip
+public class MainComponentsRegistry {
+ static {
+ SoLoader.loadLibrary("fabricjni");
+ }
+
+ @DoNotStrip private final HybridData mHybridData;
+
+ @DoNotStrip
+ private native HybridData initHybrid(ComponentFactory componentFactory);
+
+ @DoNotStrip
+ private MainComponentsRegistry(ComponentFactory componentFactory) {
+ mHybridData = initHybrid(componentFactory);
+ }
+
+ @DoNotStrip
+ public static MainComponentsRegistry register(
+ ComponentFactory componentFactory) {
+ return new MainComponentsRegistry(componentFactory);
+ }
+}
diff --git a/native/android/app/src/main/java/app/comm/android/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java b/native/android/app/src/main/java/app/comm/android/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java
new file mode 100644
index 000000000..4266d700e
--- /dev/null
+++ b/native/android/app/src/main/java/app/comm/android/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java
@@ -0,0 +1,50 @@
+package app.comm.android.newarchitecture.modules;
+
+import com.facebook.jni.HybridData;
+import com.facebook.react.ReactPackage;
+import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.soloader.SoLoader;
+import java.util.List;
+
+/**
+ * Class responsible to load the TurboModules. This class has native methods and
+ * needs a corresponding C++ implementation/header file to work correctly
+ * (already placed inside the jni/ folder for you).
+ *
+ * Please note that this class is used ONLY if you opt-in for the New
+ * Architecture (see the `newArchEnabled` property). Is ignored otherwise.
+ */
+public class MainApplicationTurboModuleManagerDelegate
+ extends ReactPackageTurboModuleManagerDelegate {
+
+ private static volatile boolean sIsSoLibraryLoaded;
+
+ protected MainApplicationTurboModuleManagerDelegate(
+ ReactApplicationContext reactApplicationContext,
+ List