diff --git a/app/build.gradle b/app/build.gradle index ab932e1a4f..2cc0bab751 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -89,8 +89,8 @@ dependencies { compile "com.android.support:design:$SUPPORT_LIBRARY_VERSION" compile "com.android.support:recyclerview-v7:$SUPPORT_LIBRARY_VERSION" compile "com.android.support:support-annotations:$SUPPORT_LIBRARY_VERSION" - compile 'com.squareup.okhttp:okhttp-urlconnection:2.7.0' - compile 'com.squareup.okhttp:okhttp:2.7.0' + compile 'com.squareup.okhttp:okhttp-urlconnection:2.7.2' + compile 'com.squareup.okhttp:okhttp:2.7.2' compile 'com.squareup.okio:okio:1.6.0' compile 'com.google.code.gson:gson:2.5' compile 'com.jakewharton:disklrucache:2.0.2' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9dfb27f338..2a062750c8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + + + + + + + + diff --git a/app/src/main/java/eu/kanade/mangafeed/data/network/NetworkHelper.java b/app/src/main/java/eu/kanade/mangafeed/data/network/NetworkHelper.java index 90a7d6b3ec..2ea70d691c 100644 --- a/app/src/main/java/eu/kanade/mangafeed/data/network/NetworkHelper.java +++ b/app/src/main/java/eu/kanade/mangafeed/data/network/NetworkHelper.java @@ -44,7 +44,7 @@ public final class NetworkHelper { } catch (Throwable e) { return Observable.error(e); } - }).retry(2); + }).retry(1); } public Observable 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 getProgressResponse(final String url, final Headers headers, final ProgressListener listener) { diff --git a/app/src/main/java/eu/kanade/mangafeed/data/preference/PreferencesHelper.java b/app/src/main/java/eu/kanade/mangafeed/data/preference/PreferencesHelper.java index 3a067429df..b48bf0c7b9 100644 --- a/app/src/main/java/eu/kanade/mangafeed/data/preference/PreferencesHelper.java +++ b/app/src/main/java/eu/kanade/mangafeed/data/preference/PreferencesHelper.java @@ -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 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); + } + } diff --git a/app/src/main/java/eu/kanade/mangafeed/data/sync/LibraryUpdateAlarm.java b/app/src/main/java/eu/kanade/mangafeed/data/sync/LibraryUpdateAlarm.java new file mode 100644 index 0000000000..49ac67bbde --- /dev/null +++ b/app/src/main/java/eu/kanade/mangafeed/data/sync/LibraryUpdateAlarm.java @@ -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); + } + + } + +} diff --git a/app/src/main/java/eu/kanade/mangafeed/data/sync/LibraryUpdateService.java b/app/src/main/java/eu/kanade/mangafeed/data/sync/LibraryUpdateService.java index 7bfd893dad..114b136c0a 100644 --- a/app/src/main/java/eu/kanade/mangafeed/data/sync/LibraryUpdateService.java +++ b/app/src/main/java/eu/kanade/mangafeed/data/sync/LibraryUpdateService.java @@ -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 mangas, final int startId) { + private Observable updateLibrary(List allLibraryMangas) { final AtomicInteger count = new AtomicInteger(0); + final List updates = new ArrayList<>(); + final List failedUpdates = new ArrayList<>(); - List updates = new ArrayList<>(); + final List 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 updates) { + private Observable updateManga(Manga manga) { + return sourceManager.get(manga.source) + .pullChaptersFromNetwork(manga.url) + .flatMap(chapters -> db.insertOrRemoveChapters(manga, chapters)); + } + + private String getUpdatedMangas(List updates, List 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(); } diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/library/LibraryFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/library/LibraryFragment.java index 2f8c7dbbf9..196b02abb5 100644 --- a/app/src/main/java/eu/kanade/mangafeed/ui/library/LibraryFragment.java +++ b/app/src/main/java/eu/kanade/mangafeed/ui/library/LibraryFragment.java @@ -105,10 +105,7 @@ public class LibraryFragment extends BaseRxFragment 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(); diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/setting/SettingsGeneralFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/setting/SettingsGeneralFragment.java index f4443983a1..a411ddd102 100644 --- a/app/src/main/java/eu/kanade/mangafeed/ui/setting/SettingsGeneralFragment.java +++ b/app/src/main/java/eu/kanade/mangafeed/ui/setting/SettingsGeneralFragment.java @@ -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; } diff --git a/app/src/main/res/drawable-xhdpi/card_background.9.png b/app/src/main/res/drawable-xhdpi/card_background.9.png index 52f1612dd4..8190c3b276 100755 Binary files a/app/src/main/res/drawable-xhdpi/card_background.9.png and b/app/src/main/res/drawable-xhdpi/card_background.9.png differ diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 1cd743672f..ce29c52763 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -48,4 +48,26 @@ 1 + + @string/update_never + @string/update_1hour + @string/update_2hour + @string/update_3hour + @string/update_6hour + @string/update_12hour + @string/update_24hour + @string/update_48hour + + + + 0 + 1 + 2 + 3 + 6 + 12 + 24 + 48 + + \ No newline at end of file diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 66fbbf4a51..71612af6b1 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -10,6 +10,8 @@ pref_library_columns_dialog_key pref_library_columns_portrait_key pref_library_columns_landscape_key + pref_library_update_interval_key + pref_update_only_non_completed_key pref_default_viewer_key pref_hide_status_bar_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4fc74f371f..c36a194793 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -63,6 +63,16 @@ Portrait Landscape Default + Library update period + Update only non completed mangas + Manual + Hourly + Every 2 hours + Every 3 hours + Every 6 hours + Every 12 hours + Daily + Every 2 days Hide status bar @@ -168,9 +178,11 @@ The image could not be loaded.\nTry to change the image decoder - Update progress: %1$d/%2$d - Update completed + Update progress: %1$d/%2$d + Update completed + An unexpected error occurred updating the library No new chapters found Found new chapters for: + Failed updating mangas: diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml index c0ad448711..4d4747cc23 100644 --- a/app/src/main/res/xml/pref_general.xml +++ b/app/src/main/res/xml/pref_general.xml @@ -6,4 +6,17 @@ android:persistent="false" android:title="@string/pref_library_columns"/> + + + + \ No newline at end of file