Add an option to update the library automatically after a specified time and an option to update only non completed mangas. Other minor changes.

This commit is contained in:
inorichi
2016-01-10 17:47:43 +01:00
parent 6eb321bb06
commit fcb5bf4dd4
13 changed files with 209 additions and 46 deletions

View File

@ -44,7 +44,7 @@ public final class NetworkHelper {
} catch (Throwable e) {
return Observable.error(e);
}
}).retry(2);
}).retry(1);
}
public Observable<String> mapResponseToString(final Response response) {
@ -74,7 +74,7 @@ public final class NetworkHelper {
} catch (Throwable e) {
return Observable.error(e);
}
}).retry(2);
}).retry(1);
}
public Observable<Response> getProgressResponse(final String url, final Headers headers, final ProgressListener listener) {

View File

@ -97,6 +97,10 @@ public class PreferencesHelper {
return rxPrefs.getInteger(getKey(R.string.pref_library_columns_landscape_key), 0);
}
public boolean updateOnlyNonCompleted() {
return prefs.getBoolean(getKey(R.string.pref_update_only_non_completed_key), false);
}
public Preference<Integer> imageDecoder() {
return rxPrefs.getInteger(getKey(R.string.pref_image_decoder_key), 0);
}
@ -148,4 +152,9 @@ public class PreferencesHelper {
return rxPrefs.getInteger(getKey(R.string.pref_download_slots_key), 1);
}
public static int getLibraryUpdateInterval(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getInt(
context.getString(R.string.pref_library_update_interval_key), 0);
}
}

View File

@ -0,0 +1,62 @@
package eu.kanade.mangafeed.data.sync;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import eu.kanade.mangafeed.data.preference.PreferencesHelper;
import timber.log.Timber;
public class LibraryUpdateAlarm extends BroadcastReceiver {
public static final String LIBRARY_UPDATE_ACTION = "eu.kanade.UPDATE_LIBRARY";
public static void startAlarm(Context context) {
startAlarm(context, PreferencesHelper.getLibraryUpdateInterval(context));
}
public static void startAlarm(Context context, int intervalInHours) {
stopAlarm(context);
if (intervalInHours == 0)
return;
int intervalInMillis = intervalInHours * 60 * 60 * 1000;
long nextRun = SystemClock.elapsedRealtime() + intervalInMillis;
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = getPendingIntent(context);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
nextRun, intervalInMillis, pendingIntent);
Timber.i("Alarm set. Library will update on " + nextRun);
}
public static void stopAlarm(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = getPendingIntent(context);
alarmManager.cancel(pendingIntent);
}
private static PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, LibraryUpdateAlarm.class);
intent.setAction(LIBRARY_UPDATE_ACTION);
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == null)
return;
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
startAlarm(context);
} else if (intent.getAction().equals(LIBRARY_UPDATE_ACTION)) {
LibraryUpdateService.start(context);
}
}
}

View File

@ -17,6 +17,7 @@ import eu.kanade.mangafeed.BuildConfig;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.DatabaseHelper;
import eu.kanade.mangafeed.data.database.models.Manga;
import eu.kanade.mangafeed.data.preference.PreferencesHelper;
import eu.kanade.mangafeed.data.source.SourceManager;
import eu.kanade.mangafeed.util.AndroidComponentUtil;
import eu.kanade.mangafeed.util.NetworkUtil;
@ -31,16 +32,23 @@ public class LibraryUpdateService extends Service {
@Inject DatabaseHelper db;
@Inject SourceManager sourceManager;
@Inject PreferencesHelper preferences;
private Subscription updateSubscription;
private Subscription subscription;
public static final int UPDATE_NOTIFICATION_ID = 1;
public static Intent getStartIntent(Context context) {
public static void start(Context context) {
if (!isRunning(context)) {
context.startService(getStartIntent(context));
}
}
private static Intent getStartIntent(Context context) {
return new Intent(context, LibraryUpdateService.class);
}
public static boolean isRunning(Context context) {
private static boolean isRunning(Context context) {
return AndroidComponentUtil.isServiceRunning(context, LibraryUpdateService.class);
}
@ -52,8 +60,10 @@ public class LibraryUpdateService extends Service {
@Override
public void onDestroy() {
if (updateSubscription != null)
updateSubscription.unsubscribe();
if (subscription != null)
subscription.unsubscribe();
// Reset the alarm
LibraryUpdateAlarm.startAlarm(this);
super.onDestroy();
}
@ -68,44 +78,56 @@ public class LibraryUpdateService extends Service {
return START_NOT_STICKY;
}
Observable.fromCallable(() -> db.getFavoriteMangas().executeAsBlocking())
subscription = Observable.fromCallable(() -> db.getFavoriteMangas().executeAsBlocking())
.subscribeOn(Schedulers.io())
.subscribe(mangas -> {
startUpdating(mangas, startId);
});
.flatMap(this::updateLibrary)
.subscribe(next -> {},
error -> {
NotificationUtil.create(this, UPDATE_NOTIFICATION_ID,
getString(R.string.notification_update_error), "");
stopSelf(startId);
}, () -> {
Timber.i("Library updated");
stopSelf(startId);
});
return START_STICKY;
}
private void startUpdating(final List<Manga> mangas, final int startId) {
private Observable<MangaUpdate> updateLibrary(List<Manga> allLibraryMangas) {
final AtomicInteger count = new AtomicInteger(0);
final List<MangaUpdate> updates = new ArrayList<>();
final List<Manga> failedUpdates = new ArrayList<>();
List<MangaUpdate> updates = new ArrayList<>();
final List<Manga> mangas = !preferences.updateOnlyNonCompleted() ? allLibraryMangas :
Observable.from(allLibraryMangas)
.filter(manga -> manga.status != Manga.COMPLETED)
.toList().toBlocking().single();
updateSubscription = Observable.from(mangas)
.doOnNext(manga -> {
NotificationUtil.create(this, UPDATE_NOTIFICATION_ID,
getString(R.string.notification_progress,
count.incrementAndGet(), mangas.size()), manga.title);
})
.concatMap(manga -> sourceManager.get(manga.source)
.pullChaptersFromNetwork(manga.url)
.flatMap(chapters -> db.insertOrRemoveChapters(manga, chapters))
.filter(result -> result.getNumberOfRowsInserted() > 0)
.flatMap(result -> Observable.just(new MangaUpdate(manga, result))))
.subscribe(update -> {
updates.add(update);
}, error -> {
Timber.e("Error syncing");
stopSelf(startId);
}, () -> {
NotificationUtil.createBigText(this, UPDATE_NOTIFICATION_ID,
getString(R.string.notification_completed), getUpdatedMangas(updates));
stopSelf(startId);
});
return Observable.from(mangas)
.doOnNext(manga -> NotificationUtil.create(this, UPDATE_NOTIFICATION_ID,
getString(R.string.notification_update_progress,
count.incrementAndGet(), mangas.size()), manga.title))
.concatMap(manga -> updateManga(manga)
.onErrorReturn(error -> {
failedUpdates.add(manga);
return new PostResult(0, 0, 0);
})
.filter(result -> result.getNumberOfRowsInserted() > 0)
.map(result -> new MangaUpdate(manga, result)))
.doOnNext(updates::add)
.doOnCompleted(() -> NotificationUtil.createBigText(this, UPDATE_NOTIFICATION_ID,
getString(R.string.notification_update_completed),
getUpdatedMangas(updates, failedUpdates)));
}
private String getUpdatedMangas(List<MangaUpdate> updates) {
private Observable<PostResult> updateManga(Manga manga) {
return sourceManager.get(manga.source)
.pullChaptersFromNetwork(manga.url)
.flatMap(chapters -> db.insertOrRemoveChapters(manga, chapters));
}
private String getUpdatedMangas(List<MangaUpdate> updates, List<Manga> failedUpdates) {
final StringBuilder result = new StringBuilder();
if (updates.isEmpty()) {
result.append(getString(R.string.notification_no_new_chapters)).append("\n");
@ -116,6 +138,13 @@ public class LibraryUpdateService extends Service {
result.append("\n").append(update.getManga().title);
}
}
if (!failedUpdates.isEmpty()) {
result.append("\n");
result.append(getString(R.string.notification_manga_update_failed));
for (Manga manga : failedUpdates) {
result.append("\n").append(manga.title);
}
}
return result.toString();
}

View File

@ -105,10 +105,7 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter>
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_refresh:
if (!LibraryUpdateService.isRunning(getActivity())) {
Intent intent = LibraryUpdateService.getStartIntent(getActivity());
getActivity().startService(intent);
}
LibraryUpdateService.start(getActivity());
return true;
case R.id.action_edit_categories:
onEditCategories();

View File

@ -7,12 +7,12 @@ import android.view.ViewGroup;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.preference.PreferencesHelper;
import eu.kanade.mangafeed.data.sync.LibraryUpdateAlarm;
import eu.kanade.mangafeed.ui.setting.preference.IntListPreference;
import eu.kanade.mangafeed.ui.setting.preference.LibraryColumnsDialog;
public class SettingsGeneralFragment extends SettingsNestedFragment {
private LibraryColumnsDialog columnsDialog;
public static SettingsNestedFragment newInstance(int resourcePreference, int resourceTitle) {
SettingsNestedFragment fragment = new SettingsGeneralFragment();
fragment.setArgs(resourcePreference, resourceTitle);
@ -25,11 +25,19 @@ public class SettingsGeneralFragment extends SettingsNestedFragment {
PreferencesHelper preferences = getSettingsActivity().preferences;
columnsDialog = (LibraryColumnsDialog) findPreference(
LibraryColumnsDialog columnsDialog = (LibraryColumnsDialog) findPreference(
getString(R.string.pref_library_columns_dialog_key));
columnsDialog.setPreferencesHelper(preferences);
IntListPreference updateInterval = (IntListPreference) findPreference(
getString(R.string.pref_library_update_interval_key));
updateInterval.setOnPreferenceChangeListener((preference, newValue) -> {
LibraryUpdateAlarm.startAlarm(getActivity(), Integer.parseInt((String) newValue));
return true;
});
return view;
}