1 package expo.modules.mailcomposer 2 3 import android.content.Intent 4 import android.content.pm.LabeledIntent 5 import android.net.Uri 6 import android.os.Bundle 7 import expo.modules.kotlin.Promise 8 import expo.modules.kotlin.exception.Exceptions 9 import expo.modules.kotlin.modules.Module 10 import expo.modules.kotlin.modules.ModuleDefinition 11 12 class MailComposerModule : Module() { 13 private val context 14 get() = appContext.reactContext ?: throw Exceptions.ReactContextLost() 15 private val currentActivity 16 get() = appContext.currentActivity ?: throw Exceptions.MissingActivity() 17 private var composerOpened = false 18 private var pendingPromise: Promise? = null 19 20 override fun definition() = ModuleDefinition { 21 Name("ExpoMailComposer") 22 23 AsyncFunction("isAvailableAsync") { 24 return@AsyncFunction true 25 } 26 27 AsyncFunction("composeAsync") { options: MailComposerOptions, promise: Promise -> 28 val intent = Intent(Intent.ACTION_SENDTO).apply { data = Uri.parse("mailto:") } 29 val application = currentActivity.application 30 val resolveInfo = context.packageManager.queryIntentActivities(intent, 0) 31 32 val mailIntents = resolveInfo.map { info -> 33 val mailIntentBuilder = MailIntentBuilder(options) 34 .setComponentName(info.activityInfo.packageName, info.activityInfo.name) 35 .putRecipients(Intent.EXTRA_EMAIL) 36 .putCcRecipients(Intent.EXTRA_CC) 37 .putBccRecipients(Intent.EXTRA_BCC) 38 .putSubject(Intent.EXTRA_SUBJECT) 39 .putBody(Intent.EXTRA_TEXT, options.isHtml ?: false) 40 .putAttachments( 41 Intent.EXTRA_STREAM, 42 application 43 ) 44 45 LabeledIntent( 46 mailIntentBuilder.build(), 47 info.activityInfo.packageName, 48 info.loadLabel(context.packageManager), 49 info.icon 50 ) 51 }.toMutableList() 52 53 val chooser = Intent.createChooser( 54 mailIntents.removeAt(mailIntents.size - 1), 55 null 56 ).apply { 57 putExtra(Intent.EXTRA_INITIAL_INTENTS, mailIntents.toTypedArray()) 58 addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 59 } 60 61 pendingPromise = promise 62 currentActivity.startActivityForResult(chooser, REQUEST_CODE) 63 composerOpened = true 64 } 65 66 OnActivityResult { _, payload -> 67 if (payload.requestCode == REQUEST_CODE && pendingPromise != null) { 68 val promise = pendingPromise ?: return@OnActivityResult 69 if (composerOpened) { 70 composerOpened = false 71 promise.resolve(Bundle().apply { putString("status", "sent") }) 72 } 73 } 74 } 75 } 76 77 companion object { 78 private const val REQUEST_CODE = 8675 79 } 80 } 81