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:
		
							parent
							
								
									6eb321bb06
								
							
						
					
					
						commit
						fcb5bf4dd4
					
				| @ -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' | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
|     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | ||||
|     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | ||||
|     <uses-permission android:name="android.permission.WAKE_LOCK" /> | ||||
|     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> | ||||
| 
 | ||||
|     <application | ||||
|         android:name=".App" | ||||
| @ -67,6 +68,14 @@ | ||||
|             </intent-filter> | ||||
|         </receiver> | ||||
| 
 | ||||
|         <receiver | ||||
|             android:name=".data.sync.LibraryUpdateAlarm"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.BOOT_COMPLETED"/> | ||||
|                 <action android:name="eu.kanade.UPDATE_LIBRARY" /> | ||||
|             </intent-filter> | ||||
|         </receiver> | ||||
| 
 | ||||
|         <meta-data | ||||
|             android:name="eu.kanade.mangafeed.data.cache.CoverGlideModule" | ||||
|             android:value="GlideModule" /> | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -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); | ||||
|         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); | ||||
|                         }) | ||||
|                 .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); | ||||
|                 }); | ||||
|                         .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(); | ||||
|     } | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 934 B After Width: | Height: | Size: 755 B | 
| @ -48,4 +48,26 @@ | ||||
|         <item>1</item> | ||||
|     </string-array> | ||||
| 
 | ||||
|     <string-array name="library_update_interval"> | ||||
|         <item>@string/update_never</item> | ||||
|         <item>@string/update_1hour</item> | ||||
|         <item>@string/update_2hour</item> | ||||
|         <item>@string/update_3hour</item> | ||||
|         <item>@string/update_6hour</item> | ||||
|         <item>@string/update_12hour</item> | ||||
|         <item>@string/update_24hour</item> | ||||
|         <item>@string/update_48hour</item> | ||||
|     </string-array> | ||||
| 
 | ||||
|     <string-array name="library_update_interval_values"> | ||||
|         <item>0</item> | ||||
|         <item>1</item> | ||||
|         <item>2</item> | ||||
|         <item>3</item> | ||||
|         <item>6</item> | ||||
|         <item>12</item> | ||||
|         <item>24</item> | ||||
|         <item>48</item> | ||||
|     </string-array> | ||||
| 
 | ||||
| </resources> | ||||
| @ -10,6 +10,8 @@ | ||||
|     <string name="pref_library_columns_dialog_key">pref_library_columns_dialog_key</string> | ||||
|     <string name="pref_library_columns_portrait_key">pref_library_columns_portrait_key</string> | ||||
|     <string name="pref_library_columns_landscape_key">pref_library_columns_landscape_key</string> | ||||
|     <string name="pref_library_update_interval_key">pref_library_update_interval_key</string> | ||||
|     <string name="pref_update_only_non_completed_key">pref_update_only_non_completed_key</string> | ||||
| 
 | ||||
|     <string name="pref_default_viewer_key">pref_default_viewer_key</string> | ||||
|     <string name="pref_hide_status_bar_key">pref_hide_status_bar_key</string> | ||||
|  | ||||
| @ -63,6 +63,16 @@ | ||||
|     <string name="portrait">Portrait</string> | ||||
|     <string name="landscape">Landscape</string> | ||||
|     <string name="default_columns">Default</string> | ||||
|     <string name="pref_library_update_interval">Library update period</string> | ||||
|     <string name="pref_update_only_non_completed">Update only non completed mangas</string> | ||||
|     <string name="update_never">Manual</string> | ||||
|     <string name="update_1hour">Hourly</string> | ||||
|     <string name="update_2hour">Every 2 hours</string> | ||||
|     <string name="update_3hour">Every 3 hours</string> | ||||
|     <string name="update_6hour">Every 6 hours</string> | ||||
|     <string name="update_12hour">Every 12 hours</string> | ||||
|     <string name="update_24hour">Daily</string> | ||||
|     <string name="update_48hour">Every 2 days</string> | ||||
| 
 | ||||
|       <!-- Reader section --> | ||||
|     <string name="pref_hide_status_bar">Hide status bar</string> | ||||
| @ -168,9 +178,11 @@ | ||||
|     <string name="decode_image_error">The image could not be loaded.\nTry to change the image decoder</string> | ||||
| 
 | ||||
|     <!-- Library update service notifications --> | ||||
|     <string name="notification_progress">Update progress: %1$d/%2$d</string> | ||||
|     <string name="notification_completed">Update completed</string> | ||||
|     <string name="notification_update_progress">Update progress: %1$d/%2$d</string> | ||||
|     <string name="notification_update_completed">Update completed</string> | ||||
|     <string name="notification_update_error">An unexpected error occurred updating the library</string> | ||||
|     <string name="notification_no_new_chapters">No new chapters found</string> | ||||
|     <string name="notification_new_chapters">Found new chapters for:</string> | ||||
|     <string name="notification_manga_update_failed">Failed updating mangas:</string> | ||||
| 
 | ||||
| </resources> | ||||
|  | ||||
| @ -6,4 +6,17 @@ | ||||
|         android:persistent="false" | ||||
|         android:title="@string/pref_library_columns"/> | ||||
| 
 | ||||
|     <eu.kanade.mangafeed.ui.setting.preference.IntListPreference | ||||
|         android:key="@string/pref_library_update_interval_key" | ||||
|         android:title="@string/pref_library_update_interval" | ||||
|         android:entries="@array/library_update_interval" | ||||
|         android:entryValues="@array/library_update_interval_values" | ||||
|         android:defaultValue="0" | ||||
|         android:summary="%s"/> | ||||
| 
 | ||||
|     <CheckBoxPreference | ||||
|         android:key="@string/pref_update_only_non_completed_key" | ||||
|         android:title="@string/pref_update_only_non_completed" | ||||
|         android:defaultValue="false"/> | ||||
| 
 | ||||
| </PreferenceScreen> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 inorichi
						inorichi