mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 03:21:51 +01:00
Allow to update one category
This commit is contained in:
parent
5e24054a0b
commit
1226023dc2
@ -14,6 +14,7 @@ import com.github.pwittchen.reactivenetwork.library.ReactiveNetwork
|
||||
import eu.kanade.tachiyomi.App
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.source.SourceManager
|
||||
@ -36,38 +37,51 @@ import javax.inject.Inject
|
||||
*/
|
||||
class LibraryUpdateService : Service() {
|
||||
|
||||
// Dependencies injected through dagger.
|
||||
/**
|
||||
* Database helper.
|
||||
*/
|
||||
@Inject lateinit var db: DatabaseHelper
|
||||
|
||||
/**
|
||||
* Source manager.
|
||||
*/
|
||||
@Inject lateinit var sourceManager: SourceManager
|
||||
|
||||
/**
|
||||
* Preferences.
|
||||
*/
|
||||
@Inject lateinit var preferences: PreferencesHelper
|
||||
|
||||
// Wake lock that will be held until the service is destroyed.
|
||||
/**
|
||||
* Wake lock that will be held until the service is destroyed.
|
||||
*/
|
||||
private lateinit var wakeLock: PowerManager.WakeLock
|
||||
|
||||
// Subscription where the update is done.
|
||||
/**
|
||||
* Subscription where the update is done.
|
||||
*/
|
||||
private var subscription: Subscription? = null
|
||||
|
||||
|
||||
companion object {
|
||||
val UPDATE_NOTIFICATION_ID = 1
|
||||
|
||||
// Intent key for manual library update
|
||||
val UPDATE_IS_MANUAL = "is_manual"
|
||||
/**
|
||||
* Id of the library update notification.
|
||||
*/
|
||||
const val UPDATE_NOTIFICATION_ID = 1
|
||||
|
||||
/**
|
||||
* Get the start intent for [LibraryUpdateService].
|
||||
* @param context the application context.
|
||||
* @param isManual true when user triggers library update.
|
||||
* @return the intent of the service.
|
||||
* Key for manual library update.
|
||||
*/
|
||||
fun getIntent(context: Context, isManual: Boolean = false): Intent {
|
||||
return Intent(context, LibraryUpdateService::class.java).apply {
|
||||
putExtra(UPDATE_IS_MANUAL, isManual)
|
||||
}
|
||||
}
|
||||
const val UPDATE_IS_MANUAL = "is_manual"
|
||||
|
||||
/**
|
||||
* Key for category to update.
|
||||
*/
|
||||
const val UPDATE_CATEGORY = "category"
|
||||
|
||||
/**
|
||||
* Returns the status of the service.
|
||||
*
|
||||
* @param context the application context.
|
||||
* @return true if the service is running, false otherwise.
|
||||
*/
|
||||
@ -76,19 +90,30 @@ class LibraryUpdateService : Service() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to start the service. It will be started only if there isn't another
|
||||
* instance already running.
|
||||
* Starts the service. It will be started only if there isn't another instance already
|
||||
* running.
|
||||
*
|
||||
* @param context the application context.
|
||||
* @param isManual whether the update has been manually triggered.
|
||||
* @param category a specific category to update, or null for all in the library.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun start(context: Context, isForced: Boolean = false) {
|
||||
fun start(context: Context, isManual: Boolean = false, category: Category? = null) {
|
||||
if (!isRunning(context)) {
|
||||
context.startService(getIntent(context, isForced))
|
||||
val intent = Intent(context, LibraryUpdateService::class.java).apply {
|
||||
putExtra(UPDATE_IS_MANUAL, isManual)
|
||||
category?.let { putExtra(UPDATE_CATEGORY, it.id) }
|
||||
}
|
||||
context.startService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the service.
|
||||
*
|
||||
* @param context the application context.
|
||||
*/
|
||||
fun stop(context: Context) {
|
||||
context.stopService(getIntent(context))
|
||||
context.stopService(Intent(context, LibraryUpdateService::class.java))
|
||||
}
|
||||
|
||||
}
|
||||
@ -104,7 +129,7 @@ class LibraryUpdateService : Service() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called when the service is destroyed. It destroy the running subscription, resets
|
||||
* Method called when the service is destroyed. It destroys the running subscription, resets
|
||||
* the alarm and release the wake lock.
|
||||
*/
|
||||
override fun onDestroy() {
|
||||
@ -121,9 +146,9 @@ class LibraryUpdateService : Service() {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method called when the service receives an intent.
|
||||
*
|
||||
* @param intent the start intent from.
|
||||
* @param flags the flags of the command.
|
||||
* @param startId the start id of this command.
|
||||
@ -145,7 +170,7 @@ class LibraryUpdateService : Service() {
|
||||
|
||||
// Check if device has internet connection
|
||||
// Check if device has wifi connection if only wifi is enabled
|
||||
if (connection == ConnectivityStatus.OFFLINE || ("wifi" in restrictions
|
||||
if (connection == ConnectivityStatus.OFFLINE || (!isManualUpdate && "wifi" in restrictions
|
||||
&& connection != ConnectivityStatus.WIFI_CONNECTED_HAS_INTERNET)) {
|
||||
|
||||
if (isManualUpdate) {
|
||||
@ -174,7 +199,7 @@ class LibraryUpdateService : Service() {
|
||||
subscription?.unsubscribe()
|
||||
|
||||
// Update favorite manga. Destroy service when completed or in case of an error.
|
||||
subscription = Observable.defer { updateLibrary() }
|
||||
subscription = Observable.defer { updateMangaList(getMangaToUpdate(intent)) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe({},
|
||||
{
|
||||
@ -188,13 +213,36 @@ class LibraryUpdateService : Service() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that updates the library. It's called in a background thread, so it's safe to do
|
||||
* heavy operations or network calls here.
|
||||
* Returns the list of manga to be updated.
|
||||
*
|
||||
* @param intent the update intent.
|
||||
* @return a list of manga to update
|
||||
*/
|
||||
fun getMangaToUpdate(intent: Intent?): List<Manga> {
|
||||
val categoryId = intent?.getIntExtra(UPDATE_CATEGORY, -1) ?: -1
|
||||
|
||||
var toUpdate = if (categoryId != -1)
|
||||
db.getLibraryMangas().executeAsBlocking().filter { it.category == categoryId }
|
||||
else
|
||||
db.getFavoriteMangas().executeAsBlocking()
|
||||
|
||||
if (preferences.updateOnlyNonCompleted()) {
|
||||
toUpdate = toUpdate.filter { it.status != Manga.COMPLETED }
|
||||
}
|
||||
|
||||
return toUpdate
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that updates the given list of manga. It's called in a background thread, so it's safe
|
||||
* to do heavy operations or network calls here.
|
||||
* For each manga it calls [updateManga] and updates the notification showing the current
|
||||
* progress.
|
||||
*
|
||||
* @param mangaToUpdate the list to update
|
||||
* @return an observable delivering the progress of each update.
|
||||
*/
|
||||
fun updateLibrary(): Observable<Manga> {
|
||||
fun updateMangaList(mangaToUpdate: List<Manga>): Observable<Manga> {
|
||||
// Initialize the variables holding the progress of the updates.
|
||||
val count = AtomicInteger(0)
|
||||
val newUpdates = ArrayList<Manga>()
|
||||
@ -203,17 +251,10 @@ class LibraryUpdateService : Service() {
|
||||
val cancelIntent = PendingIntent.getBroadcast(this, 0,
|
||||
Intent(this, CancelUpdateReceiver::class.java), 0)
|
||||
|
||||
// Get the manga list that is going to be updated.
|
||||
val allLibraryMangas = db.getFavoriteMangas().executeAsBlocking()
|
||||
val toUpdate = if (!preferences.updateOnlyNonCompleted())
|
||||
allLibraryMangas
|
||||
else
|
||||
allLibraryMangas.filter { it.status != Manga.COMPLETED }
|
||||
|
||||
// Emit each manga and update it sequentially.
|
||||
return Observable.from(toUpdate)
|
||||
return Observable.from(mangaToUpdate)
|
||||
// Notify manga that will update.
|
||||
.doOnNext { showProgressNotification(it, count.andIncrement, toUpdate.size, cancelIntent) }
|
||||
.doOnNext { showProgressNotification(it, count.andIncrement, mangaToUpdate.size, cancelIntent) }
|
||||
// Update the chapters of the manga.
|
||||
.concatMap { manga ->
|
||||
updateManga(manga)
|
||||
@ -241,6 +282,7 @@ class LibraryUpdateService : Service() {
|
||||
|
||||
/**
|
||||
* Updates the chapters for the given manga and adds them to the database.
|
||||
*
|
||||
* @param manga the manga to update.
|
||||
* @return a pair of the inserted and removed chapters.
|
||||
*/
|
||||
@ -253,6 +295,7 @@ class LibraryUpdateService : Service() {
|
||||
|
||||
/**
|
||||
* Returns the text that will be displayed in the notification when there are new chapters.
|
||||
*
|
||||
* @param updates a list of manga that contains new chapters.
|
||||
* @param failedUpdates a list of manga that failed to update.
|
||||
* @return the body of the notification to display.
|
||||
@ -301,6 +344,7 @@ class LibraryUpdateService : Service() {
|
||||
|
||||
/**
|
||||
* Shows the notification with the given title and body.
|
||||
*
|
||||
* @param title the title of the notification.
|
||||
* @param body the body of the notification.
|
||||
*/
|
||||
@ -314,6 +358,7 @@ class LibraryUpdateService : Service() {
|
||||
|
||||
/**
|
||||
* Shows the notification containing the currently updating manga and the progress.
|
||||
*
|
||||
* @param manga the manga that's being updated.
|
||||
* @param current the current progress.
|
||||
* @param total the total progress.
|
||||
@ -331,6 +376,7 @@ class LibraryUpdateService : Service() {
|
||||
|
||||
/**
|
||||
* Shows the notification containing the result of the update done by the service.
|
||||
*
|
||||
* @param updates a list of manga with new updates.
|
||||
* @param failed a list of manga that failed to update.
|
||||
*/
|
||||
@ -371,6 +417,7 @@ class LibraryUpdateService : Service() {
|
||||
class SyncOnConnectionAvailable : BroadcastReceiver() {
|
||||
/**
|
||||
* Method called when a network change occurs.
|
||||
*
|
||||
* @param context the application context.
|
||||
* @param intent the intent received.
|
||||
*/
|
||||
@ -388,6 +435,7 @@ class LibraryUpdateService : Service() {
|
||||
class SyncOnPowerConnected: BroadcastReceiver() {
|
||||
/**
|
||||
* Method called when AC is connected.
|
||||
*
|
||||
* @param context the application context.
|
||||
* @param intent the intent received.
|
||||
*/
|
||||
@ -403,6 +451,7 @@ class LibraryUpdateService : Service() {
|
||||
class CancelUpdateReceiver : BroadcastReceiver() {
|
||||
/**
|
||||
* Method called when user wants a library update.
|
||||
*
|
||||
* @param context the application context.
|
||||
* @param intent the intent received.
|
||||
*/
|
||||
|
@ -156,8 +156,8 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
||||
searchView.clearFocus()
|
||||
}
|
||||
|
||||
filterDownloadedItem.isChecked = isFilterDownloaded;
|
||||
filterUnreadItem.isChecked = isFilterUnread;
|
||||
filterDownloadedItem.isChecked = isFilterDownloaded
|
||||
filterUnreadItem.isChecked = isFilterUnread
|
||||
|
||||
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
@ -200,7 +200,13 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
||||
// Apply filter
|
||||
onFilterCheckboxChanged()
|
||||
}
|
||||
R.id.action_refresh -> LibraryUpdateService.start(activity, true) // Force refresh
|
||||
R.id.action_update_library -> {
|
||||
LibraryUpdateService.start(activity, true)
|
||||
}
|
||||
R.id.action_update_category -> {
|
||||
val category = presenter.categories[view_pager.currentItem]
|
||||
LibraryUpdateService.start(activity, true, category)
|
||||
}
|
||||
R.id.action_edit_categories -> {
|
||||
val intent = CategoryActivity.newIntent(activity)
|
||||
startActivity(intent)
|
||||
@ -218,7 +224,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
||||
presenter.updateLibrary()
|
||||
adapter.notifyDataSetChanged()
|
||||
adapter.refreshRegisteredAdapters()
|
||||
activity.supportInvalidateOptionsMenu();
|
||||
activity.supportInvalidateOptionsMenu()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -249,12 +255,10 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
||||
// Get the current active category.
|
||||
val activeCat = if (adapter.categories != null) view_pager.currentItem else activeCategory
|
||||
|
||||
// Add the default category if it contains manga.
|
||||
if (mangaMap[0] != null) {
|
||||
setCategories(arrayListOf(Category.createDefault()) + categories)
|
||||
} else {
|
||||
setCategories(categories)
|
||||
}
|
||||
// Set the categories
|
||||
adapter.categories = categories
|
||||
tabs.setupWithViewPager(view_pager)
|
||||
tabs.visibility = if (categories.size <= 1) View.GONE else View.VISIBLE
|
||||
|
||||
// Restore active category.
|
||||
view_pager.setCurrentItem(activeCat, false)
|
||||
@ -270,17 +274,6 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
||||
presenter.libraryMangaSubject.onNext(LibraryMangaEvent(mangaMap))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the categories in the adapter and the tab layout.
|
||||
*
|
||||
* @param categories the categories to set.
|
||||
*/
|
||||
private fun setCategories(categories: List<Category>) {
|
||||
adapter.categories = categories
|
||||
tabs.setupWithViewPager(view_pager)
|
||||
tabs.visibility = if (categories.size <= 1) View.GONE else View.VISIBLE
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of the action mode.
|
||||
*
|
||||
|
@ -98,7 +98,15 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
|
||||
*/
|
||||
fun getLibraryObservable(): Observable<Pair<List<Category>, Map<Int, List<Manga>>>> {
|
||||
return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable(),
|
||||
{ a, b -> Pair(a, b) })
|
||||
{ dbCategories, libraryManga ->
|
||||
val categories = if (libraryManga.containsKey(0))
|
||||
arrayListOf(Category.createDefault()) + dbCategories
|
||||
else
|
||||
dbCategories
|
||||
|
||||
this.categories = categories
|
||||
Pair(categories, libraryManga)
|
||||
})
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
@ -109,7 +117,6 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
|
||||
*/
|
||||
fun getCategoriesObservable(): Observable<List<Category>> {
|
||||
return db.getCategories().asRxObservable()
|
||||
.doOnNext { categories -> this.categories = categories }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,11 +30,16 @@
|
||||
app:actionViewClass="android.support.v7.widget.SearchView" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
android:id="@+id/action_update_library"
|
||||
android:title="@string/action_update_library"
|
||||
android:icon="@drawable/ic_refresh_white_24dp"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_update_category"
|
||||
android:title="@string/action_update_category"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_edit_categories"
|
||||
android:title="@string/action_edit_categories"
|
||||
|
@ -20,7 +20,6 @@
|
||||
<string name="action_filter_unread">Unread</string>
|
||||
<string name="action_filter_empty">Remove filter</string>
|
||||
<string name="action_search">Search</string>
|
||||
<string name="action_refresh">Refresh</string>
|
||||
<string name="action_select_all">Select all</string>
|
||||
<string name="action_mark_as_read">Mark as read</string>
|
||||
<string name="action_mark_as_unread">Mark as unread</string>
|
||||
@ -28,6 +27,8 @@
|
||||
<string name="action_download">Download</string>
|
||||
<string name="action_delete">Delete</string>
|
||||
<string name="action_update">Update</string>
|
||||
<string name="action_update_library">Update library</string>
|
||||
<string name="action_update_category">Update active category</string>
|
||||
<string name="action_edit">Edit</string>
|
||||
<string name="action_add_category">Add category</string>
|
||||
<string name="action_edit_categories">Edit categories</string>
|
||||
|
@ -95,7 +95,7 @@ public class LibraryUpdateServiceTest {
|
||||
when(service.db.insertOrRemoveChapters(manga1, chapters, source)).thenReturn(Observable.just(Pair.create(2, 0)));
|
||||
when(service.db.insertOrRemoveChapters(manga3, chapters, source)).thenReturn(Observable.just(Pair.create(2, 0)));
|
||||
|
||||
service.updateLibrary().subscribe();
|
||||
service.updateMangaList(service.getMangaToUpdate(null)).subscribe();
|
||||
|
||||
// There are 3 network attempts and 2 insertions (1 request failed)
|
||||
verify(source, times(3)).pullChaptersFromNetwork((String)any());
|
||||
|
Loading…
Reference in New Issue
Block a user