1 package abi47_0_0.expo.modules.intentlauncher
2 
3 import abi47_0_0.expo.modules.core.Promise
4 import abi47_0_0.expo.modules.core.ExportedModule
5 import abi47_0_0.expo.modules.core.ModuleRegistry
6 import abi47_0_0.expo.modules.core.interfaces.ExpoMethod
7 import abi47_0_0.expo.modules.core.ModuleRegistryDelegate
8 import abi47_0_0.expo.modules.core.arguments.ReadableArguments
9 import abi47_0_0.expo.modules.core.interfaces.ActivityProvider
10 import abi47_0_0.expo.modules.core.interfaces.services.UIManager
11 import abi47_0_0.expo.modules.core.interfaces.ActivityEventListener
12 import abi47_0_0.expo.modules.core.errors.CurrentActivityNotFoundException
13 import abi47_0_0.expo.modules.intentlauncher.exceptions.ActivityAlreadyStartedException
14 
15 import android.net.Uri
16 import android.os.Bundle
17 import android.app.Activity
18 import android.content.Intent
19 import android.content.ComponentName
20 import android.content.ActivityNotFoundException
21 import android.content.Context
22 
23 private const val NAME = "ExpoIntentLauncher"
24 
25 private const val REQUEST_CODE = 12
26 private const val ATTR_ACTION = "action"
27 private const val ATTR_TYPE = "type"
28 private const val ATTR_CATEGORY = "category"
29 private const val ATTR_EXTRA = "extra"
30 private const val ATTR_DATA = "data"
31 private const val ATTR_FLAGS = "flags"
32 private const val ATTR_PACKAGE_NAME = "packageName"
33 private const val ATTR_CLASS_NAME = "className"
34 
35 class IntentLauncherModule(
36   context: Context,
37   private val moduleRegistryDelegate: ModuleRegistryDelegate = ModuleRegistryDelegate()
38 ) : ExportedModule(context), ActivityEventListener {
39   private var pendingPromise: Promise? = null
40   private val uiManager: UIManager by moduleRegistry()
41   private val activityProvider: ActivityProvider by moduleRegistry()
42 
moduleRegistrynull43   private inline fun <reified T> moduleRegistry() = moduleRegistryDelegate.getFromModuleRegistry<T>()
44 
45   override fun getName() = NAME
46 
47   override fun onCreate(moduleRegistry: ModuleRegistry) {
48     moduleRegistryDelegate.onCreate(moduleRegistry)
49   }
50 
51   @ExpoMethod
startActivitynull52   fun startActivity(activityAction: String, params: ReadableArguments, promise: Promise) {
53     if (pendingPromise != null) {
54       promise.reject(ActivityAlreadyStartedException())
55       return
56     }
57 
58     val activity = activityProvider.currentActivity
59     if (activity == null) {
60       promise.reject(CurrentActivityNotFoundException())
61       return
62     }
63 
64     val intent = Intent(activityAction)
65 
66     if (params.containsKey(ATTR_CLASS_NAME)) {
67       intent.component =
68         if (params.containsKey(ATTR_PACKAGE_NAME)) ComponentName(params.getString(ATTR_PACKAGE_NAME), params.getString(ATTR_CLASS_NAME))
69         else ComponentName(context, params.getString(ATTR_CLASS_NAME))
70     }
71 
72     // `setData` and `setType` are exclusive, so we need to use `setDateAndType` in that case.
73     if (params.containsKey(ATTR_DATA) && params.containsKey(ATTR_TYPE)) {
74       intent.setDataAndType(Uri.parse(params.getString(ATTR_DATA)), params.getString(ATTR_TYPE))
75     } else {
76       if (params.containsKey(ATTR_DATA)) {
77         intent.data = Uri.parse(params.getString(ATTR_DATA))
78       } else if (params.containsKey(ATTR_TYPE)) {
79         intent.type = params.getString(ATTR_TYPE)
80       }
81     }
82 
83     params.getArguments(ATTR_EXTRA)?.let { intent.putExtras(it.toBundle()) }
84     params.getInt(ATTR_FLAGS)?.let { intent.addFlags(it) }
85     params.getString(ATTR_CATEGORY)?.let { intent.addCategory(it) }
86 
87     uiManager.registerActivityEventListener(this)
88     pendingPromise = promise
89 
90     try {
91       activity.startActivityForResult(intent, REQUEST_CODE)
92     } catch (e: ActivityNotFoundException) {
93       promise.reject(e)
94       pendingPromise = null
95     }
96   }
97 
98   //region ActivityEventListener
99 
onActivityResultnull100   override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, intent: Intent?) {
101     if (requestCode != REQUEST_CODE) return
102 
103     val response = Bundle().apply {
104       putInt("resultCode", resultCode)
105       if (intent != null) {
106         intent.data?.let { putString(ATTR_DATA, it.toString()) }
107         intent.extras?.let { putBundle(ATTR_EXTRA, it) }
108       }
109     }
110 
111     pendingPromise?.resolve(response)
112     pendingPromise = null
113 
114     uiManager.unregisterActivityEventListener(this)
115   }
116 
onNewIntentnull117   override fun onNewIntent(intent: Intent) = Unit
118 
119   //endregion
120 }
121