From 108daed5a95a3aaded136538d1e1364791b602f0 Mon Sep 17 00:00:00 2001 From: Martin Fietz Date: Tue, 12 May 2015 17:43:34 +0200 Subject: [PATCH] Sync actions regularly, local subscriptions changes overwrite remote ones --- .../model/GpodnetEpisodeAction.java | 2 +- .../core/preferences/GpodnetPreferences.java | 5 +- .../core/service/GpodnetSyncService.java | 144 ++++++++++++------ 3 files changed, 104 insertions(+), 47 deletions(-) diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java index 0c431d60b..bd6210d13 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java +++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java @@ -258,7 +258,7 @@ public class GpodnetEpisodeAction { private int total = -1; public Builder(FeedItem item, Action action) { - this(item.getFeed().getDownload_url(), item.getItemIdentifier(), action); + this(item.getFeed().getDownload_url(), item.getMedia().getDownload_url(), action); } public Builder(String podcast, String episode, Action action) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java index cfdd0c5d6..c3c6ce8c5 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java @@ -173,7 +173,7 @@ public class GpodnetPreferences { writePreference(PREF_SYNC_REMOVED, removedFeeds); } feedListLock.unlock(); - GpodnetSyncService.sendSyncIntent(ClientConfig.applicationCallbacks.getApplicationInstance()); + GpodnetSyncService.sendSyncSubscriptionsIntent(ClientConfig.applicationCallbacks.getApplicationInstance()); } public static void addRemovedFeed(String feed) { @@ -186,7 +186,7 @@ public class GpodnetPreferences { writePreference(PREF_SYNC_ADDED, addedFeeds); } feedListLock.unlock(); - GpodnetSyncService.sendSyncIntent(ClientConfig.applicationCallbacks.getApplicationInstance()); + GpodnetSyncService.sendSyncSubscriptionsIntent(ClientConfig.applicationCallbacks.getApplicationInstance()); } public static Set getAddedFeedsCopy() { @@ -225,6 +225,7 @@ public class GpodnetPreferences { ensurePreferencesLoaded(); queuedEpisodeActions.add(action); writePreference(PREF_SYNC_EPISODE_ACTIONS, writeEpisodeActionsToString(queuedEpisodeActions)); + GpodnetSyncService.sendSyncActionsIntent(ClientConfig.applicationCallbacks.getApplicationInstance()); } public static List getQueuedEpisodeActions() { diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java index 6e318dd36..3f2222f42 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java @@ -12,7 +12,6 @@ import android.util.Log; import android.util.Pair; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -22,6 +21,7 @@ import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.gpoddernet.GpodnetService; import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceAuthenticationException; import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException; @@ -50,17 +50,38 @@ public class GpodnetSyncService extends Service { public static final String ARG_ACTION = "action"; public static final String ACTION_SYNC = "de.danoeh.antennapod.intent.action.sync"; + public static final String ACTION_SYNC_SUBSCRIPTIONS = "de.danoeh.antennapod.intent.action.sync_subscriptions"; + public static final String ACTION_SYNC_ACTIONS = "de.danoeh.antennapod.intent.action.sync_ACTIONS"; private GpodnetService service; + private boolean syncSubscriptions = false; + private boolean syncActions = false; + @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_SYNC)) { - Log.d(TAG, String.format("Waiting %d milliseconds before uploading changes", WAIT_INTERVAL)); - syncWaiterThread.restart(); + if (action != null) { + switch(action) { + case ACTION_SYNC: + syncSubscriptions = true; + syncActions = true; + break; + case ACTION_SYNC_SUBSCRIPTIONS: + syncSubscriptions = true; + break; + case ACTION_SYNC_ACTIONS: + syncActions = true; + break; + default: + Log.e(TAG, "Received invalid intent: action argument is invalid"); + } + if(syncSubscriptions || syncActions) { + Log.d(TAG, String.format("Waiting %d milliseconds before uploading changes", WAIT_INTERVAL)); + syncWaiterThread.restart(); + } } else { - Log.e(TAG, "Received invalid intent: action argument is null or invalid"); + Log.e(TAG, "Received invalid intent: action argument is null"); } return START_FLAG_REDELIVERY; } @@ -91,8 +112,14 @@ public class GpodnetSyncService extends Service { stopSelf(); return; } - syncSubscriptionChanges(); - syncEpisodeActions(); + if(syncSubscriptions) { + syncSubscriptionChanges(); + syncSubscriptions = false; + } + if(syncActions) { + syncEpisodeActions(); + syncActions = false; + } stopSelf(); } @@ -100,37 +127,36 @@ public class GpodnetSyncService extends Service { final long timestamp = GpodnetPreferences.getLastSubscriptionSyncTimestamp(); try { final List localSubscriptions = DBReader.getFeedListDownloadUrls(this); + Collection localAdded = GpodnetPreferences.getAddedFeedsCopy(); + Collection localRemoved = GpodnetPreferences.getRemovedFeedsCopy(); GpodnetService service = tryLogin(); // first sync: download all subscriptions... GpodnetSubscriptionChange subscriptionChanges = service.getSubscriptionChanges(GpodnetPreferences.getUsername(), GpodnetPreferences.getDeviceID(), timestamp); - long lastUpdate = subscriptionChanges.getTimestamp(); + long newTimeStamp = subscriptionChanges.getTimestamp(); Log.d(TAG, "Downloaded subscription changes: " + subscriptionChanges); - processSubscriptionChanges(localSubscriptions, subscriptionChanges); + processSubscriptionChanges(localSubscriptions, localAdded, localRemoved, subscriptionChanges); - Collection added; - Collection removed; - if (timestamp == 0) { - added = localSubscriptions; - GpodnetPreferences.removeRemovedFeeds(GpodnetPreferences.getRemovedFeedsCopy()); - removed = Collections.emptyList(); - } else { - added = GpodnetPreferences.getAddedFeedsCopy(); - removed = GpodnetPreferences.getRemovedFeedsCopy(); + if(timestamp == 0) { + // this is this apps first sync with gpodder: + // only submit changes gpodder has not just sent us + localAdded = localSubscriptions; + localAdded.removeAll(subscriptionChanges.getAdded()); + localRemoved.removeAll(subscriptionChanges.getRemoved()); } - if(added.size() > 0 || removed.size() > 0) { + if(localAdded.size() > 0 || localRemoved.size() > 0) { Log.d(TAG, String.format("Uploading subscriptions, Added: %s\nRemoved: %s", - added, removed)); + localAdded, localRemoved)); GpodnetUploadChangesResponse uploadResponse = service.uploadChanges(GpodnetPreferences.getUsername(), - GpodnetPreferences.getDeviceID(), added, removed); - lastUpdate = uploadResponse.timestamp; + GpodnetPreferences.getDeviceID(), localAdded, localRemoved); + newTimeStamp = uploadResponse.timestamp; Log.d(TAG, "Upload changes response: " + uploadResponse); - GpodnetPreferences.removeAddedFeeds(added); - GpodnetPreferences.removeRemovedFeeds(removed); + GpodnetPreferences.removeAddedFeeds(localAdded); + GpodnetPreferences.removeRemovedFeeds(localRemoved); } - GpodnetPreferences.setLastSubscriptionSyncTimestamp(lastUpdate); + GpodnetPreferences.setLastSubscriptionSyncTimestamp(newTimeStamp); clearErrorNotifications(); } catch (GpodnetServiceException e) { e.printStackTrace(); @@ -140,6 +166,27 @@ public class GpodnetSyncService extends Service { } } + private synchronized void processSubscriptionChanges(List localSubscriptions, + Collection localAdded, + Collection localRemoved, + GpodnetSubscriptionChange changes) throws DownloadRequestException { + // local changes are always superior to remote changes! + // add subscription if (1) not already subscribed and (2) not just unsubscribed + for (String downloadUrl : changes.getAdded()) { + if (false == localSubscriptions.contains(downloadUrl) && + false == localRemoved.contains(downloadUrl)) { + Feed feed = new Feed(downloadUrl, new Date(0)); + DownloadRequester.getInstance().downloadFeed(this, feed); + } + } + // remove subscription if not just subscribed (again) + for (String downloadUrl : changes.getRemoved()) { + if(false == localAdded.contains(downloadUrl)) { + DBTasks.removeFeedWithDownloadUrl(GpodnetSyncService.this, downloadUrl); + } + } + } + private synchronized void syncEpisodeActions() { final long timestamp = GpodnetPreferences.getLastEpisodeActionsSyncTimestamp(); Log.d(TAG, "last episode actions sync timestamp: " + timestamp); @@ -173,25 +220,13 @@ public class GpodnetSyncService extends Service { } } - 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(0)); - DownloadRequester.getInstance().downloadFeed(this, feed); - } - } - for (String downloadUrl : changes.getRemoved()) { - DBTasks.removeFeedWithDownloadUrl(GpodnetSyncService.this, downloadUrl); - } - } - private synchronized void processEpisodeActions(List localActions, List remoteActions) throws DownloadRequestException { + private synchronized void processEpisodeActions(List localActions, + List remoteActions) throws DownloadRequestException { if(remoteActions.size() == 0) { return; } Map, GpodnetEpisodeAction> localMostRecentPlayAction = new HashMap, GpodnetEpisodeAction>(); - Map, GpodnetEpisodeAction> remoteMostRecentPlayAction = new HashMap, GpodnetEpisodeAction>(); - // make sure more recent local actions are not overwritten by older remote actions for(GpodnetEpisodeAction action : localActions) { Pair key = new Pair(action.getPodcast(), action.getEpisode()); GpodnetEpisodeAction mostRecent = localMostRecentPlayAction.get(key); @@ -201,6 +236,9 @@ public class GpodnetSyncService extends Service { localMostRecentPlayAction.put(key, action); } } + + // make sure more recent local actions are not overwritten by older remote actions + Map, GpodnetEpisodeAction> mostRecentPlayAction = new HashMap, GpodnetEpisodeAction>(); for (GpodnetEpisodeAction action : remoteActions) { switch (action.getAction()) { case NEW: @@ -218,11 +256,11 @@ public class GpodnetSyncService extends Service { GpodnetEpisodeAction localMostRecent = localMostRecentPlayAction.get(key); if(localMostRecent == null || localMostRecent.getTimestamp().before(action.getTimestamp())) { - GpodnetEpisodeAction mostRecent = remoteMostRecentPlayAction.get(key); + GpodnetEpisodeAction mostRecent = mostRecentPlayAction.get(key); if (mostRecent == null) { - remoteMostRecentPlayAction.put(key, action); + mostRecentPlayAction.put(key, action); } else if (mostRecent.getTimestamp().before(action.getTimestamp())) { - remoteMostRecentPlayAction.put(key, action); + mostRecentPlayAction.put(key, action); } } break; @@ -231,10 +269,12 @@ public class GpodnetSyncService extends Service { break; } } - for (GpodnetEpisodeAction action : remoteMostRecentPlayAction.values()) { + for (GpodnetEpisodeAction action : mostRecentPlayAction.values()) { FeedItem playItem = DBReader.getFeedItem(this, action.getPodcast(), action.getEpisode()); if (playItem != null) { - playItem.getMedia().setPosition(action.getPosition() * 1000); + FeedMedia media = playItem.getMedia(); + media.setPosition(action.getPosition() * 1000); + DBWriter.setFeedMedia(this, media); if(playItem.getMedia().hasAlmostEnded()) { DBWriter.markItemRead(this, playItem, true, true); DBWriter.addItemToPlaybackHistory(this, playItem.getMedia()); @@ -342,4 +382,20 @@ public class GpodnetSyncService extends Service { context.startService(intent); } } + + public static void sendSyncSubscriptionsIntent(Context context) { + if (GpodnetPreferences.loggedIn()) { + Intent intent = new Intent(context, GpodnetSyncService.class); + intent.putExtra(ARG_ACTION, ACTION_SYNC_SUBSCRIPTIONS); + context.startService(intent); + } + } + + public static void sendSyncActionsIntent(Context context) { + if (GpodnetPreferences.loggedIn()) { + Intent intent = new Intent(context, GpodnetSyncService.class); + intent.putExtra(ARG_ACTION, ACTION_SYNC_ACTIONS); + context.startService(intent); + } + } }