diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
index 3a280d26..0a1c0b15 100644
--- a/.github/actions/setup/action.yml
+++ b/.github/actions/setup/action.yml
@@ -5,11 +5,22 @@ runs:
using: composite
steps:
- name: Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@65d868f8d4d85d7d4abb7de0875cde3fcc8798f5 # v6
with:
node-version: 'lts/*'
cache: 'yarn'
+ - name: Restore turbo cache
+ uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
+ with:
+ path: |
+ .turbo
+ apps/*/.turbo
+ packages/*/.turbo
+ key: ${{ runner.os }}-turbo-${{ hashFiles('.turbo', '**/.turbo') }}
+ restore-keys: |
+ ${{ runner.os }}-turbo-
+
- name: Install dependencies
run: yarn install
shell: bash
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index f27e0aa0..00000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,78 +0,0 @@
-name: Build
-
-on:
- push:
- branches: [main]
- pull_request:
- branches: [main]
-
-jobs:
- android:
- name: Build Android App
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v4
-
- - name: Setup Java
- uses: actions/setup-java@v4
- with:
- distribution: 'zulu'
- java-version: '17'
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 'lts/*'
- cache: 'yarn'
-
- - name: Install dependencies
- run: yarn install
-
- - name: Build package
- run: yarn build
-
- - name: Build Android App
- run: |
- cd apps/example/kotlin
- ./gradlew assembleDebug
-
- ios:
- name: Build iOS App
- runs-on: macos-latest
-
- steps:
- - uses: actions/checkout@v4
-
- - name: Use appropriate Xcode version
- uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
- with:
- xcode-version: '16'
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 'lts/*'
- cache: 'yarn'
-
- - name: Setup Ruby
- uses: ruby/setup-ruby@v1
- with:
- ruby-version: '3.2'
- bundler-cache: true
-
- - name: Install dependencies
- run: yarn install
-
- - name: Build package
- run: yarn build
-
- - name: Install pods
- run: |
- cd apps/example/swift
- pod install
-
- - name: Build iOS App
- run: |
- cd apps/example/swift
- xcodebuild -workspace SwiftExample.xcworkspace -scheme SwiftExample -configuration Debug -sdk iphonesimulator build CODE_SIGNING_ALLOWED=NO
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c2d7c69c..c06fb21d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,17 +6,22 @@ on:
pull_request:
branches: [main]
+concurrency:
+ group: pr-${{ github.event.pull_request.number }}
+ cancel-in-progress: true
+
jobs:
- lint:
+ build-lint:
+ name: Build & static code analysis
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Setup
uses: ./.github/actions/setup
- - name: Build package
+ - name: Build packages
run: yarn build
- name: Lint files
@@ -24,3 +29,107 @@ jobs:
- name: Typecheck files
run: yarn typecheck
+
+ tester-android:
+ name: Integrated tester Android App
+ runs-on: ubuntu-latest
+ needs: build-lint
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+
+ - name: Validate Gradle Wrapper
+ uses: gradle/actions/wrapper-validation@6f229686ee4375cc4a86b2514c89bac4930e82c4 # v5
+
+ - name: Setup Java
+ uses: actions/setup-java@5d7b2146334bacf88728daaa70414a99f5164e0f # v5
+ with:
+ distribution: 'zulu'
+ java-version: '17'
+
+ - name: Setup Node.js
+ uses: actions/setup-node@65d868f8d4d85d7d4abb7de0875cde3fcc8798f5 # v6
+ with:
+ node-version: 'lts/*'
+ cache: 'yarn'
+
+ - name: Install dependencies
+ run: yarn install
+
+ - name: Build packages
+ run: yarn build
+
+ - name: Restore android build cache
+ uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
+ with:
+ path: |
+ apps/TesterIntegrated/kotlin/build
+ apps/TesterIntegrated/kotlin/app/.cxx
+ apps/TesterIntegrated/kotlin/app/build
+ key: ${{ runner.os }}-tester-android-build-${{ github.sha }}
+ restore-keys: |
+ ${{ runner.os }}-tester-android-build-
+
+ - name: Resture Gradle cache
+ uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-tester-integrated-android-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-tester-integrated-android-gradle-
+
+ - name: Build integrated Android tester app
+ run: yarn run build:tester-integrated:android
+
+ tester-ios:
+ name: Integrated tester iOS App
+ runs-on: macos-latest
+ needs: build-lint
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
+
+ - name: Use appropriate Xcode version
+ uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
+ with:
+ xcode-version: '16'
+
+ - name: Setup Node.js
+ uses: actions/setup-node@65d868f8d4d85d7d4abb7de0875cde3fcc8798f5 # v6
+ with:
+ node-version: 'lts/*'
+ cache: 'yarn'
+
+ - name: Setup Ruby
+ uses: ruby/setup-ruby@5dd816ae0186f20dfa905997a64104db9a8221c7 # v1.280.0
+ with:
+ ruby-version: '3.2'
+ bundler-cache: true
+
+ - name: Install dependencies
+ run: yarn install
+
+ - name: Build packages
+ run: yarn build
+
+ - name: Restore Pods cache
+ uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
+ with:
+ path: |
+ apps/TesterIntegrated/swift/Pods
+ key: ${{ runner.os }}-tester-ios-pods-${{ hashFiles('apps/TesterIntegrated/swift/Podfile.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-tester-ios-pods-
+
+ - name: Install pods
+ run: |
+ cd apps/TesterIntegrated/swift
+ pod install
+
+ - name: Build integrated iOS tester app
+ run: |
+ yarn run build:tester-integrated:ios
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index 2f1b0a1f..4d966256 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -21,14 +21,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled
- name: Setup
uses: ./.github/actions/setup
- name: Setup Pages
- uses: actions/configure-pages@v5
+ uses: actions/configure-pages@d5606572c479bee637007364c6b4800ac4fc8573 # v5
- name: Install dependencies
working-directory: docs
@@ -40,7 +40,7 @@ jobs:
yarn run build
- name: Upload artifact
- uses: actions/upload-pages-artifact@v3
+ uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
with:
path: docs/doc_build
@@ -55,4 +55,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
- uses: actions/deploy-pages@v4
+ uses: actions/deploy-pages@854d7aa1b99e4509c4d1b53d69b7ba4eaf39215a # v4.0.5
diff --git a/.github/workflows/gradle-plugin-lint.yml b/.github/workflows/gradle-plugin-lint.yml
index d7cf46d9..c0159002 100644
--- a/.github/workflows/gradle-plugin-lint.yml
+++ b/.github/workflows/gradle-plugin-lint.yml
@@ -2,7 +2,7 @@ name: ktlint and detekt
on:
push:
- branches: [ "main" ]
+ branches: ['main']
pull_request:
branches: [main]
@@ -13,19 +13,19 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- name: Validate Gradle Wrapper
- uses: gradle/wrapper-validation-action@v2
+ uses: gradle/actions/wrapper-validation@6f229686ee4375cc4a86b2514c89bac4930e82c4 # v5
- name: Set up JDK
- uses: actions/setup-java@v4
+ uses: actions/setup-java@5d7b2146334bacf88728daaa70414a99f5164e0f # v5
with:
distribution: temurin
java-version: 17
- - name: Cache Gradle
- uses: actions/cache@v4
+ - name: Restore Gradle cache
+ uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
with:
path: |
~/.gradle/caches
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..f8f17037
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,26 @@
+# Contributing to React Native Brownfield
+
+## Project setup
+
+Run `yarn` in the root of the repository to install all dependencies.
+
+Depending on your needs, you may need to install CocoaPods in a subset of the below directories:
+- example React Native iOS app: `cd apps/RNApp/ios && pod install`
+- integrated iOS tester app: `cd apps/TesterIntegrated/swift && pod install`
+
+## Scripts
+
+- `lint` - runs linting on all JS/TS source files in the monorepo *[Turbo]*
+- `gradle-plugin:lint` - runs linting on the Brownfield Gradle plugin source code
+- `typecheck` - runs TypeScript type checking on all TS source files in the monorepo *[Turbo]*
+- `build` - runs all `build*` tasks in the Turbo repo - see below for more details *[Turbo]*
+- `release` - releases a new version of React Native Brownfield package using `release-it`
+- `brownfield:plugin:publish:local` - publishes the Brownfield Gradle plugin to your local Maven repository for testing purposes
+- `build:brownfield` - builds the React Native Brownfield package (`packages/react-native-brownfield`) *[Turbo]*
+- `build:docs` - builds the documentation site (`docs/`) *[Turbo]*
+- `build:tester-integrated:android` - builds the Android integrated tester app (`apps/TesterIntegrated/android`) *[Turbo]*
+- `build:tester-integrated:ios` - builds the iOS integrated tester app (`apps/TesterIntegrated/swift`) *[Turbo]*
+- `build:example:android-rn` - builds the example React Native app for Android (`apps/RNApp/android`) *[Turbo]*
+- `build:example:ios-rn` - builds the example React Native app for iOS (`apps/RNApp/ios`) *[Turbo]*
+- `build:example:android-consumer` - builds the example native Android consumer app (`apps/AndroidApp`) *[Turbo]*
+- `build:example:ios-consumer` - builds the example native Apple consumer app (`apps/AppleApp`) *[Turbo]*
\ No newline at end of file
diff --git a/README.md b/README.md
index 72daced9..fc4ce36e 100644
--- a/README.md
+++ b/README.md
@@ -62,6 +62,7 @@ This project follows the [all-contributors](https://github.com/kentcdodds/all-co
[build-badge]: https://img.shields.io/circleci/build/github/callstack/react-native-brownfield/master.svg?style=flat-square
[build]: https://circleci.com/gh/callstack/react-native-brownfield
+[ci]: https://github.com/callstack/react-native-brownfield/actions/workflows/ci.yml/badge.svg
[version-badge]: https://img.shields.io/npm/v/@callstack/react-native-brownfield.svg?style=flat-square
[package]: https://www.npmjs.com/package/@callstack/react-native-brownfield
[license-badge]: https://img.shields.io/npm/l/@callstack/react-native-brownfield.svg?style=flat-square
diff --git a/apps/AndroidApp/.gitignore b/apps/AndroidApp/.gitignore
new file mode 100644
index 00000000..aa724b77
--- /dev/null
+++ b/apps/AndroidApp/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/apps/AndroidApp/app/.gitignore b/apps/AndroidApp/app/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/apps/AndroidApp/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/apps/AndroidApp/app/build.gradle.kts b/apps/AndroidApp/app/build.gradle.kts
new file mode 100644
index 00000000..081884be
--- /dev/null
+++ b/apps/AndroidApp/app/build.gradle.kts
@@ -0,0 +1,71 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+}
+
+configurations.all {
+ resolutionStrategy {
+ // force Gradle to always check for new versions of changing modules
+ cacheChangingModulesFor(0, "seconds")
+ cacheDynamicVersionsFor(0, "seconds")
+ }
+}
+
+android {
+ namespace = "com.callstack.brownfield.android.example"
+ compileSdk {
+ version = release(36)
+ }
+
+ defaultConfig {
+ applicationId = "com.callstack.brownfield.android.example"
+ minSdk = 24
+ targetSdk = 36
+ versionCode = 1
+ versionName = "1.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+ buildFeatures {
+ compose = true
+ }
+}
+
+dependencies {
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.lifecycle.runtime.ktx)
+ implementation(libs.androidx.activity.compose)
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.compose.ui)
+ implementation(libs.androidx.compose.ui.graphics)
+ implementation(libs.androidx.compose.ui.tooling.preview)
+ implementation(libs.androidx.compose.material3)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.brownfieldlib)
+ implementation(libs.androidx.fragment.compose)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(platform(libs.androidx.compose.bom))
+ androidTestImplementation(libs.androidx.compose.ui.test.junit4)
+ debugImplementation(libs.androidx.compose.ui.tooling)
+ debugImplementation(libs.androidx.compose.ui.test.manifest)
+}
diff --git a/apps/AndroidApp/app/proguard-rules.pro b/apps/AndroidApp/app/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/apps/AndroidApp/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/androidTest/java/com/callstack/brownfield/android/example/ExampleInstrumentedTest.kt b/apps/AndroidApp/app/src/androidTest/java/com/callstack/brownfield/android/example/ExampleInstrumentedTest.kt
new file mode 100644
index 00000000..4f241dbe
--- /dev/null
+++ b/apps/AndroidApp/app/src/androidTest/java/com/callstack/brownfield/android/example/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.callstack.brownfield.android.example
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.callstack.brownfield.android.example", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/AndroidManifest.xml b/apps/AndroidApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..0e7bc5b2
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/MainActivity.kt b/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/MainActivity.kt
new file mode 100644
index 00000000..14908ac0
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/MainActivity.kt
@@ -0,0 +1,150 @@
+package com.callstack.brownfield.android.example
+
+import android.os.Bundle
+import android.widget.Toast
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Button
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.fragment.compose.AndroidFragment
+import com.callstack.brownfield.android.example.ui.theme.AndroidBrownfieldAppTheme
+import com.callstack.reactnativebrownfield.ReactNativeFragment
+import com.callstack.reactnativebrownfield.constants.ReactNativeFragmentArgNames
+import com.rnapp.brownfieldlib.ReactNativeHostManager
+
+class MainActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(null)
+ enableEdgeToEdge()
+
+ if (savedInstanceState == null) {
+ ReactNativeHostManager.initialize(application) {
+ Toast.makeText(
+ this,
+ "React Native has been loaded",
+ Toast.LENGTH_LONG
+ ).show()
+ }
+ }
+
+ setContent {
+ AndroidBrownfieldAppTheme {
+ Scaffold(
+ modifier = Modifier.fillMaxSize()
+ ) { innerPadding ->
+ MainScreen(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(innerPadding)
+ .padding(16.dp) // outer margin
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun MainScreen(modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally // center top bar content
+ ) {
+ GreetingCard(
+ name = "Android",
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ ReactNativeView(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(16.dp))
+ .background(MaterialTheme.colorScheme.surface)
+ )
+ }
+}
+
+@Composable
+fun GreetingCard(
+ name: String,
+ modifier: Modifier = Modifier
+) {
+ var counter by rememberSaveable { mutableStateOf(0) }
+
+ Card(
+ modifier = modifier,
+ shape = RoundedCornerShape(16.dp),
+ elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
+ ) {
+ Column(
+ modifier = Modifier
+ .padding(16.dp)
+ .fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ Text(
+ text = "Hello native $name 👋",
+ style = MaterialTheme.typography.titleMedium,
+ textAlign = TextAlign.Center
+ )
+
+ Text(
+ text = "You clicked the button $counter time${if (counter == 1) "" else "s"}",
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.bodyMedium
+ )
+
+ Button(onClick = { counter++ }) {
+ Text("Increment counter")
+ }
+ }
+ }
+}
+
+@Composable
+fun ReactNativeView(
+ modifier: Modifier = Modifier
+) {
+ AndroidFragment(
+ modifier = modifier,
+ arguments = Bundle().apply {
+ putString(
+ ReactNativeFragmentArgNames.ARG_MODULE_NAME,
+ "RNApp"
+ )
+ }
+ )
+}
+
+@Preview(showBackground = true)
+@Composable
+fun GreetingPreview() {
+ AndroidBrownfieldAppTheme {
+ MainScreen()
+ }
+}
diff --git a/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/ui/theme/Color.kt b/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/ui/theme/Color.kt
new file mode 100644
index 00000000..63019a00
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/ui/theme/Color.kt
@@ -0,0 +1,11 @@
+package com.callstack.brownfield.android.example.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/ui/theme/Theme.kt b/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/ui/theme/Theme.kt
new file mode 100644
index 00000000..9b012510
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/ui/theme/Theme.kt
@@ -0,0 +1,57 @@
+package com.callstack.brownfield.android.example.ui.theme
+
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+
+private val DarkColorScheme = darkColorScheme(
+ primary = Purple80,
+ secondary = PurpleGrey80,
+ tertiary = Pink80
+)
+
+private val LightColorScheme = lightColorScheme(
+ primary = Purple40,
+ secondary = PurpleGrey40,
+ tertiary = Pink40
+
+ /* Other default colors to override
+ background = Color(0xFFFFFBFE),
+ surface = Color(0xFFFFFBFE),
+ onPrimary = Color.White,
+ onSecondary = Color.White,
+ onTertiary = Color.White,
+ onBackground = Color(0xFF1C1B1F),
+ onSurface = Color(0xFF1C1B1F),
+ */
+)
+
+@Composable
+fun AndroidBrownfieldAppTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ content: @Composable () -> Unit
+) {
+ val colorScheme = when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/ui/theme/Type.kt b/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/ui/theme/Type.kt
new file mode 100644
index 00000000..47de522b
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/ui/theme/Type.kt
@@ -0,0 +1,34 @@
+package com.callstack.brownfield.android.example.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ bodyLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
+ )
+ /* Other default text styles to override
+ titleLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ labelSmall = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 11.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.5.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/res/drawable/ic_launcher_background.xml b/apps/AndroidApp/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..07d5da9c
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/AndroidApp/app/src/main/res/drawable/ic_launcher_foreground.xml b/apps/AndroidApp/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 00000000..2b068d11
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/apps/AndroidApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..6f3b755b
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/apps/AndroidApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..6f3b755b
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/apps/AndroidApp/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 00000000..c209e78e
Binary files /dev/null and b/apps/AndroidApp/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/apps/AndroidApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..b2dfe3d1
Binary files /dev/null and b/apps/AndroidApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/apps/AndroidApp/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 00000000..4f0f1d64
Binary files /dev/null and b/apps/AndroidApp/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/apps/AndroidApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..62b611da
Binary files /dev/null and b/apps/AndroidApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/apps/AndroidApp/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 00000000..948a3070
Binary files /dev/null and b/apps/AndroidApp/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/apps/AndroidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..1b9a6956
Binary files /dev/null and b/apps/AndroidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/apps/AndroidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 00000000..28d4b77f
Binary files /dev/null and b/apps/AndroidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/apps/AndroidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..9287f508
Binary files /dev/null and b/apps/AndroidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/apps/AndroidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 00000000..aa7d6427
Binary files /dev/null and b/apps/AndroidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/apps/AndroidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/apps/AndroidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..9126ae37
Binary files /dev/null and b/apps/AndroidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/apps/AndroidApp/app/src/main/res/values/colors.xml b/apps/AndroidApp/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..f8c6127d
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/res/values/strings.xml b/apps/AndroidApp/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..ef9f33be
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Android Brownfield App
+
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/res/values/themes.xml b/apps/AndroidApp/app/src/main/res/values/themes.xml
new file mode 100644
index 00000000..ac29b85b
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/res/values/themes.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/res/xml/backup_rules.xml b/apps/AndroidApp/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 00000000..4df92558
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/main/res/xml/data_extraction_rules.xml b/apps/AndroidApp/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 00000000..9ee9997b
--- /dev/null
+++ b/apps/AndroidApp/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/AndroidApp/app/src/test/java/com/callstack/brownfield/android/example/ExampleUnitTest.kt b/apps/AndroidApp/app/src/test/java/com/callstack/brownfield/android/example/ExampleUnitTest.kt
new file mode 100644
index 00000000..64bb8e23
--- /dev/null
+++ b/apps/AndroidApp/app/src/test/java/com/callstack/brownfield/android/example/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.callstack.brownfield.android.example
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/apps/AndroidApp/build.gradle.kts b/apps/AndroidApp/build.gradle.kts
new file mode 100644
index 00000000..952b9306
--- /dev/null
+++ b/apps/AndroidApp/build.gradle.kts
@@ -0,0 +1,6 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.android) apply false
+ alias(libs.plugins.kotlin.compose) apply false
+}
\ No newline at end of file
diff --git a/apps/AndroidApp/gradle.properties b/apps/AndroidApp/gradle.properties
new file mode 100644
index 00000000..20e2a015
--- /dev/null
+++ b/apps/AndroidApp/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/apps/AndroidApp/gradle/libs.versions.toml b/apps/AndroidApp/gradle/libs.versions.toml
new file mode 100644
index 00000000..c382cc7d
--- /dev/null
+++ b/apps/AndroidApp/gradle/libs.versions.toml
@@ -0,0 +1,38 @@
+[versions]
+agp = "8.13.2"
+brownfieldlib = "0.0.1-local"
+kotlin = "2.0.21"
+coreKtx = "1.17.0"
+junit = "4.13.2"
+junitVersion = "1.3.0"
+espressoCore = "3.7.0"
+lifecycleRuntimeKtx = "2.6.1"
+activityCompose = "1.8.0"
+composeBom = "2024.09.00"
+appcompat = "1.7.1"
+fragmentCompose = "1.8.9"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+brownfieldlib = { module = "com.rnapp:brownfieldlib", version.ref = "brownfieldlib" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
+androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+androidx-fragment-compose = { group = "androidx.fragment", name = "fragment-compose", version.ref = "fragmentCompose" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+
diff --git a/apps/AndroidApp/gradle/wrapper/gradle-wrapper.jar b/apps/AndroidApp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..8bdaf60c
Binary files /dev/null and b/apps/AndroidApp/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/apps/AndroidApp/gradle/wrapper/gradle-wrapper.properties b/apps/AndroidApp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..8d8645ec
--- /dev/null
+++ b/apps/AndroidApp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,8 @@
+#Thu Dec 11 23:54:21 CET 2025
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/apps/AndroidApp/gradlew b/apps/AndroidApp/gradlew
new file mode 100755
index 00000000..ef07e016
--- /dev/null
+++ b/apps/AndroidApp/gradlew
@@ -0,0 +1,251 @@
+#!/bin/sh
+
+#
+# Copyright © 2015 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH="\\\"\\\""
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/apps/AndroidApp/gradlew.bat b/apps/AndroidApp/gradlew.bat
new file mode 100644
index 00000000..5eed7ee8
--- /dev/null
+++ b/apps/AndroidApp/gradlew.bat
@@ -0,0 +1,94 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/apps/AndroidApp/package.json b/apps/AndroidApp/package.json
new file mode 100644
index 00000000..a390c2fd
--- /dev/null
+++ b/apps/AndroidApp/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@callstack/brownfield-example-android-app",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "build:example:android-consumer": "./gradlew assembleDebug"
+ }
+}
diff --git a/apps/AndroidApp/settings.gradle.kts b/apps/AndroidApp/settings.gradle.kts
new file mode 100644
index 00000000..7c3f8a54
--- /dev/null
+++ b/apps/AndroidApp/settings.gradle.kts
@@ -0,0 +1,24 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ mavenLocal()
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "Android Brownfield App"
+include(":app")
diff --git a/apps/AndroidApp/turbo.json b/apps/AndroidApp/turbo.json
new file mode 100644
index 00000000..17d8067a
--- /dev/null
+++ b/apps/AndroidApp/turbo.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "extends": ["//"],
+ "tasks": {
+ "build:example:android-consumer": {
+ "env": ["JAVA_HOME", "ANDROID_NDK", "ANDROID_SDK", "ANDROID_HOME"],
+ "inputs": [
+ "apps/AndroidApp/app",
+ "apps/AndroidApp/build.gradle",
+ "apps/AndroidApp/settings.gradle",
+ "~/.m2/repository/com/rnapp/brownfieldlib/**/*.aar"
+ ],
+ "outputs": ["app/build"]
+ }
+ }
+}
diff --git a/apps/AppleApp/.gitignore b/apps/AppleApp/.gitignore
new file mode 100644
index 00000000..378eac25
--- /dev/null
+++ b/apps/AppleApp/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/apps/AppleApp/Brownfield Apple App.xcodeproj/project.pbxproj b/apps/AppleApp/Brownfield Apple App.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..eafcdb91
--- /dev/null
+++ b/apps/AppleApp/Brownfield Apple App.xcodeproj/project.pbxproj
@@ -0,0 +1,391 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 77;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 798DE8D72F0EC98E00CFC6F3 /* BrownfieldLib.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79A9BC7E2EF5781F009EC2E3 /* BrownfieldLib.xcframework */; };
+ 798DE8D82F0EC98E00CFC6F3 /* BrownfieldLib.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 79A9BC7E2EF5781F009EC2E3 /* BrownfieldLib.xcframework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 798DE8DA2F0EC98F00CFC6F3 /* hermesvm.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79A9BC7F2EF5781F009EC2E3 /* hermesvm.xcframework */; };
+ 798DE8DB2F0EC98F00CFC6F3 /* hermesvm.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 79A9BC7F2EF5781F009EC2E3 /* hermesvm.xcframework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ 798DE8DC2F0EC99000CFC6F3 /* ReactBrownfield.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79A9BC802EF5781F009EC2E3 /* ReactBrownfield.xcframework */; };
+ 798DE8DD2F0EC99000CFC6F3 /* ReactBrownfield.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 79A9BC802EF5781F009EC2E3 /* ReactBrownfield.xcframework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 798DE8D92F0EC98E00CFC6F3 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 798DE8DB2F0EC98F00CFC6F3 /* hermesvm.xcframework in Embed Frameworks */,
+ 798DE8DD2F0EC99000CFC6F3 /* ReactBrownfield.xcframework in Embed Frameworks */,
+ 798DE8D82F0EC98E00CFC6F3 /* BrownfieldLib.xcframework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 793C76A72EEBF938008A2A34 /* Brownfield Apple App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Brownfield Apple App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 79A9BC7E2EF5781F009EC2E3 /* BrownfieldLib.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = BrownfieldLib.xcframework; path = ../RNApp/ios/out/Release/BrownfieldLib.xcframework; sourceTree = SOURCE_ROOT; };
+ 79A9BC7F2EF5781F009EC2E3 /* hermesvm.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = hermesvm.xcframework; path = ../RNApp/ios/out/Release/hermesvm.xcframework; sourceTree = SOURCE_ROOT; };
+ 79A9BC802EF5781F009EC2E3 /* ReactBrownfield.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ReactBrownfield.xcframework; path = ../RNApp/ios/out/Release/ReactBrownfield.xcframework; sourceTree = SOURCE_ROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFileSystemSynchronizedRootGroup section */
+ 793C76A92EEBF938008A2A34 /* Brownfield Apple App */ = {
+ isa = PBXFileSystemSynchronizedRootGroup;
+ path = "Brownfield Apple App";
+ sourceTree = "";
+ };
+/* End PBXFileSystemSynchronizedRootGroup section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 793C76A42EEBF938008A2A34 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 798DE8DA2F0EC98F00CFC6F3 /* hermesvm.xcframework in Frameworks */,
+ 798DE8DC2F0EC99000CFC6F3 /* ReactBrownfield.xcframework in Frameworks */,
+ 798DE8D72F0EC98E00CFC6F3 /* BrownfieldLib.xcframework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 793C769E2EEBF938008A2A34 = {
+ isa = PBXGroup;
+ children = (
+ 79A9BC7E2EF5781F009EC2E3 /* BrownfieldLib.xcframework */,
+ 79A9BC7F2EF5781F009EC2E3 /* hermesvm.xcframework */,
+ 79A9BC802EF5781F009EC2E3 /* ReactBrownfield.xcframework */,
+ 793C76A92EEBF938008A2A34 /* Brownfield Apple App */,
+ 793C76A82EEBF938008A2A34 /* Products */,
+ );
+ sourceTree = "";
+ };
+ 793C76A82EEBF938008A2A34 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 793C76A72EEBF938008A2A34 /* Brownfield Apple App.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 793C76A62EEBF938008A2A34 /* Brownfield Apple App */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 793C76B22EEBF939008A2A34 /* Build configuration list for PBXNativeTarget "Brownfield Apple App" */;
+ buildPhases = (
+ 793C76A32EEBF938008A2A34 /* Sources */,
+ 793C76A42EEBF938008A2A34 /* Frameworks */,
+ 793C76A52EEBF938008A2A34 /* Resources */,
+ 798DE8D92F0EC98E00CFC6F3 /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ fileSystemSynchronizedGroups = (
+ 793C76A92EEBF938008A2A34 /* Brownfield Apple App */,
+ );
+ name = "Brownfield Apple App";
+ packageProductDependencies = (
+ );
+ productName = "Brownfield Apple App";
+ productReference = 793C76A72EEBF938008A2A34 /* Brownfield Apple App.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 793C769F2EEBF938008A2A34 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 2610;
+ LastUpgradeCheck = 2620;
+ TargetAttributes = {
+ 793C76A62EEBF938008A2A34 = {
+ CreatedOnToolsVersion = 26.1.1;
+ };
+ };
+ };
+ buildConfigurationList = 793C76A22EEBF938008A2A34 /* Build configuration list for PBXProject "Brownfield Apple App" */;
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 793C769E2EEBF938008A2A34;
+ minimizedProjectReferenceProxies = 1;
+ preferredProjectObjectVersion = 77;
+ productRefGroup = 793C76A82EEBF938008A2A34 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 793C76A62EEBF938008A2A34 /* Brownfield Apple App */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 793C76A52EEBF938008A2A34 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 793C76A32EEBF938008A2A34 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 793C76B02EEBF939008A2A34 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ STRING_CATALOG_GENERATE_SYMBOLS = YES;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 793C76B12EEBF939008A2A34 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ STRING_CATALOG_GENERATE_SYMBOLS = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ };
+ name = Release;
+ };
+ 793C76B32EEBF939008A2A34 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEAD_CODE_STRIPPING = YES;
+ ENABLE_APP_SANDBOX = YES;
+ ENABLE_PREVIEWS = YES;
+ ENABLE_USER_SELECTED_FILES = readonly;
+ GENERATE_INFOPLIST_FILE = YES;
+ "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
+ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ IPHONEOS_DEPLOYMENT_TARGET = 26.1;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
+ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 15.6;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.callstack.brownfield.ios.example.Brownfield-iOS-App";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ REGISTER_APP_GROUPS = YES;
+ SDKROOT = auto;
+ STRING_CATALOG_GENERATE_SYMBOLS = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
+ SWIFT_APPROACHABLE_CONCURRENCY = YES;
+ SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2,7";
+ XROS_DEPLOYMENT_TARGET = 26.1;
+ };
+ name = Debug;
+ };
+ 793C76B42EEBF939008A2A34 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEAD_CODE_STRIPPING = YES;
+ ENABLE_APP_SANDBOX = YES;
+ ENABLE_PREVIEWS = YES;
+ ENABLE_USER_SELECTED_FILES = readonly;
+ GENERATE_INFOPLIST_FILE = YES;
+ "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
+ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ IPHONEOS_DEPLOYMENT_TARGET = 26.1;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
+ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 15.6;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.callstack.brownfield.ios.example.Brownfield-iOS-App";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ REGISTER_APP_GROUPS = YES;
+ SDKROOT = auto;
+ STRING_CATALOG_GENERATE_SYMBOLS = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
+ SWIFT_APPROACHABLE_CONCURRENCY = YES;
+ SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2,7";
+ XROS_DEPLOYMENT_TARGET = 26.1;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 793C76A22EEBF938008A2A34 /* Build configuration list for PBXProject "Brownfield Apple App" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 793C76B02EEBF939008A2A34 /* Debug */,
+ 793C76B12EEBF939008A2A34 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 793C76B22EEBF939008A2A34 /* Build configuration list for PBXNativeTarget "Brownfield Apple App" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 793C76B32EEBF939008A2A34 /* Debug */,
+ 793C76B42EEBF939008A2A34 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 793C769F2EEBF938008A2A34 /* Project object */;
+}
diff --git a/apps/AppleApp/Brownfield Apple App/Assets.xcassets/AccentColor.colorset/Contents.json b/apps/AppleApp/Brownfield Apple App/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 00000000..eb878970
--- /dev/null
+++ b/apps/AppleApp/Brownfield Apple App/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/apps/AppleApp/Brownfield Apple App/Assets.xcassets/AppIcon.appiconset/Contents.json b/apps/AppleApp/Brownfield Apple App/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..ffdfe150
--- /dev/null
+++ b/apps/AppleApp/Brownfield Apple App/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,85 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "tinted"
+ }
+ ],
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "16x16"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "16x16"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "32x32"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "32x32"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "128x128"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "128x128"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "256x256"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "256x256"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "512x512"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "512x512"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/apps/AppleApp/Brownfield Apple App/Assets.xcassets/Contents.json b/apps/AppleApp/Brownfield Apple App/Assets.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/apps/AppleApp/Brownfield Apple App/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/apps/AppleApp/Brownfield Apple App/BrownfieldAppleApp.swift b/apps/AppleApp/Brownfield Apple App/BrownfieldAppleApp.swift
new file mode 100644
index 00000000..648ff5ba
--- /dev/null
+++ b/apps/AppleApp/Brownfield Apple App/BrownfieldAppleApp.swift
@@ -0,0 +1,25 @@
+import BrownfieldLib
+import ReactBrownfield
+import SwiftUI
+
+class AppDelegate: NSObject, UIApplicationDelegate {
+ var window: UIWindow?
+}
+
+@main
+struct BrownfieldAppleApp: App {
+ @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
+
+ init() {
+ ReactNativeBrownfield.shared.bundle = ReactNativeBundle
+ ReactNativeBrownfield.shared.startReactNative {
+ print("React Native has been loaded")
+ }
+ }
+
+ var body: some Scene {
+ WindowGroup {
+ ContentView()
+ }
+ }
+}
diff --git a/apps/AppleApp/Brownfield Apple App/ContentView.swift b/apps/AppleApp/Brownfield Apple App/ContentView.swift
new file mode 100644
index 00000000..ff3d1478
--- /dev/null
+++ b/apps/AppleApp/Brownfield Apple App/ContentView.swift
@@ -0,0 +1,59 @@
+import ReactBrownfield
+import SwiftUI
+
+struct ContentView: View {
+ var body: some View {
+ NavigationView {
+ MainScreen()
+ .padding(16)
+ }
+ }
+}
+
+struct MainScreen: View {
+ var body: some View {
+ VStack(spacing: 16) {
+ GreetingCard(name: "iOS")
+
+ ReactNativeView(moduleName: "RNApp")
+ .navigationBarHidden(true)
+ .clipShape(RoundedRectangle(cornerRadius: 16))
+ .background(Color(UIColor.systemBackground))
+ }
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ .padding()
+ }
+}
+
+struct GreetingCard: View {
+ let name: String
+ @State private var counter: Int = 0
+
+ var body: some View {
+ VStack(spacing: 12) {
+ Text("Hello native \(name) 👋")
+ .font(.title3)
+ .multilineTextAlignment(.center)
+
+ Text(
+ "You clicked the button \(counter) time\(counter == 1 ? "" : "s")"
+ )
+ .multilineTextAlignment(.center)
+ .font(.body)
+
+ Button("Increment counter") {
+ counter += 1
+ }
+ .buttonStyle(.borderedProminent)
+ }
+ .padding(16)
+ .frame(maxWidth: .infinity)
+ .background(Color(UIColor.secondarySystemBackground))
+ .cornerRadius(16)
+ .shadow(radius: 4)
+ }
+}
+
+#Preview {
+ ContentView()
+}
diff --git a/apps/AppleApp/package.json b/apps/AppleApp/package.json
new file mode 100644
index 00000000..6643a10e
--- /dev/null
+++ b/apps/AppleApp/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@callstack/brownfield-example-ios-app",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "build:example:ios-consumer": "xcodebuild -project \"Brownfield Apple App.xcodeproj\" -scheme \"Brownfield Apple App\" -configuration Release -sdk iphonesimulator build CODE_SIGNING_ALLOWED=NO -derivedDataPath ./build"
+ }
+}
diff --git a/apps/AppleApp/turbo.json b/apps/AppleApp/turbo.json
new file mode 100644
index 00000000..003cc62e
--- /dev/null
+++ b/apps/AppleApp/turbo.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "extends": ["//"],
+ "tasks": {
+ "build:example:ios-consumer": {
+ "inputs": [
+ "apps/AppleApp/Brownfield Apple App",
+ "apps/AppleApp/Brownfield Apple App.xcodeproj",
+ "apps/RNApp/ios/out/Release/*.xcframework"
+ ],
+ "outputs": ["build"]
+ }
+ }
+}
diff --git a/apps/README.md b/apps/README.md
new file mode 100644
index 00000000..fdf39854
--- /dev/null
+++ b/apps/README.md
@@ -0,0 +1,8 @@
+# React Native Brownfield demos
+
+This directory contains demo projects showcasing the usage of the `react-native-brownfield` library.
+
+- `RNApp` - the React Native application that is packaged to AAR and XCFramework archives and integrated into native projects
+- `AndroidApp` - the native Android application that integrates the RNApp AAR package (a "consumer" of the RNApp library)
+- `iOSApp` - the native iOS application that integrates the RNApp XCFramework package (a "consumer" of the RNApp library)
+- `TesterIntegrated` - a native tester application that integrates the native projects with and React Native project inside a single application, consuming the brownfield module directly, without packaging to an artifact; this is mostly useful for internal development purposes and is not as meaningful for end users of React Native Brownfield
diff --git a/apps/RNApp/.bundle/config b/apps/RNApp/.bundle/config
new file mode 100644
index 00000000..848943bb
--- /dev/null
+++ b/apps/RNApp/.bundle/config
@@ -0,0 +1,2 @@
+BUNDLE_PATH: "vendor/bundle"
+BUNDLE_FORCE_RUBY_PLATFORM: 1
diff --git a/apps/RNApp/.gitignore b/apps/RNApp/.gitignore
new file mode 100644
index 00000000..60141bd4
--- /dev/null
+++ b/apps/RNApp/.gitignore
@@ -0,0 +1,79 @@
+# 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
+#
+build/
+.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
+
+# Brownfield
+android/BrownfieldLib/libsDebug/
+android/BrownfieldLib/libsRelease/
diff --git a/apps/RNApp/.watchmanconfig b/apps/RNApp/.watchmanconfig
new file mode 100644
index 00000000..0967ef42
--- /dev/null
+++ b/apps/RNApp/.watchmanconfig
@@ -0,0 +1 @@
+{}
diff --git a/apps/example/App.tsx b/apps/RNApp/App.tsx
similarity index 100%
rename from apps/example/App.tsx
rename to apps/RNApp/App.tsx
diff --git a/apps/RNApp/Gemfile b/apps/RNApp/Gemfile
new file mode 100644
index 00000000..6a4c5f17
--- /dev/null
+++ b/apps/RNApp/Gemfile
@@ -0,0 +1,16 @@
+source 'https://rubygems.org'
+
+# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
+ruby ">= 2.6.10"
+
+# 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'
+
+# Ruby 3.4.0 has removed some libraries from the standard library.
+gem 'bigdecimal'
+gem 'logger'
+gem 'benchmark'
+gem 'mutex_m'
diff --git a/apps/RNApp/Gemfile.lock b/apps/RNApp/Gemfile.lock
new file mode 100644
index 00000000..829a4611
--- /dev/null
+++ b/apps/RNApp/Gemfile.lock
@@ -0,0 +1,121 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ CFPropertyList (3.0.9)
+ activesupport (7.1.6)
+ 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.8)
+ public_suffix (>= 2.0.2, < 8.0)
+ algoliasearch (1.27.5)
+ httpclient (~> 2.8, >= 2.8.3)
+ json (>= 1.5.1)
+ atomos (0.1.3)
+ base64 (0.3.0)
+ benchmark (0.5.0)
+ bigdecimal (3.3.1)
+ 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.5)
+ drb (2.2.3)
+ escape (0.0.4)
+ ethon (0.15.0)
+ ffi (>= 1.15.0)
+ ffi (1.17.2)
+ 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.18.0)
+ logger (1.7.0)
+ minitest (5.26.1)
+ molinillo (0.8.0)
+ mutex_m (0.3.0)
+ nanaimo (0.3.0)
+ nap (1.1.0)
+ netrc (0.11.0)
+ public_suffix (4.0.7)
+ rexml (3.4.4)
+ ruby-macho (2.5.1)
+ securerandom (0.3.2)
+ typhoeus (1.5.0)
+ ethon (>= 0.9.0, < 0.16.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)
+ benchmark
+ bigdecimal
+ cocoapods (>= 1.13, != 1.15.1, != 1.15.0)
+ concurrent-ruby (< 1.3.4)
+ logger
+ mutex_m
+ xcodeproj (< 1.26.0)
+
+RUBY VERSION
+ ruby 2.7.6p219
+
+BUNDLED WITH
+ 2.4.12
diff --git a/apps/RNApp/README.md b/apps/RNApp/README.md
new file mode 100644
index 00000000..3e2c3f85
--- /dev/null
+++ b/apps/RNApp/README.md
@@ -0,0 +1,97 @@
+This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli).
+
+# Getting Started
+
+> **Note**: Make sure you have completed the [Set Up Your Environment](https://reactnative.dev/docs/set-up-your-environment) guide before proceeding.
+
+## Step 1: Start Metro
+
+First, you will need to run **Metro**, the JavaScript build tool for React Native.
+
+To start the Metro dev server, run the following command from the root of your React Native project:
+
+```sh
+# Using npm
+npm start
+
+# OR using Yarn
+yarn start
+```
+
+## Step 2: Build and run your app
+
+With Metro running, open a new terminal window/pane from the root of your React Native project, and use one of the following commands to build and run your Android or iOS app:
+
+### Android
+
+```sh
+# Using npm
+npm run android
+
+# OR using Yarn
+yarn android
+```
+
+### iOS
+
+For iOS, remember to install CocoaPods dependencies (this only needs to be run on first clone or after updating native deps).
+
+The first time you create a new project, run the Ruby bundler to install CocoaPods itself:
+
+```sh
+bundle install
+```
+
+Then, and every time you update your native dependencies, run:
+
+```sh
+bundle exec pod install
+```
+
+For more information, please visit [CocoaPods Getting Started guide](https://guides.cocoapods.org/using/getting-started.html).
+
+```sh
+# Using npm
+npm run ios
+
+# OR using Yarn
+yarn ios
+```
+
+If everything is set up correctly, you should see your new app running in the Android Emulator, iOS Simulator, or your connected device.
+
+This is one way to run your app — you can also build it directly from Android Studio or Xcode.
+
+## Step 3: Modify your app
+
+Now that you have successfully run the app, let's make changes!
+
+Open `App.tsx` in your text editor of choice and make some changes. When you save, your app will automatically update and reflect these changes — this is powered by [Fast Refresh](https://reactnative.dev/docs/fast-refresh).
+
+When you want to forcefully reload, for example to reset the state of your app, you can perform a full reload:
+
+- **Android**: Press the R key twice or select **"Reload"** from the **Dev Menu**, accessed via Ctrl + M (Windows/Linux) or Cmd ⌘ + M (macOS).
+- **iOS**: Press R in iOS Simulator.
+
+## Congratulations! :tada:
+
+You've successfully run and modified your React Native App. :partying_face:
+
+### Now what?
+
+- If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
+- If you're curious to learn more about React Native, check out the [docs](https://reactnative.dev/docs/getting-started).
+
+# Troubleshooting
+
+If you're having issues getting the above steps to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.
+
+# Learn More
+
+To learn more about React Native, take a look at the following resources:
+
+- [React Native Website](https://reactnative.dev) - learn more about React Native.
+- [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
+- [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
+- [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
+- [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.
diff --git a/apps/RNApp/__tests__/App.test.tsx b/apps/RNApp/__tests__/App.test.tsx
new file mode 100644
index 00000000..e532f701
--- /dev/null
+++ b/apps/RNApp/__tests__/App.test.tsx
@@ -0,0 +1,13 @@
+/**
+ * @format
+ */
+
+import React from 'react';
+import ReactTestRenderer from 'react-test-renderer';
+import App from '../App';
+
+test('renders correctly', async () => {
+ await ReactTestRenderer.act(() => {
+ ReactTestRenderer.create();
+ });
+});
diff --git a/apps/RNApp/android/BrownfieldLib/.gitignore b/apps/RNApp/android/BrownfieldLib/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/apps/RNApp/android/BrownfieldLib/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/apps/RNApp/android/BrownfieldLib/build.gradle.kts b/apps/RNApp/android/BrownfieldLib/build.gradle.kts
new file mode 100644
index 00000000..70cf1027
--- /dev/null
+++ b/apps/RNApp/android/BrownfieldLib/build.gradle.kts
@@ -0,0 +1,128 @@
+import groovy.json.JsonOutput
+import groovy.json.JsonSlurper
+
+plugins {
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+ id("com.callstack.react.brownfield")
+ `maven-publish`
+ id("com.facebook.react")
+}
+
+publishing {
+ publications {
+ create("mavenAar") {
+ groupId = "com.rnapp"
+ artifactId = "brownfieldlib"
+ version = "0.0.1-local"
+ afterEvaluate {
+ from(components.getByName("default"))
+ }
+
+ pom {
+ withXml {
+ /**
+ * As a result of `from(components.getByName("default")` all of the project
+ * dependencies are added to `pom.xml` file. We do not need the react-native
+ * third party dependencies to be a part of it as we embed those dependencies.
+ */
+ val dependenciesNode =
+ (asNode().get("dependencies") as groovy.util.NodeList).first() as groovy.util.Node
+ dependenciesNode.children()
+ .filterIsInstance()
+ .filter { (it.get("groupId") as groovy.util.NodeList).text() == rootProject.name }
+ .forEach { dependenciesNode.remove(it) }
+ }
+ }
+ }
+ }
+
+ repositories {
+ mavenLocal() // Publishes to the local Maven repository (~/.m2/repository by default)
+ }
+}
+
+val moduleBuildDir: Directory = layout.buildDirectory.get()
+
+/**
+ * As a result of `from(components.getByName("default")` all of the project
+ * dependencies are added to `module.json` file. We do not need the react-native
+ * third party dependencies to be a part of it as we embed those dependencies.
+ */
+tasks.register("removeDependenciesFromModuleFile") {
+ doLast {
+ file("$moduleBuildDir/publications/mavenAar/module.json").run {
+ val json = inputStream().use { JsonSlurper().parse(it) as Map }
+ (json["variants"] as? List>)?.forEach { variant ->
+ (variant["dependencies"] as? MutableList