1 package expo.modules.mailcomposer
2 
3 import android.content.Context
4 import android.content.Intent
5 import android.content.pm.LabeledIntent
6 import android.net.Uri
7 import android.os.Bundle
8 import expo.modules.core.ExportedModule
9 import expo.modules.core.ModuleRegistry
10 import expo.modules.core.ModuleRegistryDelegate
11 import expo.modules.core.Promise
12 import expo.modules.core.arguments.ReadableArguments
13 import expo.modules.core.interfaces.ActivityProvider
14 import expo.modules.core.interfaces.ExpoMethod
15 import expo.modules.core.interfaces.LifecycleEventListener
16 
17 class MailComposerModule(
18   context: Context,
19   private val moduleRegistryDelegate: ModuleRegistryDelegate = ModuleRegistryDelegate()
20 ) : ExportedModule(context), LifecycleEventListener {
21   private var composerOpened = false
22   private var pendingPromise: Promise? = null
23   override fun getName() = "ExpoMailComposer"
24   private val activityProvider: ActivityProvider by moduleRegistry()
25 
26   private inline fun <reified T> moduleRegistry() = moduleRegistryDelegate.getFromModuleRegistry<T>()
27 
28   override fun onCreate(moduleRegistry: ModuleRegistry) {
29     moduleRegistryDelegate.onCreate(moduleRegistry)
30   }
31 
32   @ExpoMethod
33   fun isAvailableAsync(promise: Promise) {
34     promise.resolve(true)
35   }
36 
37   @ExpoMethod
38   fun composeAsync(options: ReadableArguments, promise: Promise) {
39     val intent = Intent(Intent.ACTION_SENDTO).apply { data = Uri.parse("mailto:") }
40     val application = activityProvider.currentActivity.application
41     val resolveInfo = context.packageManager.queryIntentActivities(intent, 0)
42     val mailIntents = resolveInfo.map { info ->
43       val isHtml = options.containsKey("isHtml") && options.getBoolean("isHtml")
44       val mailIntentBuilder = MailIntentBuilder(options)
45         .setComponentName(info.activityInfo.packageName, info.activityInfo.name)
46         .putExtraIfKeyExists("recipients", Intent.EXTRA_EMAIL)
47         .putExtraIfKeyExists("ccRecipients", Intent.EXTRA_CC)
48         .putExtraIfKeyExists("bccRecipients", Intent.EXTRA_BCC)
49         .putExtraIfKeyExists("subject", Intent.EXTRA_SUBJECT)
50         .putExtraIfKeyExists("body", Intent.EXTRA_TEXT, isHtml)
51         .putParcelableArrayListExtraIfKeyExists(
52           "attachments",
53           Intent.EXTRA_STREAM,
54           application
55         )
56       LabeledIntent(
57         mailIntentBuilder.build(),
58         info.activityInfo.packageName,
59         info.loadLabel(context.packageManager),
60         info.icon
61       )
62     }.toMutableList()
63     val chooser = Intent.createChooser(
64       mailIntents.removeAt(mailIntents.size - 1),
65       null
66     ).apply {
67       putExtra(Intent.EXTRA_INITIAL_INTENTS, mailIntents.toTypedArray())
68       addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
69       addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
70     }
71     pendingPromise = promise
72     context.startActivity(chooser)
73     composerOpened = true
74   }
75 
76   override fun onHostResume() {
77     val promise = pendingPromise ?: return
78     if (composerOpened) {
79       composerOpened = false
80       promise.resolve(Bundle().apply { putString("status", "sent") })
81     }
82   }
83 
84   override fun onHostPause() = Unit
85 
86   override fun onHostDestroy() {
87     // do nothing
88   }
89 }
90