commit
5ac7f7057a
2 changed files with 173 additions and 40 deletions
|
@ -21,82 +21,163 @@ import eu.kanade.tachiyomi.data.source.base.Source;
|
||||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
|
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
|
||||||
import nucleus.factory.RequiresPresenter;
|
import nucleus.factory.RequiresPresenter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fragment that shows manga information.
|
||||||
|
* Uses R.layout.fragment_manga_info.
|
||||||
|
* UI related actions should be called from here.
|
||||||
|
*/
|
||||||
@RequiresPresenter(MangaInfoPresenter.class)
|
@RequiresPresenter(MangaInfoPresenter.class)
|
||||||
public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
|
public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
|
||||||
|
/**
|
||||||
|
* SwipeRefreshLayout showing refresh status
|
||||||
|
*/
|
||||||
@Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefresh;
|
@Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefresh;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TextView containing artist information.
|
||||||
|
*/
|
||||||
@Bind(R.id.manga_artist) TextView artist;
|
@Bind(R.id.manga_artist) TextView artist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TextView containing author information.
|
||||||
|
*/
|
||||||
@Bind(R.id.manga_author) TextView author;
|
@Bind(R.id.manga_author) TextView author;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TextView containing chapter count.
|
||||||
|
*/
|
||||||
@Bind(R.id.manga_chapters) TextView chapterCount;
|
@Bind(R.id.manga_chapters) TextView chapterCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TextView containing genres.
|
||||||
|
*/
|
||||||
@Bind(R.id.manga_genres) TextView genres;
|
@Bind(R.id.manga_genres) TextView genres;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TextView containing status (ongoing, finished).
|
||||||
|
*/
|
||||||
@Bind(R.id.manga_status) TextView status;
|
@Bind(R.id.manga_status) TextView status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TextView containing source.
|
||||||
|
*/
|
||||||
@Bind(R.id.manga_source) TextView source;
|
@Bind(R.id.manga_source) TextView source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TextView containing manga summary.
|
||||||
|
*/
|
||||||
@Bind(R.id.manga_summary) TextView description;
|
@Bind(R.id.manga_summary) TextView description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ImageView of cover.
|
||||||
|
*/
|
||||||
@Bind(R.id.manga_cover) ImageView cover;
|
@Bind(R.id.manga_cover) ImageView cover;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ImageView containing manga cover shown as blurred backdrop.
|
||||||
|
*/
|
||||||
@Bind(R.id.backdrop) ImageView backdrop;
|
@Bind(R.id.backdrop) ImageView backdrop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FAB anchored to bottom of top view used to (add / remove) manga (to / from) library.
|
||||||
|
*/
|
||||||
@Bind(R.id.fab_favorite) FloatingActionButton fabFavorite;
|
@Bind(R.id.fab_favorite) FloatingActionButton fabFavorite;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new instance of MangaInfoFragment.
|
||||||
|
*
|
||||||
|
* @return MangaInfoFragment.
|
||||||
|
*/
|
||||||
public static MangaInfoFragment newInstance() {
|
public static MangaInfoFragment newInstance() {
|
||||||
return new MangaInfoFragment();
|
return new MangaInfoFragment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedState) {
|
|
||||||
super.onCreate(savedState);
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment.
|
||||||
View view = inflater.inflate(R.layout.fragment_manga_info, container, false);
|
View view = inflater.inflate(R.layout.fragment_manga_info, container, false);
|
||||||
|
|
||||||
|
// Bind layout objects.
|
||||||
ButterKnife.bind(this, view);
|
ButterKnife.bind(this, view);
|
||||||
|
|
||||||
|
// Set onclickListener to toggle favorite when FAB clicked.
|
||||||
fabFavorite.setOnClickListener(v -> getPresenter().toggleFavorite());
|
fabFavorite.setOnClickListener(v -> getPresenter().toggleFavorite());
|
||||||
|
|
||||||
|
// Set SwipeRefresh to refresh manga data.
|
||||||
swipeRefresh.setOnRefreshListener(this::fetchMangaFromSource);
|
swipeRefresh.setOnRefreshListener(this::fetchMangaFromSource);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if manga is initialized.
|
||||||
|
* If true update view with manga information,
|
||||||
|
* if false fetch manga information
|
||||||
|
*
|
||||||
|
* @param manga manga object containing information about manga.
|
||||||
|
* @param source the source of the manga.
|
||||||
|
*/
|
||||||
public void onNextManga(Manga manga, Source source) {
|
public void onNextManga(Manga manga, Source source) {
|
||||||
if (manga.initialized) {
|
if (manga.initialized) {
|
||||||
|
// Update view.
|
||||||
setMangaInfo(manga, source);
|
setMangaInfo(manga, source);
|
||||||
} else {
|
} else {
|
||||||
// Initialize manga
|
// Initialize manga.
|
||||||
fetchMangaFromSource();
|
fetchMangaFromSource();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the info of the manga
|
* Update the view with manga information.
|
||||||
*
|
*
|
||||||
* @param manga manga object containing information about manga
|
* @param manga manga object containing information about manga.
|
||||||
* @param mangaSource the source of the manga
|
* @param mangaSource the source of the manga.
|
||||||
*/
|
*/
|
||||||
private void setMangaInfo(Manga manga, Source mangaSource) {
|
private void setMangaInfo(Manga manga, Source mangaSource) {
|
||||||
|
// Update artist TextView.
|
||||||
artist.setText(manga.artist);
|
artist.setText(manga.artist);
|
||||||
|
|
||||||
|
// Update author TextView.
|
||||||
author.setText(manga.author);
|
author.setText(manga.author);
|
||||||
|
|
||||||
|
// If manga source is known update source TextView.
|
||||||
if (mangaSource != null) {
|
if (mangaSource != null) {
|
||||||
source.setText(mangaSource.getName());
|
source.setText(mangaSource.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update genres TextView.
|
||||||
genres.setText(manga.genre);
|
genres.setText(manga.genre);
|
||||||
|
|
||||||
|
// Update status TextView.
|
||||||
status.setText(manga.getStatus(getActivity()));
|
status.setText(manga.getStatus(getActivity()));
|
||||||
|
|
||||||
|
// Update description TextView.
|
||||||
description.setText(manga.description);
|
description.setText(manga.description);
|
||||||
|
|
||||||
|
// Set the favorite drawable to the correct one.
|
||||||
setFavoriteDrawable(manga.favorite);
|
setFavoriteDrawable(manga.favorite);
|
||||||
|
|
||||||
|
// Initialize CoverCache and Glide headers to retrieve cover information.
|
||||||
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) {
|
|
||||||
|
// Check if thumbnail_url is given.
|
||||||
|
if (manga.thumbnail_url != null) {
|
||||||
|
// Check if cover is already drawn.
|
||||||
|
if (cover.getDrawable() == null) {
|
||||||
|
// If manga is in library then (download / save) (from / to) local cache if available,
|
||||||
|
// else download from network.
|
||||||
if (manga.favorite) {
|
if (manga.favorite) {
|
||||||
coverCache.saveOrLoadFromCache(cover, manga.thumbnail_url, headers);
|
coverCache.saveOrLoadFromCache(cover, manga.thumbnail_url, headers);
|
||||||
} else {
|
} else {
|
||||||
coverCache.loadFromNetwork(cover, manga.thumbnail_url, headers);
|
coverCache.loadFromNetwork(cover, manga.thumbnail_url, headers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (manga.thumbnail_url != null && backdrop.getDrawable() == null) {
|
// Check if backdrop is already drawn.
|
||||||
|
if (backdrop.getDrawable() == null) {
|
||||||
|
// If manga is in library then (download / save) (from / to) local cache if available,
|
||||||
|
// else download from network.
|
||||||
if (manga.favorite) {
|
if (manga.favorite) {
|
||||||
coverCache.saveOrLoadFromCache(backdrop, manga.thumbnail_url, headers);
|
coverCache.saveOrLoadFromCache(backdrop, manga.thumbnail_url, headers);
|
||||||
} else {
|
} else {
|
||||||
|
@ -104,31 +185,59 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update chapter count TextView.
|
||||||
|
*
|
||||||
|
* @param count number of chapters.
|
||||||
|
*/
|
||||||
public void setChapterCount(int count) {
|
public void setChapterCount(int count) {
|
||||||
chapterCount.setText(String.valueOf(count));
|
chapterCount.setText(String.valueOf(count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update FAB with correct drawable.
|
||||||
|
*
|
||||||
|
* @param isFavorite determines if manga is favorite or not.
|
||||||
|
*/
|
||||||
private void setFavoriteDrawable(boolean isFavorite) {
|
private void setFavoriteDrawable(boolean isFavorite) {
|
||||||
|
// Set the Favorite drawable to the correct one.
|
||||||
|
// Border drawable if false, filled drawable if true.
|
||||||
fabFavorite.setImageDrawable(ContextCompat.getDrawable(getContext(), isFavorite ?
|
fabFavorite.setImageDrawable(ContextCompat.getDrawable(getContext(), isFavorite ?
|
||||||
R.drawable.ic_bookmark_white_24dp :
|
R.drawable.ic_bookmark_white_24dp :
|
||||||
R.drawable.ic_bookmark_border_white_24dp));
|
R.drawable.ic_bookmark_border_white_24dp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start fetching manga information from source.
|
||||||
|
*/
|
||||||
private void fetchMangaFromSource() {
|
private void fetchMangaFromSource() {
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
|
// Call presenter and start fetching manga information
|
||||||
getPresenter().fetchMangaFromSource();
|
getPresenter().fetchMangaFromSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update swipeRefresh to stop showing refresh in progress spinner.
|
||||||
|
*/
|
||||||
public void onFetchMangaDone() {
|
public void onFetchMangaDone() {
|
||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update swipeRefresh to start showing refresh in progress spinner.
|
||||||
|
*/
|
||||||
public void onFetchMangaError() {
|
public void onFetchMangaError() {
|
||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set swipeRefresh status.
|
||||||
|
*
|
||||||
|
* @param value status of manga fetch.
|
||||||
|
*/
|
||||||
private void setRefreshing(boolean value) {
|
private void setRefreshing(boolean value) {
|
||||||
swipeRefresh.setRefreshing(value);
|
swipeRefresh.setRefreshing(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,42 +19,55 @@ import rx.Observable;
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
import rx.android.schedulers.AndroidSchedulers;
|
||||||
import rx.schedulers.Schedulers;
|
import rx.schedulers.Schedulers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Presenter of MangaInfoFragment.
|
||||||
|
* Contains information and data for fragment.
|
||||||
|
* Observable updates should be called from here.
|
||||||
|
*/
|
||||||
public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The id of the restartable.
|
* The id of the restartable.
|
||||||
*/
|
*/
|
||||||
private static final int GET_MANGA = 1;
|
private static final int GET_MANGA = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The id of the restartable.
|
* The id of the restartable.
|
||||||
*/
|
*/
|
||||||
private static final int GET_CHAPTER_COUNT = 2;
|
private static final int GET_CHAPTER_COUNT = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The id of the restartable.
|
* The id of the restartable.
|
||||||
*/
|
*/
|
||||||
private static final int FETCH_MANGA_INFO = 3;
|
private static final int FETCH_MANGA_INFO = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Source information
|
* Source information.
|
||||||
*/
|
*/
|
||||||
protected Source source;
|
protected Source source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to connect to database
|
* Used to connect to database.
|
||||||
*/
|
*/
|
||||||
@Inject DatabaseHelper db;
|
@Inject DatabaseHelper db;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to connect to different manga sources
|
* Used to connect to different manga sources.
|
||||||
*/
|
*/
|
||||||
@Inject SourceManager sourceManager;
|
@Inject SourceManager sourceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to connect to cache
|
* Used to connect to cache.
|
||||||
*/
|
*/
|
||||||
@Inject CoverCache coverCache;
|
@Inject CoverCache coverCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selected manga information
|
* Selected manga information.
|
||||||
*/
|
*/
|
||||||
private Manga manga;
|
private Manga manga;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count of chapters
|
* Count of chapters.
|
||||||
*/
|
*/
|
||||||
private int count = -1;
|
private int count = -1;
|
||||||
|
|
||||||
|
@ -62,23 +75,23 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
||||||
protected void onCreate(Bundle savedState) {
|
protected void onCreate(Bundle savedState) {
|
||||||
super.onCreate(savedState);
|
super.onCreate(savedState);
|
||||||
|
|
||||||
// Notify the view a manga is available or has changed
|
// Notify the view a manga is available or has changed.
|
||||||
startableLatestCache(GET_MANGA,
|
startableLatestCache(GET_MANGA,
|
||||||
() -> Observable.just(manga),
|
() -> Observable.just(manga),
|
||||||
(view, manga) -> view.onNextManga(manga, source));
|
(view, manga) -> view.onNextManga(manga, source));
|
||||||
|
|
||||||
// Update chapter count
|
// Update chapter count.
|
||||||
startableLatestCache(GET_CHAPTER_COUNT,
|
startableLatestCache(GET_CHAPTER_COUNT,
|
||||||
() -> Observable.just(count),
|
() -> Observable.just(count),
|
||||||
MangaInfoFragment::setChapterCount);
|
MangaInfoFragment::setChapterCount);
|
||||||
|
|
||||||
// Fetch manga info from source
|
// Fetch manga info from source.
|
||||||
startableFirst(FETCH_MANGA_INFO,
|
startableFirst(FETCH_MANGA_INFO,
|
||||||
this::fetchMangaObs,
|
this::fetchMangaObs,
|
||||||
(view, manga) -> view.onFetchMangaDone(),
|
(view, manga) -> view.onFetchMangaDone(),
|
||||||
(view, error) -> view.onFetchMangaError());
|
(view, error) -> view.onFetchMangaError());
|
||||||
|
|
||||||
// Listen for events
|
// Listen for events.
|
||||||
registerForEvents();
|
registerForEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,12 +112,13 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
||||||
public void onEvent(ChapterCountEvent event) {
|
public void onEvent(ChapterCountEvent event) {
|
||||||
if (count != event.getCount()) {
|
if (count != event.getCount()) {
|
||||||
count = event.getCount();
|
count = event.getCount();
|
||||||
|
// Update chapter count
|
||||||
start(GET_CHAPTER_COUNT);
|
start(GET_CHAPTER_COUNT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch manga info from source
|
* Fetch manga information from source.
|
||||||
*/
|
*/
|
||||||
public void fetchMangaFromSource() {
|
public void fetchMangaFromSource() {
|
||||||
if (isUnsubscribed(FETCH_MANGA_INFO)) {
|
if (isUnsubscribed(FETCH_MANGA_INFO)) {
|
||||||
|
@ -112,6 +126,11 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch manga information from source.
|
||||||
|
*
|
||||||
|
* @return manga information.
|
||||||
|
*/
|
||||||
private Observable<Manga> fetchMangaObs() {
|
private Observable<Manga> fetchMangaObs() {
|
||||||
return source.pullMangaFromNetwork(manga.url)
|
return source.pullMangaFromNetwork(manga.url)
|
||||||
.flatMap(networkManga -> {
|
.flatMap(networkManga -> {
|
||||||
|
@ -124,6 +143,9 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
||||||
.doOnNext(manga -> refreshManga());
|
.doOnNext(manga -> refreshManga());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update favorite status of manga, (removes / adds) manga (to / from) library.
|
||||||
|
*/
|
||||||
public void toggleFavorite() {
|
public void toggleFavorite() {
|
||||||
manga.favorite = !manga.favorite;
|
manga.favorite = !manga.favorite;
|
||||||
onMangaFavoriteChange(manga.favorite);
|
onMangaFavoriteChange(manga.favorite);
|
||||||
|
@ -132,7 +154,11 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Removes / Saves) cover depending on favorite status.
|
||||||
|
*
|
||||||
|
* @param isFavorite determines if manga is favorite or not.
|
||||||
|
*/
|
||||||
private void onMangaFavoriteChange(boolean isFavorite) {
|
private void onMangaFavoriteChange(boolean isFavorite) {
|
||||||
if (isFavorite) {
|
if (isFavorite) {
|
||||||
coverCache.save(manga.thumbnail_url, source.getGlideHeaders());
|
coverCache.save(manga.thumbnail_url, source.getGlideHeaders());
|
||||||
|
@ -141,12 +167,10 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Manga getManga() {
|
/**
|
||||||
return manga;
|
* Refresh MangaInfo view.
|
||||||
}
|
*/
|
||||||
|
private void refreshManga() {
|
||||||
// Used to refresh the view
|
|
||||||
protected void refreshManga() {
|
|
||||||
start(GET_MANGA);
|
start(GET_MANGA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue