@matiks/rn-app-state 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/FLOW.md +239 -0
  2. package/NitroAppState.podspec +31 -0
  3. package/README.md +72 -0
  4. package/android/CMakeLists.txt +29 -0
  5. package/android/build.gradle +140 -0
  6. package/android/fix-prefab.gradle +51 -0
  7. package/android/gradle.properties +5 -0
  8. package/android/src/main/AndroidManifest.xml +2 -0
  9. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  10. package/android/src/main/java/com/margelo/nitro/appstate/HybridAppState.kt +188 -0
  11. package/android/src/main/java/com/margelo/nitro/appstate/NitroAppStatePackage.kt +18 -0
  12. package/ios/HybridAppState.swift +224 -0
  13. package/nitro.json +24 -0
  14. package/nitrogen/generated/.gitattributes +1 -0
  15. package/nitrogen/generated/android/NitroAppState+autolinking.cmake +81 -0
  16. package/nitrogen/generated/android/NitroAppState+autolinking.gradle +27 -0
  17. package/nitrogen/generated/android/NitroAppStateOnLoad.cpp +48 -0
  18. package/nitrogen/generated/android/NitroAppStateOnLoad.hpp +25 -0
  19. package/nitrogen/generated/android/c++/JAppStateChangeEvent.hpp +57 -0
  20. package/nitrogen/generated/android/c++/JAppStateListener.hpp +67 -0
  21. package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  22. package/nitrogen/generated/android/c++/JFunc_void_AppStateChangeEvent.hpp +78 -0
  23. package/nitrogen/generated/android/c++/JHybridAppStateModuleSpec.cpp +84 -0
  24. package/nitrogen/generated/android/c++/JHybridAppStateModuleSpec.hpp +68 -0
  25. package/nitrogen/generated/android/kotlin/com/margelo/nitro/appstate/AppStateChangeEvent.kt +38 -0
  26. package/nitrogen/generated/android/kotlin/com/margelo/nitro/appstate/AppStateListener.kt +42 -0
  27. package/nitrogen/generated/android/kotlin/com/margelo/nitro/appstate/Func_void.kt +80 -0
  28. package/nitrogen/generated/android/kotlin/com/margelo/nitro/appstate/Func_void_AppStateChangeEvent.kt +80 -0
  29. package/nitrogen/generated/android/kotlin/com/margelo/nitro/appstate/HybridAppStateModuleSpec.kt +72 -0
  30. package/nitrogen/generated/android/kotlin/com/margelo/nitro/appstate/NitroAppStateOnLoad.kt +35 -0
  31. package/nitrogen/generated/ios/NitroAppState+autolinking.rb +60 -0
  32. package/nitrogen/generated/ios/NitroAppState-Swift-Cxx-Bridge.cpp +49 -0
  33. package/nitrogen/generated/ios/NitroAppState-Swift-Cxx-Bridge.hpp +112 -0
  34. package/nitrogen/generated/ios/NitroAppState-Swift-Cxx-Umbrella.hpp +51 -0
  35. package/nitrogen/generated/ios/NitroAppStateAutolinking.mm +33 -0
  36. package/nitrogen/generated/ios/NitroAppStateAutolinking.swift +26 -0
  37. package/nitrogen/generated/ios/c++/HybridAppStateModuleSpecSwift.cpp +11 -0
  38. package/nitrogen/generated/ios/c++/HybridAppStateModuleSpecSwift.hpp +106 -0
  39. package/nitrogen/generated/ios/swift/AppStateChangeEvent.swift +29 -0
  40. package/nitrogen/generated/ios/swift/AppStateListener.swift +37 -0
  41. package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
  42. package/nitrogen/generated/ios/swift/Func_void_AppStateChangeEvent.swift +46 -0
  43. package/nitrogen/generated/ios/swift/HybridAppStateModuleSpec.swift +57 -0
  44. package/nitrogen/generated/ios/swift/HybridAppStateModuleSpec_cxx.swift +172 -0
  45. package/nitrogen/generated/shared/c++/AppStateChangeEvent.hpp +83 -0
  46. package/nitrogen/generated/shared/c++/AppStateListener.hpp +83 -0
  47. package/nitrogen/generated/shared/c++/HybridAppStateModuleSpec.cpp +24 -0
  48. package/nitrogen/generated/shared/c++/HybridAppStateModuleSpec.hpp +70 -0
  49. package/package.json +54 -0
  50. package/src/index.ts +2 -0
  51. package/src/specs/AppState.nitro.ts +57 -0
  52. package/src/useNitroAppState/index.ts +2 -0
  53. package/src/useNitroAppState/types.ts +21 -0
  54. package/src/useNitroAppState/useNitroAppState.native.ts +142 -0
  55. package/src/useNitroAppState/useNitroAppState.web.ts +56 -0
package/FLOW.md ADDED
@@ -0,0 +1,239 @@
1
+ # `@matiks/rn-app-state` — How it works
2
+
3
+ This document describes how the package exposes **app lifecycle state** (`active` | `background` | `killed`) to React Native via **Nitro Modules** (JSI), and how each platform derives that state.
4
+
5
+ ---
6
+
7
+ ## 1. Purpose in one paragraph
8
+
9
+ The library ships a React hook, `useNitroAppState`, that returns:
10
+
11
+ | Field | Meaning |
12
+ |--------|---------|
13
+ | `appState` | Reactive lifecycle: `'active'`, `'background'`, or briefly `'killed'` on cold start when inferring a force-killed previous session. |
14
+ | `wasKilledLastSession` | Boolean from native storage: whether the **previous** run ended without a “clean exit” (persisted flag). |
15
+ | `getStateOnNotification` | Synchronous snapshot of foreground vs background, intended for push/notification handlers. |
16
+
17
+ On **web**, there is no Nitro bridge; the hook falls back to React Native’s `AppState` API and never reports `killed`.
18
+
19
+ ---
20
+
21
+ ## 2. High-level architecture
22
+
23
+ Nitro connects JavaScript to **one native HybridObject** registered under the name `AppStateModule`. TypeScript describes the contract; **Nitrogen** generates C++/Swift/Kotlin glue; your implementations live in `ios/HybridAppState.swift` and `android/.../HybridAppState.kt`.
24
+
25
+ ```mermaid
26
+ flowchart TB
27
+ subgraph JS["JavaScript / TypeScript"]
28
+ Hook["useNitroAppState"]
29
+ Spec["AppState.nitro.ts (interface)"]
30
+ Hook -->|"createHybridObject('AppStateModule')"| NitroJS["react-native-nitro-modules"]
31
+ Hook -.-> Spec
32
+ end
33
+
34
+ subgraph Nitro["Nitro runtime"]
35
+ JSI["JSI bridge"]
36
+ Cxx["Generated C++ HybridAppStateModuleSpec"]
37
+ NitroJS --> JSI --> Cxx
38
+ end
39
+
40
+ subgraph iOS["iOS"]
41
+ Swift["HybridAppState.swift"]
42
+ UIKit["UIKit: UIApplication + NotificationCenter"]
43
+ UD["UserDefaults (killed/clean flag)"]
44
+ Cxx <--> Swift
45
+ Swift --> UIKit
46
+ Swift --> UD
47
+ end
48
+
49
+ subgraph Android["Android"]
50
+ Kt["HybridAppState.kt"]
51
+ PLO["ProcessLifecycleOwner"]
52
+ Prefs["SharedPreferences"]
53
+ Cxx <--> Kt
54
+ Kt --> PLO
55
+ Kt --> Prefs
56
+ end
57
+
58
+ Cxx --> Swift
59
+ Cxx --> Kt
60
+ ```
61
+
62
+ **Registration:** `nitro.json` maps the HybridObject name to native classes:
63
+
64
+ ```json
65
+ "autolinking": {
66
+ "AppStateModule": {
67
+ "swift": "HybridAppState",
68
+ "kotlin": "HybridAppState"
69
+ }
70
+ }
71
+ ```
72
+
73
+ Generated code (e.g. `NitroAppStateAutolinking.swift`) constructs `HybridAppState()` when JS requests `AppStateModule`.
74
+
75
+ ---
76
+
77
+ ## 3. Repository layout (mental model)
78
+
79
+ | Path | Role |
80
+ |------|------|
81
+ | `src/specs/AppState.nitro.ts` | Type-only Nitro spec: methods and event shapes consumed by TS and codegen. |
82
+ | `src/useNitroAppState/useNitroAppState.native.ts` | Hook for iOS/Android: Nitro module + listener + `killed` UX. |
83
+ | `src/useNitroAppState/useNitroAppState.web.ts` | Web: `AppState` from `react-native`; no `killed`. |
84
+ | `src/useNitroAppState/types.ts` | `AppStateType`, `NitroAppStateResult`. |
85
+ | `ios/HybridAppState.swift` | iOS implementation (UIKit + UserDefaults). |
86
+ | `android/.../HybridAppState.kt` | Android implementation (lifecycle + SharedPreferences). |
87
+ | `nitrogen/generated/**` | Generated bridges — **do not edit**; regenerate with `npm run specs` / `npx nitrogen`. |
88
+ | `nitro.json` | Nitro project config + autolinking map. |
89
+
90
+ Metro resolves **`.native.ts`** vs **`.web.ts`** when the bundler target is native vs web, so a single export path (`useNitroAppState`) picks the right implementation.
91
+
92
+ ---
93
+
94
+ ## 4. TypeScript contract (`AppStateModule`)
95
+
96
+ The native module exposes:
97
+
98
+ - **`getCurrentState(): string`** — Current cached lifecycle string (`'active'` | `'background'`). Not `'killed'`; `killed` is JS-only for UX.
99
+ - **`wasKilledLastSession: boolean`** — Read once per process from persisted flags.
100
+ - **`addListener(cb)`** — Subscribe to `{ state: string }` events when native state changes.
101
+ - **`getStateOnNotification(): string`** — Snapshot at call time (see platform notes below).
102
+
103
+ ---
104
+
105
+ ## 5. Hook flow (native)
106
+
107
+ ### 5.1 Obtaining the module
108
+
109
+ ```mermaid
110
+ sequenceDiagram
111
+ participant React as React component
112
+ participant Hook as useNitroAppState
113
+ participant Nitro as NitroModules
114
+ participant Native as HybridAppState
115
+
116
+ React->>Hook: render
117
+ Hook->>Nitro: createHybridObject('AppStateModule')
118
+ Nitro->>Native: construct / wrap singleton
119
+ Hook->>Native: wasKilledLastSession, getCurrentState()
120
+ Hook->>Native: addListener(...)
121
+ ```
122
+
123
+ The module is cached in a module-level `_module` and in a `useRef` so the same instance is reused.
124
+
125
+ ### 5.2 Initial `appState` and the `'killed'` special case
126
+
127
+ Native **never** returns the string `'killed'` from `getCurrentState()`. The hook synthesizes `'killed'` **only** when:
128
+
129
+ 1. `wasKilledLastSession === true`, and
130
+ 2. It uses that as the **initial** `useState` value so the UI can show “resuming after kill” once.
131
+
132
+ Immediately after mount, if the initial state was `'killed'`, a `setTimeout(0)` runs `getCurrentState()` again and sets React state to the real native value (`'active'` or `'background'`). So `'killed'` is a **transient** first paint, not a streaming lifecycle state from the OS.
133
+
134
+ ```mermaid
135
+ stateDiagram-v2
136
+ direction LR
137
+ [*] --> KilledUI: wasKilledLastSession && first paint
138
+ KilledUI --> ActiveOrBg: setTimeout → getCurrentState()
139
+ ActiveOrBg --> ActiveOrBg: addListener events
140
+ ```
141
+
142
+ ### 5.3 Ongoing updates
143
+
144
+ `addListener` receives `AppStateChangeEvent` with `state: 'active' | 'background'`. The effect unsubscribes on unmount via `listener.remove()`.
145
+
146
+ ### 5.4 `getStateOnNotification`
147
+
148
+ Exposed as a **stable** `useCallback` (depends only on `mod`). It calls native `getStateOnNotification()` so push handlers get a fresh snapshot rather than a stale React closure.
149
+
150
+ ---
151
+
152
+ ## 6. iOS (`HybridAppState.swift`)
153
+
154
+ ### 6.1 Live state: UIKit
155
+
156
+ - **Initial:** `readApplicationState()` reads `UIApplication.shared.applicationState` on the **main thread** and maps `.active` → `"active"`, `.background` / `.inactive` → `"background"`.
157
+ - **Updates:** `NotificationCenter` observers call `updateState` on the main thread:
158
+ - `didBecomeActive` → `"active"`
159
+ - `willResignActive` → `"background"` (covers control center, incoming call overlay, etc.)
160
+ - `didEnterBackground` → `"background"` and `markCleanExit()`
161
+ - `willTerminate` → `markCleanExit()`
162
+
163
+ `currentState` is only mutated on the main thread. `getCurrentState()` uses `Thread.isMainThread` / `DispatchQueue.main.sync` so callers from arbitrary Nitro threads still read consistent main-thread state.
164
+
165
+ ### 6.2 `wasKilledLastSession`: UserDefaults
166
+
167
+ - Key stores whether the last run **exited cleanly** (background or terminate handler set the flag).
168
+ - On launch: if the app had run before and the flag says **not** clean, `wasKilledLastSession` is `true`.
169
+ - Then the flag is reset to “not clean” until the next clean exit.
170
+
171
+ ### 6.3 `getStateOnNotification` on iOS
172
+
173
+ Calls `readApplicationState()` on the main thread — i.e. **live `UIApplication` state** at invocation time, not only the cached `currentState`.
174
+
175
+ ---
176
+
177
+ ## 7. Android (`HybridAppState.kt`)
178
+
179
+ ### 7.1 Live state: `ProcessLifecycleOwner`
180
+
181
+ - Observer on `onStart` → `"active"`, `onStop` → `"background"` (and `markCleanExit()` on stop).
182
+ - `currentState` is `@Volatile` for safe reads from any thread; `getCurrentState()` returns it directly.
183
+
184
+ ### 7.2 `wasKilledLastSession`: SharedPreferences
185
+
186
+ Same **clean-exit** idea as iOS: `hasLaunchedBefore` + `didExitCleanly` in app prefs.
187
+
188
+ ### 7.3 `getStateOnNotification` on Android
189
+
190
+ Returns the module’s **`currentState`** (updated by process lifecycle), not a separate system API.
191
+
192
+ ---
193
+
194
+ ## 8. Web (`useNitroAppState.web.ts`)
195
+
196
+ - Uses `AppState` from `react-native` (e.g. visibility-based behavior under `react-native-web`).
197
+ - Maps everything except `'active'` to `'background'`.
198
+ - `wasKilledLastSession` is always `false`; `'killed'` is never used.
199
+
200
+ ---
201
+
202
+ ## 9. Codegen and native load
203
+
204
+ - **`nitro.json`** sets namespaces and links `AppStateModule` → `HybridAppState`.
205
+ - **`npm run specs`** runs Nitrogen (`npx nitrogen@0.33.9`) to refresh `nitrogen/generated/**` when the spec or config changes.
206
+ - Android loads the C++ library in generated `NitroAppStateOnLoad` (`System.loadLibrary("NitroAppState")`); iOS links via CocoaPods / generated autolinking.
207
+
208
+ ---
209
+
210
+ ## 10. End-to-end: user backgrounds the app (native)
211
+
212
+ ```mermaid
213
+ sequenceDiagram
214
+ participant OS as OS lifecycle
215
+ participant Native as HybridAppState
216
+ participant JSI as JSI / Nitro
217
+ participant JS as useNitroAppState
218
+
219
+ OS->>Native: e.g. willResignActive / onStop
220
+ Native->>Native: updateState("background")
221
+ Native->>JSI: listener({ state: "background" })
222
+ JSI->>JS: setAppState("background")
223
+ JS->>JS: React re-render
224
+ ```
225
+
226
+ ---
227
+
228
+ ## 11. Summary table
229
+
230
+ | Layer | Responsibility |
231
+ |--------|----------------|
232
+ | `AppState.nitro.ts` | Contract for JS and codegen. |
233
+ | `useNitroAppState.native.ts` | Wire Nitro + React; synthesize brief `'killed'` UX; subscribe/unsubscribe. |
234
+ | `useNitroAppState.web.ts` | RN `AppState` only. |
235
+ | iOS `HybridAppState` | UIKit notifications + `UIApplication` state; UserDefaults for kill inference. |
236
+ | Android `HybridAppState` | `ProcessLifecycleOwner`; SharedPreferences for kill inference. |
237
+ | Nitrogen output | JSI/C++ bridges and autolinking factories — keep in sync with spec via Nitrogen. |
238
+
239
+ This should be enough to navigate the codebase, extend the spec safely, or debug lifecycle mismatches per platform.
@@ -0,0 +1,31 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "NitroAppState"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/matiks-com/rn-app-state.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = [
17
+ # Implementation (Swift)
18
+ "ios/**/*.{swift}",
19
+ # Autolinking/Registration (Objective-C++)
20
+ "ios/**/*.{m,mm}",
21
+ # Implementation (C++ objects)
22
+ "cpp/**/*.{hpp,cpp}",
23
+ ]
24
+
25
+ load 'nitrogen/generated/ios/NitroAppState+autolinking.rb'
26
+ add_nitrogen_files(s)
27
+
28
+ s.dependency 'React-jsi'
29
+ s.dependency 'React-callinvoker'
30
+ install_modules_dependencies(s)
31
+ end
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # @matiks/rn-app-state
2
+
3
+ Nitro-powered native app state module for React Native. Provides a reactive hook that tracks app lifecycle state (`active`, `background`, `killed`) using native code via JSI -- zero bridge overhead.
4
+
5
+ ## Features
6
+
7
+ - **Reactive hook** -- `useNitroAppState()` triggers re-renders on state changes
8
+ - **Native lifecycle observation** -- iOS (NotificationCenter) + Android (ProcessLifecycleOwner)
9
+ - **Kill detection** -- `wasKilledLastSession` flag detected on cold start via persisted clean-exit flag
10
+ - **Notification support** -- `getStateOnNotification()` returns the app state at the moment of a push/silent notification
11
+ - **Web fallback** -- Uses React Native's AppState API on web (no Nitro required)
12
+ - **Thread-safe** -- Both iOS and Android implementations handle concurrent access correctly
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ yarn add @matiks/rn-app-state
18
+ cd ios && pod install
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```tsx
24
+ import { useNitroAppState } from '@matiks/rn-app-state'
25
+
26
+ function MyComponent() {
27
+ const { appState, wasKilledLastSession, getStateOnNotification } = useNitroAppState()
28
+
29
+ useEffect(() => {
30
+ if (appState === 'active') {
31
+ // Refresh data, resume animations, etc.
32
+ }
33
+ }, [appState])
34
+
35
+ return <Text>App State: {appState}</Text>
36
+ }
37
+ ```
38
+
39
+ ## API
40
+
41
+ ### `useNitroAppState()`
42
+
43
+ Returns:
44
+
45
+ | Property | Type | Description |
46
+ |---|---|---|
47
+ | `appState` | `'active' \| 'background' \| 'killed'` | Current app lifecycle state. Reactive. |
48
+ | `wasKilledLastSession` | `boolean` | Whether the app was force-killed in the previous session. |
49
+ | `getStateOnNotification` | `() => AppStateType` | Synchronous call to get native state at notification receipt time. |
50
+
51
+ ### States
52
+
53
+ - **`active`** -- App is in the foreground and interactive
54
+ - **`background`** -- App is in the background (iOS `inactive` is merged into `background`)
55
+ - **`killed`** -- Previous session was force-terminated (reported briefly on cold start, then transitions to `active`)
56
+
57
+ ## Requirements
58
+
59
+ - React Native 0.76+
60
+ - `react-native-nitro-modules` 0.33.x
61
+ - iOS 16.0+
62
+ - Android minSdk 23+
63
+
64
+ ## Development
65
+
66
+ ```bash
67
+ # Generate nitrogen bridge code
68
+ yarn specs
69
+
70
+ # Type check
71
+ yarn typecheck
72
+ ```
@@ -0,0 +1,29 @@
1
+ project(NitroAppState)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set (PACKAGE_NAME NitroAppState)
5
+ set (CMAKE_VERBOSE_MAKEFILE ON)
6
+ set (CMAKE_CXX_STANDARD 20)
7
+
8
+ # Define C++ library and add all sources
9
+ add_library(${PACKAGE_NAME} SHARED
10
+ src/main/cpp/cpp-adapter.cpp
11
+ )
12
+
13
+ # Add Nitrogen specs
14
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroAppState+autolinking.cmake)
15
+
16
+ # Set up local includes
17
+ include_directories(
18
+ "src/main/cpp"
19
+ "../cpp"
20
+ )
21
+
22
+ find_library(LOG_LIB log)
23
+
24
+ # Link all libraries together
25
+ target_link_libraries(
26
+ ${PACKAGE_NAME}
27
+ ${LOG_LIB}
28
+ android
29
+ )
@@ -0,0 +1,140 @@
1
+ buildscript {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+
7
+ dependencies {
8
+ classpath "com.android.tools.build:gradle:8.13.2"
9
+ }
10
+ }
11
+
12
+ def reactNativeArchitectures() {
13
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
14
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
15
+ }
16
+
17
+ def isNewArchitectureEnabled() {
18
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
19
+ }
20
+
21
+ apply plugin: "com.android.library"
22
+ apply plugin: 'org.jetbrains.kotlin.android'
23
+ apply from: '../nitrogen/generated/android/NitroAppState+autolinking.gradle'
24
+ apply from: "./fix-prefab.gradle"
25
+
26
+
27
+ def getExtOrDefault(name) {
28
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NitroAppState_" + name]
29
+ }
30
+
31
+ def getExtOrIntegerDefault(name) {
32
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroAppState_" + name]).toInteger()
33
+ }
34
+
35
+ android {
36
+ namespace "com.margelo.nitro.appstate"
37
+
38
+ ndkVersion getExtOrDefault("ndkVersion")
39
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
40
+
41
+ defaultConfig {
42
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
43
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
44
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
45
+
46
+ externalNativeBuild {
47
+ cmake {
48
+ cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
49
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
50
+ abiFilters (*reactNativeArchitectures())
51
+
52
+ buildTypes {
53
+ debug {
54
+ cppFlags "-O1 -g"
55
+ }
56
+ release {
57
+ cppFlags "-O2"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ externalNativeBuild {
65
+ cmake {
66
+ path "CMakeLists.txt"
67
+ }
68
+ }
69
+
70
+ packagingOptions {
71
+ excludes = [
72
+ "META-INF",
73
+ "META-INF/**",
74
+ "**/libc++_shared.so",
75
+ "**/libNitroModules.so",
76
+ "**/libfbjni.so",
77
+ "**/libjsi.so",
78
+ "**/libfolly_json.so",
79
+ "**/libfolly_runtime.so",
80
+ "**/libglog.so",
81
+ "**/libhermes.so",
82
+ "**/libhermes-executor-debug.so",
83
+ "**/libhermes_executor.so",
84
+ "**/libreactnative.so",
85
+ "**/libreactnativejni.so",
86
+ "**/libturbomodulejsijni.so",
87
+ "**/libreact_nativemodule_core.so",
88
+ "**/libjscexecutor.so"
89
+ ]
90
+ }
91
+
92
+ buildFeatures {
93
+ buildConfig true
94
+ prefab true
95
+ }
96
+
97
+ buildTypes {
98
+ release {
99
+ minifyEnabled false
100
+ }
101
+ }
102
+
103
+ lintOptions {
104
+ disable "GradleCompatible"
105
+ }
106
+
107
+ compileOptions {
108
+ sourceCompatibility JavaVersion.VERSION_1_8
109
+ targetCompatibility JavaVersion.VERSION_1_8
110
+ }
111
+
112
+ sourceSets {
113
+ main {
114
+ if (isNewArchitectureEnabled()) {
115
+ java.srcDirs += [
116
+ // React Codegen files
117
+ "${project.buildDir}/generated/source/codegen/java"
118
+ ]
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ repositories {
125
+ mavenCentral()
126
+ google()
127
+ }
128
+
129
+
130
+ dependencies {
131
+ //noinspection GradleDynamicVersion
132
+ compileOnly "com.facebook.react:react-native:+"
133
+
134
+ // Add a dependency on NitroModules
135
+ compileOnly project(":react-native-nitro-modules")
136
+
137
+ // ProcessLifecycleOwner for app lifecycle observation
138
+ implementation "androidx.lifecycle:lifecycle-process:2.8.7"
139
+ implementation "androidx.lifecycle:lifecycle-common:2.8.7"
140
+ }
@@ -0,0 +1,51 @@
1
+ tasks.configureEach { task ->
2
+ // Make sure that we generate our prefab publication file only after having built the native library
3
+ // so that not a header publication file, but a full configuration publication will be generated, which
4
+ // will include the .so file
5
+
6
+ def prefabConfigurePattern = ~/^prefab(.+)ConfigurePackage$/
7
+ def matcher = task.name =~ prefabConfigurePattern
8
+ if (matcher.matches()) {
9
+ def variantName = matcher[0][1]
10
+ task.outputs.upToDateWhen { false }
11
+ task.dependsOn("externalNativeBuild${variantName}")
12
+ }
13
+ }
14
+
15
+ afterEvaluate {
16
+ def abis = reactNativeArchitectures()
17
+ rootProject.allprojects.each { proj ->
18
+ if (proj === rootProject) return
19
+
20
+ def dependsOnThisLib = proj.configurations.findAll { it.canBeResolved }.any { config ->
21
+ config.dependencies.any { dep ->
22
+ dep.group == project.group && dep.name == project.name
23
+ }
24
+ }
25
+ if (!dependsOnThisLib && proj != project) return
26
+
27
+ if (!proj.plugins.hasPlugin('com.android.application') && !proj.plugins.hasPlugin('com.android.library')) {
28
+ return
29
+ }
30
+
31
+ def variants = proj.android.hasProperty('applicationVariants') ? proj.android.applicationVariants : proj.android.libraryVariants
32
+ // Touch the prefab_config.json files to ensure that in ExternalNativeJsonGenerator.kt we will re-trigger the prefab CLI to
33
+ // generate a libnameConfig.cmake file that will contain our native library (.so).
34
+ // See this condition: https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/ExternalNativeJsonGenerator.kt;l=207-219?q=createPrefabBuildSystemGlue
35
+ variants.all { variant ->
36
+ def variantName = variant.name
37
+ abis.each { abi ->
38
+ def searchDir = new File(proj.projectDir, ".cxx/${variantName}")
39
+ if (!searchDir.exists()) return
40
+ def matches = []
41
+ searchDir.eachDir { randomDir ->
42
+ def prefabFile = new File(randomDir, "${abi}/prefab_config.json")
43
+ if (prefabFile.exists()) matches << prefabFile
44
+ }
45
+ matches.each { prefabConfig ->
46
+ prefabConfig.setLastModified(System.currentTimeMillis())
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,5 @@
1
+ NitroAppState_kotlinVersion=2.1.20
2
+ NitroAppState_minSdkVersion=23
3
+ NitroAppState_targetSdkVersion=36
4
+ NitroAppState_compileSdkVersion=36
5
+ NitroAppState_ndkVersion=27.1.12297006
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,6 @@
1
+ #include <jni.h>
2
+ #include "NitroAppStateOnLoad.hpp"
3
+
4
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
5
+ return margelo::nitro::appstate::initialize(vm);
6
+ }