mirror of
https://github.com/cwinfo/envayasms.git
synced 2024-11-09 10:20:25 +00:00
refactoring
This commit is contained in:
parent
e535266d9a
commit
e79fe7ce23
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
36
src/org/envaya/kalsms/ForwarderTask.java
Executable file
36
src/org/envaya/kalsms/ForwarderTask.java
Executable 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
50
src/org/envaya/kalsms/IncomingMessage.java
Executable file
50
src/org/envaya/kalsms/IncomingMessage.java
Executable 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
|
107
src/org/envaya/kalsms/OutgoingMessage.java
Executable file
107
src/org/envaya/kalsms/OutgoingMessage.java
Executable 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
19
src/org/envaya/kalsms/PollerTask.java
Executable file
19
src/org/envaya/kalsms/PollerTask.java
Executable 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
src/org/envaya/kalsms/QueuedMessage.java
Executable file
69
src/org/envaya/kalsms/QueuedMessage.java
Executable 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();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user