5
0
mirror of https://github.com/cwinfo/envayasms.git synced 2024-09-18 21:39:34 +00:00

do http requests in AsyncTask so they don't freeze the UI thread; improve log messages; make poll interval configurable

This commit is contained in:
Jesse Young 2011-09-11 20:59:51 -07:00
parent 9473ab1610
commit fc6cda4ea3
14 changed files with 580 additions and 316 deletions

View File

@ -23,10 +23,7 @@
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<receiver android:name=".OutgoingMessagePoller">
</receiver>
</receiver>
<receiver android:name=".MessageStatusNotifier">
<intent-filter>
@ -34,9 +31,11 @@
</intent-filter>
</receiver>
<receiver android:name=".OutgoingMessagePoller">
</receiver>
<activity android:name=".Prefs"
android:label="@string/app_name">
</activity>
</application>
</manifest>

View File

@ -8,6 +8,6 @@
# project structure.
# Project target.
target=android-4
target=android-8
# Indicates whether an apk should be generated for each density.
split.density=false

View File

@ -1,10 +0,0 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked in Version Control Systems,
# as it contains information specific to your local configuration.
# location of the SDK. This is only used by Ant
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=C:\\android-sdk

View File

@ -3,11 +3,14 @@
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:background="#333333">
<ScrollView android:id="@+id/info_scroll" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_weight="1">
<TextView
android:scrollbars="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/info"
android:textColor="#FFFFFF"
android:layout_margin="5px"></TextView>
</ScrollView>
</LinearLayout>

27
res/values/arrays.xml Executable file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="check_intervals">
<item>15 sec</item>
<item>30 sec</item>
<item>1 minute</item>
<item>2 minutes</item>
<item>5 minutes</item>
<item>10 minutes</item>
<item>30 minutes</item>
<item>1 hour</item>
<item>never</item>
</string-array>
<string-array name="check_intervals_values">
<item>15</item>
<item>30</item>
<item>60</item>
<item>120</item>
<item>300</item>
<item>600</item>
<item>1800</item>
<item>3600</item>
<item>0</item>
</string-array>
</resources>

View File

@ -4,13 +4,13 @@
<EditTextPreference
android:key="server_url"
android:title="Server URL"
android:defaultValue="http://192.168.70.1:3000/"
></EditTextPreference>
android:defaultValue=""
></EditTextPreference>
<EditTextPreference
android:key="phone_number"
android:title="Your Phone Number"
android:defaultValue="16507993371"
android:defaultValue=""
></EditTextPreference>
<EditTextPreference
@ -18,10 +18,20 @@
android:title="Password"
android:password="true"
></EditTextPreference>
<ListPreference
android:key="outgoing_interval"
android:title="Poll interval"
android:defaultValue="60"
android:entries="@array/check_intervals"
android:entryValues="@array/check_intervals_values"
></ListPreference>
<!--
<CheckBoxPreference
android:key="detailed_log"
android:title="Detailed log messages?"
android:disableDependentsState="false"
></CheckBoxPreference>
-->
</PreferenceScreen>

View File

@ -4,6 +4,7 @@
*/
package org.envaya.kalsms;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context;
@ -11,18 +12,28 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.telephony.SmsManager;
import android.util.Log;
import java.text.DateFormat;
import java.util.Date;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
/**
*
* @author Jesse
*/
public class App {
public static final int OUTGOING_POLL_SECONDS = 30;
public static final String ACTION_OUTGOING = "outgoing";
public static final String ACTION_INCOMING = "incoming";
public static final String ACTION_SEND_STATUS = "send_status";
public static final int STATUS_QUEUED = 1;
public static final int STATUS_FAILED = 2;
public static final int STATUS_SENT = 3;
public static final String LOG_NAME = "KALSMS";
public static final String LOG_INTENT = "org.envaya.kalsms.LOG";
public static final String SEND_STATUS_INTENT = "org.envaya.kalsms.SEND_STATUS";
@ -30,17 +41,28 @@ public class App {
public Context context;
public SharedPreferences settings;
public App(Context context)
protected App(Context context)
{
this.context = context;
this.settings = PreferenceManager.getDefaultSharedPreferences(context);
}
private static App app;
public static App getInstance(Context context)
{
if (app == null)
{
app = new App(context);
}
return app;
}
static void debug(String msg)
{
Log.d(LOG_NAME, msg);
}
public void log(String msg)
{
Log.d(LOG_NAME, msg);
@ -50,6 +72,34 @@ public class App {
context.sendBroadcast(broadcast);
}
public void setOutgoingMessageAlarm()
{
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0,
new Intent(context, OutgoingMessagePoller.class),
0);
alarm.cancel(pendingIntent);
int pollSeconds = getOutgoingPollSeconds();
if (pollSeconds > 0)
{
alarm.setRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
pollSeconds * 1000,
pendingIntent);
log("Checking for outgoing messages every " + pollSeconds + " sec");
}
else
{
log("Not checking for outgoing messages.");
}
}
public void logError(Throwable ex)
{
logError("ERROR", ex);
@ -77,20 +127,17 @@ public class App {
}
}
}
public String getIncomingUrl()
public String getDisplayString(String str)
{
return getServerUrl() + "/pg/receive_sms";
}
public String getOutgoingUrl()
{
return getServerUrl() + "/pg/dequeue_sms";
}
public String getSendStatusUrl()
{
return getServerUrl() + "/pg/sms_sent";
if (str.length() == 0)
{
return "(not set)";
}
else
{
return str;
}
}
public String getServerUrl()
@ -103,21 +150,30 @@ public class App {
return settings.getString("phone_number", "");
}
public int getOutgoingPollSeconds()
{
return Integer.parseInt(settings.getString("outgoing_interval", "0"));
}
public String getPassword()
{
return settings.getString("password", "");
}
}
private SQLiteDatabase db;
public SQLiteDatabase getWritableDatabase()
{
if (db == null)
{
db = new DBHelper(context).getWritableDatabase();
}
return db;
return new DBHelper(context).getWritableDatabase();
}
public HttpClient getHttpClient()
{
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, 8000);
HttpConnectionParams.setSoTimeout(httpParameters, 8000);
return new DefaultHttpClient(httpParameters);
}
public void sendSMS(OutgoingSmsMessage sms)
{
String serverId = sms.getServerId();
@ -126,7 +182,7 @@ public class App {
{
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor =
db.rawQuery("select 1 from sent_sms where server_id=?", new String[] { serverId });
db.rawQuery("select 1 from sms_status where server_id=?", new String[] { serverId });
boolean exists = (cursor.getCount() > 0);
cursor.close();
@ -138,7 +194,10 @@ public class App {
ContentValues values = new ContentValues();
values.put("server_id", serverId);
db.insert("sent_sms", null, values);
values.put("status", App.STATUS_QUEUED);
db.insert("sms_status", null, values);
db.close();
}
SmsManager smgr = SmsManager.getDefault();

View File

@ -14,11 +14,14 @@ import android.database.sqlite.SQLiteOpenHelper;
*/
public class DBHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final int DATABASE_VERSION = 2;
private static final String DATABASE_NAME = "org.envaya.kalsms.db";
private static final String SENT_SMS_TABLE_CREATE =
"CREATE TABLE sent_sms (server_id text);"
private static final String SMS_STATUS_TABLE_DROP =
" DROP TABLE sms_status;";
private static final String SMS_STATUS_TABLE_CREATE =
"CREATE TABLE sms_status (server_id text, status int);"
+ "CREATE INDEX server_id_index ON sent_sms (server_id);";
public DBHelper(Context context) {
@ -27,11 +30,14 @@ public class DBHelper extends SQLiteOpenHelper {
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SENT_SMS_TABLE_CREATE);
db.execSQL(SMS_STATUS_TABLE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2)
{
db.execSQL(SMS_STATUS_TABLE_CREATE);
}
}
}

View File

@ -0,0 +1,181 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.envaya.kalsms;
import android.os.AsyncTask;
import android.util.Base64;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class HttpTask extends AsyncTask<BasicNameValuePair, Void, HttpResponse> {
private App app;
public HttpTask(App app)
{
super();
this.app = app;
}
private String getSignature(String url, BasicNameValuePair... params)
{
try {
Arrays.sort(params, new Comparator() {
public int compare(Object o1, Object o2)
{
return ((BasicNameValuePair)o1).getName().compareTo(((BasicNameValuePair)o2).getName());
}
});
StringBuilder builder = new StringBuilder();
builder.append(url);
for (BasicNameValuePair param : params)
{
builder.append(",");
builder.append(param.getName());
builder.append("=");
builder.append(param.getValue());
}
builder.append(",");
builder.append(app.getPassword());
String value = builder.toString();
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(value.getBytes("utf-8"));
byte[] digest = md.digest();
return Base64.encodeToString(digest, Base64.NO_WRAP);
} catch (Exception ex) {
app.logError("Error computing signature", ex);
}
return "";
}
protected HttpResponse doInBackground(BasicNameValuePair... params) {
try
{
String url = app.getServerUrl();
HttpPost post = new HttpPost(url);
post.setEntity(new UrlEncodedFormEntity(Arrays.asList(params)));
String signature = this.getSignature(url, params);
post.setHeader("X-Kalsms-PhoneNumber", app.getPhoneNumber());
post.setHeader("X-Kalsms-Signature", signature);
HttpClient client = app.getHttpClient();
HttpResponse response = client.execute(post);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200)
{
return response;
}
else if (statusCode == 403)
{
app.log("Failed to authenticate to server");
app.log("(Phone number or password may be incorrect)");
return null;
}
else
{
app.log("Received HTTP " + statusCode + " from server");
return null;
}
}
catch (Throwable ex)
{
app.logError("Error while contacting server", ex);
return null;
}
}
protected String getDefaultToAddress()
{
return "";
}
protected List<OutgoingSmsMessage> parseResponseXML(HttpResponse response)
throws IOException, ParserConfigurationException, SAXException
{
List<OutgoingSmsMessage> messages = new ArrayList<OutgoingSmsMessage>();
InputStream responseStream = response.getEntity().getContent();
DocumentBuilder xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document xml = xmlBuilder.parse(responseStream);
NodeList smsNodes = xml.getElementsByTagName("Sms");
for (int i = 0; i < smsNodes.getLength(); i++) {
Element smsElement = (Element) smsNodes.item(i);
OutgoingSmsMessage sms = new OutgoingSmsMessage();
sms.setFrom(app.getPhoneNumber());
String to = smsElement.getAttribute("to");
sms.setTo(to.equals("") ? getDefaultToAddress() : to);
String serverId = smsElement.getAttribute("id");
sms.setServerId(serverId.equals("") ? null : serverId);
Node firstChild = smsElement.getFirstChild();
sms.setMessage(firstChild != null ? firstChild.getNodeValue(): "");
messages.add(sms);
}
return messages;
}
@Override
protected void onPostExecute(HttpResponse response) {
if (response != null)
{
try
{
handleResponse(response);
}
catch (Throwable ex)
{
app.logError("Error processing server response", ex);
handleFailure();
}
}
else
{
handleFailure();
}
}
protected void handleResponse(HttpResponse response) throws Exception
{
}
protected void handleFailure()
{
}
}

View File

@ -1,121 +1,97 @@
package org.envaya.kalsms;
import android.app.Activity;
import android.app.PendingIntent;
import java.util.ArrayList;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.http.HttpResponse;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.apache.http.NameValuePair;
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;
public class IncomingMessageForwarder extends BroadcastReceiver {
private App app;
private List<SmsStatus> retryList = new ArrayList<SmsStatus>();
private class SmsStatus
{
public SmsMessage smsMessage;
public long nextAttemptTime;
public int numAttempts = 0;
}
public List<OutgoingSmsMessage> sendMessageToServer(SmsMessage sms) {
private class ForwarderTask extends HttpTask {
String message = sms.getDisplayMessageBody();
String sender = sms.getDisplayOriginatingAddress();
private SmsMessage originalSms;
public ForwarderTask(SmsMessage originalSms) {
super(app);
this.originalSms = originalSms;
}
@Override
protected String getDefaultToAddress()
{
return originalSms.getOriginatingAddress();
}
@Override
protected void handleResponse(HttpResponse response) throws Exception {
for (OutgoingSmsMessage reply : parseResponseXML(response)) {
app.sendSMS(reply);
}
}
}
public void sendMessageToServer(SmsMessage sms)
{
String serverUrl = app.getServerUrl();
String message = sms.getMessageBody();
String sender = sms.getOriginatingAddress();
String recipient = app.getPhoneNumber();
app.log("Received SMS from " + sender);
if (message == null || message.length() == 0) {
return new ArrayList<OutgoingSmsMessage>();
}
List<OutgoingSmsMessage> replies = new ArrayList<OutgoingSmsMessage>();
try {
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("from", sender));
params.add(new BasicNameValuePair("to", recipient));
params.add(new BasicNameValuePair("message", message));
params.add(new BasicNameValuePair("secret", app.getPassword()));
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(app.getIncomingUrl());
post.setEntity(new UrlEncodedFormEntity(params));
if (serverUrl.length() == 0) {
app.log("Can't forward SMS to server; Server URL not set");
} else {
app.log("Forwarding incoming SMS to server");
HttpResponse response = client.execute(post);
InputStream responseStream = response.getEntity().getContent();
DocumentBuilder xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document xml = xmlBuilder.parse(responseStream);
NodeList smsNodes = xml.getElementsByTagName("Sms");
for (int i = 0; i < smsNodes.getLength(); i++) {
Element smsElement = (Element) smsNodes.item(i);
OutgoingSmsMessage reply = new OutgoingSmsMessage();
reply.setFrom(recipient);
reply.setTo(sender);
reply.setMessage(smsElement.getFirstChild().getNodeValue());
replies.add(reply);
}
} catch (SAXException ex) {
app.logError("Error parsing response from server while forwarding incoming message", ex);
} catch (IOException ex) {
app.logError("Error forwarding incoming message to server", ex);
} catch (ParserConfigurationException ex) {
app.logError("Error configuring XML parser", ex);
new ForwarderTask(sms).execute(
new BasicNameValuePair("from", sender),
new BasicNameValuePair("to", recipient),
new BasicNameValuePair("message", message),
new BasicNameValuePair("action", App.ACTION_INCOMING)
);
}
return replies;
}
public void smsReceived(Intent intent) {
for (SmsMessage sms : getMessagesFromIntent(intent)) {
List<OutgoingSmsMessage> replies = sendMessageToServer(sms);
for (OutgoingSmsMessage reply : replies)
{
app.sendSMS(reply);
}
sendMessageToServer(sms);
//DeleteSMSFromInbox(context, mesg);
}
}
@Override
// source: http://www.devx.com/wireless/Article/39495/1954
public void onReceive(Context context, Intent intent) {
try {
this.app = new App(context);
this.app = App.getInstance(context);
String action = intent.getAction();
if (action.equals("android.provider.Telephony.SMS_RECEIVED")) {
smsReceived(intent);
}
}
} catch (Throwable ex) {
app.logError("Unexpected error in IncomingMessageForwarder", ex, true);
}
@ -123,30 +99,28 @@ public class IncomingMessageForwarder extends BroadcastReceiver {
/*
private void DeleteSMSFromInbox(Context context, SmsMessage mesg) {
Log.d("KALSMS", "try to delete SMS");
try {
Uri uriSms = Uri.parse("content://sms/inbox");
StringBuilder sb = new StringBuilder();
sb.append("address='" + mesg.getOriginatingAddress() + "' AND ");
sb.append("body='" + mesg.getMessageBody() + "'");
Cursor c = context.getContentResolver().query(uriSms, null, sb.toString(), null, null);
c.moveToFirst();
int thread_id = c.getInt(1);
context.getContentResolver().delete(Uri.parse("content://sms/conversations/" + thread_id), null, null);
c.close();
} catch (Exception ex) {
// deletions don't work most of the time since the timing of the
// receipt and saving to the inbox
// makes it difficult to match up perfectly. the SMS might not be in
// the inbox yet when this receiver triggers!
Log.d("SmsReceiver", "Error deleting sms from inbox: " + ex.getMessage());
}
Log.d("KALSMS", "try to delete SMS");
try {
Uri uriSms = Uri.parse("content://sms/inbox");
StringBuilder sb = new StringBuilder();
sb.append("address='" + mesg.getOriginatingAddress() + "' AND ");
sb.append("body='" + mesg.getMessageBody() + "'");
Cursor c = context.getContentResolver().query(uriSms, null, sb.toString(), null, null);
c.moveToFirst();
int thread_id = c.getInt(1);
context.getContentResolver().delete(Uri.parse("content://sms/conversations/" + thread_id), null, null);
c.close();
} catch (Exception ex) {
// deletions don't work most of the time since the timing of the
// receipt and saving to the inbox
// makes it difficult to match up perfectly. the SMS might not be in
// the inbox yet when this receiver triggers!
Log.d("SmsReceiver", "Error deleting sms from inbox: " + ex.getMessage());
}
*/
}
*/
// from http://github.com/dimagi/rapidandroid
// source: http://www.devx.com/wireless/Article/39495/1954
private SmsMessage[] getMessagesFromIntent(Intent intent) {
SmsMessage retMsgs[] = null;
Bundle bdl = intent.getExtras();

View File

@ -1,19 +1,20 @@
package org.envaya.kalsms;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.text.Html;
import android.text.method.ScrollingMovementMethod;
import android.view.Menu;
import android.view.View;
import android.widget.ScrollView;
import android.widget.TextView;
import java.text.DateFormat;
import java.util.Date;
public class Main extends Activity {
@ -23,15 +24,54 @@ public class Main extends Activity {
showLogMessage(intent.getExtras().getString("message"));
}
};
private long lastLogTime = 0;
public void showLogMessage(String message)
{
TextView info = (TextView) Main.this.findViewById(R.id.info);
if (message != null)
{
int length = info.length();
int maxLength = 20000;
if (length > maxLength)
{
CharSequence text = info.getText();
int startPos = length - maxLength / 2;
for (int cur = startPos; cur < startPos + 100 && cur < length; cur++)
{
if (text.charAt(cur) == '\n')
{
startPos = cur;
break;
}
}
CharSequence endSequence = text.subSequence(startPos, length);
info.setText("[Older log messages not shown]");
info.append(endSequence);
}
long logTime = System.currentTimeMillis();
if (logTime - lastLogTime > 60000)
{
Date date = new Date(logTime);
info.append("[" + DateFormat.getTimeInstance().format(date) + "]\n");
lastLogTime = logTime;
}
info.append(message + "\n");
final ScrollView scrollView = (ScrollView) this.findViewById(R.id.info_scroll);
scrollView.post(new Runnable() { public void run() {
scrollView.fullScroll(View.FOCUS_DOWN);
} });
}
}
public void onResume() {
App.debug("RESUME");
@ -45,40 +85,31 @@ public class Main extends Activity {
App.debug("STARTED");
App app = App.getInstance(this.getApplication());
setContentView(R.layout.main);
PreferenceManager.setDefaultValues(this, R.xml.prefs, false);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this,
0,
new Intent(this, OutgoingMessagePoller.class),
0);
alarm.setRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
App.OUTGOING_POLL_SECONDS * 1000,
pendingIntent);
App app = new App(this.getApplication());
TextView info = (TextView) this.findViewById(R.id.info);
info.setText(Html.fromHtml("<b>SMS Gateway running.</b><br />"));
info.append(Html.fromHtml("<b>Press Menu to edit settings.</b><br />"));
showLogMessage("Server URL is: " + app.getServerUrl());
showLogMessage("Your phone number is: " + app.getPhoneNumber());
showLogMessage("Checking for outgoing messages every " + App.OUTGOING_POLL_SECONDS + " sec");
info.append(Html.fromHtml("<b>Press Menu to edit settings.</b><br />"));
showLogMessage("Server URL is: " + app.getDisplayString(app.getServerUrl()));
showLogMessage("Your phone number is: " + app.getDisplayString(app.getPhoneNumber()));
info.setMovementMethod(new ScrollingMovementMethod());
IntentFilter logReceiverFilter = new IntentFilter();
IntentFilter logReceiverFilter = new IntentFilter();
logReceiverFilter.addAction(App.LOG_INTENT);
registerReceiver(logReceiver, logReceiverFilter);
app.setOutgoingMessageAlarm();
for (int i = 0; i < 30; i++)
{
showLogMessage(" " + i);
}
}
// first time the Menu key is pressed

View File

@ -9,73 +9,71 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.SmsManager;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
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;
public class MessageStatusNotifier extends BroadcastReceiver {
private App app;
public void notifySuccess(String serverId)
public void notifyStatus(String serverId, int status, String errorMessage)
{
String logMessage;
switch (status)
{
case App.STATUS_SENT:
logMessage = "sent successfully";
break;
case App.STATUS_FAILED:
logMessage = "could not be sent (" + errorMessage + ")";
break;
default:
logMessage = "queued";
break;
}
String smsDesc = serverId == null ? "SMS reply" : ("SMS id=" + serverId);
if (serverId != null)
{
try {
app.log("Notifying server of sent SMS id=" + serverId);
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("from", app.getPhoneNumber()));
params.add(new BasicNameValuePair("secret", app.getPassword()));
params.add(new BasicNameValuePair("id", serverId));
app.log("Notifying server " + smsDesc + " " + logMessage);
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(app.getSendStatusUrl());
post.setEntity(new UrlEncodedFormEntity(params));
client.execute(post);
}
catch (IOException ex)
{
app.logError("Error while notifying server of outgoing message", ex);
}
new HttpTask(app).execute(
new BasicNameValuePair("from", app.getPhoneNumber()),
new BasicNameValuePair("id", serverId),
new BasicNameValuePair("status", "" + status),
new BasicNameValuePair("error", errorMessage),
new BasicNameValuePair("action", App.ACTION_SEND_STATUS)
);
}
else
{
app.log(smsDesc + " " + logMessage);
}
}
@Override
public void onReceive(Context context, Intent intent) {
app = new App(context);
app = App.getInstance(context);
String serverId = intent.getExtras().getString("serverId");
String desc = serverId == null ? "SMS reply" : ("SMS id=" + serverId);
switch (getResultCode()) {
case Activity.RESULT_OK:
app.log(desc + " sent successfully");
this.notifySuccess(serverId);
this.notifyStatus(serverId, App.STATUS_SENT, "");
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
app.log(desc + " could not be sent (generic failure)");
this.notifyStatus(serverId, App.STATUS_FAILED, "generic failure");
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
app.log(desc + " could not be sent (radio off)");
this.notifyStatus(serverId, App.STATUS_FAILED, "radio off");
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
app.log(desc + " could not be sent (no service)");
this.notifyStatus(serverId, App.STATUS_FAILED, "no service");
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
app.log(desc + " could not be sent (null PDU");
this.notifyStatus(serverId, App.STATUS_FAILED, "null PDU");
break;
default:
app.log("SMS could not be sent (unknown error)");
this.notifyStatus(serverId, App.STATUS_FAILED, "unknown error");
break;
}
}

View File

@ -1,89 +1,42 @@
package org.envaya.kalsms;
import java.util.ArrayList;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
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.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class OutgoingMessagePoller extends BroadcastReceiver {
private App app;
@Override
public void onReceive(Context context, Intent intent) {
try
{
app = new App(context);
private App app;
app.log("Checking for outgoing messages");
private class PollerTask extends HttpTask {
for (OutgoingSmsMessage sms : getOutgoingMessages())
{
app.sendSMS(sms);
}
}
catch (Throwable ex)
{
app.logError("Unexpected error in OutgoingMessagePoller", ex, true);
}
}
public List<OutgoingSmsMessage> getOutgoingMessages() {
List<OutgoingSmsMessage> messages = new ArrayList<OutgoingSmsMessage>();
try {
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("from", app.getPhoneNumber()));
params.add(new BasicNameValuePair("secret", app.getPassword()));
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(app.getOutgoingUrl());
post.setEntity(new UrlEncodedFormEntity(params));
HttpResponse response = client.execute(post);
InputStream responseStream = response.getEntity().getContent();
DocumentBuilder xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document xml = xmlBuilder.parse(responseStream);
NodeList smsNodes = xml.getElementsByTagName("Sms");
for (int i = 0; i < smsNodes.getLength(); i++) {
Element smsElement = (Element) smsNodes.item(i);
OutgoingSmsMessage sms = new OutgoingSmsMessage();
sms.setFrom(app.getPhoneNumber());
sms.setTo(smsElement.getAttribute("to"));
sms.setMessage(smsElement.getFirstChild().getNodeValue());
sms.setServerId(smsElement.getAttribute("id"));
messages.add(sms);
}
} catch (SAXException ex) {
app.logError("Error parsing response from server while retreiving outgoing messages", ex);
} catch (IOException ex) {
app.logError("Error retreiving outgoing messages from server", ex);
} catch (ParserConfigurationException ex) {
app.logError("Error configuring XML parser", ex);
public PollerTask()
{
super(app);
}
return messages;
@Override
protected void handleResponse(HttpResponse response) throws Exception {
for (OutgoingSmsMessage reply : parseResponseXML(response)) {
app.sendSMS(reply);
}
}
}
@Override
public void onReceive(Context context, Intent intent) {
app = App.getInstance(context);
String serverUrl = app.getServerUrl();
if (serverUrl.length() > 0)
{
app.log("Checking for outgoing messages");
new PollerTask().execute(
new BasicNameValuePair("from", app.getPhoneNumber()),
new BasicNameValuePair("action", App.ACTION_OUTGOING)
);
}
}
}

View File

@ -4,7 +4,11 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.view.Menu;
public class Prefs extends PreferenceActivity implements OnSharedPreferenceChangeListener {
@ -13,47 +17,76 @@ public class Prefs extends PreferenceActivity implements OnSharedPreferenceChang
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.prefs);
}
PreferenceScreen screen = this.getPreferenceScreen();
int numPrefs = screen.getPreferenceCount();
for(int i=0; i < numPrefs;i++)
{
updatePrefSummary(screen.getPreference(i));
}
}
@Override
protected void onResume() {
@Override
protected void onResume(){
super.onResume();
// Set up a listener whenever a key changes
// Set up a listener whenever a key changes
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onPause() {
@Override
protected void onPause() {
super.onPause();
// Unregister the listener whenever a key changes
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
// Unregister the listener whenever a key changes
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
/*
Preference pref = findPreference(key);
if (pref instanceof EditTextPreference) {
EditTextPreference textPref = (EditTextPreference) pref;
pref.setSummary(textPref.getSummary());
Log.d("KALSMS", "textPref.getSummary(): " + textPref.getSummary());
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
App app = App.getInstance(this.getApplication());
if (key.equals("outgoing_interval"))
{
app.setOutgoingMessageAlarm();
}
if(pref instanceof CheckBoxPreference) {
CheckBoxPreference checkbox = (CheckBoxPreference) pref;
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent pintent = new Intent(this, SMSSender.class);
PendingIntent pIntent = PendingIntent.getBroadcast(this,0,pintent, 0);
if(checkbox.isChecked()) {
alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
AlarmManager.INTERVAL_FIFTEEN_MINUTES, pIntent);
Log.d("KALSMS", "alarm manager turned on");
} else {
alarm.cancel(pIntent);
Log.d("SMS_GATEWAY", "alarm manager turned off");
else if (key.equals("server_url"))
{
app.log("Server URL changed to: " + app.getDisplayString(app.getServerUrl()));
}
else if (key.equals("phone_number"))
{
app.log("Phone number changed to: " + app.getDisplayString(app.getPhoneNumber()));
}
*/
}
else if (key.equals("password"))
{
app.log("Password changed");
}
updatePrefSummary(findPreference(key));
}
private void updatePrefSummary(Preference p)
{
if (p instanceof ListPreference) {
p.setSummary(((ListPreference)p).getEntry());
}
else if (p instanceof EditTextPreference) {
EditTextPreference textPref = (EditTextPreference)p;
String text = textPref.getText();
if (text.equals(""))
{
p.setSummary("(not set)");
}
else if (p.getKey().equals("password"))
{
p.setSummary("********");
}
else
{
p.setSummary(text);
}
}
}
// first time the Menu key is pressed
@Override