1 // Copyright 2015-present 650 Industries. All rights reserved.
2 package host.exp.exponent.di
3 
4 import android.app.Application
5 import android.content.Context
6 import android.os.Handler
7 import android.os.Looper
8 import com.facebook.proguard.annotations.DoNotStrip
9 import expo.modules.updates.db.DatabaseHolder
10 import expo.modules.updates.db.UpdatesDatabase
11 import host.exp.exponent.ExpoHandler
12 import host.exp.exponent.ExponentManifest
13 import host.exp.exponent.analytics.EXL
14 import host.exp.exponent.kernel.services.ExpoKernelServiceRegistry
15 import host.exp.exponent.network.ExponentNetwork
16 import host.exp.exponent.storage.ExponentSharedPreferences
17 import java.lang.reflect.Field
18 import javax.inject.Inject
19 
20 class NativeModuleDepsProvider(application: Application) {
21   @Inject
22   @DoNotStrip
23   val mContext: Context = application
24 
25   @Inject
26   @DoNotStrip
27   val mApplicationContext: Application = application
28 
29   @Inject
30   @DoNotStrip
31   val mExpoHandler: ExpoHandler = ExpoHandler(Handler(Looper.getMainLooper()))
32 
33   @Inject
34   @DoNotStrip
35   val mExponentSharedPreferences: ExponentSharedPreferences = ExponentSharedPreferences(mContext)
36 
37   @Inject
38   @DoNotStrip
39   val mExponentNetwork: ExponentNetwork = ExponentNetwork(mContext, mExponentSharedPreferences)
40 
41   @Inject
42   @DoNotStrip
43   var mExponentManifest: ExponentManifest = ExponentManifest(mContext, mExponentSharedPreferences)
44 
45   @Inject
46   @DoNotStrip
47   var mKernelServiceRegistry: ExpoKernelServiceRegistry = ExpoKernelServiceRegistry(mContext, mExponentSharedPreferences)
48 
49   @Inject
50   @DoNotStrip
51   val mUpdatesDatabaseHolder: DatabaseHolder = DatabaseHolder(UpdatesDatabase.getInstance(mContext))
52 
53   private val classToInstanceMap = mutableMapOf<Class<*>, Any>()
54 
addnull55   fun add(clazz: Class<*>, instance: Any) {
56     classToInstanceMap[clazz] = instance
57   }
58 
injectnull59   fun inject(clazz: Class<*>, target: Any) {
60     for (field in clazz.declaredFields) {
61       injectFieldInTarget(target, field)
62     }
63   }
64 
injectFieldInTargetnull65   private fun injectFieldInTarget(target: Any, field: Field) {
66     if (field.isAnnotationPresent(Inject::class.java)) {
67       val fieldClazz = field.type
68       if (!classToInstanceMap.containsKey(fieldClazz)) {
69         throw RuntimeException("NativeModuleDepsProvider could not find object for class $fieldClazz")
70       }
71       val instance = classToInstanceMap[fieldClazz]
72       try {
73         field.isAccessible = true
74         field[target] = instance
75       } catch (e: IllegalAccessException) {
76         EXL.e(TAG, e.toString())
77       }
78     }
79   }
80 
81   companion object {
82     private val TAG = NativeModuleDepsProvider::class.java.simpleName
83 
84     @JvmStatic lateinit var instance: NativeModuleDepsProvider
85       private set
86 
87     private var useTestInstance = false
88 
initializenull89     fun initialize(application: Application) {
90       if (!useTestInstance) {
91         instance = NativeModuleDepsProvider(application)
92       }
93     }
94 
95     // Only for testing!
setTestInstancenull96     fun setTestInstance(instance: NativeModuleDepsProvider) {
97       Companion.instance = instance
98       useTestInstance = true
99     }
100   }
101 
102   init {
fieldnull103     for (field in NativeModuleDepsProvider::class.java.declaredFields) {
104       if (field.isAnnotationPresent(Inject::class.java)) {
105         try {
106           classToInstanceMap[field.type] = field[this]
107         } catch (e: IllegalAccessException) {
108           EXL.e(TAG, e.toString())
109         }
110       }
111     }
112   }
113 }
114