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.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<String> getAddedFeedsCopy() {
@ -152,7 +154,7 @@ public class GpodnetPreferences {
return copy;
}
public static void removeAddedFeeds(Set<String> removed) {
public static void removeAddedFeeds(Collection<String> removed) {
ensurePreferencesLoaded();
feedListLock.lock();
addedFeeds.removeAll(removed);
@ -169,7 +171,7 @@ public class GpodnetPreferences {
return copy;
}
public static void removeRemovedFeeds(Set<String> removed) {
public static void removeRemovedFeeds(Collection<String> 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);
}

View File

@ -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<String> 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<String> 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<String>());
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<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) {
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<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) {
e.printStackTrace();
} catch (ExecutionException e) {
@ -161,6 +132,24 @@ public class GpodnetSyncService extends Service {
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) {
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);