update flutter_sms to the latest version

This commit is contained in:
2026-01-14 00:23:07 -08:00
parent 33258c580c
commit 6250a11afd
38 changed files with 1192 additions and 480 deletions
@@ -25,6 +25,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
namespace "com.example.flutter_sms"
compileSdkVersion 34
sourceSets {
@@ -37,6 +38,13 @@ android {
lintOptions {
disable 'InvalidPackage'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
}
dependencies {
@@ -2,28 +2,21 @@ package com.example.flutter_sms
import android.annotation.TargetApi
import android.app.Activity
import android.app.PendingIntent
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.telephony.SmsManager
import android.util.Log
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
class FlutterSmsPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
private lateinit var mChannel: MethodChannel
private var activity: Activity? = null
class FlutterSmsPlugin: FlutterPlugin, SmsHostApi, ActivityAware {
var activity: Activity? = null
private val REQUEST_CODE_SEND_SMS = 205
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
@@ -43,97 +36,60 @@ class FlutterSmsPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
}
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
setupCallbackChannels(flutterPluginBinding.binaryMessenger)
Log.d("FlutterSmsPlugin", "onAttachedToEngine")
SmsHostApi.setUp(flutterPluginBinding.binaryMessenger, this)
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
teardown()
}
private fun setupCallbackChannels(messenger: BinaryMessenger) {
mChannel = MethodChannel(messenger, "flutter_sms")
mChannel.setMethodCallHandler(this)
}
private fun teardown() {
mChannel.setMethodCallHandler(null)
SmsHostApi.setUp(binding.binaryMessenger, null)
}
// V1 embedding entry point. This is deprecated and will be removed in a future Flutter
// release but we leave it here in case someone's app does not utilize the V2 embedding yet.
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val inst = FlutterSmsPlugin()
inst.activity = registrar.activity()
inst.setupCallbackChannels(registrar.messenger())
}
}
override fun onMethodCall(call: MethodCall, result: Result) {
when (call.method) {
"sendSMS" -> {
if (!canSendSMS()) {
result.error(
"device_not_capable",
"The current device is not capable of sending text messages.",
"A device may be unable to send messages if it does not support messaging or if it is not currently configured to send messages. This only applies to the ability to send text messages via iMessage, SMS, and MMS.")
return
}
val message = call.argument<String?>("message") ?: ""
val recipients = call.argument<String?>("recipients") ?: ""
val sendDirect = call.argument<Boolean?>("sendDirect") ?: false
sendSMS(result, recipients, message!!, sendDirect)
}
"canSendSMS" -> result.success(canSendSMS())
else -> result.notImplemented()
override fun sendSms(message: String, recipients: List<String>, callback: (Result<String>) -> Unit) {
if (!checkCanSendSms()) {
callback(Result.failure(FlutterError("device_not_capable", "The current device is not capable of sending text messages.", "A device may be unable to send messages if it does not support messaging or if it is not currently configured to send messages. This only applies to the ability to send text messages via iMessage, SMS, and MMS.")))
return
}
val phones = recipients.joinToString(";")
callback(Result.success(sendSMSDialog(phones, message)))
}
@TargetApi(Build.VERSION_CODES.ECLAIR)
private fun canSendSMS(): Boolean {
if (!activity!!.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY))
return false
override fun canSendSms(callback: (Result<Boolean>) -> Unit) {
callback(Result.success(checkCanSendSms()))
}
@TargetApi(Build.VERSION_CODES.ECLAIR)
private fun checkCanSendSms(): Boolean {
if (activity == null) {
Log.d("FlutterSmsPlugin", "Activity is null")
return false
}
if (!activity!!.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d("FlutterSmsPlugin", "No TELEPHONY feature")
return false
}
val intent = Intent(Intent.ACTION_SENDTO)
intent.data = Uri.parse("smsto:")
val activityInfo = intent.resolveActivityInfo(activity!!.packageManager, intent.flags.toInt())
return !(activityInfo == null || !activityInfo.exported)
intent.data = Uri.parse("smsto:123456")
val activityInfo = intent.resolveActivityInfo(activity!!.packageManager, 0)
if (activityInfo == null || !activityInfo.exported) {
Log.d("FlutterSmsPlugin", "No activity to handle smsto intent or not exported")
return false
}
return true
}
private fun sendSMS(result: Result, phones: String, message: String, sendDirect: Boolean) {
if (sendDirect) {
sendSMSDirect(result, phones, message);
}
else {
sendSMSDialog(result, phones, message);
}
}
private fun sendSMSDirect(result: Result, phones: String, message: String) {
// SmsManager is android.telephony
val sentIntent = PendingIntent.getBroadcast(activity, 0, Intent("SMS_SENT_ACTION"), PendingIntent.FLAG_IMMUTABLE)
val mSmsManager = SmsManager.getDefault()
val numbers = phones.split(";")
for (num in numbers) {
Log.d("Flutter SMS", "msg.length() : " + message.toByteArray().size)
if (message.toByteArray().size > 80) {
val partMessage = mSmsManager.divideMessage(message)
mSmsManager.sendMultipartTextMessage(num, null, partMessage, null, null)
} else {
mSmsManager.sendTextMessage(num, null, message, sentIntent, null)
}
}
result.success("SMS Sent!")
}
private fun sendSMSDialog(result: Result, phones: String, message: String) {
private fun sendSMSDialog(phones: String, message: String): String {
val intent = Intent(Intent.ACTION_SENDTO)
intent.data = Uri.parse("smsto:$phones")
intent.putExtra("sms_body", message)
intent.putExtra(Intent.EXTRA_TEXT, message)
activity?.startActivityForResult(intent, REQUEST_CODE_SEND_SMS)
result.success("SMS Sent!")
return "SMS Sent!"
}
}
@@ -0,0 +1,115 @@
// Autogenerated from Pigeon (v26.1.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
package com.example.flutter_sms
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMethodCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private object MessagesPigeonUtils {
fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
}
fun wrapError(exception: Throwable): List<Any?> {
return if (exception is FlutterError) {
listOf(
exception.code,
exception.message,
exception.details
)
} else {
listOf(
exception.javaClass.simpleName,
exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
)
}
}
}
/**
* Error class for passing custom error details to Flutter via a thrown PlatformException.
* @property code The error code.
* @property message The error message.
* @property details The error details. Must be a datatype supported by the api codec.
*/
class FlutterError (
val code: String,
override val message: String? = null,
val details: Any? = null
) : Throwable()
private open class MessagesPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return super.readValueOfType(type, buffer)
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
super.writeValue(stream, value)
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface SmsHostApi {
fun sendSms(message: String, recipients: List<String>, callback: (Result<String>) -> Unit)
fun canSendSms(callback: (Result<Boolean>) -> Unit)
companion object {
/** The codec used by SmsHostApi. */
val codec: MessageCodec<Any?> by lazy {
MessagesPigeonCodec()
}
/** Sets up an instance of `SmsHostApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: SmsHostApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_sms.SmsHostApi.sendSms$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val messageArg = args[0] as String
val recipientsArg = args[1] as List<String>
api.sendSms(messageArg, recipientsArg) { result: Result<String> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(MessagesPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(MessagesPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_sms.SmsHostApi.canSendSms$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
api.canSendSms{ result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(MessagesPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(MessagesPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}