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