Using WorkManager for feed updates

This commit is contained in:
ByteHamster 2019-05-06 23:04:19 +02:00
parent d7768d33e2
commit dbda4a9aa8
11 changed files with 93 additions and 246 deletions

View File

@ -207,7 +207,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
checkFirstLaunch();
NotificationUtils.createChannels(this);
UserPreferences.restartUpdateAlarm(false);
}
private void saveLastNavFragment(String tag) {
@ -474,7 +473,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
protected void onResume() {
super.onResume();
StorageUtils.checkStorageAvailability(this);
AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext());
Intent intent = getIntent();
if (intent.hasExtra(EXTRA_FEED_ID) ||

View File

@ -178,7 +178,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
@Override
protected void onResume() {
super.onResume();
AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext());
EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);

View File

@ -45,6 +45,7 @@ project.ext {
targetSdkVersion = 26
supportVersion = "27.1.1"
workManagerVersion = "1.0.1"
awaitilityVersion = "3.1.2"
commonsioVersion = "2.5"
commonslangVersion = "3.6"

View File

@ -45,6 +45,8 @@ dependencies {
implementation "com.android.support:appcompat-v7:$supportVersion"
implementation "com.android.support:preference-v14:$supportVersion"
implementation "com.android.support:percent:$supportVersion"
implementation "android.arch.work:work-runtime:$workManagerVersion"
implementation "org.apache.commons:commons-lang3:$commonslangVersion"
implementation "org.apache.commons:commons-text:$commonstextVersion"
implementation ("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {

View File

@ -41,29 +41,10 @@
</intent-filter>
</receiver>
<receiver android:name=".receiver.AlarmUpdateReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data
android:path="de.danoeh.antennapod"
android:scheme="package" />
</intent-filter>
</receiver>
<receiver android:name=".receiver.FeedUpdateReceiver"
android:label="@string/feed_update_receiver_name"
android:exported="true"> <!-- allow feeds update to be triggered by external apps -->
</receiver>
<service
android:name=".service.FeedUpdateJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
</application>
</manifest>

View File

@ -561,7 +561,7 @@ public class UserPreferences {
.apply();
// when updating with an interval, we assume the user wants
// to update *now* and then every 'hours' interval thereafter.
restartUpdateAlarm(true);
restartUpdateAlarm();
}
/**
@ -571,7 +571,7 @@ public class UserPreferences {
prefs.edit()
.putString(PREF_UPDATE_INTERVAL, hourOfDay + ":" + minute)
.apply();
restartUpdateAlarm(false);
restartUpdateAlarm();
}
/**
@ -814,18 +814,23 @@ public class UserPreferences {
}
}
public static void restartUpdateAlarm(boolean now) {
int[] timeOfDay = getUpdateTimeOfDay();
Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
if (timeOfDay.length == 2) {
AutoUpdateManager.restartUpdateTimeOfDayAlarm(context, timeOfDay[0], timeOfDay[1]);
/**
*
* @return true if auto update is set to a specific time
* false if auto update is set to interval
*/
public static boolean isAutoUpdateTimeOfDay() {
return getUpdateTimeOfDay().length == 2;
}
public static void restartUpdateAlarm() {
if (isAutoUpdateTimeOfDay()) {
int[] timeOfDay = getUpdateTimeOfDay();
Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
AutoUpdateManager.restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
} else {
long milliseconds = getUpdateInterval();
long startTrigger = milliseconds;
if (now) {
startTrigger = TimeUnit.SECONDS.toMillis(10);
}
AutoUpdateManager.restartUpdateIntervalAlarm(context, startTrigger, milliseconds);
AutoUpdateManager.restartUpdateIntervalAlarm(milliseconds);
}
}

View File

@ -1,29 +0,0 @@
package de.danoeh.antennapod.core.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.preferences.UserPreferences;
/** Listens for events that make it necessary to reset the update alarm. */
public class AlarmUpdateReceiver extends BroadcastReceiver {
private static final String TAG = "AlarmUpdateReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received intent");
if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) {
Log.d(TAG, "Resetting update alarm after reboot");
} else if (TextUtils.equals(intent.getAction(), Intent.ACTION_PACKAGE_REPLACED)) {
Log.d(TAG, "Resetting update alarm after app upgrade");
}
ClientConfig.initialize(context);
UserPreferences.restartUpdateAlarm(false);
}
}

View File

@ -21,7 +21,6 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
Log.d(TAG, "Received intent");
ClientConfig.initialize(context);
FeedUpdateUtils.startAutoUpdate(context, null);
UserPreferences.restartUpdateAlarm(false);
}
}

View File

@ -1,35 +0,0 @@
package de.danoeh.antennapod.core.service;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.FeedUpdateUtils;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class FeedUpdateJobService extends JobService {
private static final String TAG = "FeedUpdateJobService";
@Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "Job started");
ClientConfig.initialize(getApplicationContext());
FeedUpdateUtils.startAutoUpdate(getApplicationContext(), () -> {
UserPreferences.restartUpdateAlarm(false);
jobFinished(params, false); // needsReschedule = false
});
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
return true;
}
}

View File

@ -0,0 +1,37 @@
package de.danoeh.antennapod.core.service;
import android.content.Context;
import android.support.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.FeedUpdateUtils;
import org.awaitility.Awaitility;
import java.util.concurrent.atomic.AtomicBoolean;
public class FeedUpdateWorker extends Worker {
public FeedUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@Override
@NonNull
public Result doWork() {
ClientConfig.initialize(getApplicationContext());
AtomicBoolean finished = new AtomicBoolean(false);
FeedUpdateUtils.startAutoUpdate(getApplicationContext(), () -> finished.set(true));
Awaitility.await().until(finished::get);
if (UserPreferences.isAutoUpdateTimeOfDay()) {
// WorkManager does not allow to set specific time for repeated tasks.
// We repeatedly schedule a OneTimeWorkRequest instead.
UserPreferences.restartUpdateAlarm();
}
return Result.success();
}
}

View File

@ -1,29 +1,21 @@
package de.danoeh.antennapod.core.util.download;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.SystemClock;
import android.support.annotation.RequiresApi;
import android.util.Log;
import androidx.work.Constraints;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.FeedUpdateWorker;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.receiver.FeedUpdateReceiver;
import de.danoeh.antennapod.core.service.FeedUpdateJobService;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.FeedUpdateUtils;
public class AutoUpdateManager {
private static final int JOB_ID_FEED_UPDATE = 42;
private static final String WORK_ID_FEED_UPDATE = FeedUpdateWorker.class.getName();
private static final String TAG = "AutoUpdateManager";
private AutoUpdateManager() {
@ -33,20 +25,22 @@ public class AutoUpdateManager {
/**
* Sets the interval in which the feeds are refreshed automatically
*/
public static void restartUpdateIntervalAlarm(Context context, long triggerAtMillis, long intervalMillis) {
public static void restartUpdateIntervalAlarm(long intervalMillis) {
Log.d(TAG, "Restarting update alarm.");
if (Build.VERSION.SDK_INT >= 24) {
restartJobServiceInterval(context, intervalMillis);
} else {
restartAlarmManagerInterval(context, triggerAtMillis, intervalMillis);
}
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(FeedUpdateWorker.class,
intervalMillis, TimeUnit.MILLISECONDS)
.setConstraints(getConstraints())
.build();
WorkManager.getInstance().enqueueUniquePeriodicWork(
WORK_ID_FEED_UPDATE, ExistingPeriodicWorkPolicy.REPLACE, workRequest);
}
/**
* Sets time of day the feeds are refreshed automatically
*/
public static void restartUpdateTimeOfDayAlarm(Context context, int hoursOfDay, int minute) {
public static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) {
Log.d(TAG, "Restarting update alarm.");
Calendar now = Calendar.getInstance();
@ -56,130 +50,25 @@ public class AutoUpdateManager {
if (alarm.before(now) || alarm.equals(now)) {
alarm.add(Calendar.DATE, 1);
}
long triggerAtMillis = alarm.getTimeInMillis() - now.getTimeInMillis();
if (Build.VERSION.SDK_INT >= 24) {
long triggerAtMillis = alarm.getTimeInMillis() - now.getTimeInMillis();
restartJobServiceTriggerAt(context, triggerAtMillis);
OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(FeedUpdateWorker.class)
.setConstraints(getConstraints())
.setInitialDelay(triggerAtMillis, TimeUnit.MILLISECONDS)
.build();
WorkManager.getInstance().enqueueUniqueWork(WORK_ID_FEED_UPDATE, ExistingWorkPolicy.REPLACE, workRequest);
}
private static Constraints getConstraints() {
Constraints.Builder constraints = new Constraints.Builder();
if (UserPreferences.isAllowMobileUpdate()) {
constraints.setRequiredNetworkType(NetworkType.CONNECTED);
} else {
restartAlarmManagerTimeOfDay(context, alarm);
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private static JobInfo.Builder getFeedUpdateJobBuilder(Context context) {
ComponentName serviceComponent = new ComponentName(context, FeedUpdateJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID_FEED_UPDATE, serviceComponent);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
builder.setPersisted(true);
return builder;
}
@RequiresApi(api = Build.VERSION_CODES.N)
private static void restartJobServiceInterval(Context context, long intervalMillis) {
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
if (jobScheduler == null) {
Log.d(TAG, "JobScheduler was null.");
return;
}
JobInfo oldJob = jobScheduler.getPendingJob(JOB_ID_FEED_UPDATE);
if (oldJob != null && oldJob.getIntervalMillis() == intervalMillis) {
Log.d(TAG, "JobScheduler was already set at interval " + intervalMillis + ", ignoring.");
return;
}
JobInfo.Builder builder = getFeedUpdateJobBuilder(context);
builder.setPeriodic(intervalMillis);
jobScheduler.cancel(JOB_ID_FEED_UPDATE);
if (intervalMillis <= 0) {
Log.d(TAG, "Automatic update was deactivated");
return;
}
jobScheduler.schedule(builder.build());
Log.d(TAG, "JobScheduler was set at interval " + intervalMillis);
}
private static void restartAlarmManagerInterval(Context context, long triggerAtMillis, long intervalMillis) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager == null) {
Log.d(TAG, "AlarmManager was null");
return;
}
PendingIntent updateIntent = PendingIntent.getBroadcast(context, 0,
new Intent(context, FeedUpdateReceiver.class), 0);
alarmManager.cancel(updateIntent);
if (intervalMillis <= 0) {
Log.d(TAG, "Automatic update was deactivated");
return;
}
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + triggerAtMillis,
updateIntent);
Log.d(TAG, "Changed alarm to new interval " + TimeUnit.MILLISECONDS.toHours(intervalMillis) + " h");
}
@RequiresApi(api = Build.VERSION_CODES.N)
private static void restartJobServiceTriggerAt(Context context, long triggerAtMillis) {
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
if (jobScheduler == null) {
Log.d(TAG, "JobScheduler was null.");
return;
}
JobInfo.Builder builder = getFeedUpdateJobBuilder(context);
builder.setMinimumLatency(triggerAtMillis);
jobScheduler.cancel(JOB_ID_FEED_UPDATE);
jobScheduler.schedule(builder.build());
Log.d(TAG, "JobScheduler was set for " + triggerAtMillis);
}
private static void restartAlarmManagerTimeOfDay(Context context, Calendar alarm) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager == null) {
Log.d(TAG, "AlarmManager was null");
return;
}
PendingIntent updateIntent = PendingIntent.getBroadcast(context, 0,
new Intent(context, FeedUpdateReceiver.class), 0);
alarmManager.cancel(updateIntent);
Log.d(TAG, "Alarm set for: " + alarm.toString() + " : " + alarm.getTimeInMillis());
alarmManager.set(AlarmManager.RTC_WAKEUP,
alarm.getTimeInMillis(),
updateIntent);
Log.d(TAG, "Changed alarm to new time of day " + alarm.get(Calendar.HOUR_OF_DAY) + ":" + alarm.get(Calendar.MINUTE));
}
/*
* Checks if the app should refresh all feeds, i.e. if the last auto refresh failed.
*
* The feeds are only refreshed if an update interval or time of day is set and the last
* (successful) refresh was before the last interval or more than a day ago, respectively.
*
*/
public static void checkShouldRefreshFeeds(Context context) {
long interval = 0;
if(UserPreferences.getUpdateInterval() > 0) {
interval = UserPreferences.getUpdateInterval();
} else if(UserPreferences.getUpdateTimeOfDay().length > 0){
interval = TimeUnit.DAYS.toMillis(1);
}
if(interval == 0) { // auto refresh is disabled
return;
}
long lastRefresh = DBTasks.getLastRefreshAllFeedsTimeMillis(context);
Log.d(TAG, "last refresh: " + Converter.getDurationStringLocalized(context,
System.currentTimeMillis() - lastRefresh) + " ago");
if(lastRefresh <= System.currentTimeMillis() - interval) {
FeedUpdateUtils.startAutoUpdate(context, null);
constraints.setRequiredNetworkType(NetworkType.UNMETERED);
}
constraints.setRequiresCharging(!UserPreferences.isEnableAutodownloadOnBattery());
return constraints.build();
}
}