mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-07 11:17:25 +01:00
Reader presenter in Kotlin + remove Icepick
This commit is contained in:
parent
8e0a9d6d66
commit
0d519b3d16
@ -105,7 +105,6 @@ dependencies {
|
|||||||
final OKHTTP_VERSION = '3.2.0'
|
final OKHTTP_VERSION = '3.2.0'
|
||||||
final RETROFIT_VERSION = '2.0.0'
|
final RETROFIT_VERSION = '2.0.0'
|
||||||
final STORIO_VERSION = '1.8.0'
|
final STORIO_VERSION = '1.8.0'
|
||||||
final ICEPICK_VERSION = '3.2.0'
|
|
||||||
final MOCKITO_VERSION = '1.10.19'
|
final MOCKITO_VERSION = '1.10.19'
|
||||||
|
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
@ -138,8 +137,6 @@ dependencies {
|
|||||||
compile 'com.github.bumptech.glide:glide:3.7.0'
|
compile 'com.github.bumptech.glide:glide:3.7.0'
|
||||||
compile 'com.jakewharton.timber:timber:4.1.1'
|
compile 'com.jakewharton.timber:timber:4.1.1'
|
||||||
compile 'ch.acra:acra:4.8.3'
|
compile 'ch.acra:acra:4.8.3'
|
||||||
compile "frankiesardo:icepick:$ICEPICK_VERSION"
|
|
||||||
provided "frankiesardo:icepick-processor:$ICEPICK_VERSION"
|
|
||||||
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
|
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
|
||||||
compile 'eu.davidea:flexible-adapter:4.2.0'
|
compile 'eu.davidea:flexible-adapter:4.2.0'
|
||||||
compile 'com.nononsenseapps:filepicker:2.5.2'
|
compile 'com.nononsenseapps:filepicker:2.5.2'
|
||||||
|
@ -58,7 +58,7 @@ public class App extends Application {
|
|||||||
|
|
||||||
protected void setupEventBus() {
|
protected void setupEventBus() {
|
||||||
EventBus.builder()
|
EventBus.builder()
|
||||||
.addIndex(new EventBusIndex())
|
// .addIndex(new EventBusIndex())
|
||||||
.logNoSubscriberMessages(false)
|
.logNoSubscriberMessages(false)
|
||||||
.installDefaultEventBus();
|
.installDefaultEventBus();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package eu.kanade.tachiyomi.ui.base.activity
|
package eu.kanade.tachiyomi.ui.base.activity
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
|
||||||
import android.support.design.widget.Snackbar
|
import android.support.design.widget.Snackbar
|
||||||
import android.support.v7.app.AppCompatActivity
|
import android.support.v7.app.AppCompatActivity
|
||||||
import android.support.v7.widget.Toolbar
|
import android.support.v7.widget.Toolbar
|
||||||
@ -10,21 +9,10 @@ import android.view.View
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import eu.kanade.tachiyomi.App
|
import eu.kanade.tachiyomi.App
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import icepick.Icepick
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
|
||||||
open class BaseActivity : AppCompatActivity() {
|
open class BaseActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
|
||||||
super.onCreate(savedState)
|
|
||||||
Icepick.restoreInstanceState(this, savedState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
|
||||||
super.onSaveInstanceState(outState)
|
|
||||||
Icepick.saveInstanceState(this, outState)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun setupToolbar(toolbar: Toolbar) {
|
protected fun setupToolbar(toolbar: Toolbar) {
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(toolbar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
@ -1,23 +1,11 @@
|
|||||||
package eu.kanade.tachiyomi.ui.base.fragment
|
package eu.kanade.tachiyomi.ui.base.fragment
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.support.v4.app.Fragment
|
import android.support.v4.app.Fragment
|
||||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||||
import icepick.Icepick
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
|
||||||
open class BaseFragment : Fragment() {
|
open class BaseFragment : Fragment() {
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
|
||||||
super.onCreate(savedState)
|
|
||||||
Icepick.restoreInstanceState(this, savedState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
|
||||||
super.onSaveInstanceState(outState)
|
|
||||||
Icepick.saveInstanceState(this, outState)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setToolbarTitle(title: String) {
|
fun setToolbarTitle(title: String) {
|
||||||
baseActivity.setToolbarTitle(title)
|
baseActivity.setToolbarTitle(title)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package eu.kanade.tachiyomi.ui.base.presenter
|
package eu.kanade.tachiyomi.ui.base.presenter
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
|
||||||
import icepick.Icepick
|
|
||||||
import nucleus.view.ViewWithPresenter
|
import nucleus.view.ViewWithPresenter
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
|
||||||
@ -10,16 +8,6 @@ open class BasePresenter<V : ViewWithPresenter<*>> : RxPresenter<V>() {
|
|||||||
|
|
||||||
lateinit var context: Context
|
lateinit var context: Context
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
|
||||||
super.onCreate(savedState)
|
|
||||||
Icepick.restoreInstanceState(this, savedState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSave(state: Bundle) {
|
|
||||||
super.onSave(state)
|
|
||||||
Icepick.saveInstanceState(this, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun registerForEvents() {
|
fun registerForEvents() {
|
||||||
EventBus.getDefault().register(this)
|
EventBus.getDefault().register(this)
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleViewHold
|
|||||||
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||||
|
|
||||||
val onItemSelected = object : AdapterView.OnItemSelectedListener {
|
val onItemSelected = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(parent: AdapterView<*>, view: View? , position: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
|
||||||
val source = spinnerAdapter.getItem(position)
|
val source = spinnerAdapter.getItem(position)
|
||||||
if (selectedIndex != position || adapter.isEmpty) {
|
if (selectedIndex != position || adapter.isEmpty) {
|
||||||
// Set previous selection if it's not a valid source and notify the user
|
// Set previous selection if it's not a valid source and notify the user
|
||||||
|
@ -144,7 +144,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
|||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
presenter.onChapterLeft()
|
presenter.onChapterLeft()
|
||||||
|
|
||||||
val chapterToUpdate = presenter.mangaSyncChapterToUpdate
|
val chapterToUpdate = presenter.getMangaSyncChapterToUpdate()
|
||||||
|
|
||||||
if (chapterToUpdate > 0) {
|
if (chapterToUpdate > 0) {
|
||||||
if (presenter.prefs.askUpdateMangaSync()) {
|
if (presenter.prefs.askUpdateMangaSync()) {
|
||||||
|
@ -1,424 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.util.Pair;
|
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.cache.ChapterCache;
|
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper;
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter;
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.MangaSync;
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager;
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download;
|
|
||||||
import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager;
|
|
||||||
import eu.kanade.tachiyomi.data.mangasync.UpdateMangaSyncService;
|
|
||||||
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService;
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper;
|
|
||||||
import eu.kanade.tachiyomi.data.source.SourceManager;
|
|
||||||
import eu.kanade.tachiyomi.data.source.base.Source;
|
|
||||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
|
||||||
import eu.kanade.tachiyomi.event.ReaderEvent;
|
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
|
||||||
import icepick.State;
|
|
||||||
import rx.Observable;
|
|
||||||
import rx.Subscription;
|
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
|
||||||
import rx.schedulers.Schedulers;
|
|
||||||
import rx.subjects.PublishSubject;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
|
||||||
|
|
||||||
@Inject PreferencesHelper prefs;
|
|
||||||
@Inject DatabaseHelper db;
|
|
||||||
@Inject DownloadManager downloadManager;
|
|
||||||
@Inject MangaSyncManager syncManager;
|
|
||||||
@Inject SourceManager sourceManager;
|
|
||||||
@Inject ChapterCache chapterCache;
|
|
||||||
|
|
||||||
@State Manga manga;
|
|
||||||
@State Chapter activeChapter;
|
|
||||||
@State int requestedPage;
|
|
||||||
private Page currentPage;
|
|
||||||
private Source source;
|
|
||||||
private Chapter nextChapter;
|
|
||||||
private Chapter previousChapter;
|
|
||||||
private List<MangaSync> mangaSyncList;
|
|
||||||
|
|
||||||
private PublishSubject<Page> retryPageSubject;
|
|
||||||
private PublishSubject<Chapter> pageInitializerSubject;
|
|
||||||
|
|
||||||
private boolean seamlessMode;
|
|
||||||
private Subscription appenderSubscription;
|
|
||||||
|
|
||||||
private static final int GET_PAGE_LIST = 1;
|
|
||||||
private static final int GET_ADJACENT_CHAPTERS = 2;
|
|
||||||
private static final int GET_MANGA_SYNC = 3;
|
|
||||||
private static final int PRELOAD_NEXT_CHAPTER = 4;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedState) {
|
|
||||||
super.onCreate(savedState);
|
|
||||||
|
|
||||||
if (savedState != null) {
|
|
||||||
source = sourceManager.get(manga.source);
|
|
||||||
initializeSubjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
seamlessMode = prefs.seamlessMode();
|
|
||||||
|
|
||||||
startableLatestCache(GET_ADJACENT_CHAPTERS, this::getAdjacentChaptersObservable,
|
|
||||||
(view, pair) -> view.onAdjacentChapters(pair.first, pair.second));
|
|
||||||
|
|
||||||
startable(PRELOAD_NEXT_CHAPTER, this::getPreloadNextChapterObservable,
|
|
||||||
next -> {},
|
|
||||||
error -> Timber.e("Error preloading chapter"));
|
|
||||||
|
|
||||||
|
|
||||||
restartable(GET_MANGA_SYNC, () -> getMangaSyncObservable().subscribe());
|
|
||||||
|
|
||||||
restartableLatestCache(GET_PAGE_LIST,
|
|
||||||
() -> getPageListObservable(activeChapter),
|
|
||||||
(view, chapter) -> view.onChapterReady(manga, activeChapter, currentPage),
|
|
||||||
(view, error) -> view.onChapterError());
|
|
||||||
|
|
||||||
if (savedState == null) {
|
|
||||||
registerForEvents();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
unregisterForEvents();
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSave(@NonNull Bundle state) {
|
|
||||||
onChapterLeft();
|
|
||||||
super.onSave(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
|
||||||
public void onEvent(ReaderEvent event) {
|
|
||||||
EventBus.getDefault().removeStickyEvent(event);
|
|
||||||
manga = event.getManga();
|
|
||||||
source = sourceManager.get(manga.source);
|
|
||||||
initializeSubjects();
|
|
||||||
loadChapter(event.getChapter());
|
|
||||||
if (prefs.autoUpdateMangaSync()) {
|
|
||||||
start(GET_MANGA_SYNC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeSubjects() {
|
|
||||||
// Listen for pages initialization events
|
|
||||||
pageInitializerSubject = PublishSubject.create();
|
|
||||||
add(pageInitializerSubject
|
|
||||||
.observeOn(Schedulers.io())
|
|
||||||
.concatMap(chapter -> {
|
|
||||||
Observable observable;
|
|
||||||
if (chapter.isDownloaded()) {
|
|
||||||
File chapterDir = downloadManager.getAbsoluteChapterDirectory(source, manga, chapter);
|
|
||||||
observable = Observable.from(chapter.getPages())
|
|
||||||
.flatMap(page -> downloadManager.getDownloadedImage(page, chapterDir));
|
|
||||||
} else {
|
|
||||||
observable = source.getAllImageUrlsFromPageList(chapter.getPages())
|
|
||||||
.flatMap(source::getCachedImage, 2)
|
|
||||||
.doOnCompleted(() -> source.savePageList(chapter.url, chapter.getPages()));
|
|
||||||
}
|
|
||||||
return observable.doOnCompleted(() -> {
|
|
||||||
if (!seamlessMode && activeChapter == chapter) {
|
|
||||||
preloadNextChapter();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.subscribe());
|
|
||||||
|
|
||||||
// Listen por retry events
|
|
||||||
retryPageSubject = PublishSubject.create();
|
|
||||||
add(retryPageSubject
|
|
||||||
.observeOn(Schedulers.io())
|
|
||||||
.flatMap(page -> page.getImageUrl() == null ?
|
|
||||||
source.getImageUrlFromPage(page) :
|
|
||||||
Observable.just(page))
|
|
||||||
.flatMap(source::getCachedImage)
|
|
||||||
.subscribe());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the page list of a chapter
|
|
||||||
private Observable<Chapter> getPageListObservable(Chapter chapter) {
|
|
||||||
return (chapter.isDownloaded() ?
|
|
||||||
// Fetch the page list from disk
|
|
||||||
Observable.just(downloadManager.getSavedPageList(source, manga, chapter)) :
|
|
||||||
// Fetch the page list from cache or fallback to network
|
|
||||||
source.getCachedPageListOrPullFromNetwork(chapter.url)
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
).map(pages -> {
|
|
||||||
for (Page page : pages) {
|
|
||||||
page.setChapter(chapter);
|
|
||||||
}
|
|
||||||
chapter.setPages(pages);
|
|
||||||
if (requestedPage >= -1 || currentPage == null) {
|
|
||||||
if (requestedPage == -1) {
|
|
||||||
currentPage = pages.get(pages.size() - 1);
|
|
||||||
} else {
|
|
||||||
currentPage = pages.get(requestedPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
requestedPage = -2;
|
|
||||||
pageInitializerSubject.onNext(chapter);
|
|
||||||
return chapter;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Observable<Pair<Chapter, Chapter>> getAdjacentChaptersObservable() {
|
|
||||||
return Observable.zip(
|
|
||||||
db.getPreviousChapter(activeChapter).asRxObservable().take(1),
|
|
||||||
db.getNextChapter(activeChapter).asRxObservable().take(1),
|
|
||||||
Pair::create)
|
|
||||||
.doOnNext(pair -> {
|
|
||||||
previousChapter = pair.first;
|
|
||||||
nextChapter = pair.second;
|
|
||||||
})
|
|
||||||
.observeOn(AndroidSchedulers.mainThread());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preload the first pages of the next chapter. Only for non seamless mode
|
|
||||||
private Observable<Page> getPreloadNextChapterObservable() {
|
|
||||||
return source.getCachedPageListOrPullFromNetwork(nextChapter.url)
|
|
||||||
.flatMap(pages -> {
|
|
||||||
nextChapter.setPages(pages);
|
|
||||||
int pagesToPreload = Math.min(pages.size(), 5);
|
|
||||||
return Observable.from(pages).take(pagesToPreload);
|
|
||||||
})
|
|
||||||
// Preload up to 5 images
|
|
||||||
.concatMap(page -> page.getImageUrl() == null ?
|
|
||||||
source.getImageUrlFromPage(page) :
|
|
||||||
Observable.just(page))
|
|
||||||
// Download the first image
|
|
||||||
.concatMap(page -> page.getPageNumber() == 0 ?
|
|
||||||
source.getCachedImage(page) :
|
|
||||||
Observable.just(page))
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.doOnCompleted(this::stopPreloadingNextChapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Observable<List<MangaSync>> getMangaSyncObservable() {
|
|
||||||
return db.getMangasSync(manga).asRxObservable()
|
|
||||||
.take(1)
|
|
||||||
.doOnNext(mangaSync -> this.mangaSyncList = mangaSync);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadChapter(Chapter chapter) {
|
|
||||||
loadChapter(chapter, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loads the given chapter
|
|
||||||
private void loadChapter(Chapter chapter, int requestedPage) {
|
|
||||||
if (seamlessMode) {
|
|
||||||
if (appenderSubscription != null)
|
|
||||||
remove(appenderSubscription);
|
|
||||||
} else {
|
|
||||||
stopPreloadingNextChapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.activeChapter = chapter;
|
|
||||||
chapter.status = isChapterDownloaded(chapter) ? Download.DOWNLOADED : Download.NOT_DOWNLOADED;
|
|
||||||
|
|
||||||
// If the chapter is partially read, set the starting page to the last the user read
|
|
||||||
if (!chapter.read && chapter.last_page_read != 0)
|
|
||||||
this.requestedPage = chapter.last_page_read;
|
|
||||||
else
|
|
||||||
this.requestedPage = requestedPage;
|
|
||||||
|
|
||||||
// Reset next and previous chapter. They have to be fetched again
|
|
||||||
nextChapter = null;
|
|
||||||
previousChapter = null;
|
|
||||||
|
|
||||||
start(GET_PAGE_LIST);
|
|
||||||
start(GET_ADJACENT_CHAPTERS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setActiveChapter(Chapter chapter) {
|
|
||||||
onChapterLeft();
|
|
||||||
this.activeChapter = chapter;
|
|
||||||
nextChapter = null;
|
|
||||||
previousChapter = null;
|
|
||||||
start(GET_ADJACENT_CHAPTERS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void appendNextChapter() {
|
|
||||||
if (nextChapter == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (appenderSubscription != null)
|
|
||||||
remove(appenderSubscription);
|
|
||||||
|
|
||||||
nextChapter.status = isChapterDownloaded(nextChapter) ? Download.DOWNLOADED : Download.NOT_DOWNLOADED;
|
|
||||||
|
|
||||||
appenderSubscription = getPageListObservable(nextChapter)
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.compose(deliverLatestCache())
|
|
||||||
.subscribe(split((view, chapter) -> {
|
|
||||||
view.onAppendChapter(chapter);
|
|
||||||
}, (view, error) -> {
|
|
||||||
view.onChapterAppendError();
|
|
||||||
}));
|
|
||||||
|
|
||||||
add(appenderSubscription);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the given chapter is downloaded
|
|
||||||
public boolean isChapterDownloaded(Chapter chapter) {
|
|
||||||
return downloadManager.isChapterDownloaded(source, manga, chapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void retryPage(Page page) {
|
|
||||||
if (page != null) {
|
|
||||||
page.setStatus(Page.QUEUE);
|
|
||||||
if (page.getImagePath() != null) {
|
|
||||||
File file = new File(page.getImagePath());
|
|
||||||
chapterCache.removeFileFromCache(file.getName());
|
|
||||||
}
|
|
||||||
retryPageSubject.onNext(page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called before loading another chapter or leaving the reader. It allows to do operations
|
|
||||||
// over the chapter read like saving progress
|
|
||||||
public void onChapterLeft() {
|
|
||||||
List<Page> pages = activeChapter.getPages();
|
|
||||||
if (pages == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Get the last page read
|
|
||||||
int activePageNumber = activeChapter.last_page_read;
|
|
||||||
|
|
||||||
// Just in case, avoid out of index exceptions
|
|
||||||
if (activePageNumber >= pages.size()) {
|
|
||||||
activePageNumber = pages.size() - 1;
|
|
||||||
}
|
|
||||||
Page activePage = pages.get(activePageNumber);
|
|
||||||
|
|
||||||
// Cache current page list progress for online chapters to allow a faster reopen
|
|
||||||
if (!activeChapter.isDownloaded()) {
|
|
||||||
source.savePageList(activeChapter.url, pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save current progress of the chapter. Mark as read if the chapter is finished
|
|
||||||
if (activePage.isLastPage()) {
|
|
||||||
activeChapter.read = true;
|
|
||||||
}
|
|
||||||
db.insertChapter(activeChapter).asRxObservable().subscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMangaSyncChapterToUpdate() {
|
|
||||||
if (activeChapter.getPages() == null || mangaSyncList == null || mangaSyncList.isEmpty())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
int lastChapterReadLocal = 0;
|
|
||||||
// If the current chapter has been read, we check with this one
|
|
||||||
if (activeChapter.read)
|
|
||||||
lastChapterReadLocal = (int) Math.floor(activeChapter.chapter_number);
|
|
||||||
// If not, we check if the previous chapter has been read
|
|
||||||
else if (previousChapter != null && previousChapter.read)
|
|
||||||
lastChapterReadLocal = (int) Math.floor(previousChapter.chapter_number);
|
|
||||||
|
|
||||||
// We know the chapter we have to check, but we don't know yet if an update is required.
|
|
||||||
// This boolean is used to return 0 if no update is required
|
|
||||||
boolean hasToUpdate = false;
|
|
||||||
|
|
||||||
for (MangaSync mangaSync : mangaSyncList) {
|
|
||||||
if (lastChapterReadLocal > mangaSync.last_chapter_read) {
|
|
||||||
mangaSync.last_chapter_read = lastChapterReadLocal;
|
|
||||||
mangaSync.update = true;
|
|
||||||
hasToUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hasToUpdate ? lastChapterReadLocal : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateMangaSyncLastChapterRead() {
|
|
||||||
for (MangaSync mangaSync : mangaSyncList) {
|
|
||||||
MangaSyncService service = syncManager.getService(mangaSync.sync_id);
|
|
||||||
if (service.isLogged() && mangaSync.update) {
|
|
||||||
UpdateMangaSyncService.start(getContext(), mangaSync);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCurrentPage(Page currentPage) {
|
|
||||||
this.currentPage = currentPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean loadNextChapter() {
|
|
||||||
if (hasNextChapter()) {
|
|
||||||
onChapterLeft();
|
|
||||||
loadChapter(nextChapter, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean loadPreviousChapter() {
|
|
||||||
if (hasPreviousChapter()) {
|
|
||||||
onChapterLeft();
|
|
||||||
loadChapter(previousChapter, -1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNextChapter() {
|
|
||||||
return nextChapter != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasPreviousChapter() {
|
|
||||||
return previousChapter != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preloadNextChapter() {
|
|
||||||
if (hasNextChapter() && !isChapterDownloaded(nextChapter)) {
|
|
||||||
start(PRELOAD_NEXT_CHAPTER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopPreloadingNextChapter() {
|
|
||||||
if (!isUnsubscribed(PRELOAD_NEXT_CHAPTER)) {
|
|
||||||
stop(PRELOAD_NEXT_CHAPTER);
|
|
||||||
if (nextChapter.getPages() != null)
|
|
||||||
source.savePageList(nextChapter.url, nextChapter.getPages());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateMangaViewer(int viewer) {
|
|
||||||
manga.viewer = viewer;
|
|
||||||
db.insertManga(manga).executeAsBlocking();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Manga getManga() {
|
|
||||||
return manga;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Page getCurrentPage() {
|
|
||||||
return currentPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSeamlessMode() {
|
|
||||||
return seamlessMode;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,421 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.reader
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Pair
|
||||||
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.MangaSync
|
||||||
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
|
import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager
|
||||||
|
import eu.kanade.tachiyomi.data.mangasync.UpdateMangaSyncService
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.data.source.SourceManager
|
||||||
|
import eu.kanade.tachiyomi.data.source.base.Source
|
||||||
|
import eu.kanade.tachiyomi.data.source.model.Page
|
||||||
|
import eu.kanade.tachiyomi.event.ReaderEvent
|
||||||
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import org.greenrobot.eventbus.Subscribe
|
||||||
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
|
import rx.Observable
|
||||||
|
import rx.Subscription
|
||||||
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
|
import rx.schedulers.Schedulers
|
||||||
|
import rx.subjects.PublishSubject
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class ReaderPresenter : BasePresenter<ReaderActivity>() {
|
||||||
|
|
||||||
|
@Inject lateinit var prefs: PreferencesHelper
|
||||||
|
@Inject lateinit var db: DatabaseHelper
|
||||||
|
@Inject lateinit var downloadManager: DownloadManager
|
||||||
|
@Inject lateinit var syncManager: MangaSyncManager
|
||||||
|
@Inject lateinit var sourceManager: SourceManager
|
||||||
|
@Inject lateinit var chapterCache: ChapterCache
|
||||||
|
|
||||||
|
lateinit var manga: Manga
|
||||||
|
private set
|
||||||
|
|
||||||
|
lateinit var chapter: Chapter
|
||||||
|
private set
|
||||||
|
|
||||||
|
lateinit var source: Source
|
||||||
|
private set
|
||||||
|
|
||||||
|
var requestedPage: Int = 0
|
||||||
|
var currentPage: Page? = null
|
||||||
|
private var nextChapter: Chapter? = null
|
||||||
|
private var previousChapter: Chapter? = null
|
||||||
|
private var mangaSyncList: List<MangaSync>? = null
|
||||||
|
|
||||||
|
private lateinit var retryPageSubject: PublishSubject<Page>
|
||||||
|
private lateinit var pageInitializerSubject: PublishSubject<Chapter>
|
||||||
|
|
||||||
|
val isSeamlessMode by lazy { prefs.seamlessMode() }
|
||||||
|
|
||||||
|
private var appenderSubscription: Subscription? = null
|
||||||
|
|
||||||
|
private val GET_PAGE_LIST = 1
|
||||||
|
private val GET_ADJACENT_CHAPTERS = 2
|
||||||
|
private val GET_MANGA_SYNC = 3
|
||||||
|
private val PRELOAD_NEXT_CHAPTER = 4
|
||||||
|
|
||||||
|
private val MANGA_KEY = "manga_key"
|
||||||
|
private val CHAPTER_KEY = "chapter_key"
|
||||||
|
private val PAGE_KEY = "page_key"
|
||||||
|
|
||||||
|
override fun onCreate(savedState: Bundle?) {
|
||||||
|
super.onCreate(savedState)
|
||||||
|
|
||||||
|
if (savedState != null) {
|
||||||
|
source = sourceManager.get(manga.source)!!
|
||||||
|
manga = savedState.getSerializable(MANGA_KEY) as Manga
|
||||||
|
chapter = savedState.getSerializable(CHAPTER_KEY) as Chapter
|
||||||
|
requestedPage = savedState.getInt(PAGE_KEY)
|
||||||
|
initializeSubjects()
|
||||||
|
}
|
||||||
|
|
||||||
|
startableLatestCache(GET_ADJACENT_CHAPTERS,
|
||||||
|
{ getAdjacentChaptersObservable() },
|
||||||
|
{ view, pair -> view.onAdjacentChapters(pair.first, pair.second) })
|
||||||
|
|
||||||
|
startable(PRELOAD_NEXT_CHAPTER, { getPreloadNextChapterObservable() },
|
||||||
|
{ },
|
||||||
|
{ error -> Timber.e("Error preloading chapter") })
|
||||||
|
|
||||||
|
|
||||||
|
restartable(GET_MANGA_SYNC,
|
||||||
|
{ getMangaSyncObservable().subscribe() })
|
||||||
|
|
||||||
|
restartableLatestCache(GET_PAGE_LIST,
|
||||||
|
{ getPageListObservable(chapter) },
|
||||||
|
{ view, chapter -> view.onChapterReady(manga, chapter, currentPage) },
|
||||||
|
{ view, error -> view.onChapterError() })
|
||||||
|
|
||||||
|
if (savedState == null) {
|
||||||
|
registerForEvents()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
unregisterForEvents()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSave(state: Bundle) {
|
||||||
|
onChapterLeft()
|
||||||
|
state.putSerializable(MANGA_KEY, manga)
|
||||||
|
state.putSerializable(CHAPTER_KEY, chapter)
|
||||||
|
state.putSerializable(PAGE_KEY, requestedPage)
|
||||||
|
super.onSave(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||||
|
fun onEvent(event: ReaderEvent) {
|
||||||
|
EventBus.getDefault().removeStickyEvent(event)
|
||||||
|
manga = event.manga
|
||||||
|
source = sourceManager.get(manga.source)!!
|
||||||
|
initializeSubjects()
|
||||||
|
loadChapter(event.chapter)
|
||||||
|
if (prefs.autoUpdateMangaSync()) {
|
||||||
|
start(GET_MANGA_SYNC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeSubjects() {
|
||||||
|
// Listen for pages initialization events
|
||||||
|
pageInitializerSubject = PublishSubject.create<Chapter>()
|
||||||
|
add(pageInitializerSubject.observeOn(Schedulers.io())
|
||||||
|
.concatMap { ch ->
|
||||||
|
val observable: Observable<Page>
|
||||||
|
if (ch.isDownloaded) {
|
||||||
|
val chapterDir = downloadManager.getAbsoluteChapterDirectory(source, manga, ch)
|
||||||
|
observable = Observable.from(ch.pages)
|
||||||
|
.flatMap { downloadManager.getDownloadedImage(it, chapterDir) }
|
||||||
|
} else {
|
||||||
|
observable = source.getAllImageUrlsFromPageList(ch.pages)
|
||||||
|
.flatMap({ source.getCachedImage(it) }, 2)
|
||||||
|
.doOnCompleted { source.savePageList(ch.url, ch.pages) }
|
||||||
|
}
|
||||||
|
observable.doOnCompleted {
|
||||||
|
if (!isSeamlessMode && chapter === ch) {
|
||||||
|
preloadNextChapter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.subscribe())
|
||||||
|
|
||||||
|
// Listen por retry events
|
||||||
|
retryPageSubject = PublishSubject.create<Page>()
|
||||||
|
add(retryPageSubject.observeOn(Schedulers.io())
|
||||||
|
.flatMap { page ->
|
||||||
|
if (page.imageUrl == null)
|
||||||
|
source.getImageUrlFromPage(page)
|
||||||
|
else
|
||||||
|
Observable.just<Page>(page)
|
||||||
|
}
|
||||||
|
.flatMap { source.getCachedImage(it) }
|
||||||
|
.subscribe())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the page list of a chapter
|
||||||
|
private fun getPageListObservable(chapter: Chapter): Observable<Chapter> {
|
||||||
|
val observable: Observable<List<Page>> = if (chapter.isDownloaded)
|
||||||
|
// Fetch the page list from disk
|
||||||
|
Observable.just(downloadManager.getSavedPageList(source, manga, chapter)!!)
|
||||||
|
else
|
||||||
|
// Fetch the page list from cache or fallback to network
|
||||||
|
source.getCachedPageListOrPullFromNetwork(chapter.url)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
|
||||||
|
return observable.map { pages ->
|
||||||
|
for (page in pages) {
|
||||||
|
page.chapter = chapter
|
||||||
|
}
|
||||||
|
chapter.pages = pages
|
||||||
|
if (requestedPage >= -1 || currentPage == null) {
|
||||||
|
if (requestedPage == -1) {
|
||||||
|
currentPage = pages[pages.size - 1]
|
||||||
|
} else {
|
||||||
|
currentPage = pages[requestedPage]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requestedPage = -2
|
||||||
|
pageInitializerSubject.onNext(chapter)
|
||||||
|
chapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAdjacentChaptersObservable(): Observable<Pair<Chapter, Chapter>> {
|
||||||
|
return Observable.zip(
|
||||||
|
db.getPreviousChapter(chapter).asRxObservable().take(1),
|
||||||
|
db.getNextChapter(chapter).asRxObservable().take(1),
|
||||||
|
{ a, b -> Pair.create(a, b) })
|
||||||
|
.doOnNext { pair ->
|
||||||
|
previousChapter = pair.first
|
||||||
|
nextChapter = pair.second
|
||||||
|
}
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preload the first pages of the next chapter. Only for non seamless mode
|
||||||
|
private fun getPreloadNextChapterObservable(): Observable<Page> {
|
||||||
|
return source.getCachedPageListOrPullFromNetwork(nextChapter!!.url)
|
||||||
|
.flatMap { pages ->
|
||||||
|
nextChapter!!.pages = pages
|
||||||
|
val pagesToPreload = Math.min(pages.size, 5)
|
||||||
|
Observable.from(pages).take(pagesToPreload)
|
||||||
|
}
|
||||||
|
// Preload up to 5 images
|
||||||
|
.concatMap { page ->
|
||||||
|
if (page.imageUrl == null)
|
||||||
|
source.getImageUrlFromPage(page)
|
||||||
|
else
|
||||||
|
Observable.just<Page>(page)
|
||||||
|
}
|
||||||
|
// Download the first image
|
||||||
|
.concatMap { page ->
|
||||||
|
if (page.pageNumber == 0)
|
||||||
|
source.getCachedImage(page)
|
||||||
|
else
|
||||||
|
Observable.just<Page>(page)
|
||||||
|
}
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.doOnCompleted { stopPreloadingNextChapter() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMangaSyncObservable(): Observable<List<MangaSync>> {
|
||||||
|
return db.getMangasSync(manga).asRxObservable()
|
||||||
|
.take(1)
|
||||||
|
.doOnNext { mangaSyncList = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads the given chapter
|
||||||
|
private fun loadChapter(chapter: Chapter, requestedPage: Int = 0) {
|
||||||
|
if (isSeamlessMode) {
|
||||||
|
if (appenderSubscription != null)
|
||||||
|
remove(appenderSubscription)
|
||||||
|
} else {
|
||||||
|
stopPreloadingNextChapter()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chapter = chapter
|
||||||
|
chapter.status = if (isChapterDownloaded(chapter)) Download.DOWNLOADED else Download.NOT_DOWNLOADED
|
||||||
|
|
||||||
|
// If the chapter is partially read, set the starting page to the last the user read
|
||||||
|
if (!chapter.read && chapter.last_page_read != 0)
|
||||||
|
this.requestedPage = chapter.last_page_read
|
||||||
|
else
|
||||||
|
this.requestedPage = requestedPage
|
||||||
|
|
||||||
|
// Reset next and previous chapter. They have to be fetched again
|
||||||
|
nextChapter = null
|
||||||
|
previousChapter = null
|
||||||
|
|
||||||
|
start(GET_PAGE_LIST)
|
||||||
|
start(GET_ADJACENT_CHAPTERS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setActiveChapter(chapter: Chapter) {
|
||||||
|
onChapterLeft()
|
||||||
|
this.chapter = chapter
|
||||||
|
nextChapter = null
|
||||||
|
previousChapter = null
|
||||||
|
start(GET_ADJACENT_CHAPTERS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendNextChapter() {
|
||||||
|
if (nextChapter == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (appenderSubscription != null)
|
||||||
|
remove(appenderSubscription)
|
||||||
|
|
||||||
|
nextChapter?.let {
|
||||||
|
if (appenderSubscription != null)
|
||||||
|
remove(appenderSubscription)
|
||||||
|
|
||||||
|
it.status = if (isChapterDownloaded(it)) Download.DOWNLOADED else Download.NOT_DOWNLOADED
|
||||||
|
|
||||||
|
appenderSubscription = getPageListObservable(it).subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.compose(deliverLatestCache<Chapter>())
|
||||||
|
.subscribe(split({ view, chapter ->
|
||||||
|
view.onAppendChapter(chapter)
|
||||||
|
}, { view, error ->
|
||||||
|
view.onChapterAppendError()
|
||||||
|
}))
|
||||||
|
|
||||||
|
add(appenderSubscription)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the given chapter is downloaded
|
||||||
|
fun isChapterDownloaded(chapter: Chapter): Boolean {
|
||||||
|
return downloadManager.isChapterDownloaded(source, manga, chapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun retryPage(page: Page?) {
|
||||||
|
if (page != null) {
|
||||||
|
page.status = Page.QUEUE
|
||||||
|
if (page.imagePath != null) {
|
||||||
|
val file = File(page.imagePath)
|
||||||
|
chapterCache.removeFileFromCache(file.name)
|
||||||
|
}
|
||||||
|
retryPageSubject.onNext(page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called before loading another chapter or leaving the reader. It allows to do operations
|
||||||
|
// over the chapter read like saving progress
|
||||||
|
fun onChapterLeft() {
|
||||||
|
val pages = chapter.pages ?: return
|
||||||
|
|
||||||
|
// Get the last page read
|
||||||
|
var activePageNumber = chapter.last_page_read
|
||||||
|
|
||||||
|
// Just in case, avoid out of index exceptions
|
||||||
|
if (activePageNumber >= pages.size) {
|
||||||
|
activePageNumber = pages.size - 1
|
||||||
|
}
|
||||||
|
val activePage = pages[activePageNumber]
|
||||||
|
|
||||||
|
// Cache current page list progress for online chapters to allow a faster reopen
|
||||||
|
if (!chapter.isDownloaded) {
|
||||||
|
source.savePageList(chapter.url, pages)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save current progress of the chapter. Mark as read if the chapter is finished
|
||||||
|
if (activePage.isLastPage) {
|
||||||
|
chapter.read = true
|
||||||
|
}
|
||||||
|
db.insertChapter(chapter).asRxObservable().subscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the current chapter has been read, we check with this one
|
||||||
|
// If not, we check if the previous chapter has been read
|
||||||
|
// We know the chapter we have to check, but we don't know yet if an update is required.
|
||||||
|
// This boolean is used to return 0 if no update is required
|
||||||
|
fun getMangaSyncChapterToUpdate(): Int {
|
||||||
|
if (chapter.pages == null || mangaSyncList == null || mangaSyncList!!.isEmpty())
|
||||||
|
return 0
|
||||||
|
|
||||||
|
var lastChapterReadLocal = 0
|
||||||
|
if (chapter.read)
|
||||||
|
lastChapterReadLocal = Math.floor(chapter.chapter_number.toDouble()).toInt()
|
||||||
|
else if (previousChapter != null && previousChapter!!.read)
|
||||||
|
lastChapterReadLocal = Math.floor(previousChapter!!.chapter_number.toDouble()).toInt()
|
||||||
|
var hasToUpdate = false
|
||||||
|
|
||||||
|
for (mangaSync in mangaSyncList!!) {
|
||||||
|
if (lastChapterReadLocal > mangaSync.last_chapter_read) {
|
||||||
|
mangaSync.last_chapter_read = lastChapterReadLocal
|
||||||
|
mangaSync.update = true
|
||||||
|
hasToUpdate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return if (hasToUpdate) lastChapterReadLocal else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateMangaSyncLastChapterRead() {
|
||||||
|
for (mangaSync in mangaSyncList!!) {
|
||||||
|
val service = syncManager.getService(mangaSync.sync_id)
|
||||||
|
if (service.isLogged && mangaSync.update) {
|
||||||
|
UpdateMangaSyncService.start(context, mangaSync)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadNextChapter(): Boolean {
|
||||||
|
nextChapter?.let {
|
||||||
|
onChapterLeft()
|
||||||
|
loadChapter(it, 0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadPreviousChapter(): Boolean {
|
||||||
|
previousChapter?.let {
|
||||||
|
onChapterLeft()
|
||||||
|
loadChapter(it, 0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasNextChapter(): Boolean {
|
||||||
|
return nextChapter != null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasPreviousChapter(): Boolean {
|
||||||
|
return previousChapter != null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun preloadNextChapter() {
|
||||||
|
if (hasNextChapter() && !isChapterDownloaded(nextChapter!!)) {
|
||||||
|
start(PRELOAD_NEXT_CHAPTER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopPreloadingNextChapter() {
|
||||||
|
if (!isUnsubscribed(PRELOAD_NEXT_CHAPTER)) {
|
||||||
|
stop(PRELOAD_NEXT_CHAPTER)
|
||||||
|
if (nextChapter!!.pages != null)
|
||||||
|
source.savePageList(nextChapter!!.url, nextChapter!!.pages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateMangaViewer(viewer: Int) {
|
||||||
|
manga.viewer = viewer
|
||||||
|
db.insertManga(manga).executeAsBlocking()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user