Sync improvements

This commit is contained in:
daniel oeh 2013-09-03 23:04:26 +02:00
parent 1f594ad311
commit 58f5c61a54
2 changed files with 80 additions and 100 deletions

View File

@ -2,6 +2,8 @@ package de.danoeh.antennapod.preferences;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.service.GpodnetSyncService; import de.danoeh.antennapod.service.GpodnetSyncService;
@ -127,7 +129,7 @@ public class GpodnetPreferences {
writePreference(PREF_SYNC_REMOVED, removedFeeds); writePreference(PREF_SYNC_REMOVED, removedFeeds);
} }
feedListLock.unlock(); feedListLock.unlock();
GpodnetSyncService.sendActionUploadIntent(PodcastApp.getInstance()); GpodnetSyncService.sendSyncIntent(PodcastApp.getInstance());
} }
public static void addRemovedFeed(String feed) { public static void addRemovedFeed(String feed) {
@ -140,7 +142,7 @@ public class GpodnetPreferences {
writePreference(PREF_SYNC_ADDED, addedFeeds); writePreference(PREF_SYNC_ADDED, addedFeeds);
} }
feedListLock.unlock(); feedListLock.unlock();
GpodnetSyncService.sendActionUploadIntent(PodcastApp.getInstance()); GpodnetSyncService.sendSyncIntent(PodcastApp.getInstance());
} }
public static Set<String> getAddedFeedsCopy() { public static Set<String> getAddedFeedsCopy() {
@ -152,7 +154,7 @@ public class GpodnetPreferences {
return copy; return copy;
} }
public static void removeAddedFeeds(Set<String> removed) { public static void removeAddedFeeds(Collection<String> removed) {
ensurePreferencesLoaded(); ensurePreferencesLoaded();
feedListLock.lock(); feedListLock.lock();
addedFeeds.removeAll(removed); addedFeeds.removeAll(removed);
@ -169,7 +171,7 @@ public class GpodnetPreferences {
return copy; return copy;
} }
public static void removeRemovedFeeds(Set<String> removed) { public static void removeRemovedFeeds(Collection<String> removed) {
ensurePreferencesLoaded(); ensurePreferencesLoaded();
removedFeeds.removeAll(removed); removedFeeds.removeAll(removed);
writePreference(PREF_SYNC_REMOVED, removedFeeds); writePreference(PREF_SYNC_REMOVED, removedFeeds);
@ -184,10 +186,15 @@ public class GpodnetPreferences {
return deviceID != null && username != null && password != null; 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); setUsername(null);
setPassword(null); setPassword(null);
setDeviceID(null); setDeviceID(null);
addedFeeds.clear();
writePreference(PREF_SYNC_ADDED, addedFeeds);
removedFeeds.clear();
writePreference(PREF_SYNC_REMOVED, removedFeeds);
setLastSyncTimestamp(0); setLastSyncTimestamp(0);
} }

View File

@ -21,13 +21,14 @@ import de.danoeh.antennapod.storage.*;
import de.danoeh.antennapod.util.NetworkUtils; import de.danoeh.antennapod.util.NetworkUtils;
import java.util.Date; import java.util.Date;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
/** /**
* Synchronizes local subscriptions with gpodder.net service. The service should be started with an ACTION_UPLOAD_CHANGES, * Synchronizes local subscriptions with gpodder.net service. The service should be started with ACTION_SYNC as an action argument.
* ACTION_DOWNLOAD_CHANGES or ACTION_SYNC as an action argument. This class also provides static methods for starting the GpodnetSyncService. * This class also provides static methods for starting the GpodnetSyncService.
*/ */
public class GpodnetSyncService extends Service { public class GpodnetSyncService extends Service {
private static final String TAG = "GpodnetSyncService"; private static final String TAG = "GpodnetSyncService";
@ -36,19 +37,6 @@ public class GpodnetSyncService extends Service {
public static final String ARG_ACTION = "action"; 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"; public static final String ACTION_SYNC = "de.danoeh.antennapod.intent.action.sync";
private GpodnetService service; private GpodnetService service;
@ -56,25 +44,11 @@ public class GpodnetSyncService extends Service {
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
final String action = (intent != null) ? intent.getStringExtra(ARG_ACTION) : null; 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)); Log.d(TAG, String.format("Waiting %d milliseconds before uploading changes", WAIT_INTERVAL));
syncWaiterThread.restart();
uploadWaiterThread.restart(); } else {
} else if (action != null && action.equals(ACTION_DOWNLOAD_CHANGES)) { Log.e(TAG, "Received invalid intent: action argument is null or invalid");
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();
} }
return START_FLAG_REDELIVERY; return START_FLAG_REDELIVERY;
} }
@ -83,7 +57,7 @@ public class GpodnetSyncService extends Service {
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
if (AppConfig.DEBUG) Log.d(TAG, "onDestroy"); if (AppConfig.DEBUG) Log.d(TAG, "onDestroy");
uploadWaiterThread.interrupt(); syncWaiterThread.interrupt();
} }
@ -92,7 +66,7 @@ public class GpodnetSyncService extends Service {
return null; return null;
} }
private GpodnetService tryLogin() throws GpodnetServiceException { private synchronized GpodnetService tryLogin() throws GpodnetServiceException {
if (service == null) { if (service == null) {
service = new GpodnetService(); service = new GpodnetService();
service.authenticate(GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword()); service.authenticate(GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
@ -100,58 +74,55 @@ public class GpodnetSyncService extends Service {
return service; return service;
} }
private synchronized void downloadChanges() { private synchronized void syncChanges() {
if (GpodnetPreferences.loggedIn() && NetworkUtils.networkAvailable(GpodnetSyncService.this)) { if (GpodnetPreferences.loggedIn() && NetworkUtils.networkAvailable(this)) {
if (AppConfig.DEBUG) Log.d(TAG, "Downloading changes"); final long timestamp = GpodnetPreferences.getLastSyncTimestamp();
try { try {
final List<String> localSubscriptions = DBReader.getFeedListDownloadUrls(this);
GpodnetService service = tryLogin(); 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()); if (timestamp == 0) {
List<String> subscriptionList = DBReader.getFeedListDownloadUrls(GpodnetSyncService.this); // 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()) { // ... then upload all local subscriptions
if (!subscriptionList.contains(downloadUrl)) { if (AppConfig.DEBUG) Log.d(TAG, "Uploading subscription list: " + localSubscriptions);
Feed feed = new Feed(downloadUrl, new Date()); GpodnetUploadChangesResponse uploadChangesResponse =
DownloadRequester.getInstance().downloadFeed(GpodnetSyncService.this, feed); service.uploadChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), localSubscriptions, new LinkedList<String>());
} if (AppConfig.DEBUG) Log.d(TAG, "Uploading changes response: " + uploadChangesResponse);
} DBWriter.updateFeedDownloadURLs(GpodnetSyncService.this, uploadChangesResponse.updatedUrls).get();
for (String downloadUrl : changes.getRemoved()) { GpodnetPreferences.removeAddedFeeds(localSubscriptions);
DBTasks.removeFeedWithDownloadUrl(GpodnetSyncService.this, downloadUrl); GpodnetPreferences.removeRemovedFeeds(GpodnetPreferences.getRemovedFeedsCopy());
GpodnetPreferences.setLastSyncTimestamp(uploadChangesResponse.timestamp);
} else {
Set<String> added = GpodnetPreferences.getAddedFeedsCopy();
Set<String> 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) { } catch (GpodnetServiceException e) {
e.printStackTrace(); e.printStackTrace();
updateErrorNotification(e); updateErrorNotification(e);
} catch (DownloadRequestException e) { } catch (DownloadRequestException e) {
e.printStackTrace(); 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<String> added = GpodnetPreferences.getAddedFeedsCopy();
Set<String> 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) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} catch (ExecutionException e) { } catch (ExecutionException e) {
@ -161,6 +132,24 @@ public class GpodnetSyncService extends Service {
stopSelf(); stopSelf();
} }
private synchronized void processSubscriptionChanges(List<String> 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) { private void updateErrorNotification(GpodnetServiceException exception) {
if (AppConfig.DEBUG) Log.d(TAG, "Posting error notification"); if (AppConfig.DEBUG) Log.d(TAG, "Posting error notification");
@ -186,10 +175,10 @@ public class GpodnetSyncService extends Service {
nm.notify(id, notification); nm.notify(id, notification);
} }
private WaiterThread uploadWaiterThread = new WaiterThread(WAIT_INTERVAL) { private WaiterThread syncWaiterThread = new WaiterThread(WAIT_INTERVAL) {
@Override @Override
public void onWaitCompleted() { 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) { public static void sendSyncIntent(Context context) {
if (GpodnetPreferences.loggedIn()) { if (GpodnetPreferences.loggedIn()) {
Intent intent = new Intent(context, GpodnetSyncService.class); Intent intent = new Intent(context, GpodnetSyncService.class);