mirror of
https://github.com/cwinfo/envayasms.git
synced 2025-04-08 17:36:22 +00:00
334 lines
10 KiB
Java
Executable File
334 lines
10 KiB
Java
Executable File
package org.envaya.kalsms;
|
|
|
|
import org.envaya.kalsms.task.PollerTask;
|
|
import org.envaya.kalsms.task.HttpTask;
|
|
import org.envaya.kalsms.receiver.OutgoingMessagePoller;
|
|
import android.app.Activity;
|
|
import android.app.AlarmManager;
|
|
import android.app.Application;
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.SharedPreferences;
|
|
import android.os.SystemClock;
|
|
import android.preference.PreferenceManager;
|
|
import android.telephony.SmsManager;
|
|
import android.text.SpannableStringBuilder;
|
|
import android.util.Log;
|
|
import java.text.DateFormat;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import org.apache.http.message.BasicNameValuePair;
|
|
|
|
public final class App extends Application {
|
|
|
|
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 String STATUS_QUEUED = "queued";
|
|
public static final String STATUS_FAILED = "failed";
|
|
public static final String STATUS_SENT = "sent";
|
|
public static final String LOG_NAME = "KALSMS";
|
|
public static final String LOG_INTENT = "org.envaya.kalsms.LOG";
|
|
|
|
public static final int MAX_DISPLAYED_LOG = 15000;
|
|
public static final int LOG_TIMESTAMP_INTERVAL = 60000;
|
|
|
|
private long lastLogTime = 0;
|
|
private SpannableStringBuilder displayedLog = new SpannableStringBuilder();
|
|
private Map<String, IncomingMessage> incomingSmsMap = new HashMap<String, IncomingMessage>();
|
|
private Map<String, OutgoingMessage> outgoingSmsMap = new HashMap<String, OutgoingMessage>();
|
|
|
|
public SharedPreferences getSettings()
|
|
{
|
|
return PreferenceManager.getDefaultSharedPreferences(this);
|
|
}
|
|
|
|
public void checkOutgoingMessages()
|
|
{
|
|
String serverUrl = getServerUrl();
|
|
if (serverUrl.length() > 0) {
|
|
log("Checking for outgoing messages");
|
|
new PollerTask(this).execute();
|
|
} else {
|
|
log("Can't check outgoing messages; server URL not set");
|
|
}
|
|
}
|
|
|
|
public void setOutgoingMessageAlarm() {
|
|
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
|
|
|
PendingIntent pendingIntent = PendingIntent.getBroadcast(this,
|
|
0,
|
|
new Intent(this, OutgoingMessagePoller.class),
|
|
0);
|
|
|
|
alarm.cancel(pendingIntent);
|
|
|
|
int pollSeconds = getOutgoingPollSeconds();
|
|
|
|
if (isEnabled())
|
|
{
|
|
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 String getDisplayString(String str) {
|
|
if (str.length() == 0) {
|
|
return "(not set)";
|
|
} else {
|
|
return str;
|
|
}
|
|
}
|
|
|
|
public String getServerUrl() {
|
|
return getSettings().getString("server_url", "");
|
|
}
|
|
|
|
public String getPhoneNumber() {
|
|
return getSettings().getString("phone_number", "");
|
|
}
|
|
|
|
public int getOutgoingPollSeconds() {
|
|
return Integer.parseInt(getSettings().getString("outgoing_interval", "0"));
|
|
}
|
|
|
|
public boolean isEnabled()
|
|
{
|
|
return getSettings().getBoolean("enabled", false);
|
|
}
|
|
|
|
public boolean getKeepInInbox()
|
|
{
|
|
return getSettings().getBoolean("keep_in_inbox", false);
|
|
}
|
|
|
|
public String getPassword() {
|
|
return getSettings().getString("password", "");
|
|
}
|
|
|
|
private void notifyStatus(OutgoingMessage sms, String status, String errorMessage) {
|
|
String serverId = sms.getServerId();
|
|
|
|
String logMessage;
|
|
if (status.equals(App.STATUS_SENT)) {
|
|
logMessage = "sent successfully";
|
|
} else if (status.equals(App.STATUS_FAILED)) {
|
|
logMessage = "could not be sent (" + errorMessage + ")";
|
|
} else {
|
|
logMessage = "queued";
|
|
}
|
|
String smsDesc = sms.getLogName();
|
|
|
|
if (serverId != null) {
|
|
log("Notifying server " + smsDesc + " " + logMessage);
|
|
|
|
new HttpTask(this,
|
|
new BasicNameValuePair("id", serverId),
|
|
new BasicNameValuePair("status", status),
|
|
new BasicNameValuePair("error", errorMessage),
|
|
new BasicNameValuePair("action", App.ACTION_SEND_STATUS)
|
|
).execute();
|
|
} else {
|
|
log(smsDesc + " " + logMessage);
|
|
}
|
|
}
|
|
|
|
public synchronized void retryStuckMessages() {
|
|
retryStuckOutgoingMessages();
|
|
retryStuckIncomingMessages();
|
|
}
|
|
|
|
public synchronized int getStuckMessageCount() {
|
|
return outgoingSmsMap.size() + incomingSmsMap.size();
|
|
}
|
|
|
|
public synchronized void retryStuckOutgoingMessages() {
|
|
for (OutgoingMessage sms : outgoingSmsMap.values()) {
|
|
sms.retryNow();
|
|
}
|
|
}
|
|
|
|
public synchronized void retryStuckIncomingMessages() {
|
|
for (IncomingMessage sms : incomingSmsMap.values()) {
|
|
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) {
|
|
OutgoingMessage sms = outgoingSmsMap.get(id);
|
|
|
|
if (sms == null) {
|
|
return;
|
|
}
|
|
|
|
switch (resultCode) {
|
|
case Activity.RESULT_OK:
|
|
this.notifyStatus(sms, App.STATUS_SENT, "");
|
|
break;
|
|
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
|
|
this.notifyStatus(sms, App.STATUS_FAILED, "generic failure");
|
|
break;
|
|
case SmsManager.RESULT_ERROR_RADIO_OFF:
|
|
this.notifyStatus(sms, App.STATUS_FAILED, "radio off");
|
|
break;
|
|
case SmsManager.RESULT_ERROR_NO_SERVICE:
|
|
this.notifyStatus(sms, App.STATUS_FAILED, "no service");
|
|
break;
|
|
case SmsManager.RESULT_ERROR_NULL_PDU:
|
|
this.notifyStatus(sms, App.STATUS_FAILED, "null PDU");
|
|
break;
|
|
default:
|
|
this.notifyStatus(sms, App.STATUS_FAILED, "unknown error");
|
|
break;
|
|
}
|
|
|
|
switch (resultCode) {
|
|
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
|
|
case SmsManager.RESULT_ERROR_RADIO_OFF:
|
|
case SmsManager.RESULT_ERROR_NO_SERVICE:
|
|
if (!sms.scheduleRetry()) {
|
|
outgoingSmsMap.remove(id);
|
|
}
|
|
break;
|
|
default:
|
|
outgoingSmsMap.remove(id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public synchronized void sendOutgoingMessage(OutgoingMessage sms) {
|
|
String id = sms.getId();
|
|
if (outgoingSmsMap.containsKey(id)) {
|
|
log(sms.getLogName() + " already sent, skipping");
|
|
return;
|
|
}
|
|
|
|
outgoingSmsMap.put(id, sms);
|
|
|
|
log("Sending " + sms.getLogName() + " to " + sms.getTo());
|
|
sms.trySend();
|
|
}
|
|
|
|
public synchronized void forwardToServer(IncomingMessage sms) {
|
|
String id = sms.getId();
|
|
|
|
if (incomingSmsMap.containsKey(id)) {
|
|
log("Duplicate incoming SMS, skipping");
|
|
return;
|
|
}
|
|
|
|
incomingSmsMap.put(id, sms);
|
|
|
|
log("Received SMS from " + sms.getFrom());
|
|
|
|
sms.tryForwardToServer();
|
|
}
|
|
|
|
public synchronized void retryIncomingMessage(String id) {
|
|
IncomingMessage sms = incomingSmsMap.get(id);
|
|
if (sms != null) {
|
|
sms.retryNow();
|
|
}
|
|
}
|
|
|
|
public synchronized void retryOutgoingMessage(String id) {
|
|
OutgoingMessage sms = outgoingSmsMap.get(id);
|
|
if (sms != null) {
|
|
sms.retryNow();
|
|
}
|
|
}
|
|
|
|
public void debug(String msg) {
|
|
Log.d(LOG_NAME, msg);
|
|
}
|
|
|
|
public void log(CharSequence msg)
|
|
{
|
|
Log.d(LOG_NAME, msg.toString());
|
|
|
|
// prevent displayed log from growing too big
|
|
int length = displayedLog.length();
|
|
if (length > MAX_DISPLAYED_LOG)
|
|
{
|
|
int startPos = length - MAX_DISPLAYED_LOG * 3 / 4;
|
|
|
|
for (int cur = startPos; cur < startPos + 100 && cur < length; cur++)
|
|
{
|
|
if (displayedLog.charAt(cur) == '\n')
|
|
{
|
|
startPos = cur;
|
|
break;
|
|
}
|
|
}
|
|
|
|
displayedLog.replace(0, startPos, "[Older log messages not shown]\n");
|
|
}
|
|
|
|
// display a timestamp in the log occasionally
|
|
long logTime = SystemClock.elapsedRealtime();
|
|
if (logTime - lastLogTime > LOG_TIMESTAMP_INTERVAL)
|
|
{
|
|
Date date = new Date();
|
|
displayedLog.append("[" + DateFormat.getTimeInstance().format(date) + "]\n");
|
|
lastLogTime = logTime;
|
|
}
|
|
|
|
displayedLog.append(msg);
|
|
displayedLog.append("\n");
|
|
|
|
Intent broadcast = new Intent(App.LOG_INTENT);
|
|
sendBroadcast(broadcast);
|
|
}
|
|
|
|
public CharSequence getDisplayedLog()
|
|
{
|
|
return displayedLog;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|