Skip to content

Commit

Permalink
Sends SMS directly on Android
Browse files Browse the repository at this point in the history
Addresses issue fluttercommunity#57
  • Loading branch information
Dan Silk committed Feb 14, 2022
1 parent f68c750 commit d4b2579
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"version": "0.2.0",
"configurations": [
{
"name": "Flutter",
"name": "Example App",
"request": "launch",
"type": "dart",
"program": "example/lib/main.dart"
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,19 @@ List<String> recipents = ["1234567890", "5556787676"];
_sendSMS(message, recipents);
```

On Android, you can skip the additional dialog with the sendDirect parameter.

``` dart
String message = "This is a test message!";
List<String> recipents = ["1234567890", "5556787676"];
String _result = await sendSMS(message: message, recipients: recipents, sendDirect: true)
.catchError((onError) {
print(onError);
});
print(_result);
```

## Screenshots

iOS SMS | Android MMS
Expand Down
53 changes: 43 additions & 10 deletions android/src/main/kotlin/com/example/flutter_sms/FlutterSmsPlugin.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
package com.example.flutter_sms

import android.annotation.TargetApi
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 android.app.Activity
import android.net.Uri
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
Expand Down Expand Up @@ -76,9 +80,10 @@ class FlutterSmsPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
"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")
sendSMS(result, recipients, message!!)
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()
Expand All @@ -95,7 +100,35 @@ class FlutterSmsPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
return !(activityInfo == null || !activityInfo.exported)
}

private fun sendSMS(result: Result, phones: String?, message: String?) {
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) {
val intent = Intent(Intent.ACTION_SENDTO)
intent.data = Uri.parse("smsto:$phones")
intent.putExtra("sms_body", message)
Expand Down
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/rodydavis/Developer/GitHub/plugins/packages/flutter_sms/","dependencies":["url_launcher"]},{"name":"url_launcher","path":"/usr/local/Caskroom/flutter/1.2.1/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.12/","dependencies":[]}],"android":[{"name":"flutter_sms","path":"/Users/rodydavis/Developer/GitHub/plugins/packages/flutter_sms/","dependencies":["url_launcher"]},{"name":"url_launcher","path":"/usr/local/Caskroom/flutter/1.2.1/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.12/","dependencies":[]}],"macos":[{"name":"url_launcher_macos","path":"/usr/local/Caskroom/flutter/1.2.1/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-2.0.2/","dependencies":[]}],"linux":[{"name":"url_launcher_linux","path":"/usr/local/Caskroom/flutter/1.2.1/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-2.0.2/","dependencies":[]}],"windows":[{"name":"url_launcher_windows","path":"/usr/local/Caskroom/flutter/1.2.1/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-2.0.2/","dependencies":[]}],"web":[{"name":"flutter_sms","path":"/Users/rodydavis/Developer/GitHub/plugins/packages/flutter_sms/","dependencies":[]},{"name":"url_launcher_web","path":"/usr/local/Caskroom/flutter/1.2.1/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_web-2.0.4/","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_sms","dependencies":["url_launcher"]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-09-27 10:56:42.626795","version":"2.5.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"}
6 changes: 5 additions & 1 deletion example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->

<uses-permission android:name="android.permission.SEND_SMS"/>

<application
android:name="io.flutter.app.FlutterApplication"
android:name="${applicationName}"
android:label="example"
android:icon="@mipmap/ic_launcher">
<activity
Expand All @@ -15,6 +18,7 @@
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:exported="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
Expand Down
2 changes: 1 addition & 1 deletion example/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.6.10'
repositories {
google()
jcenter()
Expand Down
21 changes: 17 additions & 4 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class _MyAppState extends State<MyApp> {
String? _message, body;
String _canSendSMSMessage = 'Check is not run.';
List<String> people = [];
bool sendDirect = false;

@override
void initState() {
Expand All @@ -30,7 +31,10 @@ class _MyAppState extends State<MyApp> {
Future<void> _sendSMS(List<String> recipients) async {
try {
String _result = await sendSMS(
message: _controllerMessage.text, recipients: recipients);
message: _controllerMessage.text,
recipients: recipients,
sendDirect: sendDirect,
);
setState(() => _message = _result);
} catch (error) {
setState(() => _message = error.toString());
Expand Down Expand Up @@ -89,7 +93,7 @@ class _MyAppState extends State<MyApp> {
),
body: ListView(
children: <Widget>[
if (people == null || people.isEmpty)
if (people.isEmpty)
const SizedBox(height: 0)
else
SizedBox(
Expand Down Expand Up @@ -144,12 +148,21 @@ class _MyAppState extends State<MyApp> {
},
),
),
SwitchListTile(
title: const Text('Send Direct'),
subtitle: const Text('Should we skip the additional dialog?'),
value: sendDirect,
onChanged: (bool newValue) {
setState(() {
sendDirect = newValue;
});
}),
Padding(
padding: const EdgeInsets.all(8),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith(
(states) => Theme.of(context).accentColor),
(states) => Theme.of(context).colorScheme.secondary),
padding: MaterialStateProperty.resolveWith(
(states) => const EdgeInsets.symmetric(vertical: 16)),
),
Expand All @@ -158,7 +171,7 @@ class _MyAppState extends State<MyApp> {
},
child: Text(
'SEND',
style: Theme.of(context).accentTextTheme.button,
style: Theme.of(context).textTheme.displayMedium,
),
),
),
Expand Down
10 changes: 6 additions & 4 deletions lib/flutter_sms.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import 'dart:async';

import 'package:flutter/foundation.dart';

import 'src/flutter_sms_platform.dart';

/// Open SMS Dialog on iOS/Android/Web
Future<String> sendSMS({
required String message,
required List<String> recipients,
required bool sendDirect,
}) =>
FlutterSmsPlatform.instance
.sendSMS(message: message, recipients: recipients);
FlutterSmsPlatform.instance.sendSMS(
message: message,
recipients: recipients,
sendDirect: sendDirect,
);

/// Launch SMS Url Scheme on all platforms
Future<bool> launchSms({
Expand Down
1 change: 1 addition & 0 deletions lib/flutter_sms_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class FlutterSmsPlugin extends FlutterSmsPlatform {
Future<String> sendSMS({
required String message,
required List<String> recipients,
bool sendDirect = false,
}) async {
bool _messageSent =
await FlutterSmsPlatform.instance.launchSmsMulti(recipients, message);
Expand Down
4 changes: 4 additions & 0 deletions lib/src/flutter_sms_platform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ class FlutterSmsPlatform extends PlatformInterface {
_instance = instance;
}

///
///
Future<String> sendSMS({
required String message,
required List<String> recipients,
required bool sendDirect,
}) {
final mapData = <dynamic, dynamic>{};
mapData['message'] = message;
Expand All @@ -46,6 +49,7 @@ class FlutterSmsPlatform extends PlatformInterface {
} else {
String _phones = recipients.join(';');
mapData['recipients'] = _phones;
mapData['sendDirect'] = sendDirect;
return _channel
.invokeMethod<String>('sendSMS', mapData)
.then((value) => value ?? 'Error sending sms');
Expand Down

0 comments on commit d4b2579

Please sign in to comment.