Skip to content

Commit

Permalink
Merge branch 'refs/heads/implementations/sms-handler'
Browse files Browse the repository at this point in the history
  • Loading branch information
CremaLuca committed Nov 29, 2019
2 parents 871b2c0 + 25a7a3a commit 4800754
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 5 deletions.
21 changes: 20 additions & 1 deletion smslibrary/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,21 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.eis.smslibrary" />
package="com.eis.smslibrary">

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

<application>
<receiver
android:name=".SMSReceivedBroadcastReceiver"
android:enabled="true"
android:exported="true"

android:permission="android.permission.BROADCAST_SMS">
<intent-filter
android:priority="999">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
</application>

</manifest>
145 changes: 141 additions & 4 deletions smslibrary/src/main/java/com/eis/smslibrary/SMSHandler.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,150 @@
package com.eis.smslibrary;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.eis.communication.CommunicationHandler;
import com.eis.communication.Message;
import com.eis.smslibrary.listeners.SMSReceivedListener;
import com.eis.smslibrary.listeners.SMSSentListener;

import java.lang.ref.WeakReference;

/**
* Communication handler for SMSs. It's a Singleton, you should
* access it with {@link #getInstance}, and before doing anything you
* should call {@link #setup}.
*
* @author Luca Crema, Marco Mariotto, Alberto Ursino, Marco Tommasini
*/
public class SMSHandler implements CommunicationHandler<SMSMessage> {

public static final String SENT_MESSAGE_INTENT_ACTION = "SMS_SENT";
public static final int RANDOM_STARTING_COUNTER_VALUE_RANGE = 100000;

/**
* Singleton instance
*/
private static SMSHandler instance;

/**
* Weak reference doesn't prevent garbage collector to
* de-allocate this class when it has reference to a
* context that is still running. Prevents memory leaks.
*/
private WeakReference<Context> context;

/**
* Received listener reference
*/
private SMSReceivedListener receivedListener;

/**
* This message counter is used so that we can have a different action name
* for pending intent (that will call broadcastReceiver). If we were to use the
* same action name for every message we would have a conflict and we wouldn't
* know what message has been sent
*/
private int messageCounter;

/**
* Private constructor for Singleton
*/
private SMSHandler() {
//Random because if we close and open the app the value probably differs
messageCounter = (int)(Math.random() * RANDOM_STARTING_COUNTER_VALUE_RANGE);
}

/**
* @return the current instance of this class
*/
public static SMSHandler getInstance() {
if (instance == null)
instance = new SMSHandler();

return instance;
}

public class SMSHandler implements CommunicationHandler {
/**
* Setup for the handler.
*
* @param context current context.
*/
public void setup(Context context) {
this.context = new WeakReference<>(context);
}

/**
* Sends a message to a destination peer via SMS.
* Requires {@link android.Manifest.permission#SEND_SMS}
*
* @param message to be sent in the channel to a peer
*/
@Override
public void sendMessage(Message message) {
//Test pull request
public void sendMessage(final @NonNull SMSMessage message) {
sendMessage(message, null);
}

/**
* Sends a message to a destination peer via SMS then
* calls the listener.
*
* @param message to be sent in the channel to a peer
* @param sentListener called on message sent or on error, can be null
*/
public void sendMessage(final @NonNull SMSMessage message, final @Nullable SMSSentListener sentListener) {
checkSetup();
String smsContent = SMSMessageHandler.getInstance().parseData(message);
PendingIntent sentPI = setupNewSentReceiver(message, sentListener);

SMSCore.sendMessage(smsContent, message.getPeer().getAddress(),sentPI,null);
}

/**
* @param receivedListener the listener called on message received
*/
public void setReceivedListener(SMSReceivedListener receivedListener){
this.receivedListener = receivedListener;
}

/**
* Method used by {@link SMSReceivedBroadcastReceiver} to call the listener
* for messages received
* @param receivedMessage the message that has been received
*/
protected void callReceivedListener(SMSMessage receivedMessage){
if(receivedListener != null)
receivedListener.onMessageReceived(receivedMessage);
}

/**
* Creates a new {@link SMSSentBroadcastReceiver} and registers it to receive broadcasts
* with action {@value SENT_MESSAGE_INTENT_ACTION}
*
* @param message that will be sent
* @param listener to call on broadcast received
* @return a {@link PendingIntent} to be passed to SMSCore
*/
private PendingIntent setupNewSentReceiver(final @NonNull SMSMessage message, final @Nullable SMSSentListener listener) {
if (listener == null)
return null; //Doesn't make any sense to have a BroadcastReceiver if there is no listener

SMSSentBroadcastReceiver onSentReceiver = new SMSSentBroadcastReceiver(message, listener);
String actionName = SENT_MESSAGE_INTENT_ACTION + (messageCounter++);
context.get().registerReceiver(onSentReceiver, new IntentFilter(actionName));
return PendingIntent.getBroadcast(context.get(), 0, new Intent(actionName), 0);
}

/**
* Checks if the handler has been setup
* @throws IllegalStateException if the handler has not been setup
*/
private void checkSetup(){
if(context == null)
throw new IllegalStateException("You must call setup() first");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.eis.smslibrary;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.telephony.SmsMessage;

/**
* Broadcast receiver for received messages, called by Android.
*
* @author Luca Crema, Marco Mariotto
* @since 29/11/2019
*
*/
public class SMSReceivedBroadcastReceiver extends BroadcastReceiver {

/**
* Parses message and calls listener
*/
@Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
if (extras == null) {
return;
}

Object[] smsExtras = (Object[]) extras.get("pdus");
String format = (String) extras.get("format");

if(smsExtras == null) //could be null
return;

for (Object smsData : smsExtras) {
SmsMessage receivedSMS = createMessageFromPdu(smsData, format);
String smsContent = receivedSMS.getMessageBody();
String phoneNumber = receivedSMS.getOriginatingAddress();

if (phoneNumber == null) //could be null
return;

SMSMessage parsedMessage = SMSMessageHandler.getInstance().parseMessage(phoneNumber, smsContent);
if (parsedMessage != null) {
SMSHandler.getInstance().callReceivedListener(parsedMessage);
}
}
}

/**
* Calls the appropriate method to create a message from its pdus
* @param smsData message pdus
* @param format available only on build version >= 23
* @return
*/
private SmsMessage createMessageFromPdu(Object smsData, String format) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//Requires android version >23
return SmsMessage.createFromPdu((byte[]) smsData, format);
}
return SmsMessage.createFromPdu((byte[]) smsData);
}
}

0 comments on commit 4800754

Please sign in to comment.