From 29111114906e5eb0d579016aba9552f86a343387 Mon Sep 17 00:00:00 2001 From: Jesse Young Date: Mon, 12 Sep 2011 22:03:25 -0700 Subject: [PATCH] keep messages waiting to be sent in memory; if forwarding fails (in either direction), retry a few times (1m, 10m, 1h, 1d) before giving up; add menu button to retry sending now --- AndroidManifest.xml | 3 - res/drawable-hdpi/ic_menu_magnet.png | Bin 0 -> 2310 bytes res/drawable-ldpi/ic_menu_magnet.png | Bin 0 -> 1078 bytes res/drawable-mdpi/ic_menu_magnet.png | Bin 0 -> 1399 bytes res/menu/mainmenu.xml | 3 + res/values/strings.xml | 1 + src/org/envaya/kalsms/App.java | 359 +++++++++++++++--- src/org/envaya/kalsms/DBHelper.java | 43 --- src/org/envaya/kalsms/HttpTask.java | 20 +- .../kalsms/IncomingMessageForwarder.java | 75 +--- src/org/envaya/kalsms/Main.java | 23 +- .../envaya/kalsms/MessageStatusNotifier.java | 67 +--- .../envaya/kalsms/OutgoingMessagePoller.java | 1 + src/org/envaya/kalsms/OutgoingSmsMessage.java | 19 +- 14 files changed, 373 insertions(+), 241 deletions(-) create mode 100755 res/drawable-hdpi/ic_menu_magnet.png create mode 100755 res/drawable-ldpi/ic_menu_magnet.png create mode 100755 res/drawable-mdpi/ic_menu_magnet.png delete mode 100755 src/org/envaya/kalsms/DBHelper.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ac1e36c..542e101 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -29,9 +29,6 @@ - - - diff --git a/res/drawable-hdpi/ic_menu_magnet.png b/res/drawable-hdpi/ic_menu_magnet.png new file mode 100755 index 0000000000000000000000000000000000000000..c1f5cac5faa0015d5660325527f031f2970e28f5 GIT binary patch literal 2310 zcmV+h3HkPkP)-v8j$GO26 z`{~%&*vC!%xFpg7oj7sgyM;pG1m_%#F;GfD2tgQz5JJH7JP0A076||dA+=XR2q>kY zX__C}w*8B2Hv8_OLx=v=bSF<5gX|5*j~~Cz7~7f6W?@+t7-O~XIOnKVtMGllX@P_g zwZ=Jz=Xr2l7ryTU08~}=vf1q0ot>RW4<0;NXsWZvki>#SE|*gr$HBI=7Jz-YEZG820?%z2p$lrTCHMfX$jS86`XTqGMPdymmBZx?L9FvG9sGlcnwt9 z8*;gvVwxr>r2w#i2SpAeS7>@*^%ht!nD6^=T^HqY8O34|<#IVUJ3BjGtycG)KY#v} z!-o&wZ@Tk36xkar%Yv#d>q-a_gb=^3iTa7UnN|-W1d5`7QVLDeVB0qG`8+mn-i*%9 zPFR+Oa=HA~+1c4Yoj!fKzctbvxJvnF(&d7dn|!w`fJFvg(kIv8V#C-Sv|L?J}YZ5tsL$uvzA zi^b0bLGWz=KTI+nsi%nCi~%sP96?jr-*^(!)6;FYZ{HqrUH5Cg?+*$gw)wsf&bgFG z@jO{97E!HM=WN^l{E;I^7G=jHWkH0HFbqQuz()f2Y@~~xo}T%ho}P~Zyayn2<;s-< zOG`^X0ATvQ-|$8_wrImJAcWW?gg5};E!hDjiGxByNU$n`ZdKy|JOC8{B>;ut;o%Ei zU0vUxl>RNAlYqwNB|-=?nG7^dLl6Ytlzmp9#qQB+B9~SGXfrc2`D@p%z0Wz{B9ko; zLZDKqpi-$UQA#(Bj*hM!pFD!~aFdo1S*rlxc6WEr+qV6k=(C1bwm9OZlv+U$?2#SA zb1hIzB$Bsh&z>uaqFjtWZ&aj6Ao#xjl5F1_LZAo%!0Ybr{=Q744y^3k2q8VPoo*O` z2q7T=e{gVcnh-KAc`qoXV2o{+?RUcn6cGXl!Z7?xlKq;ZDF5FAMOG9DA*N&nimYgJ zv@$l#h7t$>2q8fb1YsO7HflWpKq-|zNs!J-KM%OB`{j5u+vuJzvY@+IIbdjr4Ji=k z{Dmi_epeG^LT6F6Hk}fkj7$R{ zG86TNvY_$t@vl@Wl{eyJC!n#cV=ShoY46MSxnV45a&nTEN~N~}(1e-B#hp8M{t?TE zn+^Nsh7jo3v16aTapT5y-}k?yXH-r$-bshC9N?du-OaWE2>xQoD&~+WA zX+qa^WHK44WAC`wfl~VK;o;$Tu$)IVNeKa^Y zcm#lp=df%&iC2vzF80{(n_0n<5?f7F zbvxJ5(Q#n+?%jV!6Rs7LI1YN05KmK}x+5Jh#-OU|Kelb#_WID!&_`Hyt>&spk)VJo zUTUAEK(R3KscBRh^1=4)+g}+P8u}}MDiW?*t7+JRq7(sD6h#Co`oPw(Bk_kv|vMF3v2ZdH%0i&0foKj56d$T`R8=xDgOxL8YyTb2da zbz34(eNY(3G_g3*dso->cXsU9ak{_1{~lJXre)#)3FaWvG%r^wl@|@efNk3#gw!g` zEX%4zzRfQ6u|RQ)g2hl()jw;R_M6_`-uHIx+VxK?7drR=f>yZHL*r=l!q~;`+_{s# zdiCl_&iOvqb?vy2AlflQ*Y(=}Z!|$b2$?5@d`bxUgb?xxW9-8%TekdqU|?Vx%M-AN zbrdEhX#tfEK*Kr|P+?`i6h!Yu^xg-+>;9HXd?iKt;UbaZvXu2ET>bhqL`g|WNl8gb gNl8gbNm*z92QTp`sq%_iivR!s07*qoM6N<$f??)Jt^fc4 literal 0 HcmV?d00001 diff --git a/res/drawable-ldpi/ic_menu_magnet.png b/res/drawable-ldpi/ic_menu_magnet.png new file mode 100755 index 0000000000000000000000000000000000000000..00d70842cd4977862b51e6e8a6dda96c4abc8680 GIT binary patch literal 1078 zcmV-61j+k}P)DhR$t0PO$QV$u{)q=Kg)L#BcqsN# z8mZv9rEF0ap~XE*PlY`Q-g0PhLA30l6}DK~i-)C$+Cu56Qrm-wLYgMDDKzRhCR68o zJ?y&ZX1=)Fb@$Tz;A0MN-sAUv@4fGPKnERk@IM2o48>ycqf)6f&&&n@1VI31RtF-2 zWm#3*wzuN(_>YN1;`@Ptfp5ph#~W8g=rEhjCj0vO007l$6;jH!!J5q`j*pMwc^*8^ zLl6YN_xASQn304Ch!4P*QsT11%wT2^5rhzk$Kx0t9wuGa->pBtK zHZ}s!^R&gq#a{qmXlMwUrYQrRCv?8oG);r!IEck!FV*YyPXN5rj!uz*4#VPvmJ9%h==4d5oI{hD@9pmH zZj?%;&l`=#o4)T~_(2E($8j9r_g@3>byyH(Q~wtbksKKrIkav2J;N|+%-quSx~?M# zg6panS4!v%`Fy^{%&V$NB%)VdC?O(}p67iR1VQNaAcVN4@>TZGv!c;x1gfdDEDOv` zMAY&YJr{)lz;)eM!xUm>7>2P0#q>TGg%%bTQl97CG))siT+Eq>plRBc%J<3@T3T8% zT-W{7G)?F6DSOyIh{a+*sr;`@p_P@DXti2>eP?Iqwr$(jB9TbTGa`h5ZQFlkv)ONm zD0FpD-oo?q^B>pi^|t|N0O$$!A=9!f&2b#qw%t-HAq0}iB+}{hjY6StS4Bvvkgn@D zbGe+B%jEz7dwY8bfpG;AAfhG_HC2~f*jNJ(rpYu-ba!`i zI-S1P-`{^dpU?k!SwgDyLt9;4{rlkHAi2H04FDJ&9c{Z05fOAn)`}@sr=Jy+*PoF-0`t{rhXin8ZIZ#90%EK7Fz2sZr!@|QBnb2_na}tdGO%Dqm7M?Bg@On z2!a4oN)(GlxUQS%2ki@ubEn(fdcBTHrShlaI74G&W1-EyEeCWim%Ao}_&S@-zH5vD z5kX1`qGb7IjDb?Bb@JHm5FrFS&wD2f!_NRr+w9vSKmY^)2MH2>JNwJa%ekecrT3#K zI$5vRF9ku6snu#7MHpich9RoeYU#?AEBkE;-Se^4D@`YG0CI70@z9eePp((1)ssOG zblgr_YXm_6t@ZJ-v9U+p>|5`FmxYK75h)_t93CEC93CG2SP1b4W2|FR6A@CW6qHid zWPa@e+94t}I5_ysb=^;5?{^bHMDN*bzEJ^9qSL2O|EjhAOKXiz!x#h3dC_L!larH!8yg!xan27h#=4$B zj4`;b`vZWkVS4RbXliOI8%5EHMx*h;`uh5(zVBx|&jax9GChWK4k5(TbLY93UcStRDTrT%zu~=LuB5RnfNgVIjmuc#g zbYyX}pH8P|&YnH{9TIpSNZ8bG#dd!8Li|#ZPN(6z?)T@< z`0(`5(9qui8oj&+#n>Vc6$*t3$8oOb^ZA2uK6ER>w!r`h06YWm6u@FSo&NFEsZ$TL z*=)7B@EyODC28tY09?HMt*x6w#bYNP@6)GGpFVxs`3HpUm3;5hY0v-w002ovPDHLk FV1h(mj%fe@ literal 0 HcmV?d00001 diff --git a/res/menu/mainmenu.xml b/res/menu/mainmenu.xml index 1574d9c..4242500 100755 --- a/res/menu/mainmenu.xml +++ b/res/menu/mainmenu.xml @@ -12,4 +12,7 @@ + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index acbed8b..00fd2e7 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5,4 +5,5 @@ Test Connection Check Messages Help + Retry diff --git a/src/org/envaya/kalsms/App.java b/src/org/envaya/kalsms/App.java index e7b6edb..a1b4c4f 100755 --- a/src/org/envaya/kalsms/App.java +++ b/src/org/envaya/kalsms/App.java @@ -4,25 +4,24 @@ */ package org.envaya.kalsms; +import android.app.Activity; import android.app.AlarmManager; import android.app.PendingIntent; -import android.content.ContentValues; import android.content.Context; 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.telephony.SmsMessage; import android.util.Log; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; public class App { @@ -34,21 +33,87 @@ public class App { 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_NAME = "KALSMS"; public static final String LOG_INTENT = "org.envaya.kalsms.LOG"; - public static final String SEND_STATUS_INTENT = "org.envaya.kalsms.SEND_STATUS"; + + private static App app; + + private Map incomingSmsMap = new HashMap(); + private Map outgoingSmsMap = new HashMap(); public Context context; public SharedPreferences settings; + private class QueuedMessage + { + public T sms; + public long nextAttemptTime = 0; + public int numAttempts = 0; + + public boolean canAttemptNow() + { + return (nextAttemptTime > 0 && nextAttemptTime < System.currentTimeMillis()); + } + + public boolean scheduleNextAttempt() + { + long now = System.currentTimeMillis(); + numAttempts++; + + int sec = 1000; + + if (numAttempts == 1) + { + log("1st failure; retry in 1 minute"); + nextAttemptTime = now + sec * 60; // 1 minute + return true; + } + else if (numAttempts == 2) + { + log("2nd failure; retry in 10 minutes"); + nextAttemptTime = now + sec * 60 * 10; // 10 min + return true; + } + else if (numAttempts == 3) + { + log("3rd failure; retry in 1 hour"); + nextAttemptTime = now + sec * 60 * 60; // 1 hour + return true; + } + else if (numAttempts == 4) + { + log("4th failure: retry in 1 day"); + nextAttemptTime = now + sec * 60 * 60 * 24; // 1 day + return true; + } + else + { + log("5th failure: giving up"); + return false; + } + } + } + + private class QueuedIncomingSms extends QueuedMessage { + public QueuedIncomingSms(SmsMessage sms) + { + this.sms = sms; + } + } + + private class QueuedOutgoingSms extends QueuedMessage { + public QueuedOutgoingSms(OutgoingSmsMessage sms) + { + this.sms = sms; + } + } + 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) @@ -58,7 +123,7 @@ public class App { return app; } - static void debug(String msg) + public void debug(String msg) { Log.d(LOG_NAME, msg); } @@ -174,60 +239,166 @@ public class App { public String getPassword() { return settings.getString("password", ""); - } - - public SQLiteDatabase getWritableDatabase() + } + + private void notifyStatus(OutgoingSmsMessage sms, String status, String errorMessage) { - 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(); - if (serverId != null) + String logMessage; + if (status.equals(App.STATUS_SENT)) { - SQLiteDatabase db = this.getWritableDatabase(); - Cursor cursor = - db.rawQuery("select 1 from sms_status where server_id=?", new String[] { serverId }); + logMessage = "sent successfully"; + } + else if (status.equals(App.STATUS_FAILED)) + { + logMessage = "could not be sent (" + errorMessage + ")"; + } + else + { + logMessage = "queued"; + } + String smsDesc = sms.getLogName(); - boolean exists = (cursor.getCount() > 0); - cursor.close(); - if (exists) - { - log(sms.getLogName() + " already sent, skipping"); - return; - } - - ContentValues values = new ContentValues(); - values.put("server_id", serverId); - values.put("status", App.STATUS_QUEUED); - db.insert("sms_status", null, values); - - db.close(); - } + if (serverId != null) + { + app.log("Notifying server " + smsDesc + " " + logMessage); + new HttpTask(app).execute( + new BasicNameValuePair("id", serverId), + new BasicNameValuePair("status", status), + new BasicNameValuePair("error", errorMessage), + new BasicNameValuePair("action", App.ACTION_SEND_STATUS) + ); + } + else + { + app.log(smsDesc + " " + logMessage); + } + } + + public synchronized void retryStuckMessages(boolean retryAll) + { + retryStuckOutgoingMessages(retryAll); + retryStuckIncomingMessages(retryAll); + } + + public synchronized int getStuckMessageCount() + { + return outgoingSmsMap.size() + incomingSmsMap.size(); + } + + public synchronized void retryStuckOutgoingMessages(boolean retryAll) + { + for (Entry entry : outgoingSmsMap.entrySet()) + { + QueuedOutgoingSms queuedSms = entry.getValue(); + if (retryAll || queuedSms.canAttemptNow()) + { + queuedSms.nextAttemptTime = 0; + + log("Retrying sending " +queuedSms.sms.getLogName() + + " to " + queuedSms.sms.getTo()); + + trySendSMS(queuedSms.sms); + } + } + } + + public synchronized void retryStuckIncomingMessages(boolean retryAll) + { + for (Entry entry : incomingSmsMap.entrySet()) + { + QueuedIncomingSms queuedSms = entry.getValue(); + if (retryAll || queuedSms.canAttemptNow()) + { + queuedSms.nextAttemptTime = 0; + + log("Retrying forwarding SMS from " + queuedSms.sms.getOriginatingAddress()); + + trySendMessageToServer(queuedSms.sms); + } + } + } + + public synchronized void notifyOutgoingMessageStatus(String id, int resultCode) + { + QueuedOutgoingSms queuedSms = outgoingSmsMap.get(id); + + if (queuedSms == null) + { + return; + } + + OutgoingSmsMessage sms = queuedSms.sms; + + 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 (!queuedSms.scheduleNextAttempt()) + { + outgoingSmsMap.remove(id); + } + break; + default: + outgoingSmsMap.remove(id); + break; + } + + } + + public synchronized void sendSMS(OutgoingSmsMessage sms) + { + String id = sms.getId(); + if (outgoingSmsMap.containsKey(id)) + { + log(sms.getLogName() + " already sent, skipping"); + return; + } + + QueuedOutgoingSms queueEntry = new QueuedOutgoingSms(sms); + outgoingSmsMap.put(id, queueEntry); + + log("Sending " +sms.getLogName() + " to " + sms.getTo()); + trySendSMS(sms); + } + + private void trySendSMS(OutgoingSmsMessage sms) + { SmsManager smgr = SmsManager.getDefault(); - Intent intent = new Intent(App.SEND_STATUS_INTENT); - intent.putExtra("serverId", serverId); + Intent intent = new Intent(context, MessageStatusNotifier.class); + intent.putExtra("id", sms.getId()); PendingIntent sentIntent = PendingIntent.getBroadcast( this.context, 0, intent, PendingIntent.FLAG_ONE_SHOT); - - log("Sending " +sms.getLogName() + " to " + sms.getTo()); - smgr.sendTextMessage(sms.getTo(), null, sms.getMessage(), sentIntent, null); + + smgr.sendTextMessage(sms.getTo(), null, sms.getMessage(), sentIntent, null); } private class PollerTask extends HttpTask { @@ -245,4 +416,86 @@ public class App { } } + + 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)) + { + log("Duplicate incoming SMS, skipping"); + return; + } + + QueuedIncomingSms queuedSms = new QueuedIncomingSms(sms); + incomingSmsMap.put(id, queuedSms); + + app.log("Received SMS from " + sms.getOriginatingAddress()); + + trySendMessageToServer(sms); + } + + 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); + } + } + } } diff --git a/src/org/envaya/kalsms/DBHelper.java b/src/org/envaya/kalsms/DBHelper.java deleted file mode 100755 index 6271823..0000000 --- a/src/org/envaya/kalsms/DBHelper.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package org.envaya.kalsms; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; - -/** - * - * @author Jesse - */ -public class DBHelper extends SQLiteOpenHelper { - - private static final int DATABASE_VERSION = 2; - private static final String DATABASE_NAME = "org.envaya.kalsms.db"; - - 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) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - 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 index 74fd4db..56c374b 100755 --- a/src/org/envaya/kalsms/HttpTask.java +++ b/src/org/envaya/kalsms/HttpTask.java @@ -19,7 +19,11 @@ 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.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -36,6 +40,14 @@ public class HttpTask extends AsyncTask this.app = app; } + public HttpClient getHttpClient() + { + HttpParams httpParameters = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParameters, 8000); + HttpConnectionParams.setSoTimeout(httpParameters, 8000); + return new DefaultHttpClient(httpParameters); + } + private String getSignature(String url, BasicNameValuePair... params) { try { @@ -78,6 +90,12 @@ public class HttpTask extends AsyncTask try { String url = app.getServerUrl(); + + if (url.length() == 0) { + app.log("Can't contact server; Server URL not set"); + return null; + } + HttpPost post = new HttpPost(url); post.setEntity(new UrlEncodedFormEntity(Arrays.asList(params))); @@ -88,7 +106,7 @@ public class HttpTask extends AsyncTask post.setHeader("X-Kalsms-PhoneNumber", app.getPhoneNumber()); post.setHeader("X-Kalsms-Signature", signature); - HttpClient client = app.getHttpClient(); + HttpClient client = getHttpClient(); HttpResponse response = client.execute(post); int statusCode = response.getStatusLine().getStatusCode(); diff --git a/src/org/envaya/kalsms/IncomingMessageForwarder.java b/src/org/envaya/kalsms/IncomingMessageForwarder.java index 27e9120..31fdbe5 100755 --- a/src/org/envaya/kalsms/IncomingMessageForwarder.java +++ b/src/org/envaya/kalsms/IncomingMessageForwarder.java @@ -5,79 +5,13 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.telephony.SmsMessage; -import java.util.ArrayList; -import java.util.List; import org.apache.http.HttpResponse; 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; - - } - - 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); - } - } - } - - public void sendMessageToServer(SmsMessage sms) - { - String serverUrl = app.getServerUrl(); - String message = sms.getMessageBody(); - String sender = sms.getOriginatingAddress(); - - app.log("Received SMS from " + sender); - - if (serverUrl.length() == 0) { - app.log("Can't forward SMS to server; Server URL not set"); - } else { - app.log("Forwarding incoming SMS to server"); - - new ForwarderTask(sms).execute( - new BasicNameValuePair("from", sender), - new BasicNameValuePair("message", message), - new BasicNameValuePair("action", App.ACTION_INCOMING) - ); - } - } - - public void smsReceived(Intent intent) { - - for (SmsMessage sms : getMessagesFromIntent(intent)) { - sendMessageToServer(sms); - - //DeleteSMSFromInbox(context, mesg); - } - - } + private App app; @Override // source: http://www.devx.com/wireless/Article/39495/1954 @@ -88,7 +22,12 @@ public class IncomingMessageForwarder extends BroadcastReceiver { String action = intent.getAction(); if (action.equals("android.provider.Telephony.SMS_RECEIVED")) { - smsReceived(intent); + + for (SmsMessage sms : getMessagesFromIntent(intent)) { + app.sendMessageToServer(sms); + + //DeleteSMSFromInbox(context, mesg); + } } } catch (Throwable ex) { app.logError("Unexpected error in IncomingMessageForwarder", ex, true); diff --git a/src/org/envaya/kalsms/Main.java b/src/org/envaya/kalsms/Main.java index cfa9a5c..636bfc2 100755 --- a/src/org/envaya/kalsms/Main.java +++ b/src/org/envaya/kalsms/Main.java @@ -92,22 +92,14 @@ public class Main extends Activity { } }); } } - - - public void onResume() { - App.debug("RESUME"); - super.onResume(); - } - + /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - App.debug("STARTED"); - this.app = App.getInstance(this.getApplication()); - + setContentView(R.layout.main); PreferenceManager.setDefaultValues(this, R.xml.prefs, false); @@ -138,6 +130,9 @@ public class Main extends Activity { case R.id.check_now: app.checkOutgoingMessages(); return true; + case R.id.retry_now: + app.retryStuckMessages(true); + return true; case R.id.help: startActivity(new Intent(this, Help.class)); return true; @@ -161,6 +156,14 @@ public class Main extends Activity { return(true); } + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem item = menu.findItem(R.id.retry_now); + int stuckMessages = app.getStuckMessageCount(); + item.setEnabled(stuckMessages > 0); + item.setTitle("Retry Now (" + stuckMessages + ")"); + return true; + } @Override protected void onStop(){ diff --git a/src/org/envaya/kalsms/MessageStatusNotifier.java b/src/org/envaya/kalsms/MessageStatusNotifier.java index b4b6da0..669fc28 100755 --- a/src/org/envaya/kalsms/MessageStatusNotifier.java +++ b/src/org/envaya/kalsms/MessageStatusNotifier.java @@ -4,76 +4,21 @@ */ package org.envaya.kalsms; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.telephony.SmsManager; -import org.apache.http.message.BasicNameValuePair; public class MessageStatusNotifier extends BroadcastReceiver { - private App app; - - public void notifyStatus(String serverId, String status, String errorMessage) - { - 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 = serverId == null ? "SMS reply" : ("SMS id=" + serverId); - - if (serverId != null) - { - app.log("Notifying server " + smsDesc + " " + logMessage); - - new HttpTask(app).execute( - 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 = App.getInstance(context); - - String serverId = intent.getExtras().getString("serverId"); + App app = App.getInstance(context); - switch (getResultCode()) { - case Activity.RESULT_OK: - this.notifyStatus(serverId, App.STATUS_SENT, ""); - break; - case SmsManager.RESULT_ERROR_GENERIC_FAILURE: - this.notifyStatus(serverId, App.STATUS_FAILED, "generic failure"); - break; - case SmsManager.RESULT_ERROR_RADIO_OFF: - this.notifyStatus(serverId, App.STATUS_FAILED, "radio off"); - break; - case SmsManager.RESULT_ERROR_NO_SERVICE: - this.notifyStatus(serverId, App.STATUS_FAILED, "no service"); - break; - case SmsManager.RESULT_ERROR_NULL_PDU: - this.notifyStatus(serverId, App.STATUS_FAILED, "null PDU"); - break; - default: - this.notifyStatus(serverId, App.STATUS_FAILED, "unknown error"); - break; - } + String id = intent.getExtras().getString("id"); + + int resultCode = getResultCode(); + + app.notifyOutgoingMessageStatus(id, resultCode); } } diff --git a/src/org/envaya/kalsms/OutgoingMessagePoller.java b/src/org/envaya/kalsms/OutgoingMessagePoller.java index a8c9018..6d56aff 100755 --- a/src/org/envaya/kalsms/OutgoingMessagePoller.java +++ b/src/org/envaya/kalsms/OutgoingMessagePoller.java @@ -12,5 +12,6 @@ public class OutgoingMessagePoller extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { app = App.getInstance(context); app.checkOutgoingMessages(); + app.retryStuckMessages(false); } } diff --git a/src/org/envaya/kalsms/OutgoingSmsMessage.java b/src/org/envaya/kalsms/OutgoingSmsMessage.java index bed824a..8b7ddf5 100755 --- a/src/org/envaya/kalsms/OutgoingSmsMessage.java +++ b/src/org/envaya/kalsms/OutgoingSmsMessage.java @@ -6,10 +6,25 @@ public class OutgoingSmsMessage { private String serverId; private String message; private String from; - private String to; + 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()