1 // Copyright 2015-present 650 Industries. All rights reserved.
2 package host.exp.exponent.modules
3 
4 import com.facebook.react.bridge.*
5 import com.facebook.react.modules.core.DeviceEventManagerModule
6 import host.exp.exponent.Constants
7 import host.exp.exponent.analytics.EXL
8 import host.exp.exponent.di.NativeModuleDepsProvider
9 import host.exp.exponent.experience.ErrorActivity
10 import host.exp.exponent.kernel.DevMenuManager
11 import host.exp.exponent.kernel.ExponentKernelModuleInterface
12 import host.exp.exponent.kernel.ExponentKernelModuleProvider
13 import host.exp.exponent.kernel.ExponentKernelModuleProvider.KernelEvent
14 import host.exp.exponent.kernel.ExponentKernelModuleProvider.KernelEventCallback
15 import host.exp.exponent.kernel.Kernel
16 import host.exp.exponent.storage.ExponentSharedPreferences
17 import host.exp.exponent.utils.BundleJSONConverter
18 import org.json.JSONObject
19 import java.util.*
20 import javax.inject.Inject
21 
22 class ExponentKernelModule(reactContext: ReactApplicationContext?) :
23   ReactContextBaseJavaModule(reactContext), ExponentKernelModuleInterface {
24 
25   @Inject
26   lateinit var kernel: Kernel
27 
28   @Inject
29   lateinit var exponentSharedPreferences: ExponentSharedPreferences
30 
31   @Inject
32   lateinit var devMenuManager: DevMenuManager
33 
getConstantsnull34   override fun getConstants(): Map<String, Any> {
35     return mapOf(
36       "sdkVersions" to Constants.SDK_VERSIONS
37     )
38   }
39 
getNamenull40   override fun getName(): String {
41     return "ExponentKernel"
42   }
43 
consumeEventQueuenull44   override fun consumeEventQueue() {
45     if (ExponentKernelModuleProvider.eventQueue.size == 0) {
46       return
47     }
48 
49     val (name, data, callback) = ExponentKernelModuleProvider.eventQueue.remove()
50 
51     val eventId = UUID.randomUUID().toString()
52     data.putString("eventId", eventId)
53 
54     kernelEventCallbacks[eventId] = callback
55 
56     try {
57       reactApplicationContext
58         .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
59         .emit(name, data)
60     } catch (e: Exception) {
61       onEventFailure(eventId, e.message)
62     }
63 
64     consumeEventQueue()
65   }
66 
67   //region Exported methods
68   @ReactMethod
getSessionAsyncnull69   fun getSessionAsync(promise: Promise) {
70     val sessionString = exponentSharedPreferences.getString(ExponentSharedPreferences.ExponentSharedPreferencesKey.EXPO_AUTH_SESSION)
71     if (sessionString == null) {
72       promise.resolve(null)
73       return
74     }
75 
76     try {
77       val sessionJsonObject = JSONObject(sessionString)
78       val session = Arguments.fromBundle(BundleJSONConverter.convertToBundle(sessionJsonObject))
79       promise.resolve(session)
80     } catch (e: Exception) {
81       promise.resolve(null)
82       EXL.e(TAG, e)
83     }
84   }
85 
86   @ReactMethod
setSessionAsyncnull87   fun setSessionAsync(session: ReadableMap, promise: Promise) {
88     try {
89       val sessionJsonObject = JSONObject(session.toHashMap())
90       exponentSharedPreferences.updateSession(sessionJsonObject)
91       promise.resolve(null)
92     } catch (e: Exception) {
93       promise.reject("ERR_SESSION_NOT_SAVED", "Could not save session secret", e)
94       EXL.e(TAG, e)
95     }
96   }
97 
98   @ReactMethod
removeSessionAsyncnull99   fun removeSessionAsync(promise: Promise) {
100     try {
101       exponentSharedPreferences.removeSession()
102       promise.resolve(null)
103     } catch (e: Exception) {
104       promise.reject("ERR_SESSION_NOT_REMOVED", "Could not remove session secret", e)
105       EXL.e(TAG, e)
106     }
107   }
108 
109   @ReactMethod
goToHomeFromErrorScreennull110   fun goToHomeFromErrorScreen() {
111     if (ErrorActivity.visibleActivity == null) {
112       // shouldn't ever get here
113       EXL.e(TAG, "visibleActivity was null in goToHomeFromErrorScreen")
114       return
115     }
116     ErrorActivity.visibleActivity!!.onClickHome()
117   }
118 
119   @ReactMethod
reloadFromErrorScreennull120   fun reloadFromErrorScreen() {
121     if (ErrorActivity.visibleActivity == null) {
122       // shouldn't ever get here
123       EXL.e(TAG, "visibleActivity was null in reloadFromErrorScreen")
124       return
125     }
126     ErrorActivity.visibleActivity!!.onClickReload()
127   }
128 
129   @ReactMethod
onEventSuccessnull130   fun onEventSuccess(eventId: String, result: ReadableMap) {
131     kernelEventCallbacks.remove(eventId)?.onEventSuccess(result)
132   }
133 
134   @ReactMethod
onEventFailurenull135   fun onEventFailure(eventId: String, errorMessage: String?) {
136     kernelEventCallbacks.remove(eventId)?.onEventFailure(errorMessage)
137   }
138 
139   //region DevMenu
140   @ReactMethod
doesCurrentTaskEnableDevtoolsAsyncnull141   fun doesCurrentTaskEnableDevtoolsAsync(promise: Promise) {
142     promise.resolve(devMenuManager.isDevSupportEnabledByCurrentActivity())
143   }
144 
145   @ReactMethod
getIsOnboardingFinishedAsyncnull146   fun getIsOnboardingFinishedAsync(promise: Promise) {
147     promise.resolve(devMenuManager.isOnboardingFinished())
148   }
149 
150   @ReactMethod
setIsOnboardingFinishedAsyncnull151   fun setIsOnboardingFinishedAsync(isOnboardingFinished: Boolean, promise: Promise) {
152     devMenuManager.setIsOnboardingFinished(isOnboardingFinished)
153     promise.resolve(null)
154   }
155 
156   @ReactMethod
closeDevMenuAsyncnull157   fun closeDevMenuAsync(promise: Promise) {
158     devMenuManager.hideInCurrentActivity()
159     promise.resolve(true)
160   }
161 
162   @ReactMethod
getDevMenuItemsToShowAsyncnull163   fun getDevMenuItemsToShowAsync(promise: Promise) {
164     val devMenuItems = devMenuManager.getMenuItems()
165     promise.resolve(devMenuItems)
166   }
167 
168   @ReactMethod
selectDevMenuItemWithKeyAsyncnull169   fun selectDevMenuItemWithKeyAsync(itemKey: String?, promise: Promise) {
170     devMenuManager.selectItemWithKey(itemKey!!)
171     devMenuManager.requestToClose()
172     promise.resolve(true)
173   }
174 
175   @ReactMethod
reloadAppAsyncnull176   fun reloadAppAsync(promise: Promise) {
177     devMenuManager.reloadApp()
178     devMenuManager.requestToClose()
179     promise.resolve(true)
180   }
181 
182   @ReactMethod
goToHomeAsyncnull183   fun goToHomeAsync(promise: Promise) {
184     kernel.openHomeActivity()
185     devMenuManager.requestToClose()
186     promise.resolve(true)
187   } //endregion DevMenu
188 
189   //endregion Exported methods
190   companion object {
191     private val TAG = ExponentKernelModule::class.java.simpleName
192 
193     private var instance: ExponentKernelModule? = null
194 
195     private val kernelEventCallbacks = mutableMapOf<String, KernelEventCallback>()
196 
queueEventnull197     fun queueEvent(name: String, data: WritableMap, callback: KernelEventCallback) {
198       queueEvent(KernelEvent(name, data, callback))
199     }
200 
queueEventnull201     fun queueEvent(event: KernelEvent) {
202       ExponentKernelModuleProvider.eventQueue.add(event)
203       instance?.consumeEventQueue()
204     }
205   }
206 
207   init {
208     NativeModuleDepsProvider.instance.inject(ExponentKernelModule::class.java, this)
209     instance = this
210   }
211 }
212