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.Crypto 15 import host.exp.exponent.kernel.services.ExpoKernelServiceRegistry 16 import host.exp.exponent.network.ExponentNetwork 17 import host.exp.exponent.storage.ExponentSharedPreferences 18 import java.lang.reflect.Field 19 import javax.inject.Inject 20 21 class NativeModuleDepsProvider(application: Application) { 22 @Inject 23 @DoNotStrip 24 val mContext: Context = application 25 26 @Inject 27 @DoNotStrip 28 val mApplicationContext: Application = application 29 30 @Inject 31 @DoNotStrip 32 val mExpoHandler: ExpoHandler = ExpoHandler(Handler(Looper.getMainLooper())) 33 34 @Inject 35 @DoNotStrip 36 val mExponentSharedPreferences: ExponentSharedPreferences = ExponentSharedPreferences(mContext) 37 38 @Inject 39 @DoNotStrip 40 val mExponentNetwork: ExponentNetwork = ExponentNetwork(mContext, mExponentSharedPreferences) 41 42 @Inject 43 @DoNotStrip 44 val mCrypto: Crypto = Crypto(mExponentNetwork) 45 46 @Inject 47 @DoNotStrip 48 var mExponentManifest: ExponentManifest = ExponentManifest(mContext, mExponentNetwork, mCrypto, mExponentSharedPreferences) 49 50 @Inject 51 @DoNotStrip 52 var mKernelServiceRegistry: ExpoKernelServiceRegistry = ExpoKernelServiceRegistry(mContext, mExponentSharedPreferences) 53 54 @Inject 55 @DoNotStrip 56 val mUpdatesDatabaseHolder: DatabaseHolder = DatabaseHolder(UpdatesDatabase.getInstance(mContext)) 57 58 private val classToInstanceMap = mutableMapOf<Class<*>, Any>() 59 60 fun add(clazz: Class<*>, instance: Any) { 61 classToInstanceMap[clazz] = instance 62 } 63 64 fun inject(clazz: Class<*>, target: Any) { 65 for (field in clazz.declaredFields) { 66 injectFieldInTarget(target, field) 67 } 68 } 69 70 private fun injectFieldInTarget(target: Any, field: Field) { 71 if (field.isAnnotationPresent(Inject::class.java)) { 72 val fieldClazz = field.type 73 if (!classToInstanceMap.containsKey(fieldClazz)) { 74 throw RuntimeException("NativeModuleDepsProvider could not find object for class $fieldClazz") 75 } 76 val instance = classToInstanceMap[fieldClazz] 77 try { 78 field.isAccessible = true 79 field[target] = instance 80 } catch (e: IllegalAccessException) { 81 EXL.e(TAG, e.toString()) 82 } 83 } 84 } 85 86 companion object { 87 private val TAG = NativeModuleDepsProvider::class.java.simpleName 88 89 @JvmStatic lateinit var instance: NativeModuleDepsProvider 90 private set 91 92 private var useTestInstance = false 93 94 fun initialize(application: Application) { 95 if (!useTestInstance) { 96 instance = NativeModuleDepsProvider(application) 97 } 98 } 99 100 // Only for testing! 101 fun setTestInstance(instance: NativeModuleDepsProvider) { 102 Companion.instance = instance 103 useTestInstance = true 104 } 105 } 106 107 init { 108 for (field in NativeModuleDepsProvider::class.java.declaredFields) { 109 if (field.isAnnotationPresent(Inject::class.java)) { 110 try { 111 classToInstanceMap[field.type] = field[this] 112 } catch (e: IllegalAccessException) { 113 EXL.e(TAG, e.toString()) 114 } 115 } 116 } 117 } 118 } 119