Improve info fragment. Other minor changes and fixes.

This commit is contained in:
inorichi 2015-12-05 02:05:42 +01:00
parent c52c567eae
commit eb10d77374
8 changed files with 328 additions and 274 deletions

View file

@ -25,7 +25,6 @@ import eu.kanade.mangafeed.data.source.model.Page;
import eu.kanade.mangafeed.event.DownloadChaptersEvent; import eu.kanade.mangafeed.event.DownloadChaptersEvent;
import eu.kanade.mangafeed.util.DiskUtils; import eu.kanade.mangafeed.util.DiskUtils;
import eu.kanade.mangafeed.util.DynamicConcurrentMergeOperator; import eu.kanade.mangafeed.util.DynamicConcurrentMergeOperator;
import eu.kanade.mangafeed.util.UrlUtil;
import rx.Observable; import rx.Observable;
import rx.Subscription; import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers; import rx.android.schedulers.AndroidSchedulers;
@ -182,8 +181,10 @@ public class DownloadManager {
return pageListObservable return pageListObservable
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.doOnNext(pages -> download.setStatus(Download.DOWNLOADING)) .doOnNext(pages -> {
.doOnNext(pages -> download.downloadedImages = 0) download.downloadedImages = 0;
download.setStatus(Download.DOWNLOADING);
})
// Get all the URLs to the source images, fetch pages if necessary // Get all the URLs to the source images, fetch pages if necessary
.flatMap(download.source::getAllImageUrlsFromPageList) .flatMap(download.source::getAllImageUrlsFromPageList)
// Start downloading images, consider we can have downloaded images already // Start downloading images, consider we can have downloaded images already
@ -263,10 +264,8 @@ public class DownloadManager {
// Get the filename for an image given the page // Get the filename for an image given the page
private String getImageFilename(Page page) { private String getImageFilename(Page page) {
String url = UrlUtil.getPath(page.getImageUrl()); String url = page.getImageUrl();
return url.substring( return url.substring(url.lastIndexOf("/") + 1, url.length());
url.lastIndexOf("/") + 1,
url.length());
} }
private boolean isImageDownloaded(File imagePath) { private boolean isImageDownloaded(File imagePath) {

View file

@ -39,10 +39,10 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter> {
public final static String SOURCE_ID = "source_id"; public final static String SOURCE_ID = "source_id";
public static CatalogueFragment newInstance(int source_id) { public static CatalogueFragment newInstance(int sourceId) {
CatalogueFragment fragment = new CatalogueFragment(); CatalogueFragment fragment = new CatalogueFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putInt(SOURCE_ID, source_id); args.putInt(SOURCE_ID, sourceId);
fragment.setArguments(args); fragment.setArguments(args);
return fragment; return fragment;
} }

View file

@ -105,6 +105,9 @@ public class DownloadPresenter extends BasePresenter<DownloadFragment> {
} }
private void observePagesStatus(Download download, DownloadFragment view) { private void observePagesStatus(Download download, DownloadFragment view) {
// Initial update of the downloaded pages
view.updateDownloadedPages(download);
PublishSubject<Integer> pageStatusSubject = PublishSubject.create(); PublishSubject<Integer> pageStatusSubject = PublishSubject.create();
for (Page page : download.pages) { for (Page page : download.pages) {
if (page.getStatus() != Page.READY) if (page.getStatus() != Page.READY)

View file

@ -17,7 +17,6 @@ public class MangaPresenter extends BasePresenter<MangaActivity> {
@Inject DatabaseHelper db; @Inject DatabaseHelper db;
private long mangaId; private long mangaId;
private Manga manga;
private static final int DB_MANGA = 1; private static final int DB_MANGA = 1;
@ -25,19 +24,13 @@ public class MangaPresenter extends BasePresenter<MangaActivity> {
protected void onCreate(Bundle savedState) { protected void onCreate(Bundle savedState) {
super.onCreate(savedState); super.onCreate(savedState);
restartableLatestCache(DB_MANGA, restartableLatestCache(DB_MANGA, this::getDbMangaObservable, MangaActivity::setManga);
() -> getDbMangaObservable()
.doOnNext(manga -> this.manga = manga),
(view, manga) -> {
view.setManga(manga);
EventBus.getDefault().postSticky(manga);
});
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
// Avoid fragments receiving wrong manga // Avoid new instances receiving wrong manga
EventBus.getDefault().removeStickyEvent(Manga.class); EventBus.getDefault().removeStickyEvent(Manga.class);
} }
@ -45,7 +38,8 @@ public class MangaPresenter extends BasePresenter<MangaActivity> {
return db.getManga(mangaId).createObservable() return db.getManga(mangaId).createObservable()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.flatMap(Observable::from) .flatMap(Observable::from)
.observeOn(AndroidSchedulers.mainThread()); .observeOn(AndroidSchedulers.mainThread())
.doOnNext(manga -> EventBus.getDefault().postSticky(manga));
} }
public void queryManga(long mangaId) { public void queryManga(long mangaId) {

View file

@ -56,53 +56,39 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
restartableLatestCache(DB_CHAPTERS, restartableLatestCache(DB_CHAPTERS,
this::getDbChaptersObs, this::getDbChaptersObs,
ChaptersFragment::onNextChapters ChaptersFragment::onNextChapters);
);
restartableLatestCache(FETCH_CHAPTERS, restartableFirst(FETCH_CHAPTERS,
this::getOnlineChaptersObs, this::getOnlineChaptersObs,
(view, result) -> view.onFetchChaptersDone(), (view, result) -> view.onFetchChaptersDone(),
(view, error) -> view.onFetchChaptersError() (view, error) -> view.onFetchChaptersError());
);
restartableLatestCache(CHAPTER_STATUS_CHANGES, restartableLatestCache(CHAPTER_STATUS_CHANGES,
this::getChapterStatusObs, this::getChapterStatusObs,
(view, download) -> view.onChapterStatusChange(download), (view, download) -> view.onChapterStatusChange(download),
(view, error) -> Timber.e(error.getCause(), error.getMessage()) (view, error) -> Timber.e(error.getCause(), error.getMessage()));
);
}
@Override
protected void onTakeView(ChaptersFragment view) {
super.onTakeView(view);
registerForStickyEvents(); registerForStickyEvents();
} }
@Override
protected void onDropView() {
unregisterForEvents();
super.onDropView();
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
unregisterForEvents();
EventBus.getDefault().removeStickyEvent(ChapterCountEvent.class); EventBus.getDefault().removeStickyEvent(ChapterCountEvent.class);
super.onDestroy(); super.onDestroy();
} }
@EventBusHook @EventBusHook
public void onEventMainThread(Manga manga) { public void onEventMainThread(Manga manga) {
if (this.manga != null)
return;
this.manga = manga; this.manga = manga;
if (!isStarted(DB_CHAPTERS)) {
source = sourceManager.get(manga.source); source = sourceManager.get(manga.source);
start(DB_CHAPTERS); start(DB_CHAPTERS);
add(db.getChapters(manga).createObservable() add(db.getChapters(manga).createObservable()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.doOnNext(chapters -> { .doOnNext(chapters -> {
stop(CHAPTER_STATUS_CHANGES);
this.chapters = chapters; this.chapters = chapters;
EventBus.getDefault().postSticky(new ChapterCountEvent(chapters.size())); EventBus.getDefault().postSticky(new ChapterCountEvent(chapters.size()));
for (Chapter chapter : chapters) { for (Chapter chapter : chapters) {
@ -112,6 +98,7 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
}) })
.subscribe(chaptersSubject::onNext)); .subscribe(chaptersSubject::onNext));
} }
}
public void fetchChaptersFromSource() { public void fetchChaptersFromSource() {
hasRequested = true; hasRequested = true;

View file

@ -1,6 +1,7 @@
package eu.kanade.mangafeed.ui.manga.info; package eu.kanade.mangafeed.ui.manga.info;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -21,13 +22,15 @@ import nucleus.factory.RequiresPresenter;
@RequiresPresenter(MangaInfoPresenter.class) @RequiresPresenter(MangaInfoPresenter.class)
public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> { public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
@Bind(R.id.manga_artist) TextView mArtist; @Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefresh;
@Bind(R.id.manga_author) TextView mAuthor;
@Bind(R.id.manga_chapters) TextView mChapters; @Bind(R.id.manga_artist) TextView artist;
@Bind(R.id.manga_genres) TextView mGenres; @Bind(R.id.manga_author) TextView author;
@Bind(R.id.manga_status) TextView mStatus; @Bind(R.id.manga_chapters) TextView chapterCount;
@Bind(R.id.manga_summary) TextView mDescription; @Bind(R.id.manga_genres) TextView genres;
@Bind(R.id.manga_cover) ImageView mCover; @Bind(R.id.manga_status) TextView status;
@Bind(R.id.manga_summary) TextView description;
@Bind(R.id.manga_cover) ImageView cover;
@Bind(R.id.action_favorite) Button favoriteBtn; @Bind(R.id.action_favorite) Button favoriteBtn;
@ -52,37 +55,63 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
favoriteBtn.setOnClickListener(v -> { favoriteBtn.setOnClickListener(v -> {
getPresenter().toggleFavorite(); getPresenter().toggleFavorite();
}); });
swipeRefresh.setOnRefreshListener(this::fetchMangaFromSource);
return view; return view;
} }
public void setMangaInfo(Manga manga) { public void onNextManga(Manga manga) {
mArtist.setText(manga.artist); if (manga.initialized) {
mAuthor.setText(manga.author); setMangaInfo(manga);
mGenres.setText(manga.genre); } else {
mStatus.setText("Ongoing"); //TODO // Initialize manga
mDescription.setText(manga.description); fetchMangaFromSource();
}
}
private void setMangaInfo(Manga manga) {
artist.setText(manga.artist);
author.setText(manga.author);
genres.setText(manga.genre);
status.setText("Ongoing"); //TODO
description.setText(manga.description);
setFavoriteText(manga.favorite); setFavoriteText(manga.favorite);
if (mCover.getDrawable() == null) {
CoverCache coverCache = getPresenter().coverCache; CoverCache coverCache = getPresenter().coverCache;
LazyHeaders headers = getPresenter().source.getGlideHeaders(); LazyHeaders headers = getPresenter().source.getGlideHeaders();
if (manga.thumbnail_url != null && cover.getDrawable() == null) {
if (manga.favorite) { if (manga.favorite) {
coverCache.saveAndLoadFromCache(mCover, manga.thumbnail_url, headers); coverCache.saveAndLoadFromCache(cover, manga.thumbnail_url, headers);
} else { } else {
coverCache.loadFromNetwork(mCover, manga.thumbnail_url, headers); coverCache.loadFromNetwork(cover, manga.thumbnail_url, headers);
} }
cover.setTag(manga.thumbnail_url);
} }
} }
public void setChapterCount(int count) { public void setChapterCount(int count) {
mChapters.setText(String.valueOf(count)); chapterCount.setText(String.valueOf(count));
} }
public void setFavoriteText(boolean isFavorite) { public void setFavoriteText(boolean isFavorite) {
favoriteBtn.setText(!isFavorite ? R.string.add_to_library : R.string.remove_from_library); favoriteBtn.setText(!isFavorite ? R.string.add_to_library : R.string.remove_from_library);
} }
private void fetchMangaFromSource() {
setRefreshing(true);
getPresenter().fetchMangaFromSource();
}
public void onFetchMangaDone() {
setRefreshing(false);
}
public void onFetchMangaError() {
setRefreshing(false);
}
private void setRefreshing(boolean value) {
swipeRefresh.setRefreshing(value);
}
} }

View file

@ -13,6 +13,8 @@ import eu.kanade.mangafeed.event.ChapterCountEvent;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter; import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import eu.kanade.mangafeed.util.EventBusHook; import eu.kanade.mangafeed.util.EventBusHook;
import rx.Observable; import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> { public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
@ -24,8 +26,11 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
protected Source source; protected Source source;
private int count = -1; private int count = -1;
private boolean isFetching;
private static final int GET_MANGA = 1; private static final int GET_MANGA = 1;
private static final int GET_CHAPTER_COUNT = 2; private static final int GET_CHAPTER_COUNT = 2;
private static final int FETCH_MANGA_INFO = 3;
@Override @Override
protected void onCreate(Bundle savedState) { protected void onCreate(Bundle savedState) {
@ -33,23 +38,24 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
restartableLatestCache(GET_MANGA, restartableLatestCache(GET_MANGA,
() -> Observable.just(manga), () -> Observable.just(manga),
MangaInfoFragment::setMangaInfo); MangaInfoFragment::onNextManga);
restartableLatestCache(GET_CHAPTER_COUNT, restartableLatestCache(GET_CHAPTER_COUNT,
() -> Observable.just(count), () -> Observable.just(count),
MangaInfoFragment::setChapterCount); MangaInfoFragment::setChapterCount);
}
@Override restartableFirst(FETCH_MANGA_INFO,
protected void onTakeView(MangaInfoFragment view) { this::fetchMangaObs,
super.onTakeView(view); (view, manga) -> view.onFetchMangaDone(),
(view, error) -> view.onFetchMangaError());
registerForStickyEvents(); registerForStickyEvents();
} }
@Override @Override
protected void onDropView() { protected void onDestroy() {
unregisterForEvents(); unregisterForEvents();
super.onDropView(); super.onDestroy();
} }
@EventBusHook @EventBusHook
@ -67,9 +73,23 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
} }
} }
public void initFavoriteText() { public void fetchMangaFromSource() {
if (getView() != null) if (!isFetching) {
getView().setFavoriteText(manga.favorite); isFetching = true;
start(FETCH_MANGA_INFO);
}
}
private Observable<Manga> fetchMangaObs() {
return source.pullMangaFromNetwork(manga.url)
.flatMap(networkManga -> {
Manga.copyFromNetwork(manga, networkManga);
db.insertManga(manga).executeAsBlocking();
return Observable.just(manga);
})
.finallyDo(() -> isFetching = false)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
} }
public void toggleFavorite() { public void toggleFavorite() {

View file

@ -3,10 +3,28 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical" android:orientation="vertical"
tools:context="eu.kanade.mangafeed.ui.catalogue.CatalogueFragment"> tools:context="eu.kanade.mangafeed.ui.catalogue.CatalogueFragment">
<!-- It seems I have to wrap everything in SwipeRefreshLayout because it always take the entire height
and the description can't be seen.
Maybe with Relative layout it's better. We shouldn't put this layout inside the description layout
because the description should be scrollable and gestures could conflict with this layout.
Leaving it like this for now.
-->
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:orientation="vertical">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -206,3 +224,7 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>