use of de.danoeh.antennapod.net.sync.model.EpisodeAction in project AntennaPod by AntennaPod.
the class ResponseMapper method readEpisodeActionsFromJsonObject.
public static EpisodeActionChanges readEpisodeActionsFromJsonObject(@NonNull JSONObject object) throws JSONException {
List<EpisodeAction> episodeActions = new ArrayList<>();
long timestamp = object.getLong("timestamp");
JSONArray jsonActions = object.getJSONArray("actions");
for (int i = 0; i < jsonActions.length(); i++) {
JSONObject jsonAction = jsonActions.getJSONObject(i);
EpisodeAction episodeAction = EpisodeAction.readFromJsonObject(jsonAction);
if (episodeAction != null) {
episodeActions.add(episodeAction);
}
}
return new EpisodeActionChanges(episodeActions, timestamp);
}
use of de.danoeh.antennapod.net.sync.model.EpisodeAction in project AntennaPod by AntennaPod.
the class NextcloudSyncService method uploadEpisodeActionsPartial.
private void uploadEpisodeActionsPartial(List<EpisodeAction> queuedEpisodeActions, int from, int to) throws NextcloudSynchronizationServiceException {
try {
final JSONArray list = new JSONArray();
for (int i = from; i < to; i++) {
EpisodeAction episodeAction = queuedEpisodeActions.get(i);
JSONObject obj = episodeAction.writeToJsonObject();
if (obj != null) {
list.put(obj);
}
}
HttpUrl.Builder url = makeUrl("/index.php/apps/gpoddersync/episode_action/create");
RequestBody requestBody = RequestBody.create(MediaType.get("application/json"), list.toString());
performRequest(url, "POST", requestBody);
} catch (Exception e) {
e.printStackTrace();
throw new NextcloudSynchronizationServiceException(e);
}
}
use of de.danoeh.antennapod.net.sync.model.EpisodeAction in project AntennaPod by AntennaPod.
the class FeedItemMenuHandler method onMenuItemClicked.
/**
* Default menu handling for the given FeedItem.
*
* A Fragment instance, (rather than the more generic Context), is needed as a parameter
* to support some UI operations, e.g., creating a Snackbar.
*/
public static boolean onMenuItemClicked(@NonNull Fragment fragment, int menuItemId, @NonNull FeedItem selectedItem) {
@NonNull Context context = fragment.requireContext();
if (menuItemId == R.id.skip_episode_item) {
IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
} else if (menuItemId == R.id.remove_item) {
DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId());
} else if (menuItemId == R.id.remove_new_flag_item) {
removeNewFlagWithUndo(fragment, selectedItem);
} else if (menuItemId == R.id.mark_read_item) {
selectedItem.setPlayed(true);
DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, true);
if (SynchronizationSettings.isProviderConnected()) {
FeedMedia media = selectedItem.getMedia();
// not all items have media, Gpodder only cares about those that do
if (media != null) {
EpisodeAction actionPlay = new EpisodeAction.Builder(selectedItem, EpisodeAction.PLAY).currentTimestamp().started(media.getDuration() / 1000).position(media.getDuration() / 1000).total(media.getDuration() / 1000).build();
SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, actionPlay);
}
}
} else if (menuItemId == R.id.mark_unread_item) {
selectedItem.setPlayed(false);
DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, false);
if (selectedItem.getMedia() != null) {
EpisodeAction actionNew = new EpisodeAction.Builder(selectedItem, EpisodeAction.NEW).currentTimestamp().build();
SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, actionNew);
}
} else if (menuItemId == R.id.add_to_queue_item) {
DBWriter.addQueueItem(context, selectedItem);
} else if (menuItemId == R.id.remove_from_queue_item) {
DBWriter.removeQueueItem(context, true, selectedItem);
} else if (menuItemId == R.id.add_to_favorites_item) {
DBWriter.addFavoriteItem(selectedItem);
} else if (menuItemId == R.id.remove_from_favorites_item) {
DBWriter.removeFavoriteItem(selectedItem);
} else if (menuItemId == R.id.reset_position) {
selectedItem.getMedia().setPosition(0);
if (PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == selectedItem.getMedia().getId()) {
PlaybackPreferences.writeNoMediaPlaying();
IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
}
DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, true);
} else if (menuItemId == R.id.visit_website_item) {
IntentUtils.openInBrowser(context, FeedItemUtil.getLinkWithFallback(selectedItem));
} else if (menuItemId == R.id.share_item) {
ShareDialog shareDialog = ShareDialog.newInstance(selectedItem);
shareDialog.show((fragment.getActivity().getSupportFragmentManager()), "ShareEpisodeDialog");
} else {
Log.d(TAG, "Unknown menuItemId: " + menuItemId);
return false;
}
return true;
}
use of de.danoeh.antennapod.net.sync.model.EpisodeAction in project AntennaPod by AntennaPod.
the class DBTasks method updateFeed.
/**
* Adds new Feeds to the database or updates the old versions if they already exists. If another Feed with the same
* identifying value already exists, this method will add new FeedItems from the new Feed to the existing Feed.
* These FeedItems will be marked as unread with the exception of the most recent FeedItem.
* <p/>
* This method can update multiple feeds at once. Submitting a feed twice in the same method call can result in undefined behavior.
* <p/>
* This method should NOT be executed on the GUI thread.
*
* @param context Used for accessing the DB.
* @param newFeed The new Feed object.
* @param removeUnlistedItems The item list in the new Feed object is considered to be exhaustive.
* I.e. items are removed from the database if they are not in this item list.
* @return The updated Feed from the database if it already existed, or the new Feed from the parameters otherwise.
*/
public static synchronized Feed updateFeed(Context context, Feed newFeed, boolean removeUnlistedItems) {
Feed resultFeed;
List<FeedItem> unlistedItems = new ArrayList<>();
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
// Look up feed in the feedslist
final Feed savedFeed = searchFeedByIdentifyingValueOrID(adapter, newFeed);
if (savedFeed == null) {
Log.d(TAG, "Found no existing Feed with title " + newFeed.getTitle() + ". Adding as new one.");
// Add a new Feed
// all new feeds will have the most recent item marked as unplayed
FeedItem mostRecent = newFeed.getMostRecentItem();
if (mostRecent != null) {
mostRecent.setNew();
}
resultFeed = newFeed;
} else {
Log.d(TAG, "Feed with title " + newFeed.getTitle() + " already exists. Syncing new with existing one.");
Collections.sort(newFeed.getItems(), new FeedItemPubdateComparator());
if (newFeed.getPageNr() == savedFeed.getPageNr()) {
if (savedFeed.compareWithOther(newFeed)) {
Log.d(TAG, "Feed has updated attribute values. Updating old feed's attributes");
savedFeed.updateFromOther(newFeed);
}
} else {
Log.d(TAG, "New feed has a higher page number.");
savedFeed.setNextPageLink(newFeed.getNextPageLink());
}
if (savedFeed.getPreferences().compareWithOther(newFeed.getPreferences())) {
Log.d(TAG, "Feed has updated preferences. Updating old feed's preferences");
savedFeed.getPreferences().updateFromOther(newFeed.getPreferences());
}
// get the most recent date now, before we start changing the list
FeedItem priorMostRecent = savedFeed.getMostRecentItem();
Date priorMostRecentDate = null;
if (priorMostRecent != null) {
priorMostRecentDate = priorMostRecent.getPubDate();
}
// Look for new or updated Items
for (int idx = 0; idx < newFeed.getItems().size(); idx++) {
final FeedItem item = newFeed.getItems().get(idx);
FeedItem possibleDuplicate = searchFeedItemGuessDuplicate(newFeed.getItems(), item);
if (!newFeed.isLocalFeed() && possibleDuplicate != null && item != possibleDuplicate) {
// Canonical episode is the first one returned (usually oldest)
DBWriter.addDownloadStatus(new DownloadStatus(savedFeed, item.getTitle(), DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false, "The podcast host appears to have added the same episode twice. " + "AntennaPod still refreshed the feed and attempted to repair it." + "\n\nOriginal episode:\n" + duplicateEpisodeDetails(item) + "\n\nSecond episode that is also in the feed:\n" + duplicateEpisodeDetails(possibleDuplicate), false));
continue;
}
FeedItem oldItem = searchFeedItemByIdentifyingValue(savedFeed.getItems(), item);
if (!newFeed.isLocalFeed() && oldItem == null) {
oldItem = searchFeedItemGuessDuplicate(savedFeed.getItems(), item);
if (oldItem != null) {
Log.d(TAG, "Repaired duplicate: " + oldItem + ", " + item);
DBWriter.addDownloadStatus(new DownloadStatus(savedFeed, item.getTitle(), DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE, false, "The podcast host changed the ID of an existing episode instead of just " + "updating the episode itself. AntennaPod still refreshed the feed and " + "attempted to repair it." + "\n\nOriginal episode:\n" + duplicateEpisodeDetails(oldItem) + "\n\nNow the feed contains:\n" + duplicateEpisodeDetails(item), false));
oldItem.setItemIdentifier(item.getItemIdentifier());
if (oldItem.isPlayed() && oldItem.getMedia() != null) {
EpisodeAction action = new EpisodeAction.Builder(oldItem, EpisodeAction.PLAY).currentTimestamp().started(oldItem.getMedia().getDuration() / 1000).position(oldItem.getMedia().getDuration() / 1000).total(oldItem.getMedia().getDuration() / 1000).build();
SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, action);
}
}
}
if (oldItem != null) {
oldItem.updateFromOther(item);
} else {
// item is new
item.setFeed(savedFeed);
if (idx >= savedFeed.getItems().size()) {
savedFeed.getItems().add(item);
} else {
savedFeed.getItems().add(idx, item);
}
// New items that do not have a pubDate set are always marked as new
if (item.getPubDate() == null || priorMostRecentDate == null || priorMostRecentDate.before(item.getPubDate()) || priorMostRecentDate.equals(item.getPubDate())) {
Log.d(TAG, "Marking item published on " + item.getPubDate() + " new, prior most recent date = " + priorMostRecentDate);
item.setNew();
}
}
}
// identify items to be removed
if (removeUnlistedItems) {
Iterator<FeedItem> it = savedFeed.getItems().iterator();
while (it.hasNext()) {
FeedItem feedItem = it.next();
if (searchFeedItemByIdentifyingValue(newFeed.getItems(), feedItem) == null) {
unlistedItems.add(feedItem);
it.remove();
}
}
}
// update attributes
savedFeed.setLastUpdate(newFeed.getLastUpdate());
savedFeed.setType(newFeed.getType());
savedFeed.setLastUpdateFailed(false);
resultFeed = savedFeed;
}
try {
if (savedFeed == null) {
DBWriter.addNewFeed(context, newFeed).get();
// Update with default values that are set in database
resultFeed = searchFeedByIdentifyingValueOrID(adapter, newFeed);
} else {
DBWriter.setCompleteFeed(savedFeed).get();
}
if (removeUnlistedItems) {
DBWriter.deleteFeedItems(context, unlistedItems).get();
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
adapter.close();
if (savedFeed != null) {
EventBus.getDefault().post(new FeedListUpdateEvent(savedFeed));
} else {
EventBus.getDefault().post(new FeedListUpdateEvent(Collections.emptyList()));
}
return resultFeed;
}
use of de.danoeh.antennapod.net.sync.model.EpisodeAction in project AntennaPod by AntennaPod.
the class EpisodeActionFilter method getRemoteActionsOverridingLocalActions.
public static Map<Pair<String, String>, EpisodeAction> getRemoteActionsOverridingLocalActions(List<EpisodeAction> remoteActions, List<EpisodeAction> queuedEpisodeActions) {
// make sure more recent local actions are not overwritten by older remote actions
Map<Pair<String, String>, EpisodeAction> remoteActionsThatOverrideLocalActions = new ArrayMap<>();
Map<Pair<String, String>, EpisodeAction> localMostRecentPlayActions = createUniqueLocalMostRecentPlayActions(queuedEpisodeActions);
for (EpisodeAction remoteAction : remoteActions) {
Pair<String, String> key = new Pair<>(remoteAction.getPodcast(), remoteAction.getEpisode());
switch(remoteAction.getAction()) {
case NEW:
remoteActionsThatOverrideLocalActions.put(key, remoteAction);
break;
case DOWNLOAD:
break;
case PLAY:
EpisodeAction localMostRecent = localMostRecentPlayActions.get(key);
if (secondActionOverridesFirstAction(remoteAction, localMostRecent)) {
break;
}
EpisodeAction remoteMostRecentAction = remoteActionsThatOverrideLocalActions.get(key);
if (secondActionOverridesFirstAction(remoteAction, remoteMostRecentAction)) {
break;
}
remoteActionsThatOverrideLocalActions.put(key, remoteAction);
break;
case DELETE:
// NEVER EVER call DBWriter.deleteFeedMediaOfItem() here, leads to an infinite loop
break;
default:
Log.e(TAG, "Unknown remoteAction: " + remoteAction);
break;
}
}
return remoteActionsThatOverrideLocalActions;
}
Aggregations