Make sync service interface more standard (#7479)

This commit is contained in:
ByteHamster 2024-10-30 21:30:29 +01:00 committed by GitHub
parent f9bea87adc
commit 4a92a5e019
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 285 additions and 221 deletions

View File

@ -7,8 +7,8 @@ import de.danoeh.antennapod.net.download.service.episode.autodownload.AutoDownlo
import de.danoeh.antennapod.net.download.service.feed.FeedUpdateManagerImpl;
import de.danoeh.antennapod.net.download.serviceinterface.AutoDownloadManager;
import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
import de.danoeh.antennapod.net.sync.service.SyncService;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.net.sync.service.SynchronizationQueueImpl;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.storage.preferences.SynchronizationSettings;
import de.danoeh.antennapod.storage.preferences.SynchronizationCredentials;
import de.danoeh.antennapod.storage.preferences.PlaybackPreferences;
@ -50,7 +50,7 @@ public class ClientConfigurator {
DownloadServiceInterface.setImpl(new DownloadServiceInterfaceImpl());
FeedUpdateManager.setInstance(new FeedUpdateManagerImpl());
AutoDownloadManager.setInstance(new AutoDownloadManagerImpl());
SynchronizationQueueSink.setServiceStarterImpl(() -> SyncService.sync(context));
SynchronizationQueue.setInstance(new SynchronizationQueueImpl(context));
AntennapodHttpClient.setCacheDirectory(new File(context.getCacheDir(), "okhttp"));
AntennapodHttpClient.setProxyConfig(UserPreferences.getProxyConfig());
SleepTimerPreferences.init(context);

View File

@ -52,7 +52,7 @@ import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.net.download.service.feed.FeedUpdateManagerImpl;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.playback.cast.CastEnabledActivity;
import de.danoeh.antennapod.storage.database.DBReader;
import de.danoeh.antennapod.storage.importexport.AutomaticDatabaseExportWorker;
@ -205,7 +205,7 @@ public class MainActivity extends CastEnabledActivity {
sheetBehavior.setBottomSheetCallback(bottomSheetCallback);
FeedUpdateManager.getInstance().restartUpdateAlarm(this, false);
SynchronizationQueueSink.syncNowIfNotSyncedRecently();
SynchronizationQueue.getInstance().syncIfNotSyncedRecently();
AutomaticDatabaseExportWorker.enqueueIfNeeded(this, false);
WorkManager.getInstance(this)

View File

@ -18,7 +18,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.storage.preferences.PlaybackPreferences;
import de.danoeh.antennapod.playback.service.PlaybackServiceInterface;
import de.danoeh.antennapod.storage.database.DBWriter;
@ -195,7 +195,7 @@ public class FeedItemMenuHandler {
.position(media.getDuration() / 1000)
.total(media.getDuration() / 1000)
.build();
SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, actionPlay);
SynchronizationQueue.getInstance().enqueueEpisodeAction(actionPlay);
}
}
} else if (menuItemId == R.id.mark_unread_item) {
@ -203,10 +203,10 @@ public class FeedItemMenuHandler {
DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, false);
if (!selectedItem.getFeed().isLocalFeed() && selectedItem.getMedia() != null
&& selectedItem.getFeed().getState() == Feed.STATE_SUBSCRIBED) {
EpisodeAction actionNew = new EpisodeAction.Builder(selectedItem, EpisodeAction.NEW)
.currentTimestamp()
.build();
SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, actionNew);
SynchronizationQueue.getInstance().enqueueEpisodeAction(
new EpisodeAction.Builder(selectedItem, EpisodeAction.NEW)
.currentTimestamp()
.build());
}
} else if (menuItemId == R.id.add_to_queue_item) {
DBWriter.addQueueItem(context, selectedItem);

@ -1 +1 @@
Subproject commit 4e155a5f2af1cb4e83d6e48eba056b3a19466e96
Subproject commit f88328d81d6509487cba71bc4b2bbe96cc47206f

View File

@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
import de.danoeh.antennapod.model.MediaMetadataRetrieverCompat;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.ui.chapters.ChapterUtils;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
@ -116,10 +116,10 @@ public class MediaDownloadedHandler implements Runnable {
}
if (item != null && item.getFeed().getState() == Feed.STATE_SUBSCRIBED) {
EpisodeAction action = new EpisodeAction.Builder(item, EpisodeAction.DOWNLOAD)
.currentTimestamp()
.build();
SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, action);
SynchronizationQueue.getInstance().enqueueEpisodeAction(
new EpisodeAction.Builder(item, EpisodeAction.DOWNLOAD)
.currentTimestamp()
.build());
}
}

View File

@ -4,6 +4,8 @@ import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueStub;
import de.danoeh.antennapod.storage.database.DBReader;
import de.danoeh.antennapod.storage.database.DBWriter;
import de.danoeh.antennapod.storage.database.FeedDatabaseWriter;
@ -45,6 +47,7 @@ public class DbTasksTest {
context = InstrumentationRegistry.getInstrumentation().getTargetContext();
UserPreferences.init(context);
PlaybackPreferences.init(context);
SynchronizationQueue.setInstance(new SynchronizationQueueStub());
// create new database
PodDBAdapter.init(context);

View File

@ -13,6 +13,8 @@ import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterfaceStub;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueStub;
import de.danoeh.antennapod.storage.database.DBReader;
import de.danoeh.antennapod.storage.database.DBWriter;
import de.danoeh.antennapod.storage.database.PodDBAdapter;
@ -62,6 +64,7 @@ public class DbWriterTest {
UserPreferences.init(context);
PlaybackPreferences.init(context);
DownloadServiceInterface.setImpl(new DownloadServiceInterfaceStub());
SynchronizationQueue.setInstance(new SynchronizationQueueStub());
// create new database
PodDBAdapter.init(context);

View File

@ -0,0 +1,36 @@
package de.danoeh.antennapod.net.sync.serviceinterface;
import de.danoeh.antennapod.model.feed.FeedMedia;
public abstract class SynchronizationQueue {
private static SynchronizationQueue instance;
public static SynchronizationQueue getInstance() {
return instance;
}
public static void setInstance(SynchronizationQueue instance) {
SynchronizationQueue.instance = instance;
}
/**
* Sync bundled events after some delay to avoid spamming the sync server.
*/
public abstract void sync();
public abstract void syncImmediately();
public abstract void fullSync();
public abstract void syncIfNotSyncedRecently();
public abstract void clear();
public abstract void enqueueFeedAdded(String downloadUrl);
public abstract void enqueueFeedRemoved(String downloadUrl);
public abstract void enqueueEpisodeAction(EpisodeAction action);
public abstract void enqueueEpisodePlayed(FeedMedia media, boolean completed);
}

View File

@ -1,82 +0,0 @@
package de.danoeh.antennapod.net.sync.serviceinterface;
import android.content.Context;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.storage.preferences.SynchronizationSettings;
import de.danoeh.antennapod.model.feed.FeedMedia;
public class SynchronizationQueueSink {
// To avoid a dependency loop of every class to SyncService, and from SyncService back to every class.
private static Runnable serviceStarterImpl = () -> { };
public static void setServiceStarterImpl(Runnable serviceStarter) {
serviceStarterImpl = serviceStarter;
}
public static void syncNow() {
serviceStarterImpl.run();
}
public static void syncNowIfNotSyncedRecently() {
if (System.currentTimeMillis() - SynchronizationSettings.getLastSyncAttempt() > 1000 * 60 * 10) {
syncNow();
}
}
public static void clearQueue(Context context) {
LockingAsyncExecutor.executeLockedAsync(new SynchronizationQueueStorage(context)::clearQueue);
}
public static void enqueueFeedAddedIfSynchronizationIsActive(Context context, String downloadUrl) {
if (!SynchronizationSettings.isProviderConnected()) {
return;
}
LockingAsyncExecutor.executeLockedAsync(() -> {
new SynchronizationQueueStorage(context).enqueueFeedAdded(downloadUrl);
syncNow();
});
}
public static void enqueueFeedRemovedIfSynchronizationIsActive(Context context, String downloadUrl) {
if (!SynchronizationSettings.isProviderConnected()) {
return;
}
LockingAsyncExecutor.executeLockedAsync(() -> {
new SynchronizationQueueStorage(context).enqueueFeedRemoved(downloadUrl);
syncNow();
});
}
public static void enqueueEpisodeActionIfSynchronizationIsActive(Context context, EpisodeAction action) {
if (!SynchronizationSettings.isProviderConnected()) {
return;
}
LockingAsyncExecutor.executeLockedAsync(() -> {
new SynchronizationQueueStorage(context).enqueueEpisodeAction(action);
syncNow();
});
}
public static void enqueueEpisodePlayedIfSynchronizationIsActive(Context context, FeedMedia media,
boolean completed) {
if (!SynchronizationSettings.isProviderConnected()) {
return;
}
if (media.getItem() == null || media.getItem().getFeed().isLocalFeed()
|| media.getItem().getFeed().getState() != Feed.STATE_SUBSCRIBED) {
return;
}
if (media.getStartPosition() < 0 || (!completed && media.getStartPosition() >= media.getPosition())) {
return;
}
EpisodeAction action = new EpisodeAction.Builder(media.getItem(), EpisodeAction.PLAY)
.currentTimestamp()
.started(media.getStartPosition() / 1000)
.position((completed ? media.getDuration() : media.getPosition()) / 1000)
.total(media.getDuration() / 1000)
.build();
enqueueEpisodeActionIfSynchronizationIsActive(context, action);
}
}

View File

@ -0,0 +1,41 @@
package de.danoeh.antennapod.net.sync.serviceinterface;
import de.danoeh.antennapod.model.feed.FeedMedia;
public class SynchronizationQueueStub extends SynchronizationQueue {
@Override
public void sync() {
}
@Override
public void syncImmediately() {
}
@Override
public void fullSync() {
}
@Override
public void syncIfNotSyncedRecently() {
}
@Override
public void clear() {
}
@Override
public void enqueueFeedAdded(String downloadUrl) {
}
@Override
public void enqueueFeedRemoved(String downloadUrl) {
}
@Override
public void enqueueEpisodeAction(EpisodeAction action) {
}
@Override
public void enqueueEpisodePlayed(FeedMedia media, boolean completed) {
}
}

View File

@ -1,4 +1,4 @@
package de.danoeh.antennapod.net.sync.serviceinterface;
package de.danoeh.antennapod.net.sync.service;
import java.util.concurrent.locks.ReentrantLock;

View File

@ -9,32 +9,39 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import androidx.core.util.Pair;
import androidx.work.BackoffPolicy;
import androidx.work.Constraints;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
import de.danoeh.antennapod.event.MessageEvent;
import de.danoeh.antennapod.event.SyncServiceEvent;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.net.common.AntennapodHttpClient;
import de.danoeh.antennapod.net.common.UrlChecker;
import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
import de.danoeh.antennapod.net.sync.serviceinterface.LockingAsyncExecutor;
import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
import de.danoeh.antennapod.net.sync.nextcloud.NextcloudSyncService;
import de.danoeh.antennapod.net.sync.serviceinterface.EpisodeAction;
import de.danoeh.antennapod.net.sync.serviceinterface.EpisodeActionChanges;
import de.danoeh.antennapod.net.sync.serviceinterface.ISyncService;
import de.danoeh.antennapod.net.sync.serviceinterface.SubscriptionChanges;
import de.danoeh.antennapod.net.sync.serviceinterface.SyncServiceException;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationProvider;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueStorage;
import de.danoeh.antennapod.net.sync.serviceinterface.UploadChangesResponse;
import de.danoeh.antennapod.storage.database.DBReader;
import de.danoeh.antennapod.storage.database.DBWriter;
import de.danoeh.antennapod.storage.database.FeedDatabaseWriter;
import de.danoeh.antennapod.storage.database.LongList;
import de.danoeh.antennapod.storage.preferences.SynchronizationCredentials;
import de.danoeh.antennapod.storage.preferences.SynchronizationSettings;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.notifications.NotificationUtils;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
@ -43,31 +50,11 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.event.SyncServiceEvent;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.net.common.AntennapodHttpClient;
import de.danoeh.antennapod.storage.database.DBReader;
import de.danoeh.antennapod.storage.database.LongList;
import de.danoeh.antennapod.net.common.UrlChecker;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
import de.danoeh.antennapod.net.sync.serviceinterface.EpisodeAction;
import de.danoeh.antennapod.net.sync.serviceinterface.EpisodeActionChanges;
import de.danoeh.antennapod.net.sync.serviceinterface.ISyncService;
import de.danoeh.antennapod.net.sync.serviceinterface.SubscriptionChanges;
import de.danoeh.antennapod.net.sync.serviceinterface.SyncServiceException;
import de.danoeh.antennapod.net.sync.serviceinterface.UploadChangesResponse;
import de.danoeh.antennapod.net.sync.nextcloud.NextcloudSyncService;
public class SyncService extends Worker {
public static final String TAG = "SyncService";
private static final String WORK_ID_SYNC = "SyncServiceWorkId";
private static boolean isCurrentlyActive = false;
private static boolean currentlyActive = false;
private final SynchronizationQueueStorage synchronizationQueueStorage;
public SyncService(@NonNull Context context, @NonNull WorkerParameters params) {
@ -84,7 +71,7 @@ public class SyncService extends Worker {
}
SynchronizationSettings.updateLastSynchronizationAttempt();
setCurrentlyActive(true);
currentlyActive = true;
try {
activeSyncProvider.login();
syncSubscriptions(activeSyncProvider);
@ -111,34 +98,12 @@ public class SyncService extends Worker {
return Result.failure();
}
} finally {
setCurrentlyActive(false);
currentlyActive = false;
}
}
private static void setCurrentlyActive(boolean active) {
isCurrentlyActive = active;
}
public static void sync(Context context) {
OneTimeWorkRequest workRequest = getWorkRequest().build();
WorkManager.getInstance(context).enqueueUniqueWork(WORK_ID_SYNC, ExistingWorkPolicy.REPLACE, workRequest);
}
public static void syncImmediately(Context context) {
OneTimeWorkRequest workRequest = getWorkRequest()
.setInitialDelay(0L, TimeUnit.SECONDS)
.build();
WorkManager.getInstance(context).enqueueUniqueWork(WORK_ID_SYNC, ExistingWorkPolicy.REPLACE, workRequest);
}
public static void fullSync(Context context) {
LockingAsyncExecutor.executeLockedAsync(() -> {
SynchronizationSettings.resetTimestamps();
OneTimeWorkRequest workRequest = getWorkRequest()
.setInitialDelay(0L, TimeUnit.SECONDS)
.build();
WorkManager.getInstance(context).enqueueUniqueWork(WORK_ID_SYNC, ExistingWorkPolicy.REPLACE, workRequest);
});
/* package-private */ static boolean isCurrentlyActive() {
return currentlyActive;
}
private void syncSubscriptions(ISyncService syncServiceImpl) throws SyncServiceException {
@ -179,7 +144,7 @@ public class SyncService extends Worker {
queuedRemovedFeeds.removeAll(subscriptionChanges.getRemoved());
}
if (queuedAddedFeeds.size() > 0 || queuedRemovedFeeds.size() > 0) {
if (!queuedAddedFeeds.isEmpty() || !queuedRemovedFeeds.isEmpty()) {
Log.d(TAG, "Added: " + StringUtils.join(queuedAddedFeeds, ", "));
Log.d(TAG, "Removed: " + StringUtils.join(queuedRemovedFeeds, ", "));
@ -260,7 +225,7 @@ public class SyncService extends Worker {
private synchronized void processEpisodeActions(List<EpisodeAction> remoteActions) {
Log.d(TAG, "Processing " + remoteActions.size() + " actions");
if (remoteActions.size() == 0) {
if (remoteActions.isEmpty()) {
return;
}
@ -344,29 +309,6 @@ public class SyncService extends Worker {
}
}
private static OneTimeWorkRequest.Builder getWorkRequest() {
Constraints.Builder constraints = new Constraints.Builder();
if (UserPreferences.isAllowMobileSync()) {
constraints.setRequiredNetworkType(NetworkType.CONNECTED);
} else {
constraints.setRequiredNetworkType(NetworkType.UNMETERED);
}
OneTimeWorkRequest.Builder builder = new OneTimeWorkRequest.Builder(SyncService.class)
.setConstraints(constraints.build())
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.MINUTES);
if (isCurrentlyActive) {
// Debounce: don't start sync again immediately after it was finished.
builder.setInitialDelay(2L, TimeUnit.MINUTES);
} else {
// Give it some time, so other possible actions can be queued.
builder.setInitialDelay(20L, TimeUnit.SECONDS);
EventBus.getDefault().postSticky(new SyncServiceEvent(R.string.sync_status_started));
}
return builder;
}
private ISyncService getActiveSyncProvider() {
String selectedSyncProviderKey = SynchronizationSettings.getSelectedSyncProviderKey();
SynchronizationProvider selectedService = SynchronizationProvider

View File

@ -0,0 +1,130 @@
package de.danoeh.antennapod.net.sync.service;
import android.content.Context;
import androidx.work.BackoffPolicy;
import androidx.work.Constraints;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import de.danoeh.antennapod.event.SyncServiceEvent;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.net.sync.serviceinterface.EpisodeAction;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.storage.preferences.SynchronizationSettings;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import org.greenrobot.eventbus.EventBus;
import java.util.concurrent.TimeUnit;
public class SynchronizationQueueImpl extends SynchronizationQueue {
private static final String WORK_ID_SYNC = "SyncServiceWorkId";
private final Context context;
public SynchronizationQueueImpl(Context context) {
this.context = context;
}
public void sync() {
OneTimeWorkRequest workRequest = getWorkRequest().build();
WorkManager.getInstance(context).enqueueUniqueWork(WORK_ID_SYNC, ExistingWorkPolicy.REPLACE, workRequest);
}
public void syncIfNotSyncedRecently() {
if (System.currentTimeMillis() - SynchronizationSettings.getLastSyncAttempt() > 1000 * 60 * 10) {
sync();
}
}
public void syncImmediately() {
OneTimeWorkRequest workRequest = getWorkRequest()
.setInitialDelay(0L, TimeUnit.SECONDS)
.build();
WorkManager.getInstance(context).enqueueUniqueWork(WORK_ID_SYNC, ExistingWorkPolicy.REPLACE, workRequest);
}
public void fullSync() {
LockingAsyncExecutor.executeLockedAsync(() -> {
SynchronizationSettings.resetTimestamps();
syncImmediately();
});
}
private static OneTimeWorkRequest.Builder getWorkRequest() {
Constraints.Builder constraints = new Constraints.Builder();
if (UserPreferences.isAllowMobileSync()) {
constraints.setRequiredNetworkType(NetworkType.CONNECTED);
} else {
constraints.setRequiredNetworkType(NetworkType.UNMETERED);
}
OneTimeWorkRequest.Builder builder = new OneTimeWorkRequest.Builder(SyncService.class)
.setConstraints(constraints.build())
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.MINUTES);
if (SyncService.isCurrentlyActive()) {
// Debounce: don't start sync again immediately after it was finished.
builder.setInitialDelay(2L, TimeUnit.MINUTES);
} else {
// Give it some time, so other possible actions can be queued.
builder.setInitialDelay(20L, TimeUnit.SECONDS);
EventBus.getDefault().postSticky(new SyncServiceEvent(R.string.sync_status_started));
}
return builder;
}
public void clear() {
LockingAsyncExecutor.executeLockedAsync(new SynchronizationQueueStorage(context)::clearQueue);
}
public void enqueueFeedAdded(String downloadUrl) {
if (!SynchronizationSettings.isProviderConnected()) {
return;
}
LockingAsyncExecutor.executeLockedAsync(() -> {
new SynchronizationQueueStorage(context).enqueueFeedAdded(downloadUrl);
sync();
});
}
public void enqueueFeedRemoved(String downloadUrl) {
if (!SynchronizationSettings.isProviderConnected()) {
return;
}
LockingAsyncExecutor.executeLockedAsync(() -> {
new SynchronizationQueueStorage(context).enqueueFeedRemoved(downloadUrl);
sync();
});
}
public void enqueueEpisodeAction(EpisodeAction action) {
if (!SynchronizationSettings.isProviderConnected()) {
return;
}
LockingAsyncExecutor.executeLockedAsync(() -> {
new SynchronizationQueueStorage(context).enqueueEpisodeAction(action);
sync();
});
}
public void enqueueEpisodePlayed(FeedMedia media, boolean completed) {
if (!SynchronizationSettings.isProviderConnected()) {
return;
}
if (media.getItem() == null || media.getItem().getFeed().isLocalFeed()
|| media.getItem().getFeed().getState() != Feed.STATE_SUBSCRIBED) {
return;
}
if (media.getStartPosition() < 0 || (!completed && media.getStartPosition() >= media.getPosition())) {
return;
}
EpisodeAction action = new EpisodeAction.Builder(media.getItem(), EpisodeAction.PLAY)
.currentTimestamp()
.started(media.getStartPosition() / 1000)
.position((completed ? media.getDuration() : media.getPosition()) / 1000)
.total(media.getDuration() / 1000)
.build();
enqueueEpisodeAction(action);
}
}

View File

@ -1,8 +1,9 @@
package de.danoeh.antennapod.net.sync.serviceinterface;
package de.danoeh.antennapod.net.sync.service;
import android.content.Context;
import android.content.SharedPreferences;
import de.danoeh.antennapod.net.sync.serviceinterface.EpisodeAction;
import org.json.JSONArray;
import org.json.JSONException;

View File

@ -54,7 +54,7 @@ import androidx.lifecycle.Observer;
import androidx.media.MediaBrowserServiceCompat;
import de.danoeh.antennapod.event.PlayerStatusEvent;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.playback.service.internal.LocalPSMP;
import de.danoeh.antennapod.playback.service.internal.PlayableUtils;
import de.danoeh.antennapod.playback.service.internal.PlaybackServiceNotificationBuilder;
@ -943,8 +943,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
// Don't store position after position is already reset
saveCurrentPosition(position == Playable.INVALID_TIME, playable, position);
}
SynchronizationQueueSink.enqueueEpisodePlayedIfSynchronizationIsActive(getApplicationContext(),
media, false);
SynchronizationQueue.getInstance().enqueueEpisodePlayed(media, false);
}
playable.onPlaybackPause(getApplicationContext());
}
@ -1141,12 +1140,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
if (ended || almostEnded) {
SynchronizationQueueSink.enqueueEpisodePlayedIfSynchronizationIsActive(
getApplicationContext(), media, true);
SynchronizationQueue.getInstance().enqueueEpisodePlayed(media, true);
media.onPlaybackCompleted(getApplicationContext());
} else {
SynchronizationQueueSink.enqueueEpisodePlayedIfSynchronizationIsActive(
getApplicationContext(), media, false);
SynchronizationQueue.getInstance().enqueueEpisodePlayed(media, false);
media.onPlaybackPause(getApplicationContext());
}

View File

@ -18,7 +18,7 @@ import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.net.download.serviceinterface.AutoDownloadManager;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.ui.appstartintent.MediaButtonStarter;
import org.greenrobot.eventbus.EventBus;
@ -151,11 +151,10 @@ public class DBWriter {
FeedUpdateManager.getInstance().runOnce(context, media.getItem().getFeed());
} else {
if (media.getItem().getFeed().getState() == Feed.STATE_SUBSCRIBED) {
FeedItem item = media.getItem();
EpisodeAction action = new EpisodeAction.Builder(item, EpisodeAction.DELETE)
.currentTimestamp()
.build();
SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, action);
SynchronizationQueue.getInstance().enqueueEpisodeAction(
new EpisodeAction.Builder(media.getItem(), EpisodeAction.DELETE)
.currentTimestamp()
.build());
}
EventBus.getDefault().post(FeedItemEvent.updated(media.getItem()));
@ -185,7 +184,7 @@ public class DBWriter {
adapter.close();
if (!feed.isLocalFeed() && feed.getState() == Feed.STATE_SUBSCRIBED) {
SynchronizationQueueSink.enqueueFeedRemovedIfSynchronizationIsActive(context, feed.getDownloadUrl());
SynchronizationQueue.getInstance().enqueueFeedRemoved(feed.getDownloadUrl());
}
EventBus.getDefault().post(new FeedListUpdateEvent(feed));
});
@ -779,7 +778,7 @@ public class DBWriter {
for (Feed feed : feeds) {
if (!feed.isLocalFeed() && feed.getState() == Feed.STATE_SUBSCRIBED) {
SynchronizationQueueSink.enqueueFeedAddedIfSynchronizationIsActive(context, feed.getDownloadUrl());
SynchronizationQueue.getInstance().enqueueFeedAdded(feed.getDownloadUrl());
}
}
@ -931,13 +930,12 @@ public class DBWriter {
feed.getPreferences().setKeepUpdated(true);
DBWriter.setFeedPreferences(feed.getPreferences());
FeedUpdateManager.getInstance().runOnceOrAsk(context, feed);
SynchronizationQueueSink.enqueueFeedAddedIfSynchronizationIsActive(context, feed.getDownloadUrl());
SynchronizationQueue.getInstance().enqueueFeedAdded(feed.getDownloadUrl());
DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(),
SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE);
for (FeedItem item : feed.getItems()) {
if (item.isPlayed()) {
SynchronizationQueueSink.enqueueEpisodePlayedIfSynchronizationIsActive(
context, item.getMedia(), true);
SynchronizationQueue.getInstance().enqueueEpisodePlayed(item.getMedia(), true);
}
}
}

View File

@ -12,7 +12,7 @@ import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.net.sync.serviceinterface.EpisodeAction;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import org.greenrobot.eventbus.EventBus;
@ -165,7 +165,7 @@ public abstract class FeedDatabaseWriter {
.position(oldItem.getMedia().getDuration() / 1000)
.total(oldItem.getMedia().getDuration() / 1000)
.build();
SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, action);
SynchronizationQueue.getInstance().enqueueEpisodeAction(action);
}
}
}

View File

@ -19,9 +19,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import androidx.fragment.app.DialogFragment;
import com.google.android.material.button.MaterialButton;
import de.danoeh.antennapod.net.common.AntennapodHttpClient;
import de.danoeh.antennapod.net.sync.service.SyncService;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationProvider;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.storage.preferences.SynchronizationCredentials;
import de.danoeh.antennapod.storage.preferences.SynchronizationSettings;
import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
@ -84,7 +83,7 @@ public class GpodderAuthenticationFragment extends DialogFragment {
return;
}
SynchronizationCredentials.clear();
SynchronizationQueueSink.clearQueue(getContext());
SynchronizationQueue.getInstance().clear();
SynchronizationCredentials.setHosturl(serverUrlText.getText().toString());
service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
SynchronizationCredentials.getHosturl(), SynchronizationCredentials.getDeviceId(),
@ -235,7 +234,7 @@ public class GpodderAuthenticationFragment extends DialogFragment {
sync.setOnClickListener(v -> {
dismiss();
SyncService.sync(getContext());
SynchronizationQueue.getInstance().syncImmediately();
});
}

View File

@ -12,9 +12,8 @@ import androidx.annotation.Nullable;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import androidx.fragment.app.DialogFragment;
import de.danoeh.antennapod.net.common.AntennapodHttpClient;
import de.danoeh.antennapod.net.sync.service.SyncService;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationProvider;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.storage.preferences.SynchronizationCredentials;
import de.danoeh.antennapod.storage.preferences.SynchronizationSettings;
import de.danoeh.antennapod.net.sync.nextcloud.NextcloudLoginFlow;
@ -93,11 +92,11 @@ public class NextcloudAuthenticationFragment extends DialogFragment
SynchronizationSettings.setSelectedSyncProvider(
SynchronizationProvider.NEXTCLOUD_GPODDER.getIdentifier());
SynchronizationCredentials.clear();
SynchronizationQueueSink.clearQueue(getContext());
SynchronizationQueue.getInstance().clear();
SynchronizationCredentials.setPassword(password);
SynchronizationCredentials.setHosturl(server);
SynchronizationCredentials.setUsername(username);
SyncService.fullSync(getContext());
SynchronizationQueue.getInstance().fullSync();
if (isResumed()) {
dismiss();
} else {

View File

@ -22,9 +22,8 @@ import androidx.preference.Preference;
import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.net.sync.service.SyncService;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationProvider;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueue;
import de.danoeh.antennapod.ui.preferences.R;
import de.danoeh.antennapod.ui.preferences.screen.AnimatedPreferenceFragment;
import org.greenrobot.eventbus.EventBus;
@ -95,16 +94,16 @@ public class SynchronizationPreferencesFragment extends AnimatedPreferenceFragme
return true;
});
findPreference(PREFERENCE_SYNC).setOnPreferenceClickListener(preference -> {
SyncService.syncImmediately(getActivity().getApplicationContext());
SynchronizationQueue.getInstance().syncImmediately();
return true;
});
findPreference(PREFERENCE_FORCE_FULL_SYNC).setOnPreferenceClickListener(preference -> {
SyncService.fullSync(getContext());
SynchronizationQueue.getInstance().fullSync();
return true;
});
findPreference(PREFERENCE_LOGOUT).setOnPreferenceClickListener(preference -> {
SynchronizationCredentials.clear();
SynchronizationQueueSink.clearQueue(getContext());
SynchronizationQueue.getInstance().clear();
Snackbar.make(getView(), R.string.pref_synchronization_logout_toast, Snackbar.LENGTH_LONG).show();
SynchronizationSettings.setSelectedSyncProvider(null);
updateScreen();
@ -168,12 +167,10 @@ public class SynchronizationPreferencesFragment extends AnimatedPreferenceFragme
public View getView(int position, View convertView, ViewGroup parent) {
final LayoutInflater inflater = LayoutInflater.from(getContext());
if (convertView == null) {
convertView = inflater.inflate(
R.layout.alertdialog_sync_provider_chooser, null);
convertView = inflater.inflate(R.layout.alertdialog_sync_provider_chooser, null);
holder = new ViewHolder();
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.icon = convertView.findViewById(R.id.icon);
holder.title = convertView.findViewById(R.id.title);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();