/**
* Copyright (C) 2015 Monitordroid Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Tyler Butler
**/
package com.monitordroid.app;
import static com.monitordroid.app.CommonUtilities.SMS_URL;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.telephony.SmsManager;
import com.google.android.gcm.GCMRegistrar;
public class SMSUtilities {
boolean isUpdate;
static final int MESSAGES_PER_BATCH = 600;
/**
* Sends an SMS Message from the device to a specified phone number
*
* @param phoneNumber
* The phone number of the intended recipient
* @param SMSMessage
* The SMS message to send
*/
public void sendSMS(Context context, String phoneNumber, String SMSMessage) {
try {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNumber, null, SMSMessage, null,
null);
// Stores the new message in the device's sent SMS folder
// so that it is visible to the Android GUI
ContentValues values = new ContentValues();
values.put("address", phoneNumber);
values.put("body", SMSMessage);
context.getContentResolver().insert(
Uri.parse("content://sms/sent"), values);
}
catch (Exception e) {
}
}
/**
* Gets 600 SMS Messages from the device per iteration and then executes an
* Asynctask to upload them to the server
*
* @param iteration
* States which batch of messages to send to the server. For
* example, iteration "1" will send the most recent 600 messages
* iteration 2 will send the next 600 in order, and so on...
*
* @param resolveContacts
* Whether to resolve the phone numbers associated with text
* message to a contact's name on the device if one matches. This
* will exponentially increase the time it takes the SMS
* retrieval algorithm to complete
*/
public void fetchSMS(Context context, int iteration) {
StringBuffer messageList = new StringBuffer();
// Call smsReader to fill output with text data
JSONArray output = new JSONArray();
output = smsReader(context, iteration);
for (int i = 0; i < output.length(); i++) {
try {
messageList.append(output.get(i).toString());
}
catch (JSONException e) {
}
}
String regId = GCMRegistrar.getRegistrationId(context);
// If the program is updating the first messages, we don't want them to
// be
// concatenated to the end of other messages, so we need to signal the
// server not to.
if (iteration == 1) {
isUpdate = true;
}
else {
isUpdate = false;
}
new MyAsyncTask().execute(messageList.toString(), regId);
}
/**
* Puts a batch of 600 text messages (both inbox, outbox, and other
* messages) into JSON format and returns an array of the JSON Objects.
*
* @param iteration
* States which batch of messages to send to the server. For
* example, iteration "1" will send the most recent 600 messages
* iteration 2 will send the next 600 in order, and so on...
*
* @return Returns a JSON array, with each index being a JSON Object
* containing a single formatted text message
*/
private JSONArray smsReader(Context context, int iteration) {
JSONArray jArr = new JSONArray();
int stoppingPoint = 0;
boolean validCursor = false;
try {
Cursor cursor = context.getContentResolver().query(
Uri.parse("content://sms/"), null, null, null, null);
if (iteration == 1) {
cursor.moveToFirst();
stoppingPoint = MESSAGES_PER_BATCH;
validCursor = true;
}
else {
// Each cursor position is a SMS Message, 600 messages is around
// 60kb of data. Send 600 messages each iteration.
if (cursor != null
&& cursor.moveToPosition(MESSAGES_PER_BATCH
* (iteration - 1))) {
stoppingPoint = MESSAGES_PER_BATCH * iteration;
validCursor = true;
}
else {
cursor.close();
}
}
if (validCursor) {
do {
JSONObject jObj = new JSONObject();
for (int idx = 0; idx < cursor.getColumnCount(); idx++) {
// We only want the address(phone number), body, date,
// and type of the message
if (cursor.getColumnName(idx).equals("address")
|| cursor.getColumnName(idx).equals("body")
|| cursor.getColumnName(idx).equals("date")
|| cursor.getColumnName(idx).equals("type")) {
if (cursor.getColumnName(idx).equals("address")) {
if (cursor.getString(idx) != null) {
jObj.put("phonenumber",
cursor.getString(idx));
}
else {
jObj.put("phonenumber", "Draft Message");
}
}
else if (cursor.getColumnName(idx).equals("type")) {
if (cursor.getString(idx).contains("1")) {
jObj.put("mailbox", "Inbox");
}
else {
jObj.put("mailbox", "Outbox");
}
}
else if (cursor.getColumnName(idx).equals("date")) {
jObj.put("date", cursor.getLong(idx));
}
// The remaining field contains the SMS message
// content
else {
jObj.put("message", cursor.getString(idx));
}
}
}
jArr.put(jObj);
}
// Iterate until the desired message size is reached or there is
// no more data
while (cursor.moveToNext()
&& (cursor.getPosition() < stoppingPoint));
cursor.close();
}
}
catch (Exception e) {
// Device probably doesn't have SMS Capabilities
JSONObject errorMessage = new JSONObject();
try {
errorMessage
.put("message",
"Error retreiving SMS data from the device. Device may not have SMS capabilities");
}
catch (JSONException error) {
}
jArr.put(errorMessage);
}
return jArr;
}
// Posts UTF-8 Text data to the server
private class MyAsyncTask extends AsyncTask<String, String, Double> {
@Override
protected Double doInBackground(String... params) {
postData(params[0], params[1]);
return null;
}
protected void onPostExecute(Double result) {
}
private void postData(String smsData, String regId) {
HttpClient httpclient = new DefaultHttpClient();
String url = SMS_URL;
HttpPost httppost = new HttpPost(url);
try {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
// Signal to the server that these are the newest messages and
// should not be concatenated
if (isUpdate) {
nameValuePairs.add(new BasicNameValuePair(
"FirstUpdateData", smsData));
}
// Otherwise signal to concatenate these messages onto previous
// messages in the database
else {
nameValuePairs.add(new BasicNameValuePair("SMSData",
smsData));
}
nameValuePairs.add(new BasicNameValuePair("regName", regId));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs,
"UTF-8"));
// Execute HTTP Post Request
httpclient.execute(httppost);
}
catch (ClientProtocolException e) {
}
catch (IOException e) {
}
}
}
}