Merge pull request #112 from icewind1991/seamless-chapters

Seamless chapter transition
This commit is contained in:
inorichi 2016-02-08 22:25:56 +01:00
commit ea130a0899
25 changed files with 463 additions and 428 deletions

View File

@ -4,8 +4,11 @@ import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteColumn;
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteType;
import java.io.Serializable;
import java.util.List;
import eu.kanade.tachiyomi.data.database.tables.ChapterTable;
import eu.kanade.tachiyomi.data.download.model.Download;
import eu.kanade.tachiyomi.data.source.model.Page;
import eu.kanade.tachiyomi.util.UrlUtil;
@StorIOSQLiteType(table = ChapterTable.TABLE)
@ -40,6 +43,8 @@ public class Chapter implements Serializable {
public int status;
private transient List<Page> pages;
public Chapter() {}
public void setUrl(String url) {
@ -68,4 +73,15 @@ public class Chapter implements Serializable {
return chapter;
}
public List<Page> getPages() {
return pages;
}
public void setPages(List<Page> pages) {
this.pages = pages;
}
public boolean isDownloaded() {
return status == Download.DOWNLOADED;
}
}

View File

@ -128,6 +128,10 @@ public class PreferencesHelper {
return rxPrefs.getInteger(getKey(R.string.pref_last_catalogue_source_key), -1);
}
public boolean seamlessMode() {
return prefs.getBoolean(getKey(R.string.pref_seamless_mode_key), true);
}
public Preference<Boolean> catalogueAsList() {
return rxPrefs.getBoolean(getKey(R.string.pref_display_catalogue_as_list), false);
}

View File

@ -1,5 +1,9 @@
package eu.kanade.tachiyomi.data.source.model;
import java.io.Serializable;
import java.util.List;
import eu.kanade.tachiyomi.data.database.models.Chapter;
import eu.kanade.tachiyomi.data.network.ProgressListener;
import rx.subjects.PublishSubject;
@ -8,6 +12,7 @@ public class Page implements ProgressListener {
private int pageNumber;
private String url;
private String imageUrl;
private transient Chapter chapter;
private transient String imagePath;
private transient volatile int status;
private transient volatile int progress;
@ -82,4 +87,16 @@ public class Page implements ProgressListener {
this.statusSubject = subject;
}
public Chapter getChapter() {
return chapter;
}
public void setChapter(Chapter chapter) {
this.chapter = chapter;
}
public boolean isLastPage() {
List<Page> chapterPages = chapter.getPages();
return chapterPages.size() -1 == pageNumber;
}
}

View File

@ -158,16 +158,34 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
ToastUtil.showShort(this, R.string.page_list_error);
}
public void onChapterReady(List<Page> pages, Manga manga, Chapter chapter, int currentPage) {
if (currentPage == -1) {
currentPage = pages.size() - 1;
public void onChapterAppendError() {
// Ignore
}
public void onChapterReady(Manga manga, Chapter chapter, Page currentPage) {
List<Page> pages = chapter.getPages();
if (currentPage == null) {
currentPage = pages.get(pages.size() - 1);
}
if (viewer == null) {
viewer = getOrCreateViewer(manga);
}
viewer.onPageListReady(pages, currentPage);
readerMenu.onChapterReady(pages.size(), manga, chapter, currentPage);
viewer.onPageListReady(chapter, currentPage);
readerMenu.setActiveManga(manga);
readerMenu.setActiveChapter(chapter, currentPage.getPageNumber());
}
public void onEnterChapter(Chapter chapter, int currentPage) {
if (currentPage == -1) {
currentPage = chapter.getPages().size() - 1;
}
getPresenter().setActiveChapter(chapter);
readerMenu.setActiveChapter(chapter, currentPage);
}
public void onAppendChapter(Chapter chapter) {
viewer.onPageListAppendReady(chapter);
}
public void onAdjacentChapters(Chapter previous, Chapter next) {
@ -209,8 +227,9 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
readerMenu.onPageChanged(currentPageIndex);
}
public void setSelectedPage(int pageIndex) {
viewer.setSelectedPage(pageIndex);
public void gotoPageInCurrentChapter(int pageIndex) {
Page requestedPage = viewer.getCurrentPage().getChapter().getPages().get(pageIndex);
viewer.setSelectedPage(requestedPage);
}
public void onCenterSingleTap() {
@ -218,7 +237,7 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
}
public void requestNextChapter() {
getPresenter().setCurrentPage(viewer != null ? viewer.getCurrentPage() : 0);
getPresenter().setCurrentPage(viewer.getCurrentPage());
if (!getPresenter().loadNextChapter()) {
ToastUtil.showShort(this, R.string.no_next_chapter);
}
@ -226,7 +245,7 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
}
public void requestPreviousChapter() {
getPresenter().setCurrentPage(viewer != null ? viewer.getCurrentPage() : 0);
getPresenter().setCurrentPage(viewer.getCurrentPage());
if (!getPresenter().loadPreviousChapter()) {
ToastUtil.showShort(this, R.string.no_previous_chapter);
}

View File

@ -134,7 +134,7 @@ public class ReaderMenu {
return true;
}
public void onChapterReady(int numPages, Manga manga, Chapter chapter, int currentPageIndex) {
public void setActiveManga(Manga manga) {
if (manga.viewer == ReaderActivity.RIGHT_TO_LEFT && !inverted) {
// Invert the seekbar and textview fields for the right to left reader
seekBar.setRotation(180);
@ -144,14 +144,17 @@ public class ReaderMenu {
// Don't invert again on chapter change
inverted = true;
}
activity.setToolbarTitle(manga.title);
}
public void setActiveChapter(Chapter chapter, int currentPageIndex) {
// Set initial values
int numPages = chapter.getPages().size();
totalPages.setText("" + numPages);
currentPage.setText("" + (currentPageIndex + 1));
seekBar.setMax(numPages - 1);
seekBar.setProgress(currentPageIndex);
activity.setToolbarTitle(manga.title);
activity.setToolbarSubtitle(chapter.chapter_number != -1 ?
activity.getString(R.string.chapter_subtitle,
decimalFormat.format(chapter.chapter_number)) :
@ -353,7 +356,7 @@ public class ReaderMenu {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
activity.setSelectedPage(progress);
activity.gotoPageInCurrentChapter(progress);
}
}

View File

@ -15,6 +15,7 @@ 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.base.MangaSyncService;
import eu.kanade.tachiyomi.data.preference.PreferencesHelper;
@ -27,6 +28,7 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
import eu.kanade.tachiyomi.util.EventBusHook;
import icepick.State;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
@ -41,25 +43,25 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
@Inject SourceManager sourceManager;
@State Manga manga;
@State Chapter chapter;
@State Chapter activeChapter;
@State int sourceId;
@State boolean isDownloaded;
@State int currentPage;
@State int requestedPage;
private Page currentPage;
private Source source;
private Chapter nextChapter;
private Chapter previousChapter;
private List<Page> pageList;
private List<Page> nextChapterPageList;
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_PAGE_IMAGES = 2;
private static final int GET_ADJACENT_CHAPTERS = 3;
private static final int RETRY_IMAGES = 4;
private static final int PRELOAD_NEXT_CHAPTER = 5;
private static final int GET_MANGA_SYNC = 6;
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) {
@ -67,38 +69,29 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
if (savedState != null) {
source = sourceManager.get(sourceId);
initializeSubjects();
}
retryPageSubject = PublishSubject.create();
startable(PRELOAD_NEXT_CHAPTER, this::getPreloadNextChapterObservable,
next -> {},
error -> Timber.e("Error preloading chapter"));
startable(GET_PAGE_IMAGES, this::getPageImagesObservable,
next -> {},
error -> Timber.e("Error fetching images"));
seamlessMode = prefs.seamlessMode();
startableLatestCache(GET_ADJACENT_CHAPTERS, this::getAdjacentChaptersObservable,
(view, pair) -> view.onAdjacentChapters(pair.first, pair.second));
startable(RETRY_IMAGES, this::getRetryPageObservable);
startable(PRELOAD_NEXT_CHAPTER, this::getPreloadNextChapterObservable,
next -> {},
error -> Timber.e("Error preloading chapter"));
restartable(GET_MANGA_SYNC, () -> getMangaSyncObservable().subscribe());
restartableLatestCache(GET_PAGE_LIST,
() -> getPageListObservable()
.doOnNext(pages -> pageList = pages)
.doOnCompleted(() -> {
start(GET_ADJACENT_CHAPTERS);
start(GET_PAGE_IMAGES);
start(RETRY_IMAGES);
}),
(view, pages) -> view.onChapterReady(pages, manga, chapter, currentPage),
() -> getPageListObservable(activeChapter),
(view, chapter) -> view.onChapterReady(manga, activeChapter, currentPage),
(view, error) -> view.onChapterError());
registerForStickyEvents();
if (savedState == null) {
registerForStickyEvents();
}
}
@Override
@ -119,43 +112,79 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
manga = event.getManga();
source = event.getSource();
sourceId = source.getId();
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<List<Page>> getPageListObservable() {
return isDownloaded ?
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());
}
// Get the chapter images from network or disk
private Observable<Page> getPageImagesObservable() {
Observable<Page> pageObservable;
if (!isDownloaded) {
pageObservable = source.getAllImageUrlsFromPageList(pageList)
.flatMap(source::getCachedImage, 2);
} else {
File chapterDir = downloadManager.getAbsoluteChapterDirectory(source, manga, chapter);
pageObservable = Observable.from(pageList)
.flatMap(page -> downloadManager.getDownloadedImage(page, chapterDir));
}
return pageObservable.subscribeOn(Schedulers.io())
.doOnCompleted(this::preloadNextChapter);
.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(chapter).asRxObservable().take(1),
db.getNextChapter(chapter).asRxObservable().take(1),
db.getPreviousChapter(activeChapter).asRxObservable().take(1),
db.getNextChapter(activeChapter).asRxObservable().take(1),
Pair::create)
.doOnNext(pair -> {
previousChapter = pair.first;
@ -164,22 +193,11 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
.observeOn(AndroidSchedulers.mainThread());
}
// Listen for retry page events
private Observable<Page> getRetryPageObservable() {
return retryPageSubject
.observeOn(Schedulers.io())
.flatMap(page -> page.getImageUrl() == null ?
source.getImageUrlFromPage(page) :
Observable.just(page))
.flatMap(source::getCachedImage);
}
// Preload the first pages of the next chapter
// Preload the first pages of the next chapter. Only for non seamless mode
private Observable<Page> getPreloadNextChapterObservable() {
return source.getCachedPageListOrPullFromNetwork(nextChapter.url)
.flatMap(pages -> {
nextChapterPageList = pages;
// Preload at most 5 pages
nextChapter.setPages(pages);
int pagesToPreload = Math.min(pages.size(), 5);
return Observable.from(pages).take(pagesToPreload);
})
@ -198,6 +216,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
private Observable<List<MangaSync>> getMangaSyncObservable() {
return db.getMangasSync(manga).asRxObservable()
.take(1)
.doOnNext(mangaSync -> this.mangaSyncList = mangaSync);
}
@ -207,24 +226,58 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
// Loads the given chapter
private void loadChapter(Chapter chapter, int requestedPage) {
// Before loading the chapter, stop preloading (if it's working) and save current progress
stopPreloadingNextChapter();
if (seamlessMode) {
if (appenderSubscription != null)
remove(appenderSubscription);
} else {
stopPreloadingNextChapter();
}
this.chapter = chapter;
isDownloaded = isChapterDownloaded(chapter);
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)
currentPage = chapter.last_page_read;
this.requestedPage = chapter.last_page_read;
else
currentPage = requestedPage;
this.requestedPage = requestedPage;
// Reset next and previous chapter. They have to be fetched again
nextChapter = null;
previousChapter = null;
nextChapterPageList = null;
start(GET_PAGE_LIST);
start(GET_ADJACENT_CHAPTERS);
}
public void setActiveChapter(Chapter chapter) {
onChapterLeft(true); // force markAsRead since at this point the current page is already for the next chapter
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
@ -237,37 +290,38 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
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() {
if (pageList == null)
return;
// Cache current page list progress for online chapters to allow a faster reopen
if (!isDownloaded)
source.savePageList(chapter.url, pageList);
// Save current progress of the chapter. Mark as read if the chapter is finished
chapter.last_page_read = currentPage;
if (isChapterFinished()) {
chapter.read = true;
}
db.insertChapter(chapter).asRxObservable().subscribe();
onChapterLeft(false);
}
// Check whether the chapter has been read
private boolean isChapterFinished() {
return !chapter.read && currentPage == pageList.size() - 1;
// Called before loading another chapter or leaving the reader. It allows to do operations
// over the chapter read like saving progress
public void onChapterLeft(boolean forceMarkAsRead) {
if (activeChapter.getPages() == null)
return;
Page activePage = getCurrentPage();
// Cache current page list progress for online chapters to allow a faster reopen
if (!activeChapter.isDownloaded())
source.savePageList(activeChapter.url, activePage.getChapter().getPages());
// Save current progress of the chapter. Mark as read if the chapter is finished
activeChapter.last_page_read = activePage.getPageNumber();
if (forceMarkAsRead || activePage.isLastPage()) {
activeChapter.read = true;
}
db.insertChapter(activeChapter).asRxObservable().subscribe();
}
public int getMangaSyncChapterToUpdate() {
if (pageList == null || mangaSyncList == null || mangaSyncList.isEmpty())
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 (chapter.read)
lastChapterReadLocal = (int) Math.floor(chapter.chapter_number);
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);
@ -295,7 +349,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
}
}
public void setCurrentPage(int currentPage) {
public void setCurrentPage(Page currentPage) {
this.currentPage = currentPage;
}
@ -334,8 +388,8 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
private void stopPreloadingNextChapter() {
if (!isUnsubscribed(PRELOAD_NEXT_CHAPTER)) {
stop(PRELOAD_NEXT_CHAPTER);
if (nextChapterPageList != null)
source.savePageList(nextChapter.url, nextChapterPageList);
if (nextChapter.getPages() != null)
source.savePageList(nextChapter.url, nextChapter.getPages());
}
}
@ -348,4 +402,11 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
return manga;
}
public Page getCurrentPage() {
return currentPage;
}
public boolean isSeamlessMode() {
return seamlessMode;
}
}

View File

@ -1,15 +1,15 @@
package eu.kanade.tachiyomi.ui.reader.viewer.base;
import android.view.MotionEvent;
import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder;
import com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder;
import com.davemorrissey.labs.subscaleview.decoder.RapidImageRegionDecoder;
import com.davemorrissey.labs.subscaleview.decoder.SkiaImageDecoder;
import com.davemorrissey.labs.subscaleview.decoder.SkiaImageRegionDecoder;
import java.util.ArrayList;
import java.util.List;
import eu.kanade.tachiyomi.data.database.models.Chapter;
import eu.kanade.tachiyomi.data.source.model.Page;
import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment;
import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
@ -18,40 +18,81 @@ public abstract class BaseReader extends BaseFragment {
protected int currentPage;
protected List<Page> pages;
protected List<Chapter> chapters;
protected Class<? extends ImageRegionDecoder> regionDecoderClass;
protected Class<? extends ImageDecoder> bitmapDecoderClass;
private boolean hasRequestedNextChapter;
public static final int RAPID_DECODER = 0;
public static final int SKIA_DECODER = 1;
public void updatePageNumber() {
getReaderActivity().onPageChanged(getCurrentPage(), getTotalPages());
getReaderActivity().onPageChanged(getCurrentPage().getPageNumber(), getCurrentPage().getChapter().getPages().size());
}
public int getCurrentPage() {
return currentPage;
}
public int getPageForPosition(int position) {
return position;
}
public int getPositionForPage(int page) {
return page;
public Page getCurrentPage() {
return pages.get(currentPage);
}
public void onPageChanged(int position) {
currentPage = getPageForPosition(position);
if (getReaderActivity().getPresenter().isSeamlessMode()) {
Chapter oldChapter = pages.get(currentPage).getChapter();
Chapter newChapter = pages.get(position).getChapter();
if (!hasRequestedNextChapter && position > pages.size() - 5) {
hasRequestedNextChapter = true;
getReaderActivity().getPresenter().appendNextChapter();
}
if (!oldChapter.id.equals(newChapter.id)) {
Page page = pages.get(position);
onChapterChanged(page.getChapter(), page);
}
}
currentPage = position;
updatePageNumber();
}
public int getTotalPages() {
return pages == null ? 0 : pages.size();
private void onChapterChanged(Chapter chapter, Page currentPage) {
getReaderActivity().onEnterChapter(chapter, currentPage.getPageNumber());
}
public void setSelectedPage(Page page) {
setSelectedPage(getPageIndex(page));
}
public int getPageIndex(Page search) {
// search for the index of a page in the current list without requiring them to be the same object
for (Page page : pages) {
if (page.getPageNumber() == search.getPageNumber() &&
page.getChapter().id.equals(search.getChapter().id)) {
return pages.indexOf(page);
}
}
return 0;
}
public void onPageListReady(Chapter chapter, Page currentPage) {
if (chapters == null || !chapters.contains(chapter)) {
// if we reset the loaded page we also need to reset the loaded chapters
chapters = new ArrayList<>();
chapters.add(chapter);
onSetChapter(chapter, currentPage);
} else {
setSelectedPage(currentPage);
}
}
public void onPageListAppendReady(Chapter chapter) {
if (!chapters.contains(chapter)) {
hasRequestedNextChapter = false;
chapters.add(chapter);
onAppendChapter(chapter);
}
}
public abstract void setSelectedPage(int pageNumber);
public abstract void onPageListReady(List<Page> pages, int currentPage);
public abstract boolean onImageTouch(MotionEvent motionEvent);
public abstract void onSetChapter(Chapter chapter, Page currentPage);
public abstract void onAppendChapter(Chapter chapter);
public void setDecoderClass(int value) {
switch (value) {

View File

@ -1,7 +0,0 @@
package eu.kanade.tachiyomi.ui.reader.viewer.base;
public interface OnChapterSingleTapListener {
void onCenterTap();
void onLeftSideTap();
void onRightSideTap();
}

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.reader.viewer.base;
package eu.kanade.tachiyomi.ui.reader.viewer.pager;
public interface OnChapterBoundariesOutListener {
void onFirstPageOutEvent();

View File

@ -1,11 +1,8 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager;
import android.support.v4.view.PagerAdapter;
import android.view.MotionEvent;
import android.view.ViewGroup;
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterBoundariesOutListener;
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterSingleTapListener;
import rx.functions.Action1;
public interface Pager {
@ -24,13 +21,7 @@ public interface Pager {
PagerAdapter getAdapter();
void setAdapter(PagerAdapter adapter);
boolean onImageTouch(MotionEvent motionEvent);
void setOnChapterBoundariesOutListener(OnChapterBoundariesOutListener listener);
void setOnChapterSingleTapListener(OnChapterSingleTapListener listener);
OnChapterBoundariesOutListener getChapterBoundariesListener();
OnChapterSingleTapListener getChapterSingleTapListener();
void setOnPageChangeListener(Action1<Integer> onPageChanged);
void clearOnPageChangeListeners();

View File

@ -1,71 +0,0 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager;
import android.view.GestureDetector;
import android.view.MotionEvent;
public class PagerGestureListener extends GestureDetector.SimpleOnGestureListener {
private Pager pager;
private static final float LEFT_REGION = 0.33f;
private static final float RIGHT_REGION = 0.66f;
public PagerGestureListener(Pager pager) {
this.pager = pager;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
final int position = pager.getCurrentItem();
final float positionX = e.getX();
if (positionX < pager.getWidth() * LEFT_REGION) {
if (position != 0) {
onLeftSideTap();
} else {
onFirstPageOut();
}
} else if (positionX > pager.getWidth() * RIGHT_REGION) {
if (position != pager.getAdapter().getCount() - 1) {
onRightSideTap();
} else {
onLastPageOut();
}
} else {
onCenterTap();
}
return true;
}
private void onLeftSideTap() {
if (pager.getChapterSingleTapListener() != null) {
pager.getChapterSingleTapListener().onLeftSideTap();
}
}
private void onRightSideTap() {
if (pager.getChapterSingleTapListener() != null) {
pager.getChapterSingleTapListener().onRightSideTap();
}
}
private void onCenterTap() {
if (pager.getChapterSingleTapListener() != null) {
pager.getChapterSingleTapListener().onCenterTap();
}
}
private void onFirstPageOut() {
if (pager.getChapterBoundariesListener() != null) {
pager.getChapterBoundariesListener().onFirstPageOutEvent();
}
}
private void onLastPageOut() {
if (pager.getChapterBoundariesListener() != null) {
pager.getChapterBoundariesListener().onLastPageOutEvent();
}
}
}

View File

@ -1,16 +1,16 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ViewGroup;
import java.util.List;
import java.util.ArrayList;
import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.database.models.Chapter;
import eu.kanade.tachiyomi.data.preference.PreferencesHelper;
import eu.kanade.tachiyomi.data.source.model.Page;
import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader;
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterBoundariesOutListener;
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterSingleTapListener;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.LeftToRightReader;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.RightToLeftReader;
import rx.subscriptions.CompositeSubscription;
@ -21,8 +21,8 @@ public abstract class PagerReader extends BaseReader {
protected PagerReaderAdapter adapter;
protected Pager pager;
protected GestureDetector gestureDetector;
private boolean isReady;
protected boolean transitions;
protected CompositeSubscription subscriptions;
@ -34,6 +34,9 @@ public abstract class PagerReader extends BaseReader {
public static final int ALIGN_RIGHT = 3;
public static final int ALIGN_CENTER = 4;
private static final float LEFT_REGION = 0.33f;
private static final float RIGHT_REGION = 0.66f;
protected void initializePager(Pager pager) {
this.pager = pager;
pager.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
@ -42,30 +45,15 @@ public abstract class PagerReader extends BaseReader {
pager.setOnChapterBoundariesOutListener(new OnChapterBoundariesOutListener() {
@Override
public void onFirstPageOutEvent() {
onFirstPageOut();
getReaderActivity().requestPreviousChapter();
}
@Override
public void onLastPageOutEvent() {
onLastPageOut();
}
});
pager.setOnChapterSingleTapListener(new OnChapterSingleTapListener() {
@Override
public void onCenterTap() {
getReaderActivity().onCenterSingleTap();
}
@Override
public void onLeftSideTap() {
pager.setCurrentItem(pager.getCurrentItem() - 1, transitions);
}
@Override
public void onRightSideTap() {
pager.setCurrentItem(pager.getCurrentItem() + 1, transitions);
getReaderActivity().requestNextChapter();
}
});
gestureDetector = createGestureDetector();
adapter = new PagerReaderAdapter(getChildFragmentManager());
pager.setAdapter(adapter);
@ -77,28 +65,27 @@ public abstract class PagerReader extends BaseReader {
.doOnNext(this::setDecoderClass)
.skip(1)
.distinctUntilChanged()
.subscribe(v -> adapter.notifyDataSetChanged()));
.subscribe(v -> pager.setAdapter(adapter)));
subscriptions.add(preferences.imageScaleType()
.asObservable()
.doOnNext(this::setImageScaleType)
.skip(1)
.distinctUntilChanged()
.subscribe(v -> adapter.notifyDataSetChanged()));
.subscribe(v -> pager.setAdapter(adapter)));
subscriptions.add(preferences.zoomStart()
.asObservable()
.doOnNext(this::setZoomStart)
.skip(1)
.distinctUntilChanged()
.subscribe(v -> adapter.notifyDataSetChanged()));
.subscribe(v -> pager.setAdapter(adapter)));
subscriptions.add(preferences.enableTransitions()
.asObservable()
.subscribe(value -> transitions = value));
setPages();
isReady = true;
}
@Override
@ -107,14 +94,41 @@ public abstract class PagerReader extends BaseReader {
super.onDestroyView();
}
@Override
public void onPageListReady(List<Page> pages, int currentPage) {
if (this.pages != pages) {
this.pages = pages;
this.currentPage = currentPage;
if (isReady) {
setPages();
protected GestureDetector createGestureDetector() {
return new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
final float positionX = e.getX();
if (positionX < pager.getWidth() * LEFT_REGION) {
onLeftSideTap();
} else if (positionX > pager.getWidth() * RIGHT_REGION) {
onRightSideTap();
} else {
getReaderActivity().onCenterSingleTap();
}
return true;
}
});
}
@Override
public void onSetChapter(Chapter chapter, Page currentPage) {
pages = new ArrayList<>(chapter.getPages());
this.currentPage = getPageIndex(currentPage); // we might have a new page object
// This method can be called before the view is created
if (pager != null) {
setPages();
}
}
public void onAppendChapter(Chapter chapter) {
pages.addAll(chapter.getPages());
// This method can be called before the view is created
if (pager != null) {
adapter.setPages(pages);
}
}
@ -130,12 +144,23 @@ public abstract class PagerReader extends BaseReader {
@Override
public void setSelectedPage(int pageNumber) {
pager.setCurrentItem(getPositionForPage(pageNumber), false);
pager.setCurrentItem(pageNumber, false);
}
@Override
public boolean onImageTouch(MotionEvent motionEvent) {
return pager.onImageTouch(motionEvent);
protected void onLeftSideTap() {
if (pager.getCurrentItem() != 0) {
pager.setCurrentItem(pager.getCurrentItem() - 1, transitions);
} else {
getReaderActivity().requestPreviousChapter();
}
}
protected void onRightSideTap() {
if (pager.getCurrentItem() != pager.getAdapter().getCount() - 1) {
pager.setCurrentItem(pager.getCurrentItem() + 1, transitions);
} else {
getReaderActivity().requestNextChapter();
}
}
private void setImageScaleType(int scaleType) {
@ -155,7 +180,4 @@ public abstract class PagerReader extends BaseReader {
}
}
public abstract void onFirstPageOut();
public abstract void onLastPageOut();
}

View File

@ -31,14 +31,10 @@ public class PagerReaderAdapter extends FragmentStatePagerAdapter {
public Object instantiateItem(ViewGroup container, int position) {
PagerReaderFragment f = (PagerReaderFragment) super.instantiateItem(container, position);
f.setPage(pages.get(position));
f.setPosition(position);
return f;
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
public List<Page> getPages() {
return pages;
}
@ -48,4 +44,17 @@ public class PagerReaderAdapter extends FragmentStatePagerAdapter {
notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object) {
PagerReaderFragment f = (PagerReaderFragment) object;
int position = f.getPosition();
if (position >= 0 && position < getCount()) {
if (pages.get(position) == f.getPage()) {
return POSITION_UNCHANGED;
} else {
return POSITION_NONE;
}
}
return super.getItemPosition(object);
}
}

View File

@ -26,6 +26,7 @@ import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.source.model.Page;
import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment;
import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.RightToLeftReader;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.vertical.VerticalReader;
import rx.Observable;
import rx.Subscription;
@ -42,9 +43,12 @@ public class PagerReaderFragment extends BaseFragment {
@Bind(R.id.retry_button) Button retryButton;
private Page page;
private boolean isReady;
private Subscription progressSubscription;
private Subscription statusSubscription;
private int position = -1;
private int lightGreyColor;
private int blackColor;
public static PagerReaderFragment newInstance() {
return new PagerReaderFragment();
@ -57,8 +61,15 @@ public class PagerReaderFragment extends BaseFragment {
ReaderActivity activity = getReaderActivity();
PagerReader parentFragment = (PagerReader) getParentFragment();
lightGreyColor = ContextCompat.getColor(getContext(), R.color.light_grey);
blackColor = ContextCompat.getColor(getContext(), R.color.primary_text);
if (activity.getReaderTheme() == ReaderActivity.BLACK_THEME) {
progressText.setTextColor(ContextCompat.getColor(getContext(), R.color.light_grey));
progressText.setTextColor(lightGreyColor);
}
if (parentFragment instanceof RightToLeftReader) {
view.setRotation(-180);
}
imageView.setParallelLoadingEnabled(true);
@ -69,7 +80,7 @@ public class PagerReaderFragment extends BaseFragment {
imageView.setRegionDecoderClass(parentFragment.getRegionDecoderClass());
imageView.setBitmapDecoderClass(parentFragment.getBitmapDecoderClass());
imageView.setVerticalScrollingParent(parentFragment instanceof VerticalReader);
imageView.setOnTouchListener((v, motionEvent) -> parentFragment.onImageTouch(motionEvent));
imageView.setOnTouchListener((v, motionEvent) -> parentFragment.gestureDetector.onTouchEvent(motionEvent));
imageView.setOnImageEventListener(new SubsamplingScaleImageView.DefaultOnImageEventListener() {
@Override
public void onReady() {
@ -103,7 +114,6 @@ public class PagerReaderFragment extends BaseFragment {
});
observeStatus();
isReady = true;
return view;
}
@ -111,6 +121,7 @@ public class PagerReaderFragment extends BaseFragment {
public void onDestroyView() {
unsubscribeProgress();
unsubscribeStatus();
imageView.setOnTouchListener(null);
imageView.setOnImageEventListener(null);
ButterKnife.unbind(this);
super.onDestroyView();
@ -118,11 +129,17 @@ public class PagerReaderFragment extends BaseFragment {
public void setPage(Page page) {
this.page = page;
if (isReady) {
// This method can be called before the view is created
if (imageView != null) {
observeStatus();
}
}
public void setPosition(int position) {
this.position = position;
}
private void showImage() {
if (page == null || page.getImagePath() == null)
return;
@ -160,8 +177,7 @@ public class PagerReaderFragment extends BaseFragment {
errorText.setGravity(Gravity.CENTER);
errorText.setText(R.string.decode_image_error);
errorText.setTextColor(getReaderActivity().getReaderTheme() == ReaderActivity.BLACK_THEME ?
ContextCompat.getColor(getContext(), R.color.light_grey) :
ContextCompat.getColor(getContext(), R.color.primary_text));
lightGreyColor : blackColor);
view.addView(errorText);
}
@ -236,6 +252,14 @@ public class PagerReaderFragment extends BaseFragment {
}
}
public Page getPage() {
return page;
}
public int getPosition() {
return position;
}
private ReaderActivity getReaderActivity() {
return (ReaderActivity) getActivity();
}

View File

@ -2,38 +2,21 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterBoundariesOutListener;
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterSingleTapListener;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.OnChapterBoundariesOutListener;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.Pager;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerGestureListener;
import rx.functions.Action1;
public class HorizontalPager extends ViewPager implements Pager {
private GestureDetector gestureDetector;
private OnChapterBoundariesOutListener onChapterBoundariesOutListener;
private OnChapterSingleTapListener onChapterSingleTapListener;
private static final float SWIPE_TOLERANCE = 0.25f;
private float startDragX;
public HorizontalPager(Context context) {
super(context);
init(context);
}
public HorizontalPager(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
gestureDetector = new GestureDetector(context, new PagerGestureListener(this));
}
@Override
@ -86,31 +69,11 @@ public class HorizontalPager extends ViewPager implements Pager {
}
}
@Override
public boolean onImageTouch(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
@Override
public void setOnChapterBoundariesOutListener(OnChapterBoundariesOutListener listener) {
onChapterBoundariesOutListener = listener;
}
@Override
public void setOnChapterSingleTapListener(OnChapterSingleTapListener listener) {
onChapterSingleTapListener = listener;
}
@Override
public OnChapterBoundariesOutListener getChapterBoundariesListener() {
return onChapterBoundariesOutListener;
}
@Override
public OnChapterSingleTapListener getChapterSingleTapListener() {
return onChapterSingleTapListener;
}
@Override
public void setOnPageChangeListener(Action1<Integer> function) {
addOnPageChangeListener(new SimpleOnPageChangeListener() {

View File

@ -1,19 +0,0 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader;
public abstract class HorizontalReader extends PagerReader {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
HorizontalPager pager = new HorizontalPager(getActivity());
initializePager(pager);
return pager;
}
}

View File

@ -1,15 +1,19 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal;
public class LeftToRightReader extends HorizontalReader {
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader;
public class LeftToRightReader extends PagerReader {
@Override
public void onFirstPageOut() {
getReaderActivity().requestPreviousChapter();
}
@Override
public void onLastPageOut() {
getReaderActivity().requestNextChapter();
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
HorizontalPager pager = new HorizontalPager(getActivity());
initializePager(pager);
return pager;
}
}

View File

@ -1,38 +1,30 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import eu.kanade.tachiyomi.data.source.model.Page;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader;
public class RightToLeftReader extends HorizontalReader {
public class RightToLeftReader extends PagerReader {
@Override
public void onPageListReady(List<Page> pages, int currentPage) {
ArrayList<Page> inversedPages = new ArrayList<>(pages);
Collections.reverse(inversedPages);
super.onPageListReady(inversedPages, currentPage);
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
HorizontalPager pager = new HorizontalPager(getActivity());
pager.setRotation(180);
initializePager(pager);
return pager;
}
@Override
public int getPageForPosition(int position) {
return (getTotalPages() - 1) - position;
protected void onLeftSideTap() {
super.onRightSideTap();
}
@Override
public int getPositionForPage(int page) {
return (getTotalPages() - 1) - page;
}
@Override
public void onFirstPageOut() {
getReaderActivity().requestNextChapter();
}
@Override
public void onLastPageOut() {
getReaderActivity().requestPreviousChapter();
protected void onRightSideTap() {
super.onLeftSideTap();
}
}

View File

@ -1,38 +1,21 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager.vertical;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterBoundariesOutListener;
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterSingleTapListener;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.OnChapterBoundariesOutListener;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.Pager;
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerGestureListener;
import rx.functions.Action1;
public class VerticalPager extends VerticalViewPagerImpl implements Pager {
private GestureDetector gestureDetector;
private OnChapterBoundariesOutListener onChapterBoundariesOutListener;
private OnChapterSingleTapListener onChapterSingleTapListener;
private static final float SWIPE_TOLERANCE = 0.25f;
private float startDragY;
public VerticalPager(Context context) {
super(context);
init(context);
}
public VerticalPager(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
gestureDetector = new GestureDetector(context, new PagerGestureListener(this));
}
@Override
@ -85,31 +68,11 @@ public class VerticalPager extends VerticalViewPagerImpl implements Pager {
}
}
@Override
public boolean onImageTouch(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
@Override
public void setOnChapterBoundariesOutListener(OnChapterBoundariesOutListener listener) {
onChapterBoundariesOutListener = listener;
}
@Override
public void setOnChapterSingleTapListener(OnChapterSingleTapListener listener) {
onChapterSingleTapListener = listener;
}
@Override
public OnChapterBoundariesOutListener getChapterBoundariesListener() {
return onChapterBoundariesOutListener;
}
@Override
public OnChapterSingleTapListener getChapterSingleTapListener() {
return onChapterSingleTapListener;
}
@Override
public void setOnPageChangeListener(Action1<Integer> function) {
addOnPageChangeListener(new SimpleOnPageChangeListener() {

View File

@ -16,14 +16,4 @@ public class VerticalReader extends PagerReader {
return pager;
}
@Override
public void onFirstPageOut() {
getReaderActivity().requestPreviousChapter();
}
@Override
public void onLastPageOut() {
getReaderActivity().requestNextChapter();
}
}

View File

@ -21,7 +21,7 @@ public class WebtoonAdapter extends RecyclerView.Adapter<WebtoonHolder> {
public WebtoonAdapter(WebtoonReader fragment) {
this.fragment = fragment;
pages = new ArrayList<>();
touchListener = (v, event) -> fragment.onImageTouch(event);
touchListener = (v, event) -> fragment.gestureDetector.onTouchEvent(event);
}
public Page getItem(int position) {

View File

@ -8,8 +8,9 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import java.util.ArrayList;
import eu.kanade.tachiyomi.data.database.models.Chapter;
import eu.kanade.tachiyomi.data.source.model.Page;
import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader;
import eu.kanade.tachiyomi.widget.PreCachingLayoutManager;
@ -27,12 +28,11 @@ public class WebtoonReader extends BaseReader {
private PreCachingLayoutManager layoutManager;
private Subscription subscription;
private Subscription decoderSubscription;
private GestureDetector gestureDetector;
protected GestureDetector gestureDetector;
private boolean isReady;
private int scrollDistance;
private static final String SCROLL_STATE = "scroll_state";
private static final String SAVED_POSITION = "saved_position";
private static final float LEFT_REGION = 0.33f;
private static final float RIGHT_REGION = 0.66f;
@ -47,7 +47,7 @@ public class WebtoonReader extends BaseReader {
layoutManager = new PreCachingLayoutManager(getActivity());
layoutManager.setExtraLayoutSpace(screenHeight / 2);
if (savedState != null) {
layoutManager.onRestoreInstanceState(savedState.getParcelable(SCROLL_STATE));
layoutManager.scrollToPositionWithOffset(savedState.getInt(SAVED_POSITION), 0);
}
recycler = new RecyclerView(getActivity());
@ -80,8 +80,6 @@ public class WebtoonReader extends BaseReader {
});
setPages();
isReady = true;
return recycler;
}
@ -100,7 +98,9 @@ public class WebtoonReader extends BaseReader {
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(SCROLL_STATE, layoutManager.onSaveInstanceState());
int savedPosition = pages != null ?
pages.get(layoutManager.findFirstVisibleItemPosition()).getPageNumber() : 0;
outState.putInt(SAVED_POSITION, savedPosition);
}
private void unsubscribeStatus() {
@ -110,18 +110,30 @@ public class WebtoonReader extends BaseReader {
@Override
public void setSelectedPage(int pageNumber) {
recycler.scrollToPosition(getPositionForPage(pageNumber));
recycler.scrollToPosition(pageNumber);
}
@Override
public void onPageListReady(List<Page> pages, int currentPage) {
if (this.pages != pages) {
this.pages = pages;
// Restoring current page is not supported. It's getting weird scrolling jumps
// this.currentPage = currentPage;
if (isReady) {
setPages();
}
public void onSetChapter(Chapter chapter, Page currentPage) {
pages = new ArrayList<>(chapter.getPages());
// Restoring current page is not supported. It's getting weird scrolling jumps
// this.currentPage = currentPage;
// This method can be called before the view is created
if (recycler != null) {
setPages();
}
}
@Override
public void onAppendChapter(Chapter chapter) {
int insertStart = pages.size();
pages.addAll(chapter.getPages());
// This method can be called before the view is created
if (recycler != null) {
adapter.setPages(pages);
adapter.notifyItemRangeInserted(insertStart, chapter.getPages().size());
}
}
@ -141,19 +153,14 @@ public class WebtoonReader extends BaseReader {
recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
currentPage = layoutManager.findLastVisibleItemPosition();
updatePageNumber();
int page = layoutManager.findLastVisibleItemPosition();
if (page != currentPage) {
onPageChanged(page);
}
}
});
}
@Override
public boolean onImageTouch(MotionEvent motionEvent) {
return gestureDetector.onTouchEvent(motionEvent);
}
private void observeStatus(int position) {
if (position == pages.size())
return;

View File

@ -27,6 +27,7 @@
<string name="pref_custom_brightness_value_key">pref_custom_brightness_value_key</string>
<string name="pref_reader_theme_key">pref_reader_theme_key</string>
<string name="pref_image_decoder_key">pref_image_decoder_key</string>
<string name="pref_seamless_mode_key">pref_seamless_mode_key</string>
<string name="pref_download_directory_key">pref_download_directory_key</string>
<string name="pref_download_slots_key">pref_download_slots_key</string>

View File

@ -84,6 +84,7 @@
<string name="pref_enable_transitions">Enable transitions</string>
<string name="pref_show_page_number">Show page number</string>
<string name="pref_custom_brightness">Use custom brightness</string>
<string name="pref_seamless_mode">Enable seamless chapter transitions</string>
<string name="pref_keep_screen_on">Keep screen on</string>
<string name="pref_reader_theme">Background color</string>
<string name="white_theme">White</string>

View File

@ -21,6 +21,10 @@
android:key="@string/pref_custom_brightness_key"
android:defaultValue="false" />
<SwitchPreference android:title="@string/pref_seamless_mode"
android:key="@string/pref_seamless_mode_key"
android:defaultValue="true" />
<eu.kanade.tachiyomi.widget.preference.IntListPreference
android:title="@string/pref_viewer_type"
android:key="@string/pref_default_viewer_key"