1
0
mirror of https://github.com/tijder/SmsMatrix.git synced 2024-10-27 18:24:01 +00:00

Implement support for MMS messages

Implemented support for MMS messages, including a variety of formats
of image and video. Works by observing changes to content://mms-sms,
parsing out the new MMS contents, uploading them as assets to
the Matrix server, and posting a new image or video message to the room.
This commit is contained in:
St John Karp 2018-04-08 09:10:17 -07:00 committed by St John Karp
parent f83a23771f
commit 51377834ad
8 changed files with 398 additions and 12 deletions

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="eu.droogers.smsmatrix"> package="eu.droogers.smsmatrix">
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />

View File

@ -0,0 +1,302 @@
package eu.droogers.smsmatrix;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.provider.Telephony;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class MMSMonitor {
private MatrixService mainActivity;
private ContentResolver contentResolver = null;
private Context mainContext;
private Handler mmshandler = null;
private ContentObserver mmsObserver = null;
public boolean monitorStatus = false;
private int mmsCount = 0;
private static final String TAG = "MMSMonitor";
public MMSMonitor(final MatrixService mainActivity, final Context mainContext) {
this.mainActivity = mainActivity;
contentResolver = mainActivity.getContentResolver();
this.mainContext = mainContext;
mmshandler = new MMSHandler();
mmsObserver = new MMSObserver(mmshandler);
Log.i(TAG, "***** Start MMS Monitor *****");
}
public void startMMSMonitoring() {
try {
monitorStatus = false;
if (!monitorStatus) {
contentResolver.registerContentObserver(
Uri.parse("content://mms"),
true,
mmsObserver
);
// Save the count of MMS messages on start-up.
Uri uriMMSURI = Uri.parse("content://mms-sms");
Cursor mmsCur = mainActivity.getContentResolver().query(
uriMMSURI,
null,
Telephony.Mms.MESSAGE_BOX + " = " + Telephony.Mms.MESSAGE_BOX_INBOX,
null,
Telephony.Mms._ID
);
if (mmsCur != null && mmsCur.getCount() > 0) {
mmsCount = mmsCur.getCount();
Log.d(TAG, "Init MMSCount = " + mmsCount);
}
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
public void stopMMSMonitoring() {
try {
monitorStatus = false;
if (!monitorStatus){
contentResolver.unregisterContentObserver(mmsObserver);
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
class MMSHandler extends Handler {
public void handleMessage(final Message msg) {
//Log.i(TAG, "Handler");
}
}
class MMSObserver extends ContentObserver {
private Handler mms_handle = null;
public MMSObserver(final Handler mmshandle) {
super(mmshandle);
mms_handle = mmshandle;
}
public void onChange(final boolean bSelfChange) {
super.onChange(bSelfChange);
Log.i(TAG, "Onchange");
try {
monitorStatus = true;
// Send message to Activity.
Message msg = new Message();
mms_handle.sendMessage(msg);
// Get the MMS count.
Uri uriMMSURI = Uri.parse("content://mms/");
Cursor mmsCur = mainActivity.getContentResolver().query(
uriMMSURI,
null,
Telephony.Mms.MESSAGE_BOX + " = " + Telephony.Mms.MESSAGE_BOX_INBOX,
null,
Telephony.Mms._ID
);
int currMMSCount = 0;
if (mmsCur != null && mmsCur.getCount() > 0) {
currMMSCount = mmsCur.getCount();
}
// Proceed if there is a new message.
if (currMMSCount > mmsCount) {
mmsCount = currMMSCount;
mmsCur.moveToLast();
// Get the message id and subject.
String subject = mmsCur.getString(mmsCur.getColumnIndex(Telephony.Mms.SUBJECT));
int id = Integer.parseInt(mmsCur.getString(mmsCur.getColumnIndex(Telephony.Mms._ID)));
Log.d(TAG, "_id = " + id);
Log.d(TAG, "Subject = " + subject);
byte[] mediaData = null;
String message = "";
String address = "";
String fileName = "";
String fileType = "";
String messageType = "";
// Get parts.
Uri uriMMSPart = Uri.parse("content://mms/part");
Cursor curPart = mainActivity.getContentResolver().query(
uriMMSPart,
null,
Telephony.Mms.Part.MSG_ID + " = " + id,
null,
Telephony.Mms.Part._ID
);
Log.d(TAG, "Parts records length = " + curPart.getCount());
curPart.moveToLast();
do {
String contentType = curPart.getString(curPart.getColumnIndex(Telephony.Mms.Part.CONTENT_TYPE));
String partId = curPart.getString(curPart.getColumnIndex(Telephony.Mms.Part._ID));
fileName = curPart.getString(curPart.getColumnIndex(Telephony.Mms.Part.NAME));
Log.d(TAG, "partId = " + partId);
Log.d(TAG, "Part mime type = " + contentType);
if (contentType.equalsIgnoreCase("text/plain"))
{
// Get the message.
Log.i(TAG,"==== Get the message start ====");
messageType = Matrix.MESSAGE_TYPE_TEXT;
byte[] messageData = readMMSPart(partId);
if (messageData != null && messageData.length > 0) {
message = new String(messageData);
}
if (message.isEmpty()) {
Cursor curPart1 = mainActivity.getContentResolver().query(
uriMMSPart,
null,
Telephony.Mms.Part.MSG_ID + " = " + id + " and "+ Telephony.Mms.Part._ID + " = " + partId,
null,
Telephony.Mms.Part._ID
);
for (int i = 0; i < curPart1.getColumnCount(); i++)
{
Log.d(TAG,"Column Name : " + curPart1.getColumnName(i));
}
curPart1.moveToLast();
message = curPart1.getString(13);
}
Log.d(TAG,"Txt Message = " + message);
} else if (isImageType(contentType) || isVideoType(contentType)) {
// Get the media.
if (isImageType(contentType)) {
messageType = Matrix.MESSAGE_TYPE_IMAGE;
} else if (isVideoType(contentType)) {
messageType = Matrix.MESSAGE_TYPE_VIDEO;
}
Log.i(TAG, "==== Get the media start ====");
fileType = contentType;
mediaData = readMMSPart(partId);
Log.i(TAG, "Media data length == " + mediaData.length);
}
} while (curPart.moveToPrevious());
// Get the sender's address.
Uri uriMMSAddr = Uri.parse("content://mms/" + id + "/addr");
Cursor addrCur = mainActivity.getContentResolver().query(
uriMMSAddr,
null,
Telephony.Mms.Addr.TYPE + " = 137", // PduHeaders.FROM
null,
Telephony.Mms.Addr._ID
);
if (addrCur != null) {
addrCur.moveToLast();
do{
Log.d(TAG, "addrCur records length = " + addrCur.getCount());
if (addrCur.getCount() > 0) {
address = addrCur.getString(addrCur.getColumnIndex(Telephony.Mms.Addr.ADDRESS));
}
Log.d(TAG, "address = " + address);
if (!message.isEmpty()) {
Utilities.sendMatrix(mainActivity, message, address, messageType);
}
if (mediaData != null) {
Utilities.sendMatrix(
mainActivity,
mediaData,
address,
messageType,
fileName,
fileType
);
}
} while (addrCur.moveToPrevious());
}
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
}
private byte[] readMMSPart(String partId) {
byte[] partData = null;
Uri partURI = Uri.parse("content://mms/part/" + partId);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = null;
try {
Log.i(TAG,"Entered into readMMSPart try.");
ContentResolver mContentResolver = mainActivity.getContentResolver();
is = mContentResolver.openInputStream(partURI);
byte[] buffer = new byte[256];
int len = is.read(buffer);
while (len >= 0) {
baos.write(buffer, 0, len);
len = is.read(buffer);
}
partData = baos.toByteArray();
//Log.i(TAG, "Text Msg :: " + new String(partData));
} catch (IOException e) {
Log.e(TAG, "Exception == Failed to load part data");
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
Log.e(TAG, "Exception :: Failed to close stream");
}
}
}
return partData;
}
private boolean isImageType(String mime) {
boolean result = false;
if (mime.equalsIgnoreCase("image/jpg")
|| mime.equalsIgnoreCase("image/jpeg")
|| mime.equalsIgnoreCase("image/png")
|| mime.equalsIgnoreCase("image/gif")
|| mime.equalsIgnoreCase("image/bmp")) {
result = true;
}
return result;
}
private boolean isVideoType(String mime) {
boolean result = false;
if (mime.equalsIgnoreCase("video/3gpp")
|| mime.equalsIgnoreCase("video/3gpp2")
|| mime.equalsIgnoreCase("video/avi")
|| mime.equalsIgnoreCase("video/mp4")
|| mime.equalsIgnoreCase("video/mpeg")
|| mime.equalsIgnoreCase("video/webm")) {
result = true;
}
return result;
}
}

View File

@ -72,6 +72,7 @@ public class MainActivity extends Activity {
} }
private void checkPermissions() { private void checkPermissions() {
askPermission(Manifest.permission.READ_SMS);
askPermission(Manifest.permission.SEND_SMS); askPermission(Manifest.permission.SEND_SMS);
askPermission(Manifest.permission.READ_PHONE_STATE); askPermission(Manifest.permission.READ_PHONE_STATE);
askPermission(Manifest.permission.READ_CONTACTS); askPermission(Manifest.permission.READ_CONTACTS);

View File

@ -22,12 +22,14 @@ import org.matrix.androidsdk.data.store.IMXStoreListener;
import org.matrix.androidsdk.data.store.MXFileStore; import org.matrix.androidsdk.data.store.MXFileStore;
import org.matrix.androidsdk.data.store.MXMemoryStore; import org.matrix.androidsdk.data.store.MXMemoryStore;
import org.matrix.androidsdk.listeners.IMXEventListener; import org.matrix.androidsdk.listeners.IMXEventListener;
import org.matrix.androidsdk.listeners.MXMediaUploadListener;
import org.matrix.androidsdk.rest.callback.SimpleApiCallback; import org.matrix.androidsdk.rest.callback.SimpleApiCallback;
import org.matrix.androidsdk.rest.client.LoginRestClient; import org.matrix.androidsdk.rest.client.LoginRestClient;
import org.matrix.androidsdk.rest.model.Event; import org.matrix.androidsdk.rest.model.Event;
import org.matrix.androidsdk.rest.model.Message; import org.matrix.androidsdk.rest.model.Message;
import org.matrix.androidsdk.rest.model.login.Credentials; import org.matrix.androidsdk.rest.model.login.Credentials;
import java.io.ByteArrayInputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
@ -58,6 +60,12 @@ public class Matrix {
private String realUserid; private String realUserid;
// Message type constants.
public static final String MESSAGE_TYPE_TEXT = "m.text";
public static final String MESSAGE_TYPE_IMAGE = "m.image";
public static final String MESSAGE_TYPE_VIDEO = "m.video";
public static final String MESSAGE_TYPE_NOTICE = "m.notice";
public Matrix(final Context context, String url, String botUsername, String botPassword, String username, String device, String syncDelay, String syncTimeout) { public Matrix(final Context context, String url, String botUsername, String botPassword, String username, String device, String syncDelay, String syncTimeout) {
this.context = context; this.context = context;
hsConfig = new HomeServerConnectionConfig(Uri.parse(url)); hsConfig = new HomeServerConnectionConfig(Uri.parse(url));
@ -172,6 +180,45 @@ public class Matrix {
} }
} }
public void sendFile(
final String phoneNumber,
final byte[] body,
final String type,
final String fileName,
final String contentType
) {
String uploadID = String.valueOf(transaction);
transaction++;
session.getMediasCache().uploadContent(
new ByteArrayInputStream(body),
fileName,
contentType,
uploadID,
new MXMediaUploadListener()
{
@Override
public void onUploadComplete(final String uploadId, final String contentUri) {
Room room = getRoomByPhonenumber(phoneNumber);
JsonObject json = new JsonObject();
json.addProperty("body", fileName);
json.addProperty("msgtype", type);
json.addProperty("url", contentUri);
JsonObject info = new JsonObject();
info.addProperty("mimetype", contentType);
json.add("info", info);
session.getRoomsApiClient().sendEventToRoom(
String.valueOf(transaction),
room.getRoomId(),
"m.room.message",
json,
new SimpleApiCallback<Event>()
);
transaction++;
}
}
);
}
private void changeDisplayname(String roomId, String displayname) { private void changeDisplayname(String roomId, String displayname) {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.put("displayname", displayname); params.put("displayname", displayname);

View File

@ -23,6 +23,7 @@ public class MatrixService extends Service {
private String hsUrl; private String hsUrl;
private String syncDelay; private String syncDelay;
private String syncTimeout; private String syncTimeout;
private MMSMonitor mms;
@Override @Override
public void onCreate() { public void onCreate() {
@ -49,11 +50,25 @@ public class MatrixService extends Service {
Log.e(TAG, "onStartCommand: Service"); Log.e(TAG, "onStartCommand: Service");
String phone = intent.getStringExtra("SendSms_phone"); String phone = intent.getStringExtra("SendSms_phone");
String body = intent.getStringExtra("SendSms_body");
String type = intent.getStringExtra("SendSms_type"); String type = intent.getStringExtra("SendSms_type");
if (phone != null) { if (phone != null) {
if (type.equals(Matrix.MESSAGE_TYPE_TEXT))
{
String body = intent.getStringExtra("SendSms_body");
mx.sendMessage(phone, body, type); mx.sendMessage(phone, body, type);
} else {
byte[] body = intent.getByteArrayExtra("SendSms_body");
String fileName = intent.getStringExtra("SendSms_fileName");
String contentType = intent.getStringExtra("SendSms_contentType");
mx.sendFile(phone, body, type, fileName, contentType);
} }
}
if (this.mms == null) {
this.mms = new MMSMonitor(this , getApplicationContext());
this.mms.startMMSMonitoring();
}
return START_NOT_STICKY; return START_NOT_STICKY;
} }
@ -61,6 +76,8 @@ public class MatrixService extends Service {
@Override @Override
public void onDestroy() { public void onDestroy() {
mx.destroy(); mx.destroy();
this.mms.stopMMSMonitoring();
this.mms = null;
super.onDestroy(); super.onDestroy();
} }

View File

@ -36,7 +36,7 @@ public class ReceiverListener extends BroadcastReceiver {
msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]); msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
msg_from = msgs[i].getOriginatingAddress(); msg_from = msgs[i].getOriginatingAddress();
String msgBody = msgs[i].getMessageBody(); String msgBody = msgs[i].getMessageBody();
sendMatrix(context, msgBody, msg_from, "m.text"); Utilities.sendMatrix(context, msgBody, msg_from, Matrix.MESSAGE_TYPE_TEXT);
} }
}catch(Exception e){ }catch(Exception e){
Log.d("Exception caught",e.getMessage()); Log.d("Exception caught",e.getMessage());
@ -59,14 +59,6 @@ public class ReceiverListener extends BroadcastReceiver {
body += " is calling"; body += " is calling";
break; break;
} }
sendMatrix(context, body, cal_from, "m.notice"); Utilities.sendMatrix(context, body, cal_from, Matrix.MESSAGE_TYPE_NOTICE);
}
private void sendMatrix(Context context, String body, String phone, String type) {
Intent intent = new Intent(context, MatrixService.class);
intent.putExtra("SendSms_phone", phone);
intent.putExtra("SendSms_body", body);
intent.putExtra("SendSms_type", type);
context.startService(intent);
} }
} }

View File

@ -0,0 +1,24 @@
package eu.droogers.smsmatrix;
import android.content.Context;
import android.content.Intent;
public class Utilities {
public static void sendMatrix(Context context, String body, String phone, String type) {
Intent intent = new Intent(context, MatrixService.class);
intent.putExtra("SendSms_phone", phone);
intent.putExtra("SendSms_body", body);
intent.putExtra("SendSms_type", type);
context.startService(intent);
}
public static void sendMatrix(Context context, byte[] body, String phone, String type, String fileName, String contentType) {
Intent intent = new Intent(context, MatrixService.class);
intent.putExtra("SendSms_phone", phone);
intent.putExtra("SendSms_body", body);
intent.putExtra("SendSms_type", type);
intent.putExtra("SendSms_fileName", fileName);
intent.putExtra("SendSms_contentType", contentType);
context.startService(intent);
}
}

View File

@ -3,6 +3,7 @@
buildscript { buildscript {
repositories { repositories {
jcenter() jcenter()
google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.android.tools.build:gradle:3.0.1'
@ -15,6 +16,7 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
jcenter() jcenter()
google()
} }
} }