diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4c5848f9..8441bcbc 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -30,7 +30,7 @@
-
+
@@ -51,6 +51,8 @@
+
+
this.appInst.nodestackSize()) {
- this.setResult(RESULT_DONTPOP);
- finish();
- }
+ public void onResume() {
refreshDisplay();
+
+ IntentFilter serviceFilter = new IntentFilter(SyncService.SYNC_UPDATE);
+ registerReceiver(syncReceiver, serviceFilter);
+
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ unregisterReceiver(this.syncReceiver);
+ super.onPause();
}
/**
@@ -96,6 +101,12 @@ public void onResume() {
* data has been updated.
*/
private void refreshDisplay() {
+ // If this is the case, the parser/syncer has invalidated nodes
+ if (this.depth != 1 && this.depth > this.appInst.nodestackSize()) {
+ this.setResult(RESULT_DONTPOP);
+ finish();
+ }
+
outlineAdapter.notifyDataSetChanged();
getListView().setSelection(lastSelection);
}
@@ -111,7 +122,7 @@ public boolean onCreateOptionsMenu(Menu menu) {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_sync:
- runSynchronizer();
+ runSync();
return true;
case R.id.menu_settings:
@@ -192,9 +203,19 @@ public void onListItemClick(ListView l, View v, int position, long id) {
}
}
- public void showWizard() {
+ private void showWizard() {
startActivity(new Intent(this, WizardActivity.class));
}
+
+ private void runSync() {
+ this.syncDialog = new ProgressDialog(this);
+ this.syncDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ this.syncDialog.setCancelable(false);
+ this.syncDialog.setMessage("Started synchronization");
+ this.syncDialog.show();
+
+ startService(new Intent(this, SyncService.class));
+ }
private boolean runEditNewNodeActivity() {
Intent intent = new Intent(this, NodeEditActivity.class);
@@ -273,7 +294,47 @@ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
break;
}
}
-
+
+
+ private void showMessage(String message) {
+ this.syncDialog.setMessage(message);
+ }
+
+ private void stopSyncDialog() {
+ this.syncDialog.dismiss();
+ this.refreshDisplay();
+ }
+
+ private void showProgressDialog(int number, int total) {
+ int progress = ((40 / total) * number);
+ // TODO Fix progress bar
+ this.syncDialog.setProgress(60 + progress + 1);
+ }
+
+ private void showProgress(int total) {
+ this.syncDialog.setProgress(total);
+ }
+
+ // TODO Add the handling of thrown error messages from synchronizer
+ public class SynchServiceReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int num = intent.getIntExtra(Synchronizer.SYNC_FILES, 0);
+ int totalfiles = intent.getIntExtra(Synchronizer.SYNC_FILES_TOTAL, 0);
+ int progress = intent.getIntExtra(Synchronizer.SYNC_PROGRESS, 0);
+ String message = intent.getStringExtra(Synchronizer.SYNC_MESSAGE);
+
+ if (intent.getBooleanExtra(Synchronizer.SYNC_DONE, false))
+ stopSyncDialog();
+ if (num > 0)
+ showProgressDialog(num, totalfiles);
+ if (message != null)
+ showMessage(message);
+ if (progress > 0)
+ showProgress(progress);
+ }
+ }
+
/**
* This calls startActivityForResult() with Encryption.DECRYPT_MESSAGE. The
* result is handled by onActivityResult() in this class, which calls a
@@ -291,9 +352,10 @@ private void runDecryptAndExpandNode(Node node) {
NodeEncryption.decrypt(this, rawData);
}
}
-
+
/**
- * This function is called with the results of {@link #runDecryptAndExpandNode}.
+ * This function is called with the results of
+ * {@link #runDecryptAndExpandNode}.
*/
private void parseEncryptedNode(Intent data, Node node) {
OrgFileParser ofp = new OrgFileParser(getBaseContext(), appInst);
@@ -301,52 +363,6 @@ private void parseEncryptedNode(Intent data, Node node) {
String decryptedData = data
.getStringExtra(NodeEncryption.EXTRA_DECRYPTED_MESSAGE);
- ofp.parse(node, new BufferedReader(new StringReader(
- decryptedData)));
- }
-
- private void runSynchronizer() {
- final SyncManager synchman = new SyncManager(this, this.appInst);
-
- if (!synchman.isConfigured()) {
- Toast error = Toast.makeText((Context) this,
- getString(R.string.error_synchronizer_not_configured),
- Toast.LENGTH_LONG);
- error.show();
- this.runShowSettings();
- return;
- }
-
- Thread syncThread = new Thread() {
- public void run() {
- try {
- syncError = null;
- synchman.sync();
- } catch (IOException e) {
- syncError = e;
- } finally {
- synchman.close();
- }
- syncHandler.post(syncUpdateResults);
- }
- };
- syncThread.start();
- syncDialog = ProgressDialog.show(this, "",
- getString(R.string.sync_wait), true);
- }
-
- private final Runnable syncUpdateResults = new Runnable() {
- public void run() {
- postSynchronize();
- }
- };
-
- private void postSynchronize() {
- syncDialog.dismiss();
- if (this.syncError != null) {
- ErrorReporter.displayError(this, this.syncError.getMessage());
- } else {
- this.onResume();
- }
+ ofp.parse(node, new BufferedReader(new StringReader(decryptedData)));
}
}
diff --git a/src/com/matburt/mobileorg/Services/MobileOrgSyncService.java b/src/com/matburt/mobileorg/Services/SyncService.java
similarity index 71%
rename from src/com/matburt/mobileorg/Services/MobileOrgSyncService.java
rename to src/com/matburt/mobileorg/Services/SyncService.java
index 19e97310..09167750 100644
--- a/src/com/matburt/mobileorg/Services/MobileOrgSyncService.java
+++ b/src/com/matburt/mobileorg/Services/SyncService.java
@@ -6,7 +6,10 @@
import java.util.TimerTask;
import com.matburt.mobileorg.Parsing.MobileOrgApplication;
-import com.matburt.mobileorg.Synchronizers.SyncManager;
+import com.matburt.mobileorg.Synchronizers.DropboxSynchronizer;
+import com.matburt.mobileorg.Synchronizers.SDCardSynchronizer;
+import com.matburt.mobileorg.Synchronizers.Synchronizer;
+import com.matburt.mobileorg.Synchronizers.WebDAVSynchronizer;
import android.app.Service;
import android.content.Intent;
@@ -15,36 +18,88 @@
import android.preference.PreferenceManager;
import android.util.Log;
-public class MobileOrgSyncService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener{
+public class SyncService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener{
+ public static final String SYNC_UPDATE = "com.matburt.mobileorg.SyncService.action.SYNC_UPDATE";
+
+ private SharedPreferences appSettings;
+ private MobileOrgApplication appInst;
+
private Timer timer = new Timer();
private Date lastSyncDate;
private Boolean timerScheduled = false;
- private SharedPreferences appSettings;
private static long kMinimalSyncInterval = 30000;
-
+
@Override
public void onCreate() {
- super.onCreate();
- Log.d("MobileOrg", "Sync service created");
-
+ super.onCreate();
this.appSettings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
this.appSettings.registerOnSharedPreferenceChangeListener(this);
+ this.appInst = (MobileOrgApplication) this.getApplication();
}
-
+
+ @Override
+ public void onStart(Intent intent, int startid) {
+ startTimer();
+ }
+
@Override
public void onDestroy() {
stopTimer();
}
- @Override
- public IBinder onBind(Intent intent) {
- return null;
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ runSynchronizer();
+ return 0;
+ }
+
+ private void runSynchronizer() {
+ String syncSource = appSettings.getString("syncSource", "");
+ final Synchronizer synchronizer;
+
+ if (syncSource.equals("webdav"))
+ synchronizer = new WebDAVSynchronizer(this, this.appInst);
+ else if (syncSource.equals("sdcard"))
+ synchronizer = new SDCardSynchronizer(this, this.appInst);
+ else if (syncSource.equals("dropbox"))
+ synchronizer = new DropboxSynchronizer(this, this.appInst);
+ else
+ return; // TODO Throw error
+
+ if (!synchronizer.isConfigured())
+ return; // TODO Throw error
+
+ Thread syncThread = new Thread() {
+ public void run() {
+ try {
+ synchronizer.sync();
+ } catch (IOException e) {
+ } finally {
+ synchronizer.close();
+ }
+ }
+ };
+
+ syncThread.start();
+ this.lastSyncDate = new Date();
}
+
+
@Override
- public void onStart(Intent intent, int startid) {
- startTimer();
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if(key.equals("doAutoSync")) {
+ if(sharedPreferences.getBoolean("doAutoSync", false) && !this.timerScheduled) {
+ startTimer();
+ } else if(!sharedPreferences.getBoolean("doAutoSync", false) && this.timerScheduled) {
+ stopTimer();
+ }
+ } else if(key.equals("autoSyncInterval")) {
+ stopTimer();
+ startTimer();
+ }
}
+
private void startTimer() {
if(!this.timerScheduled) {
@@ -83,6 +138,13 @@ public void run() {
}
}
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+
private void stopTimer() {
if(this.timer != null) {
this.timer.cancel();
@@ -91,40 +153,4 @@ private void stopTimer() {
}
Log.d("MobileOrg", "Sync Service Unscheduled");
}
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- if(key.equals("doAutoSync")) {
- if(sharedPreferences.getBoolean("doAutoSync", false) && !this.timerScheduled) {
- startTimer();
- } else if(!sharedPreferences.getBoolean("doAutoSync", false) && this.timerScheduled) {
- stopTimer();
- }
- } else if(key.equals("autoSyncInterval")) {
- stopTimer();
- startTimer();
- }
- }
-
- private void runSynchronizer() {
- MobileOrgApplication appInst = (MobileOrgApplication) this.getApplication();
- final SyncManager syncman = new SyncManager(this, appInst);
-
- if (!syncman.isConfigured()) {
- return;
- }
-
- Thread syncThread = new Thread() {
- public void run() {
- try {
- syncman.sync();
- } catch (IOException e) {
- } finally {
- syncman.close();
- }
- }
- };
- syncThread.start();
- this.lastSyncDate = new Date();
- }
}
diff --git a/src/com/matburt/mobileorg/Synchronizers/DropboxSynchronizer.java b/src/com/matburt/mobileorg/Synchronizers/DropboxSynchronizer.java
index 5a1f757e..0fd227d7 100644
--- a/src/com/matburt/mobileorg/Synchronizers/DropboxSynchronizer.java
+++ b/src/com/matburt/mobileorg/Synchronizers/DropboxSynchronizer.java
@@ -15,6 +15,7 @@
import com.dropbox.client.DropboxAPI.Config;
import com.dropbox.client.DropboxAPI.FileDownload;
import com.matburt.mobileorg.R;
+import com.matburt.mobileorg.Parsing.MobileOrgApplication;
import com.matburt.mobileorg.Parsing.OrgFile;
public class DropboxSynchronizer extends Synchronizer {
@@ -25,8 +26,8 @@ public class DropboxSynchronizer extends Synchronizer {
private DropboxAPI dropboxAPI = new DropboxAPI();
private com.dropbox.client.DropboxAPI.Config dropboxConfig;
- DropboxSynchronizer(Context context) {
- super(context);
+ public DropboxSynchronizer(Context context, MobileOrgApplication appInst) {
+ super(context, appInst);
SharedPreferences sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(context);
diff --git a/src/com/matburt/mobileorg/Synchronizers/SDCardSynchronizer.java b/src/com/matburt/mobileorg/Synchronizers/SDCardSynchronizer.java
index 86ab26a4..387b8bd8 100644
--- a/src/com/matburt/mobileorg/Synchronizers/SDCardSynchronizer.java
+++ b/src/com/matburt/mobileorg/Synchronizers/SDCardSynchronizer.java
@@ -10,6 +10,7 @@
import android.content.Context;
import android.preference.PreferenceManager;
+import com.matburt.mobileorg.Parsing.MobileOrgApplication;
import com.matburt.mobileorg.Parsing.OrgFile;
public class SDCardSynchronizer extends Synchronizer {
@@ -17,8 +18,8 @@ public class SDCardSynchronizer extends Synchronizer {
private String remoteIndexPath;
private String remotePath;
- SDCardSynchronizer(Context context) {
- super(context);
+ public SDCardSynchronizer(Context context, MobileOrgApplication appInst) {
+ super(context, appInst);
this.remoteIndexPath = PreferenceManager.getDefaultSharedPreferences(
context).getString("indexFilePath", "");
diff --git a/src/com/matburt/mobileorg/Synchronizers/SyncManager.java b/src/com/matburt/mobileorg/Synchronizers/SyncManager.java
deleted file mode 100644
index 872529d7..00000000
--- a/src/com/matburt/mobileorg/Synchronizers/SyncManager.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.matburt.mobileorg.Synchronizers;
-
-import java.io.IOException;
-
-import com.matburt.mobileorg.Parsing.MobileOrgApplication;
-
-import android.content.Context;
-import android.preference.PreferenceManager;
-
-/**
- * This is a wrapper class for {@link Synchronizer}. It should be used instead of
- * creating instances of {@link Synchronizer} directly.
- */
-public class SyncManager {
- final Synchronizer synchronizer;
-
- MobileOrgApplication appInst;
-
- public SyncManager(Context context, MobileOrgApplication appInst) {
- String userSynchro = PreferenceManager.getDefaultSharedPreferences(context).getString("syncSource", "");
- if (userSynchro.equals("webdav")) {
- synchronizer = new WebDAVSynchronizer(context);
- } else if (userSynchro.equals("sdcard")) {
- synchronizer = new SDCardSynchronizer(context);
- } else if (userSynchro.equals("dropbox")) {
- synchronizer = new DropboxSynchronizer(context);
- } else
- synchronizer = null;
-
- this.appInst = appInst;
- }
-
- public boolean isConfigured() {
- if(synchronizer == null)
- return false;
-
- return synchronizer.isConfigured();
- }
-
- public boolean sync() throws IOException {
- if (!isConfigured())
- return false;
-
- synchronizer.sync(appInst);
- return true;
- }
-
- public void close() {
- synchronizer.close();
- }
-}
\ No newline at end of file
diff --git a/src/com/matburt/mobileorg/Synchronizers/Synchronizer.java b/src/com/matburt/mobileorg/Synchronizers/Synchronizer.java
index 42b9e4c5..0acae4f9 100644
--- a/src/com/matburt/mobileorg/Synchronizers/Synchronizer.java
+++ b/src/com/matburt/mobileorg/Synchronizers/Synchronizer.java
@@ -8,6 +8,7 @@
import java.util.regex.Pattern;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.preference.PreferenceManager;
@@ -16,6 +17,7 @@
import com.matburt.mobileorg.Parsing.MobileOrgApplication;
import com.matburt.mobileorg.Parsing.OrgDatabase;
import com.matburt.mobileorg.Parsing.OrgFile;
+import com.matburt.mobileorg.Services.SyncService;
/**
* This class implements many of the operations that need to be done on
@@ -26,6 +28,12 @@
* needed.
*/
abstract public class Synchronizer {
+ public final static String SYNC_PROGRESS = "sync_progress";
+ public final static String SYNC_MESSAGE = "sync_message";
+ public final static String SYNC_FILES = "sync_files";
+ public final static String SYNC_FILES_TOTAL = "sync_total";
+ public final static String SYNC_DONE = "sync_done";
+
/**
* Called before running the synchronizer to ensure that it's configuration
* is in a valid state.
@@ -49,30 +57,38 @@ abstract public class Synchronizer {
protected SharedPreferences appSettings;
protected Context context;
protected Resources r;
+ private MobileOrgApplication appInst;
- Synchronizer(Context context) {
+ Synchronizer(Context context, MobileOrgApplication appInst) {
this.context = context;
this.r = this.context.getResources();
this.appdb = new OrgDatabase((Context)context);
this.appSettings = PreferenceManager.getDefaultSharedPreferences(
context.getApplicationContext());
+ this.appInst = appInst;
+
+ announceSyncMessage("Connecting to service");
}
- public void sync(MobileOrgApplication appInst) throws IOException {
- push(OrgFile.CAPTURE_FILE, appInst);
- pull(appInst);
+ public void sync() throws IOException {
+ announceSyncMessage("Uploading " + OrgFile.CAPTURE_FILE);
+ push(OrgFile.CAPTURE_FILE);
+ announceSyncProgress(20);
+ pull();
+ announceSyncDone();
}
/**
* This method will fetch the local and the remote version of a file and
* combine their content. This combined version is transfered to the remote.
*/
- protected void push(String filename, MobileOrgApplication appInst) throws IOException {
+ protected void push(String filename) throws IOException {
OrgFile orgFile = new OrgFile(filename, context);
String localContents = orgFile.read();
String remoteContent = OrgFile.read(getRemoteFile(filename));
-
+ announceSyncProgress(10);
+
if(localContents.equals(""))
return;
@@ -89,35 +105,76 @@ protected void push(String filename, MobileOrgApplication appInst) throws IOExce
* host. Using those files, it determines the other files that need updating
* and downloads them.
*/
- protected void pull(MobileOrgApplication appInst) throws IOException {
+ protected void pull() throws IOException {
+ announceSyncMessage("Downloading index file");
String remoteIndexContents = OrgFile.read(getRemoteFile("index.org"));
+ announceSyncProgress(40);
ArrayList> todoLists = getTodos(remoteIndexContents);
this.appdb.setTodoList(todoLists);
ArrayList> priorityLists = getPriorities(remoteIndexContents);
this.appdb.setPriorityList(priorityLists);
-
+
+ announceSyncMessage("Downloading checksum file");
String remoteChecksumContents = OrgFile.read(getRemoteFile("checksums.dat"));
+ announceSyncProgress(60);
HashMap remoteChecksums = getChecksums(remoteChecksumContents);
HashMap localChecksums = this.appdb.getChecksums();
HashMap fileChecksumMap = getOrgFilesFromMaster(remoteIndexContents);
- for (String key : fileChecksumMap.keySet()) {
- String filename = fileChecksumMap.get(key);
-
+ ArrayList filesToGet = new ArrayList();
+
+ for (String key : fileChecksumMap.keySet()) {
if (localChecksums.containsKey(key) &&
remoteChecksums.containsKey(key) &&
localChecksums.get(key).equals(remoteChecksums.get(key)))
continue;
-
+
+ filesToGet.add(key);
+ }
+
+ int i = 0;
+ for(String key: filesToGet) {
+ String filename = fileChecksumMap.get(key);
+
+ i++;
+ announceSyncFile("Downloading " + filename, i, filesToGet.size());
+
OrgFile orgfile = new OrgFile(filename, context);
orgfile.fetch(getRemoteFile(filename));
appInst.addOrUpdateFile(filename, key, remoteChecksums.get(key));
- }
+ }
+ }
+
+
+ private void announceSyncProgress(int progress) {
+ Intent intent = new Intent(SyncService.SYNC_UPDATE);
+ intent.putExtra(SYNC_PROGRESS, progress);
+ this.context.sendBroadcast(intent);
+ }
+
+ private void announceSyncFile(String message, int number, int total) {
+ Intent intent = new Intent(SyncService.SYNC_UPDATE);
+ intent.putExtra(SYNC_MESSAGE, message);
+ intent.putExtra(SYNC_FILES, number);
+ intent.putExtra(SYNC_FILES_TOTAL, total);
+ this.context.sendBroadcast(intent);
+ }
+
+ private void announceSyncMessage(String message) {
+ Intent intent = new Intent(SyncService.SYNC_UPDATE);
+ intent.putExtra(SYNC_MESSAGE, message);
+ this.context.sendBroadcast(intent);
+ }
+
+ private void announceSyncDone() {
+ Intent intent = new Intent(SyncService.SYNC_UPDATE);
+ intent.putExtra(SYNC_DONE, true);
+ this.context.sendBroadcast(intent);
}
private HashMap getOrgFilesFromMaster(String master) {
diff --git a/src/com/matburt/mobileorg/Synchronizers/WebDAVSynchronizer.java b/src/com/matburt/mobileorg/Synchronizers/WebDAVSynchronizer.java
index 038041ec..d56f71d7 100644
--- a/src/com/matburt/mobileorg/Synchronizers/WebDAVSynchronizer.java
+++ b/src/com/matburt/mobileorg/Synchronizers/WebDAVSynchronizer.java
@@ -32,6 +32,7 @@
import android.preference.PreferenceManager;
import com.matburt.mobileorg.R;
+import com.matburt.mobileorg.Parsing.MobileOrgApplication;
import com.matburt.mobileorg.Parsing.OrgFile;
public class WebDAVSynchronizer extends Synchronizer {
@@ -42,8 +43,8 @@ public class WebDAVSynchronizer extends Synchronizer {
private String username;
private String password;
- WebDAVSynchronizer(Context parentContext) {
- super(parentContext);
+ public WebDAVSynchronizer(Context parentContext, MobileOrgApplication appInst) {
+ super(parentContext, appInst);
SharedPreferences sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(context);