5
0
mirror of https://github.com/cwinfo/envayasms.git synced 2024-11-09 10:20:25 +00:00

refactoring

This commit is contained in:
Jesse Young 2011-09-13 14:55:26 -07:00
parent e535266d9a
commit e79fe7ce23
10 changed files with 402 additions and 372 deletions

View File

@ -1,26 +1,17 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.envaya.kalsms; package org.envaya.kalsms;
import android.app.Activity; import android.app.Activity;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.SystemClock; import android.os.SystemClock;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.telephony.SmsManager; import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log; import android.util.Log;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.apache.http.HttpResponse;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
public class App { public class App {
@ -34,104 +25,13 @@ public class App {
public static final String LOG_NAME = "KALSMS"; public static final String LOG_NAME = "KALSMS";
public static final String LOG_INTENT = "org.envaya.kalsms.LOG"; public static final String LOG_INTENT = "org.envaya.kalsms.LOG";
private static App app; private static App app;
private Map<String, QueuedIncomingSms> incomingSmsMap = new HashMap<String, QueuedIncomingSms>();
private Map<String, QueuedOutgoingSms> outgoingSmsMap = new HashMap<String, QueuedOutgoingSms>(); private Map<String, IncomingMessage> incomingSmsMap = new HashMap<String, IncomingMessage>();
private Map<String, OutgoingMessage> outgoingSmsMap = new HashMap<String, OutgoingMessage>();
public Context context; public Context context;
public SharedPreferences settings; public SharedPreferences settings;
private abstract class QueuedMessage<T> {
public T sms;
public long nextAttemptTime = 0;
public int numAttempts = 0;
public boolean canAttemptNow() {
return (nextAttemptTime > 0 && nextAttemptTime < SystemClock.elapsedRealtime());
}
public boolean scheduleNextAttempt() {
long now = SystemClock.elapsedRealtime();
numAttempts++;
if (numAttempts > 4) {
log("5th failure: giving up");
return false;
}
int second = 1000;
int minute = second * 60;
if (numAttempts == 1) {
log("1st failure; retry in 1 minute");
nextAttemptTime = now + 1 * minute;
} else if (numAttempts == 2) {
log("2nd failure; retry in 10 minutes");
nextAttemptTime = now + 10 * minute;
} else if (numAttempts == 3) {
log("3rd failure; retry in 1 hour");
nextAttemptTime = now + 60 * minute;
} else {
log("4th failure: retry in 1 day");
nextAttemptTime = now + 24 * 60 * minute;
}
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0,
getAttemptIntent(),
0);
alarm.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
nextAttemptTime,
pendingIntent);
return true;
}
public abstract void attemptNow();
protected abstract Intent getAttemptIntent();
}
private class QueuedIncomingSms extends QueuedMessage<SmsMessage> {
public QueuedIncomingSms(SmsMessage sms) {
this.sms = sms;
}
public void attemptNow() {
log("Retrying forwarding SMS from " + sms.getOriginatingAddress());
trySendMessageToServer(sms);
}
protected Intent getAttemptIntent() {
Intent intent = new Intent(context, IncomingMessageRetry.class);
intent.setData(Uri.parse("kalsms://incoming/" + getSmsId(sms)));
return intent;
}
}
private class QueuedOutgoingSms extends QueuedMessage<OutgoingSmsMessage> {
public QueuedOutgoingSms(OutgoingSmsMessage sms) {
this.sms = sms;
}
public void attemptNow() {
log("Retrying sending " + sms.getLogName() + " to " + sms.getTo());
trySendSMS(sms);
}
protected Intent getAttemptIntent() {
Intent intent = new Intent(context, OutgoingMessageRetry.class);
intent.setData(Uri.parse("kalsms://outgoing/" + sms.getId()));
log("id=" + sms.getId());
return intent;
}
}
protected App(Context context) { protected App(Context context) {
this.context = context; this.context = context;
this.settings = PreferenceManager.getDefaultSharedPreferences(context); this.settings = PreferenceManager.getDefaultSharedPreferences(context);
@ -144,24 +44,12 @@ public class App {
return app; return app;
} }
public void debug(String msg) { public void checkOutgoingMessages()
Log.d(LOG_NAME, msg); {
}
public void log(String msg) {
Log.d(LOG_NAME, msg);
Intent broadcast = new Intent(App.LOG_INTENT);
broadcast.putExtra("message", msg);
context.sendBroadcast(broadcast);
}
public void checkOutgoingMessages() {
String serverUrl = getServerUrl(); String serverUrl = getServerUrl();
if (serverUrl.length() > 0) { if (serverUrl.length() > 0) {
log("Checking for outgoing messages"); log("Checking for outgoing messages");
new PollerTask().execute( new PollerTask(this).execute();
new BasicNameValuePair("action", App.ACTION_OUTGOING));
} else { } else {
log("Can't check outgoing messages; server URL not set"); log("Can't check outgoing messages; server URL not set");
} }
@ -191,28 +79,6 @@ public class App {
} }
} }
public void logError(Throwable ex) {
logError("ERROR", ex);
}
public void logError(String msg, Throwable ex) {
logError(msg, ex, false);
}
public void logError(String msg, Throwable ex, boolean detail) {
log(msg + ": " + ex.getClass().getName() + ": " + ex.getMessage());
if (detail) {
for (StackTraceElement elem : ex.getStackTrace()) {
log(elem.getClassName() + ":" + elem.getMethodName() + ":" + elem.getLineNumber());
}
Throwable innerEx = ex.getCause();
if (innerEx != null) {
logError("Inner exception:", innerEx, true);
}
}
}
public String getDisplayString(String str) { public String getDisplayString(String str) {
if (str.length() == 0) { if (str.length() == 0) {
return "(not set)"; return "(not set)";
@ -246,7 +112,7 @@ public class App {
return settings.getString("password", ""); return settings.getString("password", "");
} }
private void notifyStatus(OutgoingSmsMessage sms, String status, String errorMessage) { private void notifyStatus(OutgoingMessage sms, String status, String errorMessage) {
String serverId = sms.getServerId(); String serverId = sms.getServerId();
String logMessage; String logMessage;
@ -262,11 +128,12 @@ public class App {
if (serverId != null) { if (serverId != null) {
app.log("Notifying server " + smsDesc + " " + logMessage); app.log("Notifying server " + smsDesc + " " + logMessage);
new HttpTask(app).execute( new HttpTask(app,
new BasicNameValuePair("id", serverId), new BasicNameValuePair("id", serverId),
new BasicNameValuePair("status", status), new BasicNameValuePair("status", status),
new BasicNameValuePair("error", errorMessage), new BasicNameValuePair("error", errorMessage),
new BasicNameValuePair("action", App.ACTION_SEND_STATUS)); new BasicNameValuePair("action", App.ACTION_SEND_STATUS)
).execute();
} else { } else {
app.log(smsDesc + " " + logMessage); app.log(smsDesc + " " + logMessage);
} }
@ -282,26 +149,36 @@ public class App {
} }
public synchronized void retryStuckOutgoingMessages() { public synchronized void retryStuckOutgoingMessages() {
for (QueuedOutgoingSms queuedSms : outgoingSmsMap.values()) { for (OutgoingMessage sms : outgoingSmsMap.values()) {
queuedSms.attemptNow(); sms.retryNow();
} }
} }
public synchronized void retryStuckIncomingMessages() { public synchronized void retryStuckIncomingMessages() {
for (QueuedIncomingSms queuedSms : incomingSmsMap.values()) { for (IncomingMessage sms : incomingSmsMap.values()) {
queuedSms.attemptNow(); sms.retryNow();
}
}
public synchronized void setIncomingMessageStatus(IncomingMessage sms, boolean success) {
String id = sms.getId();
if (success)
{
incomingSmsMap.remove(id);
}
else if (!sms.scheduleRetry())
{
incomingSmsMap.remove(id);
} }
} }
public synchronized void notifyOutgoingMessageStatus(String id, int resultCode) { public synchronized void notifyOutgoingMessageStatus(String id, int resultCode) {
QueuedOutgoingSms queuedSms = outgoingSmsMap.get(id); OutgoingMessage sms = outgoingSmsMap.get(id);
if (queuedSms == null) { if (sms == null) {
return; return;
} }
OutgoingSmsMessage sms = queuedSms.sms;
switch (resultCode) { switch (resultCode) {
case Activity.RESULT_OK: case Activity.RESULT_OK:
this.notifyStatus(sms, App.STATUS_SENT, ""); this.notifyStatus(sms, App.STATUS_SENT, "");
@ -327,7 +204,7 @@ public class App {
case SmsManager.RESULT_ERROR_GENERIC_FAILURE: case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
case SmsManager.RESULT_ERROR_RADIO_OFF: case SmsManager.RESULT_ERROR_RADIO_OFF:
case SmsManager.RESULT_ERROR_NO_SERVICE: case SmsManager.RESULT_ERROR_NO_SERVICE:
if (!queuedSms.scheduleNextAttempt()) { if (!sms.scheduleRetry()) {
outgoingSmsMap.remove(id); outgoingSmsMap.remove(id);
} }
break; break;
@ -335,135 +212,82 @@ public class App {
outgoingSmsMap.remove(id); outgoingSmsMap.remove(id);
break; break;
} }
} }
public synchronized void sendSMS(OutgoingSmsMessage sms) { public synchronized void sendOutgoingMessage(OutgoingMessage sms) {
String id = sms.getId(); String id = sms.getId();
if (outgoingSmsMap.containsKey(id)) { if (outgoingSmsMap.containsKey(id)) {
log(sms.getLogName() + " already sent, skipping"); log(sms.getLogName() + " already sent, skipping");
return; return;
} }
QueuedOutgoingSms queueEntry = new QueuedOutgoingSms(sms); outgoingSmsMap.put(id, sms);
outgoingSmsMap.put(id, queueEntry);
log("Sending " + sms.getLogName() + " to " + sms.getTo()); log("Sending " + sms.getLogName() + " to " + sms.getTo());
trySendSMS(sms); sms.trySend();
} }
private void trySendSMS(OutgoingSmsMessage sms) { public synchronized void forwardToServer(IncomingMessage sms) {
SmsManager smgr = SmsManager.getDefault(); String id = sms.getId();
Intent intent = new Intent(context, MessageStatusNotifier.class);
intent.setData(Uri.parse("kalsms://outgoing/" + sms.getId()));
PendingIntent sentIntent = PendingIntent.getBroadcast(
this.context,
0,
intent,
PendingIntent.FLAG_ONE_SHOT);
smgr.sendTextMessage(sms.getTo(), null, sms.getMessage(), sentIntent, null);
}
private class PollerTask extends HttpTask {
public PollerTask() {
super(app);
}
@Override
protected void handleResponse(HttpResponse response) throws Exception {
for (OutgoingSmsMessage reply : parseResponseXML(response)) {
app.sendSMS(reply);
}
}
}
private class ForwarderTask extends HttpTask {
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);
}
app.notifyIncomingMessageStatus(originalSms, true);
}
@Override
protected void handleFailure() {
app.notifyIncomingMessageStatus(originalSms, false);
}
}
private String getSmsId(SmsMessage sms) {
return sms.getOriginatingAddress() + ":" + sms.getMessageBody() + ":" + sms.getTimestampMillis();
}
public synchronized void sendMessageToServer(SmsMessage sms) {
String id = getSmsId(sms);
if (incomingSmsMap.containsKey(id)) { if (incomingSmsMap.containsKey(id)) {
log("Duplicate incoming SMS, skipping"); log("Duplicate incoming SMS, skipping");
return; return;
} }
QueuedIncomingSms queuedSms = new QueuedIncomingSms(sms); incomingSmsMap.put(id, sms);
incomingSmsMap.put(id, queuedSms);
app.log("Received SMS from " + sms.getOriginatingAddress()); app.log("Received SMS from " + sms.getFrom());
trySendMessageToServer(sms); sms.tryForwardToServer();
}
public void trySendMessageToServer(SmsMessage sms) {
String message = sms.getMessageBody();
String sender = sms.getOriginatingAddress();
new ForwarderTask(sms).execute(
new BasicNameValuePair("from", sender),
new BasicNameValuePair("message", message),
new BasicNameValuePair("action", App.ACTION_INCOMING));
}
private synchronized void notifyIncomingMessageStatus(SmsMessage sms, boolean success) {
String id = getSmsId(sms);
QueuedIncomingSms queuedSms = incomingSmsMap.get(id);
if (queuedSms != null) {
if (success || !queuedSms.scheduleNextAttempt()) {
incomingSmsMap.remove(id);
}
}
} }
public synchronized void retryIncomingMessage(String id) { public synchronized void retryIncomingMessage(String id) {
QueuedIncomingSms queuedSms = incomingSmsMap.get(id); IncomingMessage sms = incomingSmsMap.get(id);
if (queuedSms != null) { if (sms != null) {
queuedSms.attemptNow(); sms.retryNow();
} }
} }
public synchronized void retryOutgoingMessage(String id) { public synchronized void retryOutgoingMessage(String id) {
QueuedOutgoingSms queuedSms = outgoingSmsMap.get(id); OutgoingMessage sms = outgoingSmsMap.get(id);
if (queuedSms != null) { if (sms != null) {
queuedSms.attemptNow(); sms.retryNow();
}
}
public void debug(String msg) {
Log.d(LOG_NAME, msg);
}
public void log(String msg) {
Log.d(LOG_NAME, msg);
Intent broadcast = new Intent(App.LOG_INTENT);
broadcast.putExtra("message", msg);
context.sendBroadcast(broadcast);
}
public void logError(Throwable ex) {
logError("ERROR", ex);
}
public void logError(String msg, Throwable ex) {
logError(msg, ex, false);
}
public void logError(String msg, Throwable ex, boolean detail) {
log(msg + ": " + ex.getClass().getName() + ": " + ex.getMessage());
if (detail) {
for (StackTraceElement elem : ex.getStackTrace()) {
log(elem.getClassName() + ":" + elem.getMethodName() + ":" + elem.getLineNumber());
}
Throwable innerEx = ex.getCause();
if (innerEx != null) {
logError("Inner exception:", innerEx, true);
} }
} }
} }
}

View File

@ -0,0 +1,36 @@
package org.envaya.kalsms;
import org.apache.http.HttpResponse;
import org.apache.http.message.BasicNameValuePair;
public class ForwarderTask extends HttpTask {
private IncomingMessage originalSms;
public ForwarderTask(IncomingMessage originalSms, BasicNameValuePair... paramsArr) {
super(originalSms.app, paramsArr);
this.originalSms = originalSms;
params.add(new BasicNameValuePair("action", App.ACTION_INCOMING));
}
@Override
protected String getDefaultToAddress() {
return originalSms.getFrom();
}
@Override
protected void handleResponse(HttpResponse response) throws Exception {
for (OutgoingMessage reply : parseResponseXML(response)) {
app.sendOutgoingMessage(reply);
}
app.setIncomingMessageStatus(originalSms, true);
}
@Override
protected void handleFailure() {
app.setIncomingMessageStatus(originalSms, false);
}
}

View File

@ -33,14 +33,21 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
public class HttpTask extends AsyncTask<BasicNameValuePair, Void, HttpResponse> { public class HttpTask extends AsyncTask<String, Void, HttpResponse> {
private App app; protected App app;
public HttpTask(App app) protected String url;
protected List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
public HttpTask(App app, BasicNameValuePair... paramsArr)
{ {
super(); super();
this.app = app; this.app = app;
this.url = app.getServerUrl();
params = new ArrayList<BasicNameValuePair>(Arrays.asList(paramsArr));
params.add(new BasicNameValuePair("version", "2"));
params.add(new BasicNameValuePair("phone_number", app.getPhoneNumber()));
} }
public HttpClient getHttpClient() public HttpClient getHttpClient()
@ -51,7 +58,7 @@ public class HttpTask extends AsyncTask<BasicNameValuePair, Void, HttpResponse>
return new DefaultHttpClient(httpParameters); return new DefaultHttpClient(httpParameters);
} }
private String getSignature(String url, List<BasicNameValuePair> params) private String getSignature()
throws NoSuchAlgorithmException, UnsupportedEncodingException throws NoSuchAlgorithmException, UnsupportedEncodingException
{ {
Collections.sort(params, new Comparator() { Collections.sort(params, new Comparator() {
@ -83,11 +90,9 @@ public class HttpTask extends AsyncTask<BasicNameValuePair, Void, HttpResponse>
return new String(Base64Coder.encode(digest)); return new String(Base64Coder.encode(digest));
} }
protected HttpResponse doInBackground(BasicNameValuePair... params) { protected HttpResponse doInBackground(String... ignored) {
try try
{ {
String url = app.getServerUrl();
if (url.length() == 0) { if (url.length() == 0) {
app.log("Can't contact server; Server URL not set"); app.log("Can't contact server; Server URL not set");
return null; return null;
@ -95,15 +100,9 @@ public class HttpTask extends AsyncTask<BasicNameValuePair, Void, HttpResponse>
HttpPost post = new HttpPost(url); HttpPost post = new HttpPost(url);
List<BasicNameValuePair> paramList post.setEntity(new UrlEncodedFormEntity(params));
= new ArrayList<BasicNameValuePair>(Arrays.asList(params));
paramList.add(new BasicNameValuePair("version", "2")); String signature = getSignature();
paramList.add(new BasicNameValuePair("phone_number", app.getPhoneNumber()));
post.setEntity(new UrlEncodedFormEntity(paramList));
String signature = this.getSignature(url, paramList);
post.setHeader("X-Kalsms-Signature", signature); post.setHeader("X-Kalsms-Signature", signature);
@ -145,10 +144,10 @@ public class HttpTask extends AsyncTask<BasicNameValuePair, Void, HttpResponse>
return ""; return "";
} }
protected List<OutgoingSmsMessage> parseResponseXML(HttpResponse response) protected List<OutgoingMessage> parseResponseXML(HttpResponse response)
throws IOException, ParserConfigurationException, SAXException throws IOException, ParserConfigurationException, SAXException
{ {
List<OutgoingSmsMessage> messages = new ArrayList<OutgoingSmsMessage>(); List<OutgoingMessage> messages = new ArrayList<OutgoingMessage>();
InputStream responseStream = response.getEntity().getContent(); InputStream responseStream = response.getEntity().getContent();
DocumentBuilder xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); DocumentBuilder xmlBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document xml = xmlBuilder.parse(responseStream); Document xml = xmlBuilder.parse(responseStream);
@ -156,7 +155,8 @@ public class HttpTask extends AsyncTask<BasicNameValuePair, Void, HttpResponse>
NodeList smsNodes = xml.getElementsByTagName("Sms"); NodeList smsNodes = xml.getElementsByTagName("Sms");
for (int i = 0; i < smsNodes.getLength(); i++) { for (int i = 0; i < smsNodes.getLength(); i++) {
Element smsElement = (Element) smsNodes.item(i); Element smsElement = (Element) smsNodes.item(i);
OutgoingSmsMessage sms = new OutgoingSmsMessage();
OutgoingMessage sms = new OutgoingMessage(app);
sms.setFrom(app.getPhoneNumber()); sms.setFrom(app.getPhoneNumber());
@ -169,7 +169,7 @@ public class HttpTask extends AsyncTask<BasicNameValuePair, Void, HttpResponse>
sms.setServerId(serverId.equals("") ? null : serverId); sms.setServerId(serverId.equals("") ? null : serverId);
Node firstChild = smsElement.getFirstChild(); Node firstChild = smsElement.getFirstChild();
sms.setMessage(firstChild != null ? firstChild.getNodeValue(): ""); sms.setMessageBody(firstChild != null ? firstChild.getNodeValue(): "");
messages.add(sms); messages.add(sms);
} }

View File

@ -0,0 +1,50 @@
package org.envaya.kalsms;
import android.content.Intent;
import android.net.Uri;
import android.telephony.SmsMessage;
import org.apache.http.message.BasicNameValuePair;
public class IncomingMessage extends QueuedMessage {
public SmsMessage sms;
public IncomingMessage(App app, SmsMessage sms) {
super(app);
this.sms = sms;
}
public String getMessageBody()
{
return sms.getMessageBody();
}
public String getFrom()
{
return sms.getOriginatingAddress();
}
public String getId()
{
return sms.getOriginatingAddress() + ":" + sms.getMessageBody() + ":" + sms.getTimestampMillis();
}
public void retryNow() {
app.log("Retrying forwarding SMS from " + sms.getOriginatingAddress());
tryForwardToServer();
}
public void tryForwardToServer() {
new ForwarderTask(this,
new BasicNameValuePair("from", getFrom()),
new BasicNameValuePair("message", getMessageBody())
).execute();
}
protected Intent getRetryIntent() {
Intent intent = new Intent(app.context, IncomingMessageRetry.class);
intent.setData(Uri.parse("kalsms://incoming/" + this.getId()));
return intent;
}
}

View File

@ -5,6 +5,8 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.telephony.SmsMessage; import android.telephony.SmsMessage;
import java.util.ArrayList;
import java.util.List;
public class IncomingMessageForwarder extends BroadcastReceiver { public class IncomingMessageForwarder extends BroadcastReceiver {
@ -21,8 +23,8 @@ public class IncomingMessageForwarder extends BroadcastReceiver {
if (action.equals("android.provider.Telephony.SMS_RECEIVED")) { if (action.equals("android.provider.Telephony.SMS_RECEIVED")) {
for (SmsMessage sms : getMessagesFromIntent(intent)) { for (IncomingMessage sms : getMessagesFromIntent(intent)) {
app.sendMessageToServer(sms); app.forwardToServer(sms);
} }
if (!app.getKeepInInbox()) if (!app.getKeepInInbox())
@ -37,15 +39,16 @@ public class IncomingMessageForwarder extends BroadcastReceiver {
// from http://github.com/dimagi/rapidandroid // from http://github.com/dimagi/rapidandroid
// source: http://www.devx.com/wireless/Article/39495/1954 // source: http://www.devx.com/wireless/Article/39495/1954
private SmsMessage[] getMessagesFromIntent(Intent intent) { private List<IncomingMessage> getMessagesFromIntent(Intent intent)
SmsMessage retMsgs[] = null; {
Bundle bdl = intent.getExtras(); Bundle bundle = intent.getExtras();
Object pdus[] = (Object[]) bdl.get("pdus"); List<IncomingMessage> messages = new ArrayList<IncomingMessage>();
retMsgs = new SmsMessage[pdus.length];
for (int n = 0; n < pdus.length; n++) { for (Object pdu : (Object[]) bundle.get("pdus"))
byte[] byteData = (byte[]) pdus[n]; {
retMsgs[n] = SmsMessage.createFromPdu(byteData); SmsMessage sms = SmsMessage.createFromPdu((byte[]) pdu);
messages.add(new IncomingMessage(app, sms));
} }
return retMsgs; return messages;
} }
} }

View File

@ -34,14 +34,13 @@ public class Main extends Activity {
private class TestTask extends HttpTask private class TestTask extends HttpTask
{ {
public TestTask() { public TestTask() {
super(app); super(Main.this.app, new BasicNameValuePair("action", App.ACTION_OUTGOING));
} }
@Override @Override
protected void handleResponse(HttpResponse response) throws Exception protected void handleResponse(HttpResponse response) throws Exception
{ {
parseResponseXML(response); parseResponseXML(response);
app.log("Server connection OK!"); app.log("Server connection OK!");
} }
} }
@ -138,9 +137,7 @@ public class Main extends Activity {
return true; return true;
case R.id.test: case R.id.test:
app.log("Testing server connection..."); app.log("Testing server connection...");
new TestTask().execute( new TestTask().execute();
new BasicNameValuePair("action", App.ACTION_OUTGOING)
);
return true; return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);

View File

@ -0,0 +1,107 @@
package org.envaya.kalsms;
import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
import android.telephony.SmsManager;
public class OutgoingMessage extends QueuedMessage {
private String serverId;
private String message;
private String from;
private String to;
private String localId;
private static int nextLocalId = 1;
public OutgoingMessage(App app)
{
super(app);
this.localId = "_o" + getNextLocalId();
}
static synchronized int getNextLocalId()
{
return nextLocalId++;
}
public String getId()
{
return (serverId == null) ? localId : serverId;
}
public String getLogName()
{
return (serverId == null) ? "SMS reply" : ("SMS id=" + serverId);
}
public String getServerId()
{
return serverId;
}
public void setServerId(String id)
{
this.serverId = id;
}
public String getMessageBody()
{
return message;
}
public void setMessageBody(String message)
{
this.message = message;
}
public String getFrom()
{
return from;
}
public void setFrom(String from)
{
this.from = from;
}
public String getTo()
{
return to;
}
public void setTo(String to)
{
this.to = to;
}
public void retryNow() {
app.log("Retrying sending " + getLogName() + " to " + getTo());
trySend();
}
public void trySend()
{
SmsManager smgr = SmsManager.getDefault();
Intent intent = new Intent(app.context, MessageStatusNotifier.class);
intent.setData(Uri.parse("kalsms://outgoing/" + getId()));
PendingIntent sentIntent = PendingIntent.getBroadcast(
app.context,
0,
intent,
PendingIntent.FLAG_ONE_SHOT);
smgr.sendTextMessage(getTo(), null, getMessageBody(), sentIntent, null);
}
protected Intent getRetryIntent() {
Intent intent = new Intent(app.context, OutgoingMessageRetry.class);
intent.setData(Uri.parse("kalsms://outgoing/" + getId()));
return intent;
}
}

View File

@ -1,75 +0,0 @@
package org.envaya.kalsms;
public class OutgoingSmsMessage {
private String serverId;
private String message;
private String from;
private String to;
private String localId;
private static int nextLocalId = 1;
public OutgoingSmsMessage()
{
this.localId = "_o" + getNextLocalId();
}
static synchronized int getNextLocalId()
{
return nextLocalId++;
}
public String getId()
{
return (serverId == null) ? localId : serverId;
}
public String getLogName()
{
return (serverId == null) ? "SMS reply" : ("SMS id=" + serverId);
}
public String getServerId()
{
return serverId;
}
public void setServerId(String id)
{
this.serverId = id;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
public String getFrom()
{
return from;
}
public void setFrom(String from)
{
this.from = from;
}
public String getTo()
{
return to;
}
public void setTo(String to)
{
this.to = to;
}
}

View File

@ -0,0 +1,19 @@
package org.envaya.kalsms;
import org.apache.http.HttpResponse;
import org.apache.http.message.BasicNameValuePair;
public class PollerTask extends HttpTask {
public PollerTask(App app) {
super(app, new BasicNameValuePair("action", App.ACTION_OUTGOING));
}
@Override
protected void handleResponse(HttpResponse response) throws Exception {
for (OutgoingMessage reply : parseResponseXML(response)) {
app.sendOutgoingMessage(reply);
}
}
}

View File

@ -0,0 +1,69 @@
package org.envaya.kalsms;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
public abstract class QueuedMessage
{
protected long nextRetryTime = 0;
protected int numRetries = 0;
public App app;
public QueuedMessage(App app)
{
this.app = app;
}
public boolean canRetryNow() {
return (nextRetryTime > 0 && nextRetryTime < SystemClock.elapsedRealtime());
}
public boolean scheduleRetry() {
long now = SystemClock.elapsedRealtime();
numRetries++;
if (numRetries > 4) {
app.log("5th failure: giving up");
return false;
}
int second = 1000;
int minute = second * 60;
if (numRetries == 1) {
app.log("1st failure; retry in 1 minute");
nextRetryTime = now + 1 * minute;
} else if (numRetries == 2) {
app.log("2nd failure; retry in 10 minutes");
nextRetryTime = now + 10 * minute;
} else if (numRetries == 3) {
app.log("3rd failure; retry in 1 hour");
nextRetryTime = now + 60 * minute;
} else {
app.log("4th failure: retry in 1 day");
nextRetryTime = now + 24 * 60 * minute;
}
AlarmManager alarm = (AlarmManager) app.context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(app.context,
0,
getRetryIntent(),
0);
alarm.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
nextRetryTime,
pendingIntent);
return true;
}
public abstract void retryNow();
protected abstract Intent getRetryIntent();
}