diff --git a/.eslintignore b/.eslintignore index 88edb62..eafd89a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ node_modules/ lib/ +example/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index facfc4a..551c1d4 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ android.iml **/android/**/build **/android/**/release **/android/**/debug +!**/android/app/src/debug/AndroidManixest.xml # Cocoapods # diff --git a/CHANGELOG.md b/CHANGELOG.md index 38283a7..3932d2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,50 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.14.0] - 2024-03-05 + +- iOS SDK version: 6.8.0 +- Android SDK version: 14.0.1 + +### React Native + +#### Added + +- `blockScreenCapture` method to block/unblock screen capture +- `isScreenCaptureBlocked` method to get the current screen capture blocking status +- New callbacks: + - `screenshot`: Detects when a screenshot is taken + - `screenRecording`: Detects when screen recording is active + +#### Changed + +- Raised Android compileSDK level to 35 + +#### Fixed + +- Compatibility issues with RN New Architecture +- Added proguard rules for malware data serialization in release mode on Android + +### Android + +#### Added + +- Passive and active screenshot/screen recording protection + +#### Changed + +- Improved root detection + +#### Fixed + +- Proguard rules to address warnings from okhttp dependency + +### iOS + +#### Added + +- Passive Screenshot/Screen Recording detection + ## [3.13.0] - 2024-12-20 - iOS SDK version: 6.6.3 diff --git a/android/build.gradle b/android/build.gradle index c0d209b..e49f910 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -90,7 +90,7 @@ dependencies { implementation "com.facebook.react:react-native:$react_native_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1" - implementation "com.aheaditec.talsec.security:TalsecSecurity-Community-ReactNative:13.2.0" + implementation "com.aheaditec.talsec.security:TalsecSecurity-Community-ReactNative:14.0.1" } if (isNewArchitectureEnabled()) { diff --git a/android/gradle.properties b/android/gradle.properties index ea55188..48b903b 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,6 +1,6 @@ FreeraspReactNative_kotlinVersion=1.7.0 FreeraspReactNative_minSdkVersion=23 -FreeraspReactNative_targetSdkVersion=31 -FreeraspReactNative_compileSdkVersion=33 +FreeraspReactNative_targetSdkVersion=35 +FreeraspReactNative_compileSdkVersion=35 FreeraspReactNative_ndkversion=21.4.7075529 FreeraspReactNative_reactNativeVersion=+ diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro index ea0d67d..f665ce5 100644 --- a/android/proguard-rules.pro +++ b/android/proguard-rules.pro @@ -1,2 +1,10 @@ -dontwarn java.lang.invoke.StringConcatFactory -keep class com.freeraspreactnative.FreeraspReactNativePackage { public *; } + +-if @kotlinx.serialization.Serializable class ** +-keep class <1> { + *; +} + +-keep class com.freeraspreactnative.models.RNSuspiciousAppInfo$Companion +-keep class com.freeraspreactnative.models.RNPackageInfo$Companion diff --git a/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt b/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt index 71bf610..4ba6c5c 100644 --- a/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt +++ b/android/src/main/java/com/freeraspreactnative/FreeraspReactNativeModule.kt @@ -1,5 +1,6 @@ package com.freeraspreactnative +import android.os.Build import android.os.Handler import android.os.HandlerThread import android.os.Looper @@ -31,11 +32,15 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex private val listener = ThreatListener(FreeraspThreatHandler, FreeraspThreatHandler) private val lifecycleListener = object : LifecycleEventListener { override fun onHostResume() { - // do nothing + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + currentActivity?.let { ScreenProtector.register(it) } + } } override fun onHostPause() { - // do nothing + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + currentActivity?.let { ScreenProtector.unregister(it) } + } } override fun onHostDestroy() { @@ -54,8 +59,7 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex @ReactMethod fun talsecStart( - options: ReadableMap, - promise: Promise + options: ReadableMap, promise: Promise ) { try { @@ -64,10 +68,18 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex listener.registerListener(reactContext) runOnUiThread { Talsec.start(reactContext, config) + mainHandler.post { + talsecStarted = true + // This code must be called only AFTER Talsec.start + currentActivity?.let { activity -> + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + ScreenProtector.register(activity) + } + } + promise.resolve("freeRASP started") + } } - promise.resolve("freeRASP started") - } catch (e: Exception) { promise.reject("TalsecInitializationError", e.message, e) } @@ -136,6 +148,43 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex } } + /** + * Method Block/Unblock screen capture + * @param enable boolean for whether want to block or unblock the screen capture + */ + @ReactMethod + fun blockScreenCapture(enable: Boolean, promise: Promise) { + val activity = currentActivity ?: run { + promise.reject( + "NativePluginError", "Cannot block screen capture, activity is null." + ) + return + } + + runOnUiThread { + try { + Talsec.blockScreenCapture(activity, enable) + promise.resolve("Screen capture is now ${if (enable) "Blocked" else "Enabled"}.") + } catch (e: Exception) { + promise.reject("NativePluginError", "Error in blockScreenCapture: ${e.message}") + } + } + } + + /** + * Method Returns whether screen capture is blocked or not + * @return boolean for is screem capture blocked or not + */ + @ReactMethod + fun isScreenCaptureBlocked(promise: Promise) { + try { + val isBlocked = Talsec.isScreenCaptureBlocked() + promise.resolve(isBlocked) + } catch (e: Exception) { + promise.reject("NativePluginError", "Error in isScreenCaptureBlocked: ${e.message}") + } + } + private fun buildTalsecConfig(config: ReadableMap): TalsecConfig { val androidConfig = config.getMapThrowing("androidConfig") val packageName = androidConfig.getStringThrowing("packageName") @@ -172,11 +221,12 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex private lateinit var appReactContext: ReactApplicationContext + internal var talsecStarted = false + private fun notifyListeners(threat: Threat) { val params = Arguments.createMap() params.putInt(THREAT_CHANNEL_KEY, threat.value) - appReactContext - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + appReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) .emit(THREAT_CHANNEL_NAME, params) } @@ -196,8 +246,7 @@ class FreeraspReactNativeModule(private val reactContext: ReactApplicationContex MALWARE_CHANNEL_KEY, encodedSuspiciousApps ) - appReactContext - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + appReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) .emit(THREAT_CHANNEL_NAME, params) } } diff --git a/android/src/main/java/com/freeraspreactnative/FreeraspThreatHandler.kt b/android/src/main/java/com/freeraspreactnative/FreeraspThreatHandler.kt index 1bf8f01..7061877 100644 --- a/android/src/main/java/com/freeraspreactnative/FreeraspThreatHandler.kt +++ b/android/src/main/java/com/freeraspreactnative/FreeraspThreatHandler.kt @@ -63,6 +63,14 @@ internal object FreeraspThreatHandler : ThreatListener.ThreatDetected, ThreatLis listener?.threatDetected(Threat.SystemVPN) } + override fun onScreenshotDetected() { + listener?.threatDetected(Threat.Screenshot) + } + + override fun onScreenRecordingDetected() { + listener?.threatDetected(Threat.ScreenRecording) + } + internal interface TalsecReactNative { fun threatDetected(threatType: Threat) diff --git a/android/src/main/java/com/freeraspreactnative/ScreenProtector.kt b/android/src/main/java/com/freeraspreactnative/ScreenProtector.kt new file mode 100644 index 0000000..8b1f3ec --- /dev/null +++ b/android/src/main/java/com/freeraspreactnative/ScreenProtector.kt @@ -0,0 +1,164 @@ +package com.freeraspreactnative + +import android.annotation.SuppressLint +import android.annotation.TargetApi +import android.app.Activity +import android.app.Activity.ScreenCaptureCallback +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import android.util.Log +import android.view.WindowManager.SCREEN_RECORDING_STATE_VISIBLE +import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat + +import com.aheaditec.talsec_security.security.api.Talsec +import java.util.function.Consumer + +@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) +internal object ScreenProtector { + private const val TAG = "TalsecScreenProtector" + private const val SCREEN_CAPTURE_PERMISSION = "android.permission.DETECT_SCREEN_CAPTURE" + private const val SCREEN_RECORDING_PERMISSION = "android.permission.DETECT_SCREEN_RECORDING" + private var registered = false + private val screenCaptureCallback = ScreenCaptureCallback { Talsec.onScreenshotDetected() } + private val screenRecordCallback: Consumer = Consumer { state -> + if (state == SCREEN_RECORDING_STATE_VISIBLE) { + Talsec.onScreenRecordingDetected() + } + } + + /** + * Registers screenshot and screen recording detector with the given activity + * + * **IMPORTANT**: android.permission.DETECT_SCREEN_CAPTURE and + * android.permission.DETECT_SCREEN_RECORDING must be + * granted for the app in the AndroidManifest.xml + */ + internal fun register(activity: Activity) { + if (!FreeraspReactNativeModule.talsecStarted || registered) { + return + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + registerScreenCapture(activity) + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + registerScreenRecording(activity) + } + registered = true + } + + /** + * Register Talsec Screen Capture (screenshot) Detector for given activity instance. + * The MainActivity of the app is registered by the plugin itself, other + * activities bust be registered manually as described in the integration guide. + * + * Missing permission is suppressed because the decision to use the screen + * capture API is made by developer, and not enforced by the library. + * + * **IMPORTANT**: android.permission.DETECT_SCREEN_CAPTURE (API 34+) must be + * granted for the app in the AndroidManifest.xml + */ + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @SuppressLint("MissingPermission") + private fun registerScreenCapture(activity: Activity) { + val context = activity.applicationContext + if (!hasPermission(context, SCREEN_CAPTURE_PERMISSION)) { + reportMissingPermission("screenshot", SCREEN_CAPTURE_PERMISSION) + return + } + + activity.registerScreenCaptureCallback(context.mainExecutor, screenCaptureCallback) + } + + /** + * Register Talsec Screen Recording Detector for given activity instance. + * The MainActivity of the app is registered by the plugin itself, other + * activities bust be registered manually as described in the integration guide. + * + * Missing permission is suppressed because the decision to use the screen + * capture API is made by developer, and not enforced by the library. + * + * **IMPORTANT**: android.permission.DETECT_SCREEN_RECORDING (API 35+) must be + * granted for the app in the AndroidManifest.xml + */ + @SuppressLint("MissingPermission") + @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) + private fun registerScreenRecording(activity: Activity) { + val context = activity.applicationContext + if (!hasPermission(context, SCREEN_RECORDING_PERMISSION)) { + reportMissingPermission("screen record", SCREEN_RECORDING_PERMISSION) + return + } + + val initialState = activity.windowManager.addScreenRecordingCallback( + context.mainExecutor, screenRecordCallback + ) + screenRecordCallback.accept(initialState) + + } + + /** + * Unregisters screenshot and screen recording detector with the given activity + * + * **IMPORTANT**: android.permission.DETECT_SCREEN_CAPTURE and + * android.permission.DETECT_SCREEN_RECORDING must be + * granted for the app in the AndroidManifest.xml + */ + @SuppressLint("MissingPermission") + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + internal fun unregister(activity: Activity) { + if (!FreeraspReactNativeModule.talsecStarted || !registered) { + return + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + unregisterScreenCapture(activity) + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + unregisterScreenRecording(activity) + } + registered = false + } + + // Missing permission is suppressed because the decision to use the screen capture API is made + // by developer, and not enforced by the library. + @SuppressLint("MissingPermission") + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + private fun unregisterScreenCapture(activity: Activity) { + val context = activity.applicationContext + if (!hasPermission(context, SCREEN_CAPTURE_PERMISSION)) { + return + } + activity.unregisterScreenCaptureCallback(screenCaptureCallback) + } + + // Missing permission is suppressed because the decision to use the screen capture API is made + // by developer, and not enforced by the library. + @SuppressLint("MissingPermission") + @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) + private fun unregisterScreenRecording(activity: Activity) { + val context = activity.applicationContext + if (!hasPermission(context, SCREEN_RECORDING_PERMISSION)) { + return + } + + activity.windowManager?.removeScreenRecordingCallback(screenRecordCallback) + } + + private fun hasPermission(context: Context, permission: String): Boolean { + return ContextCompat.checkSelfPermission( + context, permission + ) == PackageManager.PERMISSION_GRANTED + } + + private fun reportMissingPermission(protectionType: String, permission: String) { + Log.e( + TAG, + "Failed to register $protectionType callback. Check if $permission permission is granted in AndroidManifest.xml" + ) + } +} diff --git a/android/src/main/java/com/freeraspreactnative/Threat.kt b/android/src/main/java/com/freeraspreactnative/Threat.kt index 32b8e0c..c0b87e5 100644 --- a/android/src/main/java/com/freeraspreactnative/Threat.kt +++ b/android/src/main/java/com/freeraspreactnative/Threat.kt @@ -25,6 +25,8 @@ internal sealed class Threat(val value: Int) { object DevMode : Threat((10000..999999999).random()) object Malware : Threat((10000..999999999).random()) object ADBEnabled : Threat((10000..999999999).random()) + object Screenshot : Threat((10000..999999999).random()) + object ScreenRecording : Threat((10000..999999999).random()) companion object { internal fun getThreatValues(): WritableArray { @@ -43,7 +45,9 @@ internal sealed class Threat(val value: Int) { ObfuscationIssues.value, DevMode.value, Malware.value, - ADBEnabled.value + ADBEnabled.value, + Screenshot.value, + ScreenRecording.value ) ) } diff --git a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt index fec90eb..5928321 100644 --- a/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt +++ b/android/src/main/java/com/freeraspreactnative/utils/Extensions.kt @@ -61,7 +61,7 @@ internal fun ReadableMap.getNestedArraySafe(key: String): Array> { if (this.hasKey(key)) { val inputArray = this.getArray(key)!! for (i in 0 until inputArray.size()) { - outArray.add(inputArray.getArray(i).toPrimitiveArray()) + inputArray.getArray(i)?.let { outArray.add(it.toPrimitiveArray()) } } } return outArray.toTypedArray() diff --git a/example/.eslintrc.js b/example/.eslintrc.js new file mode 100644 index 0000000..fe93d7a --- /dev/null +++ b/example/.eslintrc.js @@ -0,0 +1,16 @@ +module.exports = { + root: true, + extends: '@react-native-community', + rules: { + 'prettier/prettier': [ + 'error', + { + quoteProps: 'consistent', + singleQuote: true, + tabWidth: 2, + trailingComma: 'es5', + useTabs: false, + }, + ], + }, +}; diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..730195d --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,74 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +**/.xcode.env.local + +# Android/IntelliJ +# +.idea +.gradle +local.properties +*.iml +*.hprof +.cxx/ +*.keystore +!debug.keystore +.kotlin/ + +# node.js +# +node_modules/ +npm-debug.log +yarn-error.log + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +**/fastlane/report.xml +**/fastlane/Preview.html +**/fastlane/screenshots +**/fastlane/test_output + +# Bundle artifact +*.jsbundle + +# Ruby / CocoaPods +**/Pods/ +/vendor/bundle/ + +# Temporary files created by Metro to check the health of the file watcher +.metro-health-check* + +# testing +/coverage + +# Yarn +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions diff --git a/example/.node-version b/example/.node-version deleted file mode 100644 index b6a7d89..0000000 --- a/example/.node-version +++ /dev/null @@ -1 +0,0 @@ -16 diff --git a/example/.prettierrc.js b/example/.prettierrc.js new file mode 100644 index 0000000..ce55324 --- /dev/null +++ b/example/.prettierrc.js @@ -0,0 +1,12 @@ +module.exports = { + arrowParens: 'always', + bracketSameLine: true, + bracketSpacing: true, + singleQuote: true, + trailingComma: 'all', + quoteProps: 'consistent', + singleQuote: true, + tabWidth: 2, + trailingComma: 'es5', + useTabs: false, +}; diff --git a/example/.ruby-version b/example/.ruby-version deleted file mode 100644 index a603bb5..0000000 --- a/example/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.7.5 diff --git a/example/.watchmanconfig b/example/.watchmanconfig index 9e26dfe..0967ef4 100644 --- a/example/.watchmanconfig +++ b/example/.watchmanconfig @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/example/Gemfile b/example/Gemfile index 5efda89..03278dd 100644 --- a/example/Gemfile +++ b/example/Gemfile @@ -1,6 +1,10 @@ source 'https://rubygems.org' # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version -ruby '2.7.5' +ruby ">= 2.6.10" -gem 'cocoapods', '~> 1.11', '>= 1.11.2' +# Exclude problematic versions of cocoapods and activesupport that causes build failures. +gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' +gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' +gem 'xcodeproj', '< 1.26.0' +gem 'concurrent-ruby', '< 1.3.4' diff --git a/example/Gemfile.lock b/example/Gemfile.lock new file mode 100644 index 0000000..1f0dc3e --- /dev/null +++ b/example/Gemfile.lock @@ -0,0 +1,121 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.7) + base64 + nkf + rexml + activesupport (7.1.5.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + mutex_m + securerandom (>= 0.3) + tzinfo (~> 2.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.9) + claide (1.1.0) + cocoapods (1.15.2) + addressable (~> 2.8) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.15.2) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 2.1, < 3.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.6.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.8.0) + nap (~> 1.0) + ruby-macho (>= 2.3.0, < 3.0) + xcodeproj (>= 1.23.0, < 2.0) + cocoapods-core (1.15.2) + activesupport (>= 5.0, < 8) + addressable (~> 2.8) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + netrc (~> 0.11) + public_suffix (~> 4.0) + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (2.1) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.2.0) + colored2 (3.1.2) + concurrent-ruby (1.3.3) + connection_pool (2.5.0) + drb (2.2.1) + escape (0.0.4) + ethon (0.16.0) + ffi (>= 1.15.0) + ffi (1.17.1) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + httpclient (2.9.0) + mutex_m + i18n (1.14.7) + concurrent-ruby (~> 1.0) + json (2.10.1) + logger (1.6.6) + minitest (5.25.4) + molinillo (0.8.0) + mutex_m (0.3.0) + nanaimo (0.3.0) + nap (1.1.0) + netrc (0.11.0) + nkf (0.2.0) + public_suffix (4.0.7) + rexml (3.4.1) + ruby-macho (2.5.1) + securerandom (0.3.2) + typhoeus (1.4.1) + ethon (>= 0.9.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + xcodeproj (1.25.1) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (>= 3.3.6, < 4.0) + +PLATFORMS + ruby + +DEPENDENCIES + activesupport (>= 6.1.7.5, != 7.1.0) + cocoapods (>= 1.13, != 1.15.1, != 1.15.0) + concurrent-ruby (< 1.3.4) + xcodeproj (< 1.26.0) + +RUBY VERSION + ruby 2.7.6p219 + +BUNDLED WITH + 2.1.4 diff --git a/example/android/app/_BUCK b/example/android/app/_BUCK deleted file mode 100644 index fde06f3..0000000 --- a/example/android/app/_BUCK +++ /dev/null @@ -1,55 +0,0 @@ -# To learn about Buck see [Docs](https://buckbuild.com/). -# To run your application with Buck: -# - install Buck -# - `npm start` - to start the packager -# - `cd android` -# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` -# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck -# - `buck install -r android/app` - compile, install and run application -# - -load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") - -lib_deps = [] - -create_aar_targets(glob(["libs/*.aar"])) - -create_jar_targets(glob(["libs/*.jar"])) - -android_library( - name = "all-libs", - exported_deps = lib_deps, -) - -android_library( - name = "app-code", - srcs = glob([ - "src/main/java/**/*.java", - ]), - deps = [ - ":all-libs", - ":build_config", - ":res", - ], -) - -android_build_config( - name = "build_config", - package = "com.freeraspreactnativeexample", -) - -android_resource( - name = "res", - package = "com.freeraspreactnativeexample", - res = "src/main/res", -) - -android_binary( - name = "app", - keystore = "//android/keystores:debug", - manifest = "src/main/AndroidManifest.xml", - package_type = "debug", - deps = [ - ":app-code", - ], -) diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 7129132..28e0eeb 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,214 +1,89 @@ apply plugin: "com.android.application" - -import com.android.build.OutputFile -import org.apache.tools.ant.taskdefs.condition.Os +apply plugin: "org.jetbrains.kotlin.android" +apply plugin: "com.facebook.react" /** - * 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: [] - * ] + * This is the configuration block to customize your React Native Android app. + * By default you don't need to apply any configuration, just uncomment the lines you need. */ - -project.ext.react = [ - enableHermes: true, // clean and rebuild if changing -] - -apply from: "../../node_modules/react-native/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 +react { + /* Folders */ + // The root of your project, i.e. where "package.json" lives. Default is '../..' + // root = file("../../") + // The folder where the react-native NPM package is. Default is ../../node_modules/react-native + // reactNativeDir = file("../../node_modules/react-native") + // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen + // codegenDir = file("../../node_modules/@react-native/codegen") + // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js + // cliFile = file("../../node_modules/react-native/cli.js") + + /* Variants */ + // The list of variants to that are debuggable. For those we're going to + // skip the bundling of the JS bundle and the assets. By default is just 'debug'. + // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. + // debuggableVariants = ["liteDebug", "prodDebug"] + + /* Bundling */ + // A list containing the node command and its flags. Default is just 'node'. + // nodeExecutableAndArgs = ["node"] + // + // The command to run when bundling. By default is 'bundle' + // bundleCommand = "ram-bundle" + // + // The path to the CLI configuration file. Default is empty. + // bundleConfig = file(../rn-cli.config.js) + // + // The name of the generated asset file containing your JS bundle + // bundleAssetName = "MyApplication.android.bundle" + // + // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' + // entryFile = file("../js/MyApplication.android.js") + // + // A list of extra flags to pass to the 'bundle' commands. + // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle + // extraPackagerArgs = [] + + /* Hermes Commands */ + // The hermes compiler command to run. By default it is 'hermesc' + // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" + // + // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" + // hermesFlags = ["-O", "-output-source-map"] + + /* Autolinking */ + autolinkLibrariesWithApp() +} /** - * Run Proguard to shrink the Java bytecode in release builds. + * Set this to true to Run Proguard on Release builds to minify the Java bytecode. */ def enableProguardInReleaseBuilds = true /** - * The preferred build flavor of JavaScriptCore. + * The preferred build flavor of JavaScriptCore (JSC) * * For example, to use the international variant, you can use: - * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` + * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+` * * 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 + * give correct results when using with locales other than en-US. Note that * this variant is about 6MiB larger per architecture than default. */ -def jscFlavor = 'org.webkit:android-jsc:+' - -/** - * Whether to enable the Hermes VM. - * - * This should be set on project.ext.react and that value will be read here. If it is not set - * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode - * and the benefits of using Hermes will therefore be sharply reduced. - */ -def enableHermes = project.ext.react.get("enableHermes", false); - -/** - * Architectures to build native code for. - */ -def reactNativeArchitectures() { - def value = project.getProperties().get("reactNativeArchitectures") - return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] -} +def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' android { ndkVersion rootProject.ext.ndkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + compileSdk rootProject.ext.compileSdkVersion - compileSdkVersion rootProject.ext.compileSdkVersion - + namespace "com.freerasp.rn.example" defaultConfig { - applicationId "com.freeraspreactnativeexample" + applicationId "com.freerasp.rn.example" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" - 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()) - } - } - } - } - - 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 (*reactNativeArchitectures()) - } } signingConfigs { debug { @@ -226,96 +101,20 @@ android { // Caution! In production, you need to generate your own keystore file. // see https://reactnative.dev/docs/signed-apk-android. signingConfig signingConfigs.debug - // Enables code shrinking, obfuscation, and optimization for only - // your project's release build type. Make sure to use a build - // variant with `debuggable false`. minifyEnabled enableProguardInReleaseBuilds - // Enables resource shrinking, which is performed by the - // Android Gradle plugin. shrinkResources true - // Includes the default ProGuard rules files that are packaged with - // the Android Gradle plugin. proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } } - - // applicationVariants are e.g. debug, release - applicationVariants.all { variant -> - variant.outputs.each { output -> - // For each separate APK per architecture, set a unique version code as described here: - // 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"]) - - //noinspection GradleDynamicVersion - implementation "com.facebook.react:react-native:+" // From node_modules - - implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" + // The version of react-native is set by the React Native Gradle Plugin + implementation("com.facebook.react:react-android") - debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { - exclude group:'com.facebook.fbjni' - } - - debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { - exclude group:'com.facebook.flipper' - exclude group:'com.squareup.okhttp3', module:'okhttp' - } - - debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { - exclude group:'com.facebook.flipper' - } - - if (enableHermes) { - //noinspection GradleDynamicVersion - implementation("com.facebook.react:hermes-engine:+") { // From node_modules - exclude group:'com.facebook.fbjni' - } + if (hermesEnabled.toBoolean()) { + implementation("com.facebook.react:hermes-android") } 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: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) - -def isNewArchitectureEnabled() { - // To opt-in for the New Architecture, you can either: - // - Set `newArchEnabled` to true inside the `gradle.properties` file - // - Invoke gradle with `-newArchEnabled=true` - // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` - return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" -} diff --git a/example/android/app/build_defs.bzl b/example/android/app/build_defs.bzl deleted file mode 100644 index fff270f..0000000 --- a/example/android/app/build_defs.bzl +++ /dev/null @@ -1,19 +0,0 @@ -"""Helper definitions to glob .aar and .jar targets""" - -def create_aar_targets(aarfiles): - for aarfile in aarfiles: - name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] - lib_deps.append(":" + name) - android_prebuilt_aar( - name = name, - aar = aarfile, - ) - -def create_jar_targets(jarfiles): - for jarfile in jarfiles: - name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] - lib_deps.append(":" + name) - prebuilt_jar( - name = name, - binary_jar = jarfile, - ) diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index 4b185bc..eb98c01 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -2,12 +2,8 @@ - - - - + tools:ignore="GoogleAppIndexingWarning"/> diff --git a/example/android/app/src/debug/java/com/freeraspreactnativeexample/ReactNativeFlipper.java b/example/android/app/src/debug/java/com/freeraspreactnativeexample/ReactNativeFlipper.java deleted file mode 100644 index 5deb8a9..0000000 --- a/example/android/app/src/debug/java/com/freeraspreactnativeexample/ReactNativeFlipper.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - *

This source code is licensed under the MIT license found in the LICENSE file in the root - * directory of this source tree. - */ -package com.freeraspreactnativeexample; - -import android.content.Context; -import com.facebook.flipper.android.AndroidFlipperClient; -import com.facebook.flipper.android.utils.FlipperUtils; -import com.facebook.flipper.core.FlipperClient; -import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; -import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; -import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; -import com.facebook.flipper.plugins.inspector.DescriptorMapping; -import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; -import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; -import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; -import com.facebook.flipper.plugins.react.ReactFlipperPlugin; -import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; -import com.facebook.react.ReactInstanceEventListener; -import com.facebook.react.ReactInstanceManager; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.modules.network.NetworkingModule; -import okhttp3.OkHttpClient; - -public class ReactNativeFlipper { - public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { - if (FlipperUtils.shouldEnableFlipper(context)) { - final FlipperClient client = AndroidFlipperClient.getInstance(context); - - client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); - client.addPlugin(new ReactFlipperPlugin()); - client.addPlugin(new DatabasesFlipperPlugin(context)); - client.addPlugin(new SharedPreferencesFlipperPlugin(context)); - client.addPlugin(CrashReporterPlugin.getInstance()); - - NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); - NetworkingModule.setCustomClientBuilder( - new NetworkingModule.CustomClientBuilder() { - @Override - public void apply(OkHttpClient.Builder builder) { - builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); - } - }); - client.addPlugin(networkFlipperPlugin); - client.start(); - - // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized - // Hence we run if after all native modules have been initialized - ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); - if (reactContext == null) { - reactInstanceManager.addReactInstanceEventListener( - new ReactInstanceEventListener() { - @Override - public void onReactContextInitialized(ReactContext reactContext) { - reactInstanceManager.removeReactInstanceEventListener(this); - reactContext.runOnNativeModulesQueueThread( - new Runnable() { - @Override - public void run() { - client.addPlugin(new FrescoFlipperPlugin()); - } - }); - } - }); - } else { - client.addPlugin(new FrescoFlipperPlugin()); - } - } - } -} diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index e884e74..1a609b9 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,8 @@ - + + + + android:theme="@style/AppTheme" + android:supportsRtl="true"> = + PackageList(this).packages.apply { + // Packages that cannot be autolinked yet can be added manually here, for example: + // add(MyReactNativePackage()) + } + + override fun getJSMainModuleName(): String = "index" + + override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG + + override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED + } + + override val reactHost: ReactHost + get() = getDefaultReactHost(applicationContext, reactNativeHost) + + override fun onCreate() { + super.onCreate() + SoLoader.init(this, OpenSourceMergedSoMapping) + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // If you opted-in for the New Architecture, we load the native entry point for this app. + load() + } + } +} diff --git a/example/android/app/src/main/java/com/freeraspreactnativeexample/MainActivity.java b/example/android/app/src/main/java/com/freeraspreactnativeexample/MainActivity.java deleted file mode 100644 index 5b03f25..0000000 --- a/example/android/app/src/main/java/com/freeraspreactnativeexample/MainActivity.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.freeraspreactnativeexample; - -import com.facebook.react.ReactActivity; -import com.facebook.react.ReactActivityDelegate; -import com.facebook.react.ReactRootView; - -public class MainActivity extends ReactActivity { - - /** - * Returns the name of the main component registered from JavaScript. This is used to schedule - * rendering of the component. - */ - @Override - protected String getMainComponentName() { - return "FreeraspReactNativeExample"; - } - - /** - * Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and - * you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer - * (Paper). - */ - @Override - protected ReactActivityDelegate createReactActivityDelegate() { - return new MainActivityDelegate(this, getMainComponentName()); - } - - public static class MainActivityDelegate extends ReactActivityDelegate { - public MainActivityDelegate(ReactActivity activity, String mainComponentName) { - super(activity, mainComponentName); - } - - @Override - protected ReactRootView createRootView() { - ReactRootView reactRootView = new ReactRootView(getContext()); - // If you opted-in for the New Architecture, we enable the Fabric Renderer. - reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED); - return reactRootView; - } - - @Override - protected boolean isConcurrentRootEnabled() { - // If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18). - // More on this on https://reactjs.org/blog/2022/03/29/react-v18.html - return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; - } - } -} diff --git a/example/android/app/src/main/java/com/freeraspreactnativeexample/MainApplication.java b/example/android/app/src/main/java/com/freeraspreactnativeexample/MainApplication.java deleted file mode 100644 index eb8dc25..0000000 --- a/example/android/app/src/main/java/com/freeraspreactnativeexample/MainApplication.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.freeraspreactnativeexample; - -import android.app.Application; -import android.content.Context; -import com.facebook.react.PackageList; -import com.facebook.react.ReactApplication; -import com.facebook.react.ReactInstanceManager; -import com.facebook.react.ReactNativeHost; -import com.facebook.react.ReactPackage; -import com.facebook.react.config.ReactFeatureFlags; -import com.facebook.soloader.SoLoader; -import com.freeraspreactnativeexample.newarchitecture.MainApplicationReactNativeHost; -import java.lang.reflect.InvocationTargetException; -import java.util.List; - -public class MainApplication extends Application implements ReactApplication { - - private final ReactNativeHost mReactNativeHost = - new ReactNativeHost(this) { - @Override - public boolean getUseDeveloperSupport() { - return BuildConfig.DEBUG; - } - - @Override - protected List getPackages() { - @SuppressWarnings("UnnecessaryLocalVariable") - List packages = new PackageList(this).getPackages(); - // Packages that cannot be autolinked yet can be added manually here, for example: - // packages.add(new FreeRaspNativePackage()); - return packages; - } - - @Override - protected String getJSMainModuleName() { - return "index"; - } - }; - - private final ReactNativeHost mNewArchitectureNativeHost = - new MainApplicationReactNativeHost(this); - - @Override - public ReactNativeHost getReactNativeHost() { - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - return mNewArchitectureNativeHost; - } else { - return mReactNativeHost; - } - } - - @Override - public void onCreate() { - super.onCreate(); - // If you opted-in for the New Architecture, we enable the TurboModule system - ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; - SoLoader.init(this, /* native exopackage */ false); - initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); - } - - /** - * Loads Flipper in React Native templates. Call this in the onCreate method with something like - * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); - * - * @param context - * @param reactInstanceManager - */ - private static void initializeFlipper( - Context context, ReactInstanceManager reactInstanceManager) { - if (BuildConfig.DEBUG) { - try { - /* - We use reflection here to pick up the class that initializes Flipper, - since Flipper library is not available in release mode - */ - Class aClass = Class.forName("com.freeraspreactnativeexample.ReactNativeFlipper"); - aClass - .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) - .invoke(null, context, reactInstanceManager); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - } - } -} diff --git a/example/android/app/src/main/java/com/freeraspreactnativeexample/newarchitecture/MainApplicationReactNativeHost.java b/example/android/app/src/main/java/com/freeraspreactnativeexample/newarchitecture/MainApplicationReactNativeHost.java deleted file mode 100644 index f94cee9..0000000 --- a/example/android/app/src/main/java/com/freeraspreactnativeexample/newarchitecture/MainApplicationReactNativeHost.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.freeraspreactnativeexample.newarchitecture; - -import android.app.Application; -import androidx.annotation.NonNull; -import com.facebook.react.PackageList; -import com.facebook.react.ReactInstanceManager; -import com.facebook.react.ReactNativeHost; -import com.facebook.react.ReactPackage; -import com.facebook.react.ReactPackageTurboModuleManagerDelegate; -import com.facebook.react.bridge.JSIModulePackage; -import com.facebook.react.bridge.JSIModuleProvider; -import com.facebook.react.bridge.JSIModuleSpec; -import com.facebook.react.bridge.JSIModuleType; -import com.facebook.react.bridge.JavaScriptContextHolder; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.UIManager; -import com.facebook.react.fabric.ComponentFactory; -import com.facebook.react.fabric.CoreComponentsRegistry; -import com.facebook.react.fabric.FabricJSIModuleProvider; -import com.facebook.react.fabric.ReactNativeConfig; -import com.facebook.react.uimanager.ViewManagerRegistry; -import com.freeraspreactnativeexample.BuildConfig; -import com.freeraspreactnativeexample.newarchitecture.components.MainComponentsRegistry; -import com.freeraspreactnativeexample.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate; -import java.util.ArrayList; -import java.util.List; - -/** - * A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both - * TurboModule delegates and the Fabric Renderer. - * - *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the - * `newArchEnabled` property). Is ignored otherwise. - */ -public class MainApplicationReactNativeHost extends ReactNativeHost { - public MainApplicationReactNativeHost(Application application) { - super(application); - } - - @Override - public boolean getUseDeveloperSupport() { - return BuildConfig.DEBUG; - } - - @Override - protected List getPackages() { - List packages = new PackageList(this).getPackages(); - // Packages that cannot be autolinked yet can be added manually here, for example: - // packages.add(new MyReactNativePackage()); - // TurboModules must also be loaded here providing a valid TurboReactPackage implementation: - // packages.add(new TurboReactPackage() { ... }); - // If you have custom Fabric Components, their ViewManagers should also be loaded here - // inside a ReactPackage. - return packages; - } - - @Override - protected String getJSMainModuleName() { - return "index"; - } - - @NonNull - @Override - protected ReactPackageTurboModuleManagerDelegate.Builder - getReactPackageTurboModuleManagerDelegateBuilder() { - // Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary - // for the new architecture and to use TurboModules correctly. - return new MainApplicationTurboModuleManagerDelegate.Builder(); - } - - @Override - protected JSIModulePackage getJSIModulePackage() { - return new JSIModulePackage() { - @Override - public List getJSIModules( - final ReactApplicationContext reactApplicationContext, - final JavaScriptContextHolder jsContext) { - final List specs = new ArrayList<>(); - - // Here we provide a new JSIModuleSpec that will be responsible of providing the - // custom Fabric Components. - specs.add( - new JSIModuleSpec() { - @Override - public JSIModuleType getJSIModuleType() { - return JSIModuleType.UIManager; - } - - @Override - public JSIModuleProvider getJSIModuleProvider() { - final ComponentFactory componentFactory = new ComponentFactory(); - CoreComponentsRegistry.register(componentFactory); - - // Here we register a Components Registry. - // The one that is generated with the template contains no components - // and just provides you the one from React Native core. - MainComponentsRegistry.register(componentFactory); - - final ReactInstanceManager reactInstanceManager = getReactInstanceManager(); - - ViewManagerRegistry viewManagerRegistry = - new ViewManagerRegistry( - reactInstanceManager.getOrCreateViewManagers(reactApplicationContext)); - - return new FabricJSIModuleProvider( - reactApplicationContext, - componentFactory, - ReactNativeConfig.DEFAULT_CONFIG, - viewManagerRegistry); - } - }); - return specs; - } - }; - } -} diff --git a/example/android/app/src/main/java/com/freeraspreactnativeexample/newarchitecture/components/MainComponentsRegistry.java b/example/android/app/src/main/java/com/freeraspreactnativeexample/newarchitecture/components/MainComponentsRegistry.java deleted file mode 100644 index 93664b7..0000000 --- a/example/android/app/src/main/java/com/freeraspreactnativeexample/newarchitecture/components/MainComponentsRegistry.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.freeraspreactnativeexample.newarchitecture.components; - -import com.facebook.jni.HybridData; -import com.facebook.proguard.annotations.DoNotStrip; -import com.facebook.react.fabric.ComponentFactory; -import com.facebook.soloader.SoLoader; - -/** - * Class responsible to load the custom Fabric Components. This class has native methods and needs a - * corresponding C++ implementation/header file to work correctly (already placed inside the jni/ - * folder for you). - * - *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the - * `newArchEnabled` property). Is ignored otherwise. - */ -@DoNotStrip -public class MainComponentsRegistry { - static { - SoLoader.loadLibrary("fabricjni"); - } - - @DoNotStrip private final HybridData mHybridData; - - @DoNotStrip - private native HybridData initHybrid(ComponentFactory componentFactory); - - @DoNotStrip - private MainComponentsRegistry(ComponentFactory componentFactory) { - mHybridData = initHybrid(componentFactory); - } - - @DoNotStrip - public static MainComponentsRegistry register(ComponentFactory componentFactory) { - return new MainComponentsRegistry(componentFactory); - } -} diff --git a/example/android/app/src/main/java/com/freeraspreactnativeexample/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java b/example/android/app/src/main/java/com/freeraspreactnativeexample/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java deleted file mode 100644 index 069a11a..0000000 --- a/example/android/app/src/main/java/com/freeraspreactnativeexample/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.freeraspreactnativeexample.newarchitecture.modules; - -import com.facebook.jni.HybridData; -import com.facebook.react.ReactPackage; -import com.facebook.react.ReactPackageTurboModuleManagerDelegate; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.soloader.SoLoader; -import java.util.List; - -/** - * Class responsible to load the TurboModules. This class has native methods and needs a - * corresponding C++ implementation/header file to work correctly (already placed inside the jni/ - * folder for you). - * - *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the - * `newArchEnabled` property). Is ignored otherwise. - */ -public class MainApplicationTurboModuleManagerDelegate - extends ReactPackageTurboModuleManagerDelegate { - - private static volatile boolean sIsSoLibraryLoaded; - - protected MainApplicationTurboModuleManagerDelegate( - ReactApplicationContext reactApplicationContext, List packages) { - super(reactApplicationContext, packages); - } - - protected native HybridData initHybrid(); - - native boolean canCreateTurboModule(String moduleName); - - public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder { - protected MainApplicationTurboModuleManagerDelegate build( - ReactApplicationContext context, List packages) { - return new MainApplicationTurboModuleManagerDelegate(context, packages); - } - } - - @Override - protected synchronized void maybeLoadOtherSoLibraries() { - if (!sIsSoLibraryLoaded) { - // If you change the name of your application .so file in the Android.mk file, - // make sure you update the name here as well. - SoLoader.loadLibrary("freeraspreactnativeexample_appmodules"); - sIsSoLibraryLoaded = true; - } - } -} diff --git a/example/android/app/src/main/jni/CMakeLists.txt b/example/android/app/src/main/jni/CMakeLists.txt deleted file mode 100644 index 3a9b813..0000000 --- a/example/android/app/src/main/jni/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 3.13) - -# Define the library name here. -project(freeraspreactnativeexample_appmodules) - -# This file includes all the necessary to let you build your application with the New Architecture. -include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake) diff --git a/example/android/app/src/main/jni/MainApplicationModuleProvider.cpp b/example/android/app/src/main/jni/MainApplicationModuleProvider.cpp deleted file mode 100644 index 26162dd..0000000 --- a/example/android/app/src/main/jni/MainApplicationModuleProvider.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "MainApplicationModuleProvider.h" - -#include -#include - -namespace facebook { -namespace react { - -std::shared_ptr MainApplicationModuleProvider( - const std::string &moduleName, - const JavaTurboModule::InitParams ¶ms) { - // Here you can provide your own module provider for TurboModules coming from - // either your application or from external libraries. The approach to follow - // is similar to the following (for a library called `samplelibrary`: - // - // auto module = samplelibrary_ModuleProvider(moduleName, params); - // if (module != nullptr) { - // return module; - // } - // return rncore_ModuleProvider(moduleName, params); - - // Module providers autolinked by RN CLI - auto rncli_module = rncli_ModuleProvider(moduleName, params); - if (rncli_module != nullptr) { - return rncli_module; - } - - return rncore_ModuleProvider(moduleName, params); -} - -} // namespace react -} // namespace facebook diff --git a/example/android/app/src/main/jni/MainApplicationModuleProvider.h b/example/android/app/src/main/jni/MainApplicationModuleProvider.h deleted file mode 100644 index b38ccf5..0000000 --- a/example/android/app/src/main/jni/MainApplicationModuleProvider.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace facebook { -namespace react { - -std::shared_ptr MainApplicationModuleProvider( - const std::string &moduleName, - const JavaTurboModule::InitParams ¶ms); - -} // namespace react -} // namespace facebook diff --git a/example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp b/example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp deleted file mode 100644 index 5fd688c..0000000 --- a/example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "MainApplicationTurboModuleManagerDelegate.h" -#include "MainApplicationModuleProvider.h" - -namespace facebook { -namespace react { - -jni::local_ref -MainApplicationTurboModuleManagerDelegate::initHybrid( - jni::alias_ref) { - return makeCxxInstance(); -} - -void MainApplicationTurboModuleManagerDelegate::registerNatives() { - registerHybrid({ - makeNativeMethod( - "initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid), - makeNativeMethod( - "canCreateTurboModule", - MainApplicationTurboModuleManagerDelegate::canCreateTurboModule), - }); -} - -std::shared_ptr -MainApplicationTurboModuleManagerDelegate::getTurboModule( - const std::string &name, - const std::shared_ptr &jsInvoker) { - // Not implemented yet: provide pure-C++ NativeModules here. - return nullptr; -} - -std::shared_ptr -MainApplicationTurboModuleManagerDelegate::getTurboModule( - const std::string &name, - const JavaTurboModule::InitParams ¶ms) { - return MainApplicationModuleProvider(name, params); -} - -bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule( - const std::string &name) { - return getTurboModule(name, nullptr) != nullptr || - getTurboModule(name, {.moduleName = name}) != nullptr; -} - -} // namespace react -} // namespace facebook diff --git a/example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h b/example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h deleted file mode 100644 index 3606cae..0000000 --- a/example/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include - -#include -#include - -namespace facebook { -namespace react { - -class MainApplicationTurboModuleManagerDelegate - : public jni::HybridClass< - MainApplicationTurboModuleManagerDelegate, - TurboModuleManagerDelegate> { - public: - // Adapt it to the package you used for your Java class. - static constexpr auto kJavaDescriptor = - "Lcom/freeraspreactnativeexample/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;"; - - static jni::local_ref initHybrid(jni::alias_ref); - - static void registerNatives(); - - std::shared_ptr getTurboModule( - const std::string &name, - const std::shared_ptr &jsInvoker) override; - std::shared_ptr getTurboModule( - const std::string &name, - const JavaTurboModule::InitParams ¶ms) override; - - /** - * Test-only method. Allows user to verify whether a TurboModule can be - * created by instances of this class. - */ - bool canCreateTurboModule(const std::string &name); -}; - -} // namespace react -} // namespace facebook diff --git a/example/android/app/src/main/jni/MainComponentsRegistry.cpp b/example/android/app/src/main/jni/MainComponentsRegistry.cpp deleted file mode 100644 index 54f598a..0000000 --- a/example/android/app/src/main/jni/MainComponentsRegistry.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "MainComponentsRegistry.h" - -#include -#include -#include -#include -#include - -namespace facebook { -namespace react { - -MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {} - -std::shared_ptr -MainComponentsRegistry::sharedProviderRegistry() { - auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry(); - - // Autolinked providers registered by RN CLI - rncli_registerProviders(providerRegistry); - - // Custom Fabric Components go here. You can register custom - // components coming from your App or from 3rd party libraries here. - // - // providerRegistry->add(concreteComponentDescriptorProvider< - // AocViewerComponentDescriptor>()); - return providerRegistry; -} - -jni::local_ref -MainComponentsRegistry::initHybrid( - jni::alias_ref, - ComponentFactory *delegate) { - auto instance = makeCxxInstance(delegate); - - auto buildRegistryFunction = - [](EventDispatcher::Weak const &eventDispatcher, - ContextContainer::Shared const &contextContainer) - -> ComponentDescriptorRegistry::Shared { - auto registry = MainComponentsRegistry::sharedProviderRegistry() - ->createComponentDescriptorRegistry( - {eventDispatcher, contextContainer}); - - auto mutableRegistry = - std::const_pointer_cast(registry); - - mutableRegistry->setFallbackComponentDescriptor( - std::make_shared( - ComponentDescriptorParameters{ - eventDispatcher, contextContainer, nullptr})); - - return registry; - }; - - delegate->buildRegistryFunction = buildRegistryFunction; - return instance; -} - -void MainComponentsRegistry::registerNatives() { - registerHybrid({ - makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid), - }); -} - -} // namespace react -} // namespace facebook diff --git a/example/android/app/src/main/jni/MainComponentsRegistry.h b/example/android/app/src/main/jni/MainComponentsRegistry.h deleted file mode 100644 index 5a6eaf0..0000000 --- a/example/android/app/src/main/jni/MainComponentsRegistry.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace facebook { -namespace react { - -class MainComponentsRegistry - : public facebook::jni::HybridClass { - public: - // Adapt it to the package you used for your Java class. - constexpr static auto kJavaDescriptor = - "Lcom/freeraspreactnativeexample/newarchitecture/components/MainComponentsRegistry;"; - - static void registerNatives(); - - MainComponentsRegistry(ComponentFactory *delegate); - - private: - static std::shared_ptr - sharedProviderRegistry(); - - static jni::local_ref initHybrid( - jni::alias_ref, - ComponentFactory *delegate); -}; - -} // namespace react -} // namespace facebook diff --git a/example/android/app/src/main/jni/OnLoad.cpp b/example/android/app/src/main/jni/OnLoad.cpp deleted file mode 100644 index c569b6e..0000000 --- a/example/android/app/src/main/jni/OnLoad.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include "MainApplicationTurboModuleManagerDelegate.h" -#include "MainComponentsRegistry.h" - -JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { - return facebook::jni::initialize(vm, [] { - facebook::react::MainApplicationTurboModuleManagerDelegate:: - registerNatives(); - facebook::react::MainComponentsRegistry::registerNatives(); - }); -} diff --git a/example/android/app/src/main/res/drawable/rn_edit_text_material.xml b/example/android/app/src/main/res/drawable/rn_edit_text_material.xml index f35d996..5c25e72 100644 --- a/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +++ b/example/android/app/src/main/res/drawable/rn_edit_text_material.xml @@ -17,10 +17,11 @@ android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material" android:insetRight="@dimen/abc_edit_text_inset_horizontal_material" android:insetTop="@dimen/abc_edit_text_inset_top_material" - android:insetBottom="@dimen/abc_edit_text_inset_bottom_material"> + android:insetBottom="@dimen/abc_edit_text_inset_bottom_material" + > -