PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/clients/android/branches/response-codes/src/org/moca/util/BackgroundUploader.java

http://moca.googlecode.com/
Java | 343 lines | 293 code | 32 blank | 18 comment | 36 complexity | a1e87ba8db85b5544f6bca09bfa5ae09 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
  1. package org.moca.util;
  2. import java.util.PriorityQueue;
  3. import java.util.Timer;
  4. import java.util.TimerTask;
  5. import org.moca.Constants;
  6. import org.moca.db.MocaDB.ProcedureSQLFormat;
  7. import org.moca.db.MocaDB.SavedProcedureSQLFormat;
  8. import org.moca.net.MDSInterface;
  9. import android.app.Service;
  10. import android.content.ContentResolver;
  11. import android.content.ContentUris;
  12. import android.content.ContentValues;
  13. import android.content.Context;
  14. import android.content.Intent;
  15. import android.database.Cursor;
  16. import android.net.Uri;
  17. import android.net.wifi.WifiManager;
  18. import android.os.Binder;
  19. import android.os.IBinder;
  20. import android.os.Looper;
  21. import android.telephony.TelephonyManager;
  22. import android.util.Log;
  23. import android.view.Gravity;
  24. import android.widget.Toast;
  25. /**
  26. * Background service to upload pending transfers when data service is available.
  27. * NOTE: This is used in conjunction with DataConnectionListener which also offloads
  28. * pending transfers, but only on a no connection -> connection transition.
  29. * This class will try to offload the pending transfers at a certain interval -
  30. * useful because we may have given up on a transfer during an episode with virtually
  31. * no usable connection, but technically still within GPRS service area (so a transition
  32. * never occurs).
  33. */
  34. public class BackgroundUploader extends Service {
  35. private final IBinder mBinder = new LocalBinder();
  36. private static TelephonyManager telMan;
  37. private static WifiManager wifiMan;
  38. private Timer timer = new Timer();
  39. private static final long POLL_INTERVAL = Constants.DEFAULT_POLL_PERIOD * 1000;
  40. private static PriorityQueue<Uri> queue = new PriorityQueue<Uri>();
  41. private static final String TAG = BackgroundUploader.class.toString();
  42. private static boolean uploadFlag = false;
  43. private static boolean result = false;
  44. private static ContentResolver contentResolver;
  45. private static boolean isCredentialsValidated = false;
  46. private static final String[] PROJECTION = {
  47. SavedProcedureSQLFormat._ID,
  48. SavedProcedureSQLFormat.GUID,
  49. SavedProcedureSQLFormat.PROCEDURE_ID,
  50. SavedProcedureSQLFormat.UPLOAD_QUEUE };
  51. public class LocalBinder extends Binder {
  52. BackgroundUploader getService() {
  53. return BackgroundUploader.this;
  54. }
  55. }
  56. @Override
  57. public void onCreate() {
  58. //Log.i(TAG, "in oncreate()");
  59. try {
  60. super.onCreate();
  61. startService();
  62. telMan = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
  63. wifiMan = (WifiManager) getSystemService(WIFI_SERVICE);
  64. contentResolver = getContentResolver();
  65. initQueue(getBaseContext());
  66. }
  67. catch (Exception e) {
  68. Log.e(TAG, "Exception creating background uploading service: " + e.toString());
  69. }
  70. }
  71. @Override
  72. public void onDestroy() {
  73. super.onDestroy();
  74. shutdownService();
  75. }
  76. @Override
  77. public IBinder onBind(Intent intent) {
  78. return mBinder;
  79. }
  80. //Used by methods in this class, as well as in other classes,
  81. //such as in Settings.java before validating password.
  82. public static boolean checkConnection(Context c, ContentResolver cr) {
  83. try {
  84. boolean hasConnection;
  85. telMan = (TelephonyManager) c.getSystemService(Context.TELEPHONY_SERVICE);
  86. wifiMan = (WifiManager) c.getSystemService(WIFI_SERVICE);
  87. hasConnection = telMan.getDataState() == TelephonyManager.DATA_CONNECTED || (wifiMan.isWifiEnabled() && wifiMan.pingSupplicant());
  88. Log.i(TAG, "checkConnection is returning: " + hasConnection);
  89. if (hasConnection) {
  90. try {
  91. ContentValues cv = new ContentValues();
  92. Log.i(TAG, "Here is the queue right now: " + queue);
  93. Log.i(TAG, "Right now the value of isCredentialsValidated is: " + isCredentialsValidated);
  94. if (isCredentialsValidated)
  95. cv.put(SavedProcedureSQLFormat.UPLOAD_STATUS, 1); // Signify procedures waiting in queue to be uploaded
  96. else
  97. cv.put(SavedProcedureSQLFormat.UPLOAD_STATUS, 6); // Signify username/password incorrect
  98. for (Uri procedure: queue) {
  99. Log.i(TAG, "Updating queue status in checkConnection for procedure: " + procedure);
  100. cr.update(procedure, cv, null, null);
  101. }
  102. } catch (Exception e) {
  103. Log.e(TAG, "Exception updating upload status in database: " + e.toString());
  104. }
  105. return true;
  106. } else {
  107. try {
  108. ContentValues cv = new ContentValues();
  109. cv.put(SavedProcedureSQLFormat.UPLOAD_STATUS, 4); // Signify procedures waiting for connectivity to upload
  110. for (Uri procedure: queue)
  111. cr.update(procedure, cv, null, null);
  112. } catch (Exception e) {
  113. Log.e(TAG, "Exception updating upload status in database: " + e.toString());
  114. }
  115. return false;
  116. }
  117. }
  118. catch (Exception e) {
  119. Log.e(TAG, "Exception in checkConnection(): " + e.toString());
  120. return false;
  121. }
  122. }
  123. private void startService() {
  124. try {
  125. //Log.i(TAG, "Service has been started for background uploading.");
  126. if (checkConnection(getBaseContext(), contentResolver)) {
  127. isCredentialsValidated = MDSInterface.validateCredentials(getBaseContext());
  128. }
  129. timer.scheduleAtFixedRate(
  130. new TimerTask() {
  131. public void run() {
  132. if (checkConnection(getBaseContext(), contentResolver)) {
  133. Log.i(TAG, "startService is calling inituploads");
  134. initUploads(getBaseContext());
  135. }
  136. }
  137. },
  138. 0,
  139. POLL_INTERVAL);
  140. }
  141. catch (Exception e) {
  142. Log.e(TAG, "Exception in starting service: " + e.toString());
  143. }
  144. }
  145. private void shutdownService() {
  146. if (timer != null)
  147. timer.cancel();
  148. }
  149. public static boolean addProcedureToQueue(Uri procedureUri) {
  150. Log.i(TAG, "addProcedureToQueue" + procedureUri);
  151. boolean result = queue.add(procedureUri);
  152. Log.i(TAG, "Queue is now: " + queue.toString());
  153. updateQueueInDB();
  154. if (isCredentialsValidated) {
  155. ContentValues cv = new ContentValues();
  156. cv.put(SavedProcedureSQLFormat.UPLOAD_STATUS, 1); //Signify waiting in queue to be uploaded
  157. contentResolver.update(procedureUri, cv, null, null);
  158. } else {
  159. ContentValues cv = new ContentValues();
  160. cv.put(SavedProcedureSQLFormat.UPLOAD_STATUS, 6); // Signify procedures incorrect username/password
  161. contentResolver.update(procedureUri, cv, null, null);
  162. }
  163. Log.i(TAG, "Succeeded in adding procedure to upload queue? " + result);
  164. return result;
  165. }
  166. public static boolean removeFromQueue(Uri procedureUri) {
  167. Log.i(TAG, "removeFromQueue" + procedureUri);
  168. if (isInQueue(procedureUri)) {
  169. queue.remove(procedureUri);
  170. updateQueueInDB();
  171. ContentValues cv = new ContentValues();
  172. cv.put(SavedProcedureSQLFormat.UPLOAD_QUEUE, -1);
  173. contentResolver.update(procedureUri, cv, null, null); // Signify no longer in queue
  174. return true;
  175. }
  176. return false;
  177. }
  178. public static boolean isInQueue(Uri procedureUri) {
  179. return queue.contains(procedureUri);
  180. }
  181. public static int queueIndex(Uri procedureUri) {
  182. if (isInQueue(procedureUri)) {
  183. Object[] queueList = queue.toArray();
  184. for (int i = 0; i < queueList.length; i++) {
  185. Uri test = (Uri) queueList[i];
  186. if (test.equals(procedureUri))
  187. return i;
  188. }
  189. }
  190. return -1;
  191. }
  192. //Only check credentials with openMRS when username or password have changed in settings
  193. public static void credentialsChanged(boolean credentials, ContentResolver cr) {
  194. isCredentialsValidated = credentials;
  195. Log.i(TAG, "Queue is now: " + queue);
  196. if (isCredentialsValidated) {
  197. Log.i(TAG, "In credentialsChanged, Validate Credentials returned true");
  198. ContentValues cv = new ContentValues();
  199. cv.put(SavedProcedureSQLFormat.UPLOAD_STATUS, 1); // Signify procedures waiting in queue to be uploaded
  200. for (Uri procedure: queue)
  201. cr.update(procedure, cv, null, null);
  202. } else {
  203. Log.i(TAG, "In credentialsChanged, Validate Credentials returned false - could not validate OpenMRS username/password");
  204. ContentValues cv = new ContentValues();
  205. cv.put(SavedProcedureSQLFormat.UPLOAD_STATUS, 6); // Signify procedures incorrect username/password
  206. for (Uri procedure: queue)
  207. cr.update(procedure, cv, null, null);
  208. }
  209. }
  210. public static void initUploads(final Context c) {
  211. // check if there are pending transfers in the database
  212. // if so, then spawn a thread to upload the first one
  213. Log.i(TAG, "initUploads has been called.");
  214. uploadFlag = (!queue.isEmpty()) && checkConnection(c, contentResolver);
  215. if (uploadFlag) {
  216. if (!isCredentialsValidated) {
  217. Log.i(TAG, "OpenMRS username/password incorrect - will not attempt to upload");
  218. }
  219. else {
  220. Thread t = new Thread() {
  221. public void run() {
  222. Looper.prepare();
  223. while (uploadFlag) {
  224. Uri procedure = queue.element();
  225. ContentValues cv = new ContentValues();
  226. cv.put(SavedProcedureSQLFormat.UPLOAD_STATUS, 3);
  227. contentResolver.update(procedure, cv, null, null); // Signify procedure upload in progress
  228. result = MDSInterface.postProcedureToDjangoServer(procedure, c);
  229. if(result) uploadSuccess(c, procedure);
  230. else if (!result) uploadFailure(c, procedure);
  231. uploadFlag = !queue.isEmpty() && checkConnection(c, contentResolver);
  232. Log.i(TAG, "uploadFlag after a loop: " + uploadFlag);
  233. }
  234. Looper.loop();
  235. Looper.myLooper().quit();
  236. }
  237. };
  238. Log.i(TAG, "running a new thread to do the upload.");
  239. t.start();
  240. }
  241. }
  242. }
  243. private static void uploadSuccess(Context c, Uri procedure) {
  244. Log.i(TAG, "Upload was successfull. Now in uploadSuccess()");
  245. removeFromQueue(procedure); //Remove the procedure from the queue after it has been successfully uploaded
  246. ContentValues cv = new ContentValues();
  247. cv.put(SavedProcedureSQLFormat.UPLOAD_STATUS, 2); // Signify has been uploaded
  248. contentResolver.update(procedure, cv, null, null); // Signify has been uploaded
  249. Cursor cursor = c.getContentResolver().query(procedure, new String [] { SavedProcedureSQLFormat._ID, SavedProcedureSQLFormat.PROCEDURE_ID, SavedProcedureSQLFormat.PROCEDURE_STATE }, null, null, null);
  250. cursor.moveToFirst();
  251. long savedProcedureId = cursor.getLong(cursor.getColumnIndex(SavedProcedureSQLFormat._ID));
  252. long procedureId = cursor.getLong(cursor.getColumnIndex(SavedProcedureSQLFormat.PROCEDURE_ID));
  253. cursor.deactivate();
  254. int sizeOfQueue = queue.size();
  255. Uri procedureUri = ContentUris.withAppendedId(ProcedureSQLFormat.CONTENT_URI, procedureId);;
  256. cursor = c.getContentResolver().query(procedureUri, new String[] { ProcedureSQLFormat.TITLE }, null, null, null);
  257. cursor.moveToFirst();
  258. String procedureTitle = cursor.getString(cursor.getColumnIndex(ProcedureSQLFormat.TITLE));
  259. cursor.deactivate();
  260. //String msg = "Successfully sent " + procedureTitle + " procedure\nwith ID = " + savedProcedureId;
  261. String msg = "Successfully sent procedure\nwith ID = " + savedProcedureId;
  262. if (sizeOfQueue != 0) {
  263. msg += "\nThere are still " + sizeOfQueue + "\nprocedures to be uploaded.";
  264. }
  265. else {
  266. msg += "\nAll procedures are done uploading.";
  267. }
  268. Toast toast = Toast.makeText(c, msg, Toast.LENGTH_LONG);
  269. toast.setGravity(Gravity.CENTER, 0, 0);
  270. toast.show();
  271. }
  272. private static void uploadFailure(Context c, Uri procedure) {
  273. Log.i(TAG, "Upload failed. Now in uploadFailure()");
  274. removeFromQueue(procedure); //Remove the procedure from the queue so it does not keep trying to upload
  275. ContentValues cv = new ContentValues();
  276. cv.put(SavedProcedureSQLFormat.UPLOAD_STATUS, 5); // Signify did not successfully upload
  277. contentResolver.update(procedure, cv, null, null);
  278. }
  279. private static void updateQueueInDB() {
  280. Log.i(TAG, "Updating queue information in the database");
  281. Log.i(TAG, "Queue is now: " + queue.toString());
  282. ContentValues cv;
  283. for (Uri procedureUri: queue) {
  284. cv = new ContentValues();
  285. Log.i(TAG, "In updateQueueInDB, queueIndex(" + procedureUri + ") returns: " + queueIndex(procedureUri));
  286. cv.put(SavedProcedureSQLFormat.UPLOAD_QUEUE, queueIndex(procedureUri));
  287. contentResolver.update(procedureUri, cv, null, null);
  288. }
  289. }
  290. private static void initQueue(Context c) {
  291. try {
  292. //Initialize the queue from the database
  293. Log.i(TAG, "In initQueue - getting queue from database");
  294. Cursor cursor = c.getContentResolver().query(SavedProcedureSQLFormat.CONTENT_URI, PROJECTION,
  295. SavedProcedureSQLFormat.UPLOAD_QUEUE + " >= 0",
  296. null, SavedProcedureSQLFormat.QUEUE_SORT_ORDER);
  297. cursor.moveToFirst();
  298. while (!cursor.isAfterLast()) {
  299. int savedProcedureId = cursor.getInt(0);
  300. Log.i(TAG, "savedProcedureId is: " + savedProcedureId);
  301. Uri savedProcedureUri = ContentUris.withAppendedId(SavedProcedureSQLFormat.CONTENT_URI, savedProcedureId);
  302. Log.i(TAG, "Adding procedure with this Uri to the queue: " + savedProcedureUri);
  303. queue.add(savedProcedureUri);
  304. cursor.moveToNext();
  305. }
  306. Log.i(TAG, "Queue has been extracted from database. Here is the queue: " + queue);
  307. } catch (Exception e) {
  308. Log.e(TAG, "Exception in getting queue from database: " + e.toString());
  309. }
  310. }
  311. }