1 // Copyright 2015-present 650 Industries. All rights reserved.
2 
3 package versioned.host.exp.exponent
4 
5 import com.facebook.react.ReactInstanceManager
6 import com.facebook.react.bridge.Inspector
7 import com.facebook.react.devsupport.DevServerHelper
8 import com.facebook.react.devsupport.DevSupportManagerBase
9 import com.facebook.react.devsupport.InspectorPackagerConnection
10 import expo.modules.kotlin.devtools.ExpoRequestCdpInterceptor
11 import expo.modules.manifests.core.Manifest
12 import java.io.Closeable
13 
14 @Suppress("unused")
15 class ExpoNetworkInterceptor : Closeable, ExpoRequestCdpInterceptor.Delegate {
16   private var isStarted = false
17   private val inspectorPackagerConnection = InspectorPackagerConnectionWrapper()
18   private var reactInstanceManager: ReactInstanceManager? = null
19 
startnull20   fun start(manifest: Manifest, reactInstanceManager: ReactInstanceManager) {
21     val buildProps = (manifest?.getPluginProperties("expo-build-properties")?.get("android") as? Map<*, *>)
22       ?.mapKeys { it.key.toString() }
23     val enableNetworkInspector = buildProps?.get("networkInspector") as? Boolean ?: true
24     isStarted = enableNetworkInspector
25 
26     this.onResume(reactInstanceManager)
27   }
28 
onResumenull29   fun onResume(reactInstanceManager: ReactInstanceManager) {
30     if (!isStarted || !reactInstanceManager.devSupportManager.devSupportEnabled) {
31       return
32     }
33     this.reactInstanceManager = reactInstanceManager
34     ExpoRequestCdpInterceptor.setDelegate(this)
35   }
36 
onPausenull37   fun onPause() {
38     if (!isStarted) {
39       return
40     }
41     ExpoRequestCdpInterceptor.setDelegate(null)
42     this.reactInstanceManager = null
43   }
44 
closenull45   override fun close() {
46     this.onPause()
47   }
48 
dispatchnull49   override fun dispatch(event: String) {
50     reactInstanceManager?.let {
51       inspectorPackagerConnection.sendWrappedEventToAllPages(it, event)
52     }
53   }
54 }
55 
56 /**
57  * A `InspectorPackagerConnection` wrapper to expose private members with reflection
58  */
59 internal class InspectorPackagerConnectionWrapper {
60   private val devServerHelperField = DevSupportManagerBase::class.java.getDeclaredField("mDevServerHelper")
61   private val inspectorPackagerConnectionField = DevServerHelper::class.java.getDeclaredField("mInspectorPackagerConnection")
62   private val sendWrappedEventMethod = InspectorPackagerConnection::class.java.getDeclaredMethod("sendWrappedEvent", String::class.java, String::class.java)
63 
64   init {
65     devServerHelperField.isAccessible = true
66     inspectorPackagerConnectionField.isAccessible = true
67     sendWrappedEventMethod.isAccessible = true
68   }
69 
sendWrappedEventToAllPagesnull70   fun sendWrappedEventToAllPages(reactInstanceManager: ReactInstanceManager, event: String) {
71     val devServerHelper = devServerHelperField[reactInstanceManager.devSupportManager]
72     val inspectorPackagerConnection = inspectorPackagerConnectionField[devServerHelper] as? InspectorPackagerConnection
73     for (page in Inspector.getPages()) {
74       if (!page.title.contains("Reanimated")) {
75         sendWrappedEventMethod.invoke(inspectorPackagerConnection, page.id.toString(), event)
76       }
77     }
78   }
79 }
80