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