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