Skip to content

Commit 27f2658

Browse files
authored
Android Turbo Modules (#7999)
* Implemented turbo modules for android * Implemented missing method * Complete Android Turbo module implementation * Complete Android Turbo module implementation * Undo some unneeded fixes * Fixed build on ios
1 parent 84913d7 commit 27f2658

11 files changed

Lines changed: 397 additions & 67 deletions

File tree

lib/android/app/build.gradle

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat
33

44
apply plugin: 'com.android.library'
55
apply plugin: 'kotlin-android'
6+
apply plugin: "com.facebook.react"
7+
8+
9+
react {
10+
codegenJavaPackageName = "com.reactnativenavigation.react"
11+
}
12+
613

714
def safeExtGet(prop, fallback) {
815
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
@@ -193,7 +200,7 @@ dependencies {
193200
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesCore"
194201
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
195202

196-
implementation 'androidx.appcompat:appcompat:1.3.1'
203+
implementation 'androidx.appcompat:appcompat:1.7.0'
197204
implementation 'androidx.annotation:annotation:1.2.0'
198205
implementation 'com.google.android.material:material:1.2.0-alpha03'
199206

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
11
package com.reactnativenavigation.react
22

3+
import com.facebook.react.BaseReactPackage
34
import com.facebook.react.ReactApplication
4-
import com.facebook.react.ReactHost
5-
import com.facebook.react.ReactPackage
65
import com.facebook.react.bridge.NativeModule
76
import com.facebook.react.bridge.ReactApplicationContext
7+
import com.facebook.react.module.model.ReactModuleInfo
8+
import com.facebook.react.module.model.ReactModuleInfoProvider
89
import com.facebook.react.uimanager.ViewManager
910
import com.reactnativenavigation.options.LayoutFactory
1011
import com.reactnativenavigation.react.modal.ModalViewManager
1112

12-
class NavigationPackage() : ReactPackage {
13-
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
14-
val reactApp = reactContext.applicationContext as ReactApplication
15-
return listOf(
16-
NavigationModule(
17-
reactContext,
18-
LayoutFactory(reactApp.reactHost)
19-
)
20-
)
13+
class NavigationPackage() : BaseReactPackage() {
14+
15+
override fun getModule(name: String, context: ReactApplicationContext): NativeModule? {
16+
val reactApp = context.applicationContext as ReactApplication
17+
return when (name) {
18+
NavigationTurboModule.NAME -> {
19+
NavigationTurboModule(context, LayoutFactory(reactApp.reactHost))
20+
}
21+
else -> {
22+
null
23+
}
24+
}
2125
}
2226

23-
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
27+
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
28+
mapOf(NavigationTurboModule.NAME to ReactModuleInfo(
29+
_name = NavigationTurboModule.NAME,
30+
_className = NavigationTurboModule.NAME,
31+
_canOverrideExistingModule = false,
32+
_needsEagerInit = false,
33+
isCxxModule = false,
34+
isTurboModule = true
35+
))
36+
}
2437

25-
return listOf(ModalViewManager(reactContext))
38+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
39+
return mutableListOf(ModalViewManager(reactContext))
2640
}
2741
}
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
package com.reactnativenavigation.react
2+
3+
import android.util.Log
4+
import com.facebook.react.bridge.Promise
5+
import com.facebook.react.bridge.ReactApplicationContext
6+
import com.facebook.react.bridge.ReadableArray
7+
import com.facebook.react.bridge.ReadableMap
8+
import com.reactnativenavigation.NavigationActivity
9+
import com.reactnativenavigation.NavigationApplication
10+
import com.reactnativenavigation.options.LayoutFactory
11+
import com.reactnativenavigation.options.Options
12+
import com.reactnativenavigation.options.parsers.JSONParser
13+
import com.reactnativenavigation.options.parsers.LayoutNodeParser
14+
import com.reactnativenavigation.options.parsers.TypefaceLoader
15+
import com.reactnativenavigation.react.events.EventEmitter
16+
import com.reactnativenavigation.utils.LaunchArgsParser
17+
import com.reactnativenavigation.utils.Now
18+
import com.reactnativenavigation.utils.SystemUiUtils.getStatusBarHeight
19+
import com.reactnativenavigation.utils.UiThread
20+
import com.reactnativenavigation.utils.UiUtils
21+
import com.reactnativenavigation.viewcontrollers.navigator.Navigator
22+
import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController
23+
import java.util.Objects
24+
25+
class NavigationTurboModule(
26+
reactContext: ReactApplicationContext,
27+
private val layoutFactory: LayoutFactory
28+
) : NativeRNNTurboModuleSpec(reactContext) {
29+
30+
private val now = Now()
31+
private val jsonParser: JSONParser = JSONParser()
32+
private lateinit var eventEmitter: EventEmitter
33+
34+
init {
35+
reactContext.addLifecycleEventListener(object : LifecycleEventListenerAdapter() {
36+
override fun onHostPause() {
37+
super.onHostPause()
38+
UiUtils.runOnMainThread {
39+
navigator().onHostPause()
40+
}
41+
}
42+
43+
override fun onHostResume() {
44+
eventEmitter = EventEmitter(reactContext)
45+
navigator().setEventEmitter(eventEmitter)
46+
layoutFactory.init(
47+
activity(),
48+
eventEmitter,
49+
navigator().getChildRegistry(),
50+
(activity().application as NavigationApplication).externalComponents
51+
)
52+
UiUtils.runOnMainThread { navigator().onHostResume() }
53+
}
54+
})
55+
}
56+
57+
override fun getTypedExportedConstants(): MutableMap<String, Any> {
58+
val constants = mutableMapOf<String, Any>()
59+
constants[Constants.BACK_BUTTON_JS_KEY] = Constants.BACK_BUTTON_ID
60+
constants[Constants.BOTTOM_TABS_HEIGHT_KEY] =
61+
UiUtils.pxToDp(
62+
reactApplicationContext,
63+
UiUtils.getBottomTabsHeight(reactApplicationContext).toFloat()
64+
).toDouble()
65+
constants[Constants.STATUS_BAR_HEIGHT_KEY] =
66+
UiUtils.pxToDp(reactApplicationContext, getStatusBarHeight(currentActivity).toFloat())
67+
.toDouble()
68+
constants[Constants.TOP_BAR_HEIGHT_KEY] = UiUtils.pxToDp(
69+
reactApplicationContext,
70+
UiUtils.getTopBarHeight(reactApplicationContext).toFloat()
71+
).toDouble()
72+
return constants
73+
}
74+
75+
override fun setRoot(commandId: String, layout: ReadableMap, promise: Promise) {
76+
Log.d("NavigationTurboModule", "setRoot ${Thread.currentThread()}")
77+
val layoutTree = LayoutNodeParser.parse(
78+
Objects.requireNonNull(
79+
jsonParser.parse(layout).optJSONObject("root")
80+
)
81+
)
82+
handle {
83+
Log.d("NavigationTurboModule", "setRoot handle ${Thread.currentThread()}")
84+
val viewController = layoutFactory.create(layoutTree)
85+
navigator().setRoot(
86+
viewController,
87+
NativeCommandListener("setRoot", commandId, promise, eventEmitter, now)
88+
)
89+
}
90+
}
91+
92+
override fun setDefaultOptions(options: ReadableMap?) {
93+
handle {
94+
val defaultOptions = parse(options)
95+
layoutFactory.defaultOptions = defaultOptions
96+
navigator().defaultOptions = defaultOptions
97+
}
98+
}
99+
100+
override fun mergeOptions(componentId: String?, options: ReadableMap?) {
101+
handle { navigator().mergeOptions(componentId, parse(options)) }
102+
}
103+
104+
override fun push(
105+
commandId: String,
106+
componentId: String,
107+
layout: ReadableMap,
108+
promise: Promise
109+
) {
110+
val layoutTree = LayoutNodeParser.parse(jsonParser.parse(layout))
111+
handle {
112+
val viewController = layoutFactory.create(layoutTree)
113+
navigator().push(
114+
componentId,
115+
viewController,
116+
NativeCommandListener("push", commandId, promise, eventEmitter, now)
117+
)
118+
}
119+
}
120+
121+
override fun pop(
122+
commandId: String,
123+
componentId: String,
124+
options: ReadableMap?,
125+
promise: Promise
126+
) {
127+
handle {
128+
navigator().pop(
129+
componentId,
130+
parse(options),
131+
NativeCommandListener("pop", commandId, promise, eventEmitter, now)
132+
)
133+
}
134+
}
135+
136+
override fun popTo(
137+
commandId: String,
138+
componentId: String,
139+
options: ReadableMap?,
140+
promise: Promise
141+
) {
142+
handle {
143+
navigator().popTo(
144+
componentId,
145+
parse(options),
146+
NativeCommandListener("popTo", commandId, promise, eventEmitter, now)
147+
)
148+
}
149+
}
150+
151+
override fun popToRoot(
152+
commandId: String,
153+
componentId: String,
154+
options: ReadableMap?,
155+
promise: Promise
156+
) {
157+
handle {
158+
navigator().popToRoot(
159+
componentId,
160+
parse(options),
161+
NativeCommandListener("popToRoot", commandId, promise, eventEmitter, now)
162+
)
163+
}
164+
}
165+
166+
override fun setStackRoot(
167+
commandId: String,
168+
componentId: String,
169+
children: ReadableArray,
170+
promise: Promise
171+
) {
172+
handle {
173+
val _children = ArrayList<ViewController<*>>()
174+
for (i in 0..<children.size()) {
175+
val layoutTree = LayoutNodeParser.parse(jsonParser.parse(children.getMap(i)))
176+
_children.add(layoutFactory.create(layoutTree))
177+
}
178+
navigator().setStackRoot(
179+
componentId,
180+
_children,
181+
NativeCommandListener("setStackRoot", commandId, promise, eventEmitter, now)
182+
)
183+
}
184+
}
185+
186+
override fun showModal(commandId: String, layout: ReadableMap, promise: Promise) {
187+
val layoutTree = LayoutNodeParser.parse(jsonParser.parse(layout))
188+
handle {
189+
val viewController = layoutFactory.create(layoutTree)
190+
navigator().showModal(
191+
viewController,
192+
NativeCommandListener("showModal", commandId, promise, eventEmitter, now)
193+
)
194+
}
195+
}
196+
197+
override fun dismissModal(
198+
commandId: String,
199+
componentId: String,
200+
options: ReadableMap?,
201+
promise: Promise
202+
) {
203+
handle {
204+
navigator().mergeOptions(componentId, parse(options))
205+
navigator().dismissModal(
206+
componentId,
207+
NativeCommandListener("dismissModal", commandId, promise, eventEmitter, now)
208+
)
209+
}
210+
}
211+
212+
override fun dismissAllModals(commandId: String, options: ReadableMap?, promise: Promise) {
213+
handle {
214+
navigator().dismissAllModals(
215+
parse(options),
216+
NativeCommandListener("dismissAllModals", commandId, promise, eventEmitter, now)
217+
)
218+
}
219+
}
220+
221+
override fun showOverlay(commandId: String, layout: ReadableMap, promise: Promise) {
222+
val layoutTree = LayoutNodeParser.parse(jsonParser.parse(layout))
223+
handle {
224+
val viewController = layoutFactory.create(layoutTree)
225+
navigator().showOverlay(
226+
viewController,
227+
NativeCommandListener("showOverlay", commandId, promise, eventEmitter, now)
228+
)
229+
}
230+
}
231+
232+
override fun dismissOverlay(commandId: String, componentId: String, promise: Promise) {
233+
handle {
234+
navigator().dismissOverlay(
235+
componentId,
236+
NativeCommandListener("dismissOverlay", commandId, promise, eventEmitter, now)
237+
)
238+
}
239+
}
240+
241+
override fun dismissAllOverlays(commandId: String, promise: Promise) {
242+
handle {
243+
navigator().dismissAllOverlays(
244+
NativeCommandListener(
245+
"dismissAllOverlays",
246+
commandId,
247+
promise,
248+
eventEmitter,
249+
now
250+
)
251+
)
252+
}
253+
}
254+
255+
override fun getLaunchArgs(commandId: String, promise: Promise) {
256+
promise.resolve(LaunchArgsParser.parse(activity()))
257+
258+
}
259+
260+
private fun parse(mergeOptions: ReadableMap?): Options {
261+
val ctx = reactApplicationContext
262+
return if (mergeOptions ==
263+
null
264+
) Options.EMPTY else Options.parse(
265+
ctx,
266+
TypefaceLoader(activity()),
267+
jsonParser.parse(mergeOptions)
268+
)
269+
}
270+
271+
private fun navigator(): Navigator {
272+
return activity().navigator
273+
}
274+
275+
private fun handle(task: Runnable) {
276+
UiThread.post {
277+
if (currentActivity != null && !activity().isFinishing) {
278+
task.run()
279+
}
280+
}
281+
}
282+
283+
private fun activity(): NavigationActivity {
284+
return currentActivity as NavigationActivity
285+
}
286+
287+
companion object {
288+
const val NAME = "RNNTurboModule"
289+
}
290+
291+
}

0 commit comments

Comments
 (0)