Skip to content

Commit

Permalink
Merge pull request #6 from optimalstrategy/5-background-forwarding
Browse files Browse the repository at this point in the history
Implement background sms forwarding
  • Loading branch information
optimalstrategy authored Mar 9, 2021
2 parents a9da2a1 + 2481e2d commit 019f636
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 120 deletions.
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion 28
compileSdkVersion 29

lintOptions {
disable 'InvalidPackage'
Expand All @@ -27,7 +27,7 @@ android {
defaultConfig {
applicationId "team.whatever.sms_forwarder_app"
minSdkVersion 21
targetSdkVersion 28
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Expand Down
7 changes: 7 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,12 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

<receiver android:name="com.shounakmulay.telephony.sms.IncomingSmsReceiver"
android:permission="android.permission.BROADCAST_SMS" android:exported="true">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
</application>
</manifest>
6 changes: 2 additions & 4 deletions ios/Flutter/flutter_export_environment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ export "FLUTTER_APPLICATION_PATH=/home/george/projects/sms_forwarder_app"
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "OTHER_LDFLAGS=$(inherited) -framework Flutter"
export "FLUTTER_FRAMEWORK_DIR=/opt/flutter/bin/cache/artifacts/engine/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "FLUTTER_BUILD_NAME=1.4.0"
export "FLUTTER_BUILD_NUMBER=1.4.0"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=false"
export "TREE_SHAKE_ICONS=false"
Expand Down
67 changes: 67 additions & 0 deletions lib/background_forwarder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'package:telephony/telephony.dart';

import 'forwarding.dart';
import 'manager.dart';

/// A wrapper for [ForwarderManager] that registers a background message handler.
/// Resets the background forwarder every time a field is updated.
class BackgroundForwarder {
static ForwarderManager _backgroundManager;
final ForwarderManager mgr = new ForwarderManager();

BackgroundForwarder(Telephony telephony) {
telephony.listenIncomingSms(
onNewMessage: (msg) async => await mgr.forward(msg),
onBackgroundMessage: onBackgroundMessage
);
}

static void onBackgroundMessage(SmsMessage msg) async {
if (_backgroundManager == null) {
_backgroundManager = new ForwarderManager();
await _backgroundManager.loadFromPrefs();
}
await _backgroundManager.forward(msg);
}

HttpCallbackForwarder get httpCallbackForwarder => mgr.httpCallbackForwarder;

TelegramBotForwarder get telegramBotForwarder => mgr.telegramBotForwarder;

DeployedTelegramBotForwarder get deployedTelegramBotForwarder =>
mgr.deployedTelegramBotForwarder;

set httpCallbackForwarder(HttpCallbackForwarder fwd) {
mgr.httpCallbackForwarder = fwd;
invalidateBackgroundManager();
}

set telegramBotForwarder(TelegramBotForwarder fwd) {
mgr.telegramBotForwarder = fwd;
invalidateBackgroundManager();
}

set deployedTelegramBotForwarder(DeployedTelegramBotForwarder fwd) {
mgr.deployedTelegramBotForwarder = fwd;
invalidateBackgroundManager();
}

/// Loads the forwarders from a json.
Future<Map> loadFromPrefs() async {
var result = await mgr.loadFromPrefs();
return result;
}

/// Dumps the forwarders to shared preferences.
void dumpToPrefs() async => mgr.dumpToPrefs();

/// Returns the mapping (forwarder name -> not null)
Map reportReadiness() {
return mgr.reportReadiness();
}

/// Sets the background manager to `null`.
static void invalidateBackgroundManager() {
_backgroundManager = null;
}
}
54 changes: 37 additions & 17 deletions lib/forwarding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,28 @@ import 'dart:core';
import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:sms_maintained/sms.dart';
import 'package:telephony/telephony.dart';
import 'package:flutter/foundation.dart';

extension ToMap on SmsMessage {
Map get toMap {
return {
"id": this.id,
"address": this.address,
"body": this.body,
"date": this.date,
"dateSent": this.dateSent,
"read": this.read,
"seen": this.seen,
"subject": this.subject,
"subscriptionId": this.subscriptionId,
"threadId": this.threadId,
"type": this.type,
"status": this.status,
};
}
}

/// Defines the forwarder interface.
abstract class AbstractForwarder {
/// Should forward the given sms.
Expand Down Expand Up @@ -60,9 +79,9 @@ abstract class HttpForwarder implements AbstractForwarder {
static String mapToJson(Map map) => json.encode(_castMap(map));

/// Casts all keys and values in the map to String. Removes the entry with
/// the key thread_id.
/// the key threadId.
static Map<String, String> _castMap(Map map) {
map.remove('thread_id');
map.remove('threadId');
return Map.from(map.map(
// Cast each field to string
(k, v) => MapEntry(k.toString(), v.toString())));
Expand Down Expand Up @@ -176,20 +195,20 @@ class HttpCallbackForwarder extends AbstractForwarder with HttpForwarder {
switch (method) {
case HttpMethod.GET:
// Convert the sms to JSON and merge it with the uri payload
var smsData = sms.toMap;
final smsData = sms.toMap;
smsData.addAll(uriPayload);
// Then URI encode the map and perform the request
String uriParams = HttpForwarder.mapToUri(smsData);
final uriParams = HttpForwarder.mapToUri(smsData);
return http.get("$_callbackUrl$uriParams");

case HttpMethod.POST:
case HttpMethod.PUT:
// Convert the sms to json and merge it with the json payload
var payload = sms.toMap;
final payload = sms.toMap;
payload.addAll(jsonPayload);
// URI encode the uri payload and append it to the url
var uriParams = HttpForwarder.mapToUri(uriPayload);
var url = "$_callbackUrl$uriParams";
final uriParams = HttpForwarder.mapToUri(uriPayload);
final url = "$_callbackUrl$uriParams";
// Perform the request using the required method
return method == HttpMethod.POST
? http.post(url, body: HttpForwarder.mapToJson(payload))
Expand Down Expand Up @@ -240,13 +259,14 @@ class TelegramBotForwarder extends AbstractForwarder with HttpForwarder {
/// Sends the SMS data to the user with [_chatId].
@override
Future<http.Response> send(SmsMessage sms) {
final date = DateTime.fromMillisecondsSinceEpoch(sms.date);
// Encode message
String uriParams = HttpForwarder.mapToUri({
"chat_id": _chatId,
"text": "New SMS message from ${sms.address}:\n${sms.body}\n\n"
"Date: ${sms.date}."
"Date: $date."
});
String url = this.method("sendMessage");
final url = this.method("sendMessage");
return http.post("$url$uriParams");
}

Expand Down Expand Up @@ -305,34 +325,34 @@ class DeployedTelegramBotForwarder extends HttpCallbackForwarder {

/// Checks if the user with [_tgHandle] exists on the server.
Future<bool> checkSetupURL() async {
var params = {"username": _tgHandle, "code": _tgCode};
var r =
final params = {"username": _tgHandle, "code": _tgCode};
final r =
await http.get("$_baseUrl/check_user${HttpForwarder.mapToUri(params)}");
_isSetUp = r?.statusCode == 200;
return Future<bool>(() => isSetUp);
}

/// Sends the SMS data to the server via a POST request.
Future<http.Response> send(SmsMessage sms) {
var map = sms.toMap;
map['date'] =
sms.date.toString(); // the date field is in milliseconds by default
final map = sms.toMap;
final date = DateTime.fromMillisecondsSinceEpoch(sms.date);
map['date'] = date.toString();
String payload = HttpForwarder.mapToJson(map);
String url = "$_callbackUrl?code=$_tgCode&username=$_tgHandle";
return http.post(url, body: payload);
}

/// Generates a random 8-character code.
String _genCode() {
var rand = Random();
final rand = Random();
return String.fromCharCodes(
new List.generate(8, (_) => rand.nextInt(26) + 65));
}

/// Dumps the forwarder's settings to json.
@override
String toJson() {
var fields = {
final fields = {
"tgCode": _tgCode,
"baseUrl": _baseUrl,
"tgHandle": _tgHandle,
Expand Down
1 change: 0 additions & 1 deletion lib/KeyValueSettings.dart → lib/key_value_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ class _KeyValuePairWidgetState extends State<_KeyValuePairWidget> {

@override
Widget build(BuildContext context) {
var pairs = _parentState.pairs;
return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Container(
width: 125,
Expand Down
Loading

0 comments on commit 019f636

Please sign in to comment.