1 package host.exp.exponent.taskManager 2 3 import android.content.Context 4 import android.util.Log 5 import expo.modules.adapters.react.apploader.HeadlessAppLoaderNotifier 6 import expo.modules.apploader.AppLoaderProvider 7 import expo.modules.apploader.HeadlessAppLoader 8 import expo.modules.apploader.HeadlessAppLoader.AppConfigurationError 9 import expo.modules.core.interfaces.Consumer 10 import expo.modules.core.interfaces.DoNotStrip 11 import host.exp.exponent.headless.InternalHeadlessAppLoader 12 import java.util.* 13 14 @DoNotStrip 15 class ExpoHeadlessAppLoader @DoNotStrip constructor(context: Context?) : HeadlessAppLoader { 16 private val appScopeKeysToAppRecords = mutableMapOf<String, AppRecordInterface>() 17 18 @Throws(AppConfigurationError::class) loadAppnull19 override fun loadApp( 20 context: Context, 21 params: HeadlessAppLoader.Params, 22 alreadyRunning: Runnable, 23 callback: Consumer<Boolean> 24 ) { 25 val appLoader = createAppLoader(context) 26 27 if (params.appUrl == null) { 28 throw AppConfigurationError("Cannot execute background task because application URL is invalid") 29 } else { 30 if (appScopeKeysToAppRecords.containsKey(params.appScopeKey)) { 31 alreadyRunning.run() 32 } else { 33 Log.i( 34 TAG, 35 "Loading headless app '" + params.appScopeKey + "' with url '" + params.appUrl + "'." 36 ) 37 val appRecord = appLoader.loadApp( 38 params.appUrl, mapOf(), 39 object : AppLoaderProvider.Callback() { 40 override fun onComplete(success: Boolean, exception: Exception?) { 41 if (exception != null) { 42 exception.printStackTrace() 43 Log.e(TAG, exception.message!!) 44 } 45 HeadlessAppLoaderNotifier.notifyAppLoaded(params.appScopeKey) 46 callback.apply(success) 47 if (!success) { 48 appScopeKeysToAppRecords.remove(params.appScopeKey) 49 } 50 } 51 } 52 ) 53 54 appScopeKeysToAppRecords[params.appScopeKey] = appRecord 55 } 56 } 57 } 58 invalidateAppnull59 override fun invalidateApp(appScopeKey: String): Boolean { 60 appScopeKeysToAppRecords.remove(appScopeKey) 61 HeadlessAppLoaderNotifier.notifyAppLoaded(appScopeKey) 62 return false 63 } 64 createAppLoadernull65 private fun createAppLoader(context: Context): AppLoaderInterface { 66 // for now only react-native apps in Expo are supported 67 if (appLoader == null) { 68 appLoader = InternalHeadlessAppLoader(context) 69 } 70 return appLoader!! 71 } 72 isRunningnull73 override fun isRunning(appScopeKey: String): Boolean { 74 return appScopeKeysToAppRecords.containsKey(appScopeKey) 75 } 76 77 companion object { 78 private const val TAG = "TaskManagerInternalAppL" 79 80 private var appLoader: AppLoaderInterface? = null 81 } 82 } 83