Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 0 additions & 44 deletions android/build.gradle

This file was deleted.

32 changes: 32 additions & 0 deletions android/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android") version "2.1.0"
}

group = "com.example.flutter_sms"
version = "1.0-SNAPSHOT"

android {
namespace = "com.example.flutter_sms"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
defaultConfig {
minSdk = flutter.minSdkVersion
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

sourceSets["main"].java.srcDirs("src/main/kotlin")

lint {
disable.add("InvalidPackage")
}
}

repositories {
google()
mavenCentral() // jcenter is deprecated; use mavenCentral
}

dependencies {
implementation("androidx.core:core-ktx:1.16.0")
}
2 changes: 1 addition & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
1 change: 0 additions & 1 deletion android/settings.gradle

This file was deleted.

1 change: 1 addition & 0 deletions android/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "flutter_sms"
5 changes: 2 additions & 3 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.flutter_sms">
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
174 changes: 133 additions & 41 deletions android/src/main/kotlin/com/example/flutter_sms/FlutterSmsPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.telephony.SmsManager
import android.telephony.TelephonyManager
import android.util.Log
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
Expand All @@ -18,7 +19,7 @@ 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
// import io.flutter.plugin.common.PluginRegistry.Registrar


class FlutterSmsPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
Expand Down Expand Up @@ -59,16 +60,6 @@ class FlutterSmsPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
mChannel.setMethodCallHandler(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) {
Expand All @@ -92,12 +83,33 @@ class FlutterSmsPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {

@TargetApi(Build.VERSION_CODES.ECLAIR)
private fun canSendSMS(): Boolean {
if (!activity!!.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY))
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)
return try {
// Check if we can create an SMS intent
val intent = Intent(Intent.ACTION_SENDTO)
intent.data = Uri.parse("smsto:")
val activityInfo = intent.resolveActivityInfo(activity!!.packageManager, intent.flags.toInt())

// If we can resolve the SMS intent, we can send SMS
if (activityInfo != null && activityInfo.exported) {
return true
}

// Alternative check: try to get SmsManager
try {
val smsManager = SmsManager.getDefault()
// If we can get SmsManager without exception, we can likely send SMS
return true
} catch (e: SecurityException) {
Log.w("FlutterSms", "SecurityException when checking SmsManager: ${e.message}")
return false
} catch (e: Exception) {
Log.w("FlutterSms", "Exception when checking SmsManager: ${e.message}")
return false
}
} catch (e: Exception) {
Log.e("FlutterSms", "Error checking SMS capability: ${e.message}")
false
}
}

private fun sendSMS(result: Result, phones: String, message: String, sendDirect: Boolean) {
Expand All @@ -109,31 +121,111 @@ class FlutterSmsPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
}
}

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)
}
// private fun sendSMSDirect(result: Result, phones: String, message: String) {
// try {
// // 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!")
// } catch (e: SecurityException) {
// result.error("permission_denied", "SMS permission denied: ${e.message}", null)
// } catch (e: Exception) {
// result.error("send_failed", "Failed to send SMS: ${e.message}", null)
// }
// }

private fun sendSMSDirect(result: Result, phones: String, message: String) {
try {
// Create PendingIntent with FLAG_IMMUTABLE for Android 12+ compatibility
val sentIntent = PendingIntent.getBroadcast(
activity,
0,
Intent("SMS_SENT_ACTION"),
PendingIntent.FLAG_IMMUTABLE
)

val smsManager = SmsManager.getDefault()
val numbers = phones.split(";")

for (num in numbers) {
val trimmedNum = num.trim()
if (trimmedNum.isEmpty()) continue

Log.d("Flutter SMS", "Message length (bytes): ${message.toByteArray().size}")

// Use more accurate SMS length calculation (160 chars for GSM 7-bit)
if (message.length > 160 || message.toByteArray().size > 160) {
val partMessages = smsManager.divideMessage(message)

// Create sent and delivery intents for multipart messages
val sentIntents = arrayListOf<PendingIntent>()
val deliveryIntents = arrayListOf<PendingIntent>()

repeat(partMessages.size) { index ->
sentIntents.add(
PendingIntent.getBroadcast(
activity,
index,
Intent("SMS_SENT_ACTION"),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
)
}

smsManager.sendMultipartTextMessage(
trimmedNum,
null,
partMessages,
sentIntents,
deliveryIntents.takeIf { it.isNotEmpty() }
)
} else {
smsManager.sendTextMessage(
trimmedNum,
null,
message,
sentIntent,
null
)
}
}

result.success("SMS Sent!")

} catch (e: SecurityException) {
Log.e("Flutter SMS", "Security exception: ${e.message}")
result.error("permission_denied", "SMS permission denied: ${e.message}", null)
} catch (e: IllegalArgumentException) {
Log.e("Flutter SMS", "Invalid argument: ${e.message}")
result.error("invalid_argument", "Invalid SMS parameters: ${e.message}", null)
} catch (e: Exception) {
Log.e("Flutter SMS", "SMS send failed: ${e.message}")
result.error("send_failed", "Failed to send SMS: ${e.message}", null)
}

result.success("SMS Sent!")
}
}

private fun sendSMSDialog(result: Result, phones: String, message: 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!")
try {
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!")
} catch (e: Exception) {
result.error("intent_failed", "Failed to open SMS app: ${e.message}", null)
}
}
}
}
2 changes: 1 addition & 1 deletion example/.flutter-plugins-dependencies
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_sms","path":"/Users/dsilk/Development/flutter/packages/flutter_sms/","dependencies":[]},{"name":"url_launcher_ios","path":"/Users/dsilk/.pub-cache/hosted/pub.dartlang.org/url_launcher_ios-6.0.15/","dependencies":[]}],"android":[{"name":"flutter_sms","path":"/Users/dsilk/Development/flutter/packages/flutter_sms/","dependencies":[]},{"name":"url_launcher_android","path":"/Users/dsilk/.pub-cache/hosted/pub.dartlang.org/url_launcher_android-6.0.15/","dependencies":[]}],"macos":[{"name":"url_launcher_macos","path":"/Users/dsilk/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-3.0.0/","dependencies":[]}],"linux":[{"name":"url_launcher_linux","path":"/Users/dsilk/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-3.0.0/","dependencies":[]}],"windows":[{"name":"url_launcher_windows","path":"/Users/dsilk/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-3.0.0/","dependencies":[]}],"web":[{"name":"flutter_sms","path":"/Users/dsilk/Development/flutter/packages/flutter_sms/","dependencies":[]},{"name":"url_launcher_web","path":"/Users/dsilk/.pub-cache/hosted/pub.dartlang.org/url_launcher_web-2.0.8/","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_sms","dependencies":["url_launcher"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2022-02-14 16:50:47.982162","version":"2.10.1"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_sms","path":"/Users/hafeezrana/Documents/flutter_projects/Open Source Plugins/flutter_sms/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"permission_handler_apple","path":"/Users/hafeezrana/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_ios","path":"/Users/hafeezrana/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.3/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_sms","path":"/Users/hafeezrana/Documents/flutter_projects/Open Source Plugins/flutter_sms/","native_build":true,"dependencies":[],"dev_dependency":true},{"name":"permission_handler_android","path":"/Users/hafeezrana/.pub-cache/hosted/pub.dev/permission_handler_android-13.0.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_android","path":"/Users/hafeezrana/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.16/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"url_launcher_macos","path":"/Users/hafeezrana/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.2/","native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"url_launcher_linux","path":"/Users/hafeezrana/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"permission_handler_windows","path":"/Users/hafeezrana/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_windows","path":"/Users/hafeezrana/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.4/","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"flutter_sms","path":"/Users/hafeezrana/Documents/flutter_projects/Open Source Plugins/flutter_sms/","dependencies":[],"dev_dependency":true},{"name":"permission_handler_html","path":"/Users/hafeezrana/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.3+5/","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"/Users/hafeezrana/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_sms","dependencies":["url_launcher"]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_html","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_html","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2025-05-30 22:24:13.151547","version":"3.32.0","swift_package_manager_enabled":{"ios":false,"macos":false}}
24 changes: 22 additions & 2 deletions example/.metadata
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,27 @@
# This file should be version controlled and should not be manually edited.

version:
revision: a817b4e6a4e80deefb88373d2a548b97c90d19c4
channel: master
revision: "be698c48a6750c8cb8e61c740ca9991bb947aba2"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2
base_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2
- platform: ios
create_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2
base_revision: be698c48a6750c8cb8e61c740ca9991bb947aba2

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
28 changes: 28 additions & 0 deletions example/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
7 changes: 7 additions & 0 deletions example/android/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,10 @@ gradle-wrapper.jar
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/

# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks
Loading