Allow reading downloaded chapters
@ -48,10 +48,9 @@ public class DownloadManager {
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
private Observable<Page> downloadChapter(Manga manga, Chapter chapter) {
|
||||
public Observable<Page> downloadChapter(Manga manga, Chapter chapter) {
|
||||
final Source source = sourceManager.get(manga.source);
|
||||
final File chapterDirectory = new File(
|
||||
preferences.getDownloadsDirectory(), getChapterDirectory(source, manga, chapter));
|
||||
final File chapterDirectory = getAbsoluteChapterDirectory(source, manga, chapter);
|
||||
|
||||
return source
|
||||
.pullPageListFromNetwork(chapter.url)
|
||||
@ -65,7 +64,12 @@ public class DownloadManager {
|
||||
.flatMap(page -> getDownloadedImage(page, source, chapterDirectory));
|
||||
}
|
||||
|
||||
private String getChapterDirectory(Source source, Manga manga, Chapter chapter) {
|
||||
public File getAbsoluteChapterDirectory(Source source, Manga manga, Chapter chapter) {
|
||||
return new File(preferences.getDownloadsDirectory(),
|
||||
getChapterDirectory(source, manga, chapter));
|
||||
}
|
||||
|
||||
public String getChapterDirectory(Source source, Manga manga, Chapter chapter) {
|
||||
return source.getName() +
|
||||
File.separator +
|
||||
manga.title.replaceAll("[^a-zA-Z0-9.-]", "_") +
|
||||
|
@ -32,6 +32,12 @@ public class Chapter {
|
||||
@StorIOSQLiteColumn(name = ChaptersTable.COLUMN_DATE_UPLOAD)
|
||||
public long date_upload;
|
||||
|
||||
public int downloaded;
|
||||
|
||||
public static final int UNKNOWN = 0;
|
||||
public static final int NOT_DOWNLOADED = 1;
|
||||
public static final int DOWNLOADED = 2;
|
||||
|
||||
|
||||
public Chapter() {}
|
||||
|
||||
|
@ -1,15 +1,18 @@
|
||||
package eu.kanade.mangafeed.events;
|
||||
|
||||
import eu.kanade.mangafeed.data.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.models.Manga;
|
||||
import eu.kanade.mangafeed.sources.base.Source;
|
||||
|
||||
public class SourceChapterEvent {
|
||||
public class SourceMangaChapterEvent {
|
||||
|
||||
private Source source;
|
||||
private Manga manga;
|
||||
private Chapter chapter;
|
||||
|
||||
public SourceChapterEvent(Source source, Chapter chapter) {
|
||||
public SourceMangaChapterEvent(Source source, Manga manga, Chapter chapter) {
|
||||
this.source = source;
|
||||
this.manga = manga;
|
||||
this.chapter = chapter;
|
||||
}
|
||||
|
||||
@ -17,6 +20,10 @@ public class SourceChapterEvent {
|
||||
return source;
|
||||
}
|
||||
|
||||
public Manga getManga() {
|
||||
return manga;
|
||||
}
|
||||
|
||||
public Chapter getChapter() {
|
||||
return chapter;
|
||||
}
|
@ -2,17 +2,20 @@ package eu.kanade.mangafeed.presenter;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
|
||||
import eu.kanade.mangafeed.data.helpers.DownloadManager;
|
||||
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
|
||||
import eu.kanade.mangafeed.data.helpers.SourceManager;
|
||||
import eu.kanade.mangafeed.data.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.models.Manga;
|
||||
import eu.kanade.mangafeed.events.ChapterCountEvent;
|
||||
import eu.kanade.mangafeed.events.SourceChapterEvent;
|
||||
import eu.kanade.mangafeed.events.SourceMangaChapterEvent;
|
||||
import eu.kanade.mangafeed.sources.base.Source;
|
||||
import eu.kanade.mangafeed.ui.fragment.MangaChaptersFragment;
|
||||
import eu.kanade.mangafeed.util.EventBusHook;
|
||||
@ -26,6 +29,8 @@ public class MangaChaptersPresenter extends BasePresenter<MangaChaptersFragment>
|
||||
|
||||
@Inject DatabaseHelper db;
|
||||
@Inject SourceManager sourceManager;
|
||||
@Inject PreferencesHelper preferences;
|
||||
@Inject DownloadManager downloadManager;
|
||||
|
||||
private Manga manga;
|
||||
private Source source;
|
||||
@ -111,7 +116,7 @@ public class MangaChaptersPresenter extends BasePresenter<MangaChaptersFragment>
|
||||
}
|
||||
|
||||
public void onChapterClicked(Chapter chapter) {
|
||||
EventBus.getDefault().postSticky(new SourceChapterEvent(source, chapter));
|
||||
EventBus.getDefault().postSticky(new SourceMangaChapterEvent(source, manga, chapter));
|
||||
}
|
||||
|
||||
public void markChaptersRead(Observable<Chapter> selectedChapters, boolean read) {
|
||||
@ -131,4 +136,14 @@ public class MangaChaptersPresenter extends BasePresenter<MangaChaptersFragment>
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
public void checkIsChapterDownloaded(Chapter chapter) {
|
||||
File dir = downloadManager.getAbsoluteChapterDirectory(source, manga, chapter);
|
||||
|
||||
if (dir.exists() && dir.listFiles().length > 0) {
|
||||
chapter.downloaded = Chapter.DOWNLOADED;
|
||||
} else {
|
||||
chapter.downloaded = Chapter.NOT_DOWNLOADED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,19 @@ package eu.kanade.mangafeed.presenter;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
|
||||
import eu.kanade.mangafeed.data.helpers.DownloadManager;
|
||||
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
|
||||
import eu.kanade.mangafeed.data.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.models.Manga;
|
||||
import eu.kanade.mangafeed.data.models.Page;
|
||||
import eu.kanade.mangafeed.events.SourceChapterEvent;
|
||||
import eu.kanade.mangafeed.events.SourceMangaChapterEvent;
|
||||
import eu.kanade.mangafeed.sources.base.Source;
|
||||
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
|
||||
import eu.kanade.mangafeed.util.EventBusHook;
|
||||
@ -25,14 +28,17 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
||||
|
||||
@Inject PreferencesHelper prefs;
|
||||
@Inject DatabaseHelper db;
|
||||
@Inject DownloadManager downloadManager;
|
||||
|
||||
private Source source;
|
||||
private Manga manga;
|
||||
private Chapter chapter;
|
||||
private List<Page> pageList;
|
||||
@State int currentPage;
|
||||
|
||||
private static final int GET_PAGE_LIST = 1;
|
||||
private static final int GET_PAGE_IMAGES = 2;
|
||||
private static final int GET_LOCAL_IMAGES = 3;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
@ -41,7 +47,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
||||
restartableLatestCache(GET_PAGE_LIST,
|
||||
() -> getPageListObservable()
|
||||
.doOnNext(pages -> pageList = pages)
|
||||
.doOnCompleted(() -> start(GET_PAGE_IMAGES)),
|
||||
.doOnCompleted(this::prepareChapter),
|
||||
(view, pages) -> {
|
||||
view.onPageListReady(pages);
|
||||
if (currentPage != 0)
|
||||
@ -55,6 +61,10 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
||||
(view, page) -> {
|
||||
},
|
||||
(view, error) -> Timber.e("An error occurred while downloading an image"));
|
||||
|
||||
restartableReplay(GET_LOCAL_IMAGES,
|
||||
this::getLocalImagesObservable,
|
||||
(view, page) -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -77,14 +87,16 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
||||
}
|
||||
|
||||
@EventBusHook
|
||||
public void onEventMainThread(SourceChapterEvent event) {
|
||||
public void onEventMainThread(SourceMangaChapterEvent event) {
|
||||
source = event.getSource();
|
||||
manga = event.getManga();
|
||||
chapter = event.getChapter();
|
||||
if (chapter.last_page_read != 0 && !chapter.read)
|
||||
currentPage = chapter.last_page_read;
|
||||
|
||||
start(1);
|
||||
EventBus.getDefault().removeStickyEvent(SourceChapterEvent.class);
|
||||
start(GET_PAGE_LIST);
|
||||
|
||||
EventBus.getDefault().removeStickyEvent(SourceMangaChapterEvent.class);
|
||||
}
|
||||
|
||||
private Observable<List<Page>> getPageListObservable() {
|
||||
@ -103,10 +115,26 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
private Observable<Page> getLocalImagesObservable() {
|
||||
File chapterDir = downloadManager.getAbsoluteChapterDirectory(source, manga, chapter);
|
||||
|
||||
return Observable.from(pageList)
|
||||
.flatMap(page -> downloadManager.getDownloadedImage(page, source, chapterDir))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public void setCurrentPage(int currentPage) {
|
||||
this.currentPage = currentPage;
|
||||
}
|
||||
|
||||
private void prepareChapter() {
|
||||
if (chapter.downloaded != Chapter.DOWNLOADED)
|
||||
start(GET_PAGE_IMAGES);
|
||||
else
|
||||
start(GET_LOCAL_IMAGES);
|
||||
}
|
||||
|
||||
private void saveChapter() {
|
||||
chapter.last_page_read = currentPage;
|
||||
if (currentPage == pageList.size() - 1) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
package eu.kanade.mangafeed.ui.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -11,16 +10,17 @@ import java.util.List;
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.kanade.mangafeed.R;
|
||||
import eu.kanade.mangafeed.data.models.Chapter;
|
||||
import eu.kanade.mangafeed.ui.fragment.MangaChaptersFragment;
|
||||
import eu.kanade.mangafeed.ui.fragment.base.BaseFragment;
|
||||
import eu.kanade.mangafeed.ui.holder.ChaptersHolder;
|
||||
|
||||
public class ChaptersAdapter extends FlexibleAdapter<ChaptersHolder, Chapter> {
|
||||
|
||||
private Context context;
|
||||
private BaseFragment fragment;
|
||||
public OnItemClickListener clickListener;
|
||||
|
||||
public ChaptersAdapter(BaseFragment fragment) {
|
||||
this.context = fragment.getActivity();
|
||||
this.fragment = fragment;
|
||||
mItems = new ArrayList<>();
|
||||
clickListener = (OnItemClickListener) fragment;
|
||||
}
|
||||
@ -30,14 +30,14 @@ public class ChaptersAdapter extends FlexibleAdapter<ChaptersHolder, Chapter> {
|
||||
|
||||
@Override
|
||||
public ChaptersHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(context).inflate(R.layout.item_chapter, parent, false);
|
||||
View v = LayoutInflater.from(fragment.getActivity()).inflate(R.layout.item_chapter, parent, false);
|
||||
return new ChaptersHolder(v, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ChaptersHolder holder, int position) {
|
||||
final Chapter chapter = getItem(position);
|
||||
holder.onSetValues(context, chapter);
|
||||
holder.onSetValues(fragment.getActivity(), chapter);
|
||||
}
|
||||
|
||||
public void setItems(List<Chapter> chapters) {
|
||||
@ -49,4 +49,8 @@ public class ChaptersAdapter extends FlexibleAdapter<ChaptersHolder, Chapter> {
|
||||
boolean onListItemClick(int position);
|
||||
void onListItemLongClick(int position);
|
||||
}
|
||||
|
||||
public MangaChaptersFragment getMangaChaptersFragment() {
|
||||
return (MangaChaptersFragment) fragment;
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ public class CatalogueHolder extends ItemViewHolder<Manga> {
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESULT)
|
||||
.centerCrop()
|
||||
.into(image);
|
||||
} else {
|
||||
image.setImageResource(android.R.color.transparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public class ChaptersHolder extends RecyclerView.ViewHolder implements
|
||||
|
||||
public void onSetValues(Context context, Chapter chapter) {
|
||||
title.setText(chapter.name);
|
||||
download_icon.setImageResource(R.drawable.ic_file_download_black_48dp);
|
||||
|
||||
|
||||
if (chapter.read) {
|
||||
title.setTextColor(ContextCompat.getColor(context, R.color.chapter_read_text));
|
||||
@ -52,6 +52,14 @@ public class ChaptersHolder extends RecyclerView.ViewHolder implements
|
||||
pages.setText("");
|
||||
}
|
||||
|
||||
if (chapter.downloaded == Chapter.UNKNOWN) {
|
||||
adapter.getMangaChaptersFragment().getPresenter().checkIsChapterDownloaded(chapter);
|
||||
}
|
||||
if (chapter.downloaded == Chapter.DOWNLOADED)
|
||||
download_icon.setImageResource(R.drawable.ic_action_delete_36dp);
|
||||
else if (chapter.downloaded == Chapter.NOT_DOWNLOADED)
|
||||
download_icon.setImageResource(R.drawable.ic_file_download_black_36dp);
|
||||
|
||||
toggleActivation();
|
||||
}
|
||||
|
||||
|
BIN
app/src/main/res/drawable-hdpi/ic_action_delete_36dp.png
Normal file
After Width: | Height: | Size: 213 B |
BIN
app/src/main/res/drawable-hdpi/ic_file_download_black_36dp.png
Normal file
After Width: | Height: | Size: 204 B |
BIN
app/src/main/res/drawable-ldpi/ic_action_delete_36dp.png
Normal file
After Width: | Height: | Size: 252 B |
BIN
app/src/main/res/drawable-ldpi/ic_file_download_black_36dp.png
Normal file
After Width: | Height: | Size: 265 B |
BIN
app/src/main/res/drawable-mdpi/ic_action_delete_36dp.png
Normal file
After Width: | Height: | Size: 160 B |
BIN
app/src/main/res/drawable-mdpi/ic_file_download_black_36dp.png
Normal file
After Width: | Height: | Size: 161 B |
BIN
app/src/main/res/drawable-xhdpi/ic_action_delete_36dp.png
Normal file
After Width: | Height: | Size: 201 B |
BIN
app/src/main/res/drawable-xhdpi/ic_file_download_black_36dp.png
Normal file
After Width: | Height: | Size: 200 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_action_delete_36dp.png
Normal file
After Width: | Height: | Size: 298 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_file_download_black_36dp.png
Normal file
After Width: | Height: | Size: 295 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_action_delete_36dp.png
Normal file
After Width: | Height: | Size: 365 B |
After Width: | Height: | Size: 348 B |