// Copyright 2015-present 650 Industries. All rights reserved. package host.exp.exponent.di import android.app.Application import android.content.Context import android.os.Handler import android.os.Looper import com.facebook.proguard.annotations.DoNotStrip import expo.modules.updates.db.DatabaseHolder import expo.modules.updates.db.UpdatesDatabase import host.exp.exponent.ExpoHandler import host.exp.exponent.ExponentManifest import host.exp.exponent.analytics.EXL import host.exp.exponent.kernel.Crypto import host.exp.exponent.kernel.services.ExpoKernelServiceRegistry import host.exp.exponent.network.ExponentNetwork import host.exp.exponent.storage.ExponentSharedPreferences import java.lang.reflect.Field import javax.inject.Inject class NativeModuleDepsProvider(application: Application) { @Inject @DoNotStrip val mContext: Context = application @Inject @DoNotStrip val mApplicationContext: Application = application @Inject @DoNotStrip val mExpoHandler: ExpoHandler = ExpoHandler(Handler(Looper.getMainLooper())) @Inject @DoNotStrip val mExponentSharedPreferences: ExponentSharedPreferences = ExponentSharedPreferences(mContext) @Inject @DoNotStrip val mExponentNetwork: ExponentNetwork = ExponentNetwork(mContext, mExponentSharedPreferences) @Inject @DoNotStrip val mCrypto: Crypto = Crypto(mExponentNetwork) @Inject @DoNotStrip var mExponentManifest: ExponentManifest = ExponentManifest(mContext, mExponentNetwork, mCrypto, mExponentSharedPreferences) @Inject @DoNotStrip var mKernelServiceRegistry: ExpoKernelServiceRegistry = ExpoKernelServiceRegistry(mContext, mExponentSharedPreferences) @Inject @DoNotStrip val mUpdatesDatabaseHolder: DatabaseHolder = DatabaseHolder(UpdatesDatabase.getInstance(mContext)) private val classToInstanceMap = mutableMapOf, Any>() fun add(clazz: Class<*>, instance: Any) { classToInstanceMap[clazz] = instance } fun inject(clazz: Class<*>, target: Any) { for (field in clazz.declaredFields) { injectFieldInTarget(target, field) } } private fun injectFieldInTarget(target: Any, field: Field) { if (field.isAnnotationPresent(Inject::class.java)) { val fieldClazz = field.type if (!classToInstanceMap.containsKey(fieldClazz)) { throw RuntimeException("NativeModuleDepsProvider could not find object for class $fieldClazz") } val instance = classToInstanceMap[fieldClazz] try { field.isAccessible = true field[target] = instance } catch (e: IllegalAccessException) { EXL.e(TAG, e.toString()) } } } companion object { private val TAG = NativeModuleDepsProvider::class.java.simpleName @JvmStatic lateinit var instance: NativeModuleDepsProvider private set private var useTestInstance = false fun initialize(application: Application) { if (!useTestInstance) { instance = NativeModuleDepsProvider(application) } } // Only for testing! fun setTestInstance(instance: NativeModuleDepsProvider) { Companion.instance = instance useTestInstance = true } } init { for (field in NativeModuleDepsProvider::class.java.declaredFields) { if (field.isAnnotationPresent(Inject::class.java)) { try { classToInstanceMap[field.type] = field[this] } catch (e: IllegalAccessException) { EXL.e(TAG, e.toString()) } } } } }