Migrated to WorkManager to allow retrying

This commit is contained in:
ByteHamster 2020-03-27 23:47:18 +01:00
parent 2b8c3ff04e
commit ef121892f7
3 changed files with 62 additions and 39 deletions

View File

@ -143,6 +143,7 @@ dependencies {
implementation "androidx.recyclerview:recyclerview:1.0.0" implementation "androidx.recyclerview:recyclerview:1.0.0"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
implementation "androidx.media:media:1.1.0" implementation "androidx.media:media:1.1.0"
implementation "android.arch.work:work-runtime:$workManagerVersion"
implementation "com.google.android.material:material:1.0.0" implementation "com.google.android.material:material:1.0.0"
annotationProcessor "androidx.annotation:annotation:1.1.0" annotationProcessor "androidx.annotation:annotation:1.1.0"
compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion" compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion"

View File

@ -25,11 +25,6 @@
<action android:name="android.media.browse.MediaBrowserService"/> <action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter> </intent-filter>
</service> </service>
<service
android:name=".sync.SyncService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="true"
android:exported="false" />
<receiver <receiver
android:name=".receiver.MediaButtonReceiver" android:name=".receiver.MediaButtonReceiver"

View File

@ -10,12 +10,19 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.collection.ArrayMap; import androidx.collection.ArrayMap;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.SafeJobIntentService;
import androidx.core.util.Pair; import androidx.core.util.Pair;
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.core.R; import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBTasks;
@ -29,7 +36,6 @@ import de.danoeh.antennapod.core.sync.model.ISyncService;
import de.danoeh.antennapod.core.sync.model.SubscriptionChanges; import de.danoeh.antennapod.core.sync.model.SubscriptionChanges;
import de.danoeh.antennapod.core.sync.model.SyncServiceException; import de.danoeh.antennapod.core.sync.model.SyncServiceException;
import de.danoeh.antennapod.core.sync.model.UploadChangesResponse; import de.danoeh.antennapod.core.sync.model.UploadChangesResponse;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.URLChecker; import de.danoeh.antennapod.core.util.URLChecker;
import de.danoeh.antennapod.core.util.gui.NotificationUtils; import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -39,8 +45,9 @@ import org.json.JSONException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
public class SyncService extends SafeJobIntentService { public class SyncService extends Worker {
private static final String PREF_NAME = "SyncService"; private static final String PREF_NAME = "SyncService";
private static final String PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP = "last_sync_timestamp"; private static final String PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP = "last_sync_timestamp";
private static final String PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP = "last_episode_actions_sync_timestamp"; private static final String PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP = "last_episode_actions_sync_timestamp";
@ -49,21 +56,20 @@ public class SyncService extends SafeJobIntentService {
private static final String PREF_QUEUED_EPISODE_ACTIONS = "sync_queued_episode_actions"; private static final String PREF_QUEUED_EPISODE_ACTIONS = "sync_queued_episode_actions";
private static final String PREF_LAST_SYNC_ATTEMPT_TIMESTAMP = "last_sync_attempt_timestamp"; private static final String PREF_LAST_SYNC_ATTEMPT_TIMESTAMP = "last_sync_attempt_timestamp";
private static final String TAG = "SyncService"; private static final String TAG = "SyncService";
private static final int JOB_ID = -17000; private static final String WORK_ID_SYNC = "SyncServiceWorkId";
private static final Object lock = new Object(); private static final Object lock = new Object();
private static boolean syncPending = false; private static boolean syncPending = false;
private ISyncService syncServiceImpl; private ISyncService syncServiceImpl;
@Override public SyncService(@NonNull Context context, @NonNull WorkerParameters params) {
protected void onHandleWork(@NonNull Intent intent) { super(context, params);
syncServiceImpl = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetService.DEFAULT_BASE_HOST); }
if (!NetworkUtils.networkAvailable()) { @Override
stopForeground(true); @NonNull
stopSelf(); public Result doWork() {
return; syncServiceImpl = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetService.DEFAULT_BASE_HOST);
}
try { try {
// Leave some time, so other actions can be queued // Leave some time, so other actions can be queued
@ -79,12 +85,15 @@ public class SyncService extends SafeJobIntentService {
syncEpisodeActions(); syncEpisodeActions();
syncServiceImpl.logout(); syncServiceImpl.logout();
clearErrorNotifications(); clearErrorNotifications();
return Result.success();
} catch (SyncServiceException e) { } catch (SyncServiceException e) {
e.printStackTrace(); e.printStackTrace();
updateErrorNotification(e); updateErrorNotification(e);
return Result.failure();
} finally {
getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit()
.putLong(PREF_LAST_SYNC_ATTEMPT_TIMESTAMP, System.currentTimeMillis()).apply();
} }
getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit()
.putLong(PREF_LAST_SYNC_ATTEMPT_TIMESTAMP, System.currentTimeMillis()).apply();
} }
public static void clearQueue(Context context) { public static void clearQueue(Context context) {
@ -148,7 +157,19 @@ public class SyncService extends SafeJobIntentService {
public static void sync(Context context) { public static void sync(Context context) {
if (!syncPending) { if (!syncPending) {
syncPending = true; syncPending = true;
enqueueWork(context, SyncService.class, JOB_ID, new Intent());
Constraints.Builder constraints = new Constraints.Builder();
if (UserPreferences.isAllowMobileFeedRefresh()) {
constraints.setRequiredNetworkType(NetworkType.CONNECTED);
} else {
constraints.setRequiredNetworkType(NetworkType.UNMETERED);
}
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(SyncService.class)
.setConstraints(constraints.build())
.setInitialDelay(0L, TimeUnit.MILLISECONDS)
.build();
WorkManager.getInstance().enqueueUniqueWork(WORK_ID_SYNC, ExistingWorkPolicy.REPLACE, workRequest);
} else { } else {
Log.d(TAG, "Ignored sync: Job already enqueued"); Log.d(TAG, "Ignored sync: Job already enqueued");
} }
@ -169,7 +190,7 @@ public class SyncService extends SafeJobIntentService {
private List<EpisodeAction> getQueuedEpisodeActions() { private List<EpisodeAction> getQueuedEpisodeActions() {
ArrayList<EpisodeAction> actions = new ArrayList<>(); ArrayList<EpisodeAction> actions = new ArrayList<>();
try { try {
SharedPreferences prefs = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); SharedPreferences prefs = getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
String json = prefs.getString(PREF_QUEUED_EPISODE_ACTIONS, "[]"); String json = prefs.getString(PREF_QUEUED_EPISODE_ACTIONS, "[]");
JSONArray queue = new JSONArray(json); JSONArray queue = new JSONArray(json);
for (int i = 0; i < queue.length(); i++) { for (int i = 0; i < queue.length(); i++) {
@ -184,7 +205,7 @@ public class SyncService extends SafeJobIntentService {
private List<String> getQueuedRemovedFeeds() { private List<String> getQueuedRemovedFeeds() {
ArrayList<String> actions = new ArrayList<>(); ArrayList<String> actions = new ArrayList<>();
try { try {
SharedPreferences prefs = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); SharedPreferences prefs = getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
String json = prefs.getString(PREF_QUEUED_FEEDS_REMOVED, "[]"); String json = prefs.getString(PREF_QUEUED_FEEDS_REMOVED, "[]");
JSONArray queue = new JSONArray(json); JSONArray queue = new JSONArray(json);
for (int i = 0; i < queue.length(); i++) { for (int i = 0; i < queue.length(); i++) {
@ -199,7 +220,7 @@ public class SyncService extends SafeJobIntentService {
private List<String> getQueuedAddedFeeds() { private List<String> getQueuedAddedFeeds() {
ArrayList<String> actions = new ArrayList<>(); ArrayList<String> actions = new ArrayList<>();
try { try {
SharedPreferences prefs = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); SharedPreferences prefs = getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
String json = prefs.getString(PREF_QUEUED_FEEDS_ADDED, "[]"); String json = prefs.getString(PREF_QUEUED_FEEDS_ADDED, "[]");
JSONArray queue = new JSONArray(json); JSONArray queue = new JSONArray(json);
for (int i = 0; i < queue.length(); i++) { for (int i = 0; i < queue.length(); i++) {
@ -212,7 +233,7 @@ public class SyncService extends SafeJobIntentService {
} }
private void syncSubscriptions() throws SyncServiceException { private void syncSubscriptions() throws SyncServiceException {
final long lastSync = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) final long lastSync = getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
.getLong(PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP, 0); .getLong(PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP, 0);
final List<String> localSubscriptions = DBReader.getFeedListDownloadUrls(); final List<String> localSubscriptions = DBReader.getFeedListDownloadUrls();
SubscriptionChanges subscriptionChanges = syncServiceImpl.getSubscriptionChanges(lastSync); SubscriptionChanges subscriptionChanges = syncServiceImpl.getSubscriptionChanges(lastSync);
@ -226,7 +247,7 @@ public class SyncService extends SafeJobIntentService {
if (!URLChecker.containsUrl(localSubscriptions, downloadUrl) && !queuedRemovedFeeds.contains(downloadUrl)) { if (!URLChecker.containsUrl(localSubscriptions, downloadUrl) && !queuedRemovedFeeds.contains(downloadUrl)) {
Feed feed = new Feed(downloadUrl, null); Feed feed = new Feed(downloadUrl, null);
try { try {
DownloadRequester.getInstance().downloadFeed(this, feed); DownloadRequester.getInstance().downloadFeed(getApplicationContext(), feed);
} catch (DownloadRequestException e) { } catch (DownloadRequestException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -236,7 +257,7 @@ public class SyncService extends SafeJobIntentService {
// remove subscription if not just subscribed (again) // remove subscription if not just subscribed (again)
for (String downloadUrl : subscriptionChanges.getRemoved()) { for (String downloadUrl : subscriptionChanges.getRemoved()) {
if (!queuedAddedFeeds.contains(downloadUrl)) { if (!queuedAddedFeeds.contains(downloadUrl)) {
DBTasks.removeFeedWithDownloadUrl(this, downloadUrl); DBTasks.removeFeedWithDownloadUrl(getApplicationContext(), downloadUrl);
} }
} }
@ -254,19 +275,19 @@ public class SyncService extends SafeJobIntentService {
synchronized (lock) { synchronized (lock) {
UploadChangesResponse uploadResponse = syncServiceImpl UploadChangesResponse uploadResponse = syncServiceImpl
.uploadSubscriptionChanges(queuedAddedFeeds, queuedRemovedFeeds); .uploadSubscriptionChanges(queuedAddedFeeds, queuedRemovedFeeds);
getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit() getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit()
.putString(PREF_QUEUED_FEEDS_ADDED, "[]").apply(); .putString(PREF_QUEUED_FEEDS_ADDED, "[]").apply();
getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit() getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit()
.putString(PREF_QUEUED_FEEDS_REMOVED, "[]").apply(); .putString(PREF_QUEUED_FEEDS_REMOVED, "[]").apply();
newTimeStamp = uploadResponse.timestamp; newTimeStamp = uploadResponse.timestamp;
} }
} }
getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit() getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit()
.putLong(PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP, newTimeStamp).apply(); .putLong(PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP, newTimeStamp).apply();
} }
private void syncEpisodeActions() throws SyncServiceException { private void syncEpisodeActions() throws SyncServiceException {
final long lastSync = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) final long lastSync = getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
.getLong(PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP, 0); .getLong(PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP, 0);
EpisodeActionChanges getResponse = syncServiceImpl.getEpisodeActionChanges(lastSync); EpisodeActionChanges getResponse = syncServiceImpl.getEpisodeActionChanges(lastSync);
long newTimeStamp = getResponse.getTimestamp(); long newTimeStamp = getResponse.getTimestamp();
@ -281,11 +302,11 @@ public class SyncService extends SafeJobIntentService {
UploadChangesResponse postResponse = syncServiceImpl.uploadEpisodeActions(queuedEpisodeActions); UploadChangesResponse postResponse = syncServiceImpl.uploadEpisodeActions(queuedEpisodeActions);
newTimeStamp = postResponse.timestamp; newTimeStamp = postResponse.timestamp;
Log.d(TAG, "Upload episode response: " + postResponse); Log.d(TAG, "Upload episode response: " + postResponse);
getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit() getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit()
.putString(PREF_QUEUED_EPISODE_ACTIONS, "[]").apply(); .putString(PREF_QUEUED_EPISODE_ACTIONS, "[]").apply();
} }
} }
getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit() getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit()
.putLong(PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP, newTimeStamp).apply(); .putLong(PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP, newTimeStamp).apply();
} }
@ -356,26 +377,32 @@ public class SyncService extends SafeJobIntentService {
} }
private void clearErrorNotifications() { private void clearErrorNotifications() {
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager nm = (NotificationManager) getApplicationContext()
.getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel(R.id.notification_gpodnet_sync_error); nm.cancel(R.id.notification_gpodnet_sync_error);
nm.cancel(R.id.notification_gpodnet_sync_autherror); nm.cancel(R.id.notification_gpodnet_sync_autherror);
} }
private void updateErrorNotification(SyncServiceException exception) { private void updateErrorNotification(SyncServiceException exception) {
Log.d(TAG, "Posting error notification"); Log.d(TAG, "Posting error notification");
final String description = getString(R.string.gpodnetsync_error_descr) + exception.getMessage(); final String description = getApplicationContext().getString(R.string.gpodnetsync_error_descr)
+ exception.getMessage();
Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); Intent intent = getApplicationContext().getPackageManager().getLaunchIntentForPackage(
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); getApplicationContext().getPackageName());
Notification notification = new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_ERROR) PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,
.setContentTitle(getString(R.string.gpodnetsync_error_title)) PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(getApplicationContext(),
NotificationUtils.CHANNEL_ID_ERROR)
.setContentTitle(getApplicationContext().getString(R.string.gpodnetsync_error_title))
.setContentText(description) .setContentText(description)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.ic_notification_sync_error) .setSmallIcon(R.drawable.ic_notification_sync_error)
.setAutoCancel(true) .setAutoCancel(true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.build(); .build();
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager nm = (NotificationManager) getApplicationContext()
.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(R.id.notification_gpodnet_sync_error, notification); nm.notify(R.id.notification_gpodnet_sync_error, notification);
} }
} }