diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7de0f06..023fbb9 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -23,10 +23,7 @@
-
-
-
-
+
@@ -34,9 +31,11 @@
+
+
+
-
\ No newline at end of file
diff --git a/default.properties b/default.properties
index 9d6f70d..4735723 100644
--- a/default.properties
+++ b/default.properties
@@ -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
diff --git a/local.properties b/local.properties
deleted file mode 100755
index 407948f..0000000
--- a/local.properties
+++ /dev/null
@@ -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
diff --git a/res/layout/main.xml b/res/layout/main.xml
index 634ae00..61c6b84 100755
--- a/res/layout/main.xml
+++ b/res/layout/main.xml
@@ -3,11 +3,14 @@
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:background="#333333">
+
+
+
\ No newline at end of file
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
new file mode 100755
index 0000000..49f28f1
--- /dev/null
+++ b/res/values/arrays.xml
@@ -0,0 +1,27 @@
+
+
+
+
+ - 15 sec
+ - 30 sec
+ - 1 minute
+ - 2 minutes
+ - 5 minutes
+ - 10 minutes
+ - 30 minutes
+ - 1 hour
+ - never
+
+
+
+ - 15
+ - 30
+ - 60
+ - 120
+ - 300
+ - 600
+ - 1800
+ - 3600
+ - 0
+
+
\ No newline at end of file
diff --git a/res/xml/prefs.xml b/res/xml/prefs.xml
index e3d2c3c..2ba5cb1 100755
--- a/res/xml/prefs.xml
+++ b/res/xml/prefs.xml
@@ -4,13 +4,13 @@
+ android:defaultValue=""
+ >
+
+
+
\ No newline at end of file
diff --git a/src/org/envaya/kalsms/App.java b/src/org/envaya/kalsms/App.java
index 97866ed..de1a4b9 100755
--- a/src/org/envaya/kalsms/App.java
+++ b/src/org/envaya/kalsms/App.java
@@ -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();
diff --git a/src/org/envaya/kalsms/DBHelper.java b/src/org/envaya/kalsms/DBHelper.java
index 0710128..6271823 100755
--- a/src/org/envaya/kalsms/DBHelper.java
+++ b/src/org/envaya/kalsms/DBHelper.java
@@ -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);
+ }
}
}
diff --git a/src/org/envaya/kalsms/HttpTask.java b/src/org/envaya/kalsms/HttpTask.java
new file mode 100755
index 0000000..8eedc33
--- /dev/null
+++ b/src/org/envaya/kalsms/HttpTask.java
@@ -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 {
+
+ 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 parseResponseXML(HttpResponse response)
+ throws IOException, ParserConfigurationException, SAXException
+ {
+ List messages = new ArrayList();
+ 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()
+ {
+ }
+}
diff --git a/src/org/envaya/kalsms/IncomingMessageForwarder.java b/src/org/envaya/kalsms/IncomingMessageForwarder.java
index 3eb2727..bb336fb 100755
--- a/src/org/envaya/kalsms/IncomingMessageForwarder.java
+++ b/src/org/envaya/kalsms/IncomingMessageForwarder.java
@@ -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 retryList = new ArrayList();
+
+ private class SmsStatus
+ {
+ public SmsMessage smsMessage;
+ public long nextAttemptTime;
+ public int numAttempts = 0;
+
+ }
- public List 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();
- }
-
- List replies = new ArrayList();
-
- try {
-
- List params = new ArrayList();
- 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 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();
diff --git a/src/org/envaya/kalsms/Main.java b/src/org/envaya/kalsms/Main.java
index a4e4e8f..2ac991c 100755
--- a/src/org/envaya/kalsms/Main.java
+++ b/src/org/envaya/kalsms/Main.java
@@ -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("SMS Gateway running.
"));
+ info.append(Html.fromHtml("Press Menu to edit settings.
"));
- 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("Press Menu to edit settings.
"));
+ 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
diff --git a/src/org/envaya/kalsms/MessageStatusNotifier.java b/src/org/envaya/kalsms/MessageStatusNotifier.java
index a2bad10..f059a80 100755
--- a/src/org/envaya/kalsms/MessageStatusNotifier.java
+++ b/src/org/envaya/kalsms/MessageStatusNotifier.java
@@ -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 params = new ArrayList();
- 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;
}
}
diff --git a/src/org/envaya/kalsms/OutgoingMessagePoller.java b/src/org/envaya/kalsms/OutgoingMessagePoller.java
index f204cac..5a4c085 100755
--- a/src/org/envaya/kalsms/OutgoingMessagePoller.java
+++ b/src/org/envaya/kalsms/OutgoingMessagePoller.java
@@ -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 getOutgoingMessages() {
- List messages = new ArrayList();
-
- try {
- List params = new ArrayList();
- 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)
+ );
+ }
}
-
}
diff --git a/src/org/envaya/kalsms/Prefs.java b/src/org/envaya/kalsms/Prefs.java
index 4d00416..c254e9b 100755
--- a/src/org/envaya/kalsms/Prefs.java
+++ b/src/org/envaya/kalsms/Prefs.java
@@ -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