From 58f5c61a54975e829b455bdda84e2ea040ea323d Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Tue, 3 Sep 2013 23:04:26 +0200 Subject: [PATCH] Sync improvements --- .../preferences/GpodnetPreferences.java | 17 +- .../service/GpodnetSyncService.java | 163 ++++++++---------- 2 files changed, 80 insertions(+), 100 deletions(-) diff --git a/src/de/danoeh/antennapod/preferences/GpodnetPreferences.java b/src/de/danoeh/antennapod/preferences/GpodnetPreferences.java index bb98fb998..44b0f3cc3 100644 --- a/src/de/danoeh/antennapod/preferences/GpodnetPreferences.java +++ b/src/de/danoeh/antennapod/preferences/GpodnetPreferences.java @@ -2,6 +2,8 @@ package de.danoeh.antennapod.preferences; import android.content.Context; import android.content.SharedPreferences; +import android.util.Log; +import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.service.GpodnetSyncService; @@ -127,7 +129,7 @@ public class GpodnetPreferences { writePreference(PREF_SYNC_REMOVED, removedFeeds); } feedListLock.unlock(); - GpodnetSyncService.sendActionUploadIntent(PodcastApp.getInstance()); + GpodnetSyncService.sendSyncIntent(PodcastApp.getInstance()); } public static void addRemovedFeed(String feed) { @@ -140,7 +142,7 @@ public class GpodnetPreferences { writePreference(PREF_SYNC_ADDED, addedFeeds); } feedListLock.unlock(); - GpodnetSyncService.sendActionUploadIntent(PodcastApp.getInstance()); + GpodnetSyncService.sendSyncIntent(PodcastApp.getInstance()); } public static Set getAddedFeedsCopy() { @@ -152,7 +154,7 @@ public class GpodnetPreferences { return copy; } - public static void removeAddedFeeds(Set removed) { + public static void removeAddedFeeds(Collection removed) { ensurePreferencesLoaded(); feedListLock.lock(); addedFeeds.removeAll(removed); @@ -169,7 +171,7 @@ public class GpodnetPreferences { return copy; } - public static void removeRemovedFeeds(Set removed) { + public static void removeRemovedFeeds(Collection removed) { ensurePreferencesLoaded(); removedFeeds.removeAll(removed); writePreference(PREF_SYNC_REMOVED, removedFeeds); @@ -184,10 +186,15 @@ public class GpodnetPreferences { return deviceID != null && username != null && password != null; } - public static void logout() { + public static synchronized void logout() { + if (AppConfig.DEBUG) Log.d(TAG, "Logout: Clearing preferences"); setUsername(null); setPassword(null); setDeviceID(null); + addedFeeds.clear(); + writePreference(PREF_SYNC_ADDED, addedFeeds); + removedFeeds.clear(); + writePreference(PREF_SYNC_REMOVED, removedFeeds); setLastSyncTimestamp(0); } diff --git a/src/de/danoeh/antennapod/service/GpodnetSyncService.java b/src/de/danoeh/antennapod/service/GpodnetSyncService.java index 464b057a6..71e128b55 100644 --- a/src/de/danoeh/antennapod/service/GpodnetSyncService.java +++ b/src/de/danoeh/antennapod/service/GpodnetSyncService.java @@ -21,13 +21,14 @@ import de.danoeh.antennapod.storage.*; import de.danoeh.antennapod.util.NetworkUtils; import java.util.Date; +import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; /** - * Synchronizes local subscriptions with gpodder.net service. The service should be started with an ACTION_UPLOAD_CHANGES, - * ACTION_DOWNLOAD_CHANGES or ACTION_SYNC as an action argument. This class also provides static methods for starting the GpodnetSyncService. + * Synchronizes local subscriptions with gpodder.net service. The service should be started with ACTION_SYNC as an action argument. + * This class also provides static methods for starting the GpodnetSyncService. */ public class GpodnetSyncService extends Service { private static final String TAG = "GpodnetSyncService"; @@ -36,19 +37,6 @@ public class GpodnetSyncService extends Service { public static final String ARG_ACTION = "action"; - /** - * Starts a new upload action. The service will not upload immediately, but wait for a certain amount of time in - * case any other upload requests occur. - */ - public static final String ACTION_UPLOAD_CHANGES = "de.danoeh.antennapod.intent.action.upload_changes"; - /** - * Starts a new download action. The service will download all changes in the subscription list since the last sync. - * New subscriptions will be added to the database, removed subscriptions will be removed from the database - */ - public static final String ACTION_DOWNLOAD_CHANGES = "de.danoeh.antennapod.intent.action.download_changes"; - /** - * Starts a new upload action immediately and a new download action after that. - */ public static final String ACTION_SYNC = "de.danoeh.antennapod.intent.action.sync"; private GpodnetService service; @@ -56,25 +44,11 @@ public class GpodnetSyncService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { final String action = (intent != null) ? intent.getStringExtra(ARG_ACTION) : null; - if (action != null && action.equals(ACTION_UPLOAD_CHANGES)) { + if (action != null && action.equals(ACTION_SYNC)) { Log.d(TAG, String.format("Waiting %d milliseconds before uploading changes", WAIT_INTERVAL)); - - uploadWaiterThread.restart(); - } else if (action != null && action.equals(ACTION_DOWNLOAD_CHANGES)) { - new Thread() { - @Override - public void run() { - downloadChanges(); - } - }.start(); - } else if (action != null && action.equals(ACTION_SYNC)) { - new Thread() { - @Override - public void run() { - downloadChanges(); - uploadChanges(); - } - }.start(); + syncWaiterThread.restart(); + } else { + Log.e(TAG, "Received invalid intent: action argument is null or invalid"); } return START_FLAG_REDELIVERY; } @@ -83,7 +57,7 @@ public class GpodnetSyncService extends Service { public void onDestroy() { super.onDestroy(); if (AppConfig.DEBUG) Log.d(TAG, "onDestroy"); - uploadWaiterThread.interrupt(); + syncWaiterThread.interrupt(); } @@ -92,7 +66,7 @@ public class GpodnetSyncService extends Service { return null; } - private GpodnetService tryLogin() throws GpodnetServiceException { + private synchronized GpodnetService tryLogin() throws GpodnetServiceException { if (service == null) { service = new GpodnetService(); service.authenticate(GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword()); @@ -100,58 +74,55 @@ public class GpodnetSyncService extends Service { return service; } - private synchronized void downloadChanges() { - if (GpodnetPreferences.loggedIn() && NetworkUtils.networkAvailable(GpodnetSyncService.this)) { - if (AppConfig.DEBUG) Log.d(TAG, "Downloading changes"); + private synchronized void syncChanges() { + if (GpodnetPreferences.loggedIn() && NetworkUtils.networkAvailable(this)) { + final long timestamp = GpodnetPreferences.getLastSyncTimestamp(); try { + final List localSubscriptions = DBReader.getFeedListDownloadUrls(this); GpodnetService service = tryLogin(); - GpodnetSubscriptionChange changes = service.getSubscriptionChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), GpodnetPreferences.getLastSyncTimestamp()); - if (AppConfig.DEBUG) Log.d(TAG, "Changes " + changes.toString()); - GpodnetPreferences.setLastSyncTimestamp(changes.getTimestamp()); - List subscriptionList = DBReader.getFeedListDownloadUrls(GpodnetSyncService.this); + if (timestamp == 0) { + // first sync: download all subscriptions... + GpodnetSubscriptionChange changes = + service.getSubscriptionChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), 0); + if (AppConfig.DEBUG) Log.d(TAG, "Downloaded subscription changes: " + changes); + processSubscriptionChanges(localSubscriptions, changes); - for (String downloadUrl : changes.getAdded()) { - if (!subscriptionList.contains(downloadUrl)) { - Feed feed = new Feed(downloadUrl, new Date()); - DownloadRequester.getInstance().downloadFeed(GpodnetSyncService.this, feed); - } - } - for (String downloadUrl : changes.getRemoved()) { - DBTasks.removeFeedWithDownloadUrl(GpodnetSyncService.this, downloadUrl); + // ... then upload all local subscriptions + if (AppConfig.DEBUG) Log.d(TAG, "Uploading subscription list: " + localSubscriptions); + GpodnetUploadChangesResponse uploadChangesResponse = + service.uploadChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), localSubscriptions, new LinkedList()); + if (AppConfig.DEBUG) Log.d(TAG, "Uploading changes response: " + uploadChangesResponse); + DBWriter.updateFeedDownloadURLs(GpodnetSyncService.this, uploadChangesResponse.updatedUrls).get(); + GpodnetPreferences.removeAddedFeeds(localSubscriptions); + GpodnetPreferences.removeRemovedFeeds(GpodnetPreferences.getRemovedFeedsCopy()); + GpodnetPreferences.setLastSyncTimestamp(uploadChangesResponse.timestamp); + } else { + Set added = GpodnetPreferences.getAddedFeedsCopy(); + Set removed = GpodnetPreferences.getRemovedFeedsCopy(); + + // download remote changes first... + GpodnetSubscriptionChange subscriptionChanges = service.getSubscriptionChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), timestamp); + if (AppConfig.DEBUG) Log.d(TAG, "Downloaded subscription changes: " + subscriptionChanges); + processSubscriptionChanges(localSubscriptions, subscriptionChanges); + + // ... then upload changes local changes + if (AppConfig.DEBUG) Log.d(TAG, String.format("Uploading subscriptions, Added: %s\nRemoved: %s", + added.toString(), removed)); + GpodnetUploadChangesResponse uploadChangesResponse = service.uploadChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), added, removed); + if (AppConfig.DEBUG) Log.d(TAG, "Upload subscriptions response: " + uploadChangesResponse); + + GpodnetPreferences.removeAddedFeeds(added); + GpodnetPreferences.removeRemovedFeeds(removed); + DBWriter.updateFeedDownloadURLs(GpodnetSyncService.this, uploadChangesResponse.updatedUrls).get(); + GpodnetPreferences.setLastSyncTimestamp(uploadChangesResponse.timestamp); } + clearErrorNotifications(); } catch (GpodnetServiceException e) { e.printStackTrace(); updateErrorNotification(e); } catch (DownloadRequestException e) { e.printStackTrace(); - } - } - stopSelf(); - } - - private synchronized void uploadChanges() { - if (GpodnetPreferences.loggedIn() && NetworkUtils.networkAvailable(GpodnetSyncService.this)) { - try { - if (AppConfig.DEBUG) Log.d(TAG, "Uploading subscription list"); - GpodnetService service = tryLogin(); - - Set added = GpodnetPreferences.getAddedFeedsCopy(); - Set removed = GpodnetPreferences.getRemovedFeedsCopy(); - - if (AppConfig.DEBUG) Log.d(TAG, String.format("Uploading subscriptions, Added: %s\nRemoved: %s", - added.toString(), removed.toString())); - - GpodnetUploadChangesResponse response = service.uploadChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), added, removed); - - if (AppConfig.DEBUG) Log.d(TAG, "Upload subscriptions response: " + response.toString()); - - GpodnetPreferences.removeAddedFeeds(added); - GpodnetPreferences.removeRemovedFeeds(removed); - DBWriter.updateFeedDownloadURLs(GpodnetSyncService.this, response.updatedUrls).get(); - } catch (GpodnetServiceException e) { - e.printStackTrace(); - updateErrorNotification(e); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { @@ -161,6 +132,24 @@ public class GpodnetSyncService extends Service { stopSelf(); } + private synchronized void processSubscriptionChanges(List localSubscriptions, GpodnetSubscriptionChange changes) throws DownloadRequestException { + for (String downloadUrl : changes.getAdded()) { + if (!localSubscriptions.contains(downloadUrl)) { + Feed feed = new Feed(downloadUrl, new Date()); + DownloadRequester.getInstance().downloadFeed(this, feed); + } + } + for (String downloadUrl : changes.getRemoved()) { + DBTasks.removeFeedWithDownloadUrl(GpodnetSyncService.this, downloadUrl); + } + } + + private void clearErrorNotifications() { + NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + nm.cancel(R.id.notification_gpodnet_sync_error); + nm.cancel(R.id.notification_gpodnet_sync_autherror); + } + private void updateErrorNotification(GpodnetServiceException exception) { if (AppConfig.DEBUG) Log.d(TAG, "Posting error notification"); @@ -186,10 +175,10 @@ public class GpodnetSyncService extends Service { nm.notify(id, notification); } - private WaiterThread uploadWaiterThread = new WaiterThread(WAIT_INTERVAL) { + private WaiterThread syncWaiterThread = new WaiterThread(WAIT_INTERVAL) { @Override public void onWaitCompleted() { - uploadChanges(); + syncChanges(); } }; @@ -244,22 +233,6 @@ public class GpodnetSyncService extends Service { } } - public static void sendActionDownloadIntent(Context context) { - if (GpodnetPreferences.loggedIn()) { - Intent intent = new Intent(context, GpodnetSyncService.class); - intent.putExtra(ARG_ACTION, ACTION_DOWNLOAD_CHANGES); - context.startService(intent); - } - } - - public static void sendActionUploadIntent(Context context) { - if (GpodnetPreferences.loggedIn()) { - Intent intent = new Intent(context, GpodnetSyncService.class); - intent.putExtra(ARG_ACTION, ACTION_UPLOAD_CHANGES); - context.startService(intent); - } - } - public static void sendSyncIntent(Context context) { if (GpodnetPreferences.loggedIn()) { Intent intent = new Intent(context, GpodnetSyncService.class);