package com.jparkie.aizoban.presenters; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.app.FragmentActivity; import android.util.Pair; import com.jparkie.aizoban.BuildConfig; import com.jparkie.aizoban.controllers.AizobanManager; import com.jparkie.aizoban.controllers.QueryManager; import com.jparkie.aizoban.controllers.events.ChapterQueryEvent; import com.jparkie.aizoban.controllers.factories.DefaultFactory; import com.jparkie.aizoban.models.Chapter; import com.jparkie.aizoban.models.Manga; import com.jparkie.aizoban.models.databases.RecentChapter; import com.jparkie.aizoban.presenters.mapper.MangaMapper; import com.jparkie.aizoban.utils.wrappers.RequestWrapper; import com.jparkie.aizoban.views.MangaView; import com.jparkie.aizoban.views.activities.ChapterActivity; import com.jparkie.aizoban.views.activities.MangaActivity; import com.jparkie.aizoban.views.adapters.ChapterListingsAdapter; import com.jparkie.aizoban.views.fragments.AddToQueueFragment; import com.jparkie.aizoban.views.fragments.MarkReadFragment; import com.jparkie.aizoban.views.fragments.ResumeChapterFragment; import java.util.List; import de.greenrobot.event.EventBus; import rx.Observable; import rx.Observer; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Func1; import rx.functions.Func2; import rx.functions.Func3; import rx.schedulers.Schedulers; public class MangaPresenterOnlineImpl implements MangaPresenter { public static final String TAG = MangaPresenterOnlineImpl.class.getSimpleName(); private static final String REQUEST_PARCELABLE_KEY = TAG + ":" + "RequestParcelableKey"; private static final String INITIALIZED_PARCELABLE_KEY = TAG + ":" + "InitializedParcelableKey"; private static final String POSITION_PARCELABLE_KEY = TAG + ":" + "PositionParcelableKey"; private MangaView mMangaView; private MangaMapper mMangaMapper; private ChapterListingsAdapter mChapterListingsAdapter; private RequestWrapper mRequest; private Manga mManga; private com.jparkie.aizoban.models.databases.FavouriteManga mFavouriteManga; private boolean mInitialized; private Parcelable mPositionSavedState; private Subscription mQueryBothMangaAndChaptersSubscription; private Subscription mQueryFavouriteMangaSubscription; private Subscription mUpdateSubscription; private Subscription mQueryRecentChapterSubscription; public MangaPresenterOnlineImpl(MangaView mangaView, MangaMapper mangaMapper) { mMangaView = mangaView; mMangaMapper = mangaMapper; } @Override public void handleInitialArguments(Intent arguments) { if (arguments != null) { if (arguments.hasExtra(MangaActivity.REQUEST_ARGUMENT_KEY)) { mRequest = arguments.getParcelableExtra(MangaActivity.REQUEST_ARGUMENT_KEY); arguments.removeExtra(MangaActivity.REQUEST_ARGUMENT_KEY); } } } @Override public void initializeViews() { mMangaView.initializeToolbar(); mMangaView.initializeSwipeRefreshLayout(); mMangaView.initializeAbsListView(); mMangaView.initializeEmptyRelativeLayout(); } @Override public void initializeDataFromUrl() { mChapterListingsAdapter = new ChapterListingsAdapter(mMangaView.getContext()); mMangaMapper.registerAdapter(mChapterListingsAdapter); initializeFavouriteManga(); if (!mInitialized) { updateDataFromUrl(); } } @Override public void registerForEvents() { EventBus.getDefault().register(this); } public void onEventMainThread(ChapterQueryEvent event) { if (event != null) { queryBothMangaAndChaptersFromUrl(); } } @Override public void unregisterForEvents() { EventBus.getDefault().unregister(this); } @Override public void onResume() { queryBothMangaAndChaptersFromUrl(); } @Override public void saveState(Bundle outState) { if (mRequest != null) { outState.putParcelable(REQUEST_PARCELABLE_KEY, mRequest); } outState.putBoolean(INITIALIZED_PARCELABLE_KEY, mInitialized); if (mMangaMapper.getPositionState() != null) { outState.putParcelable(POSITION_PARCELABLE_KEY, mMangaMapper.getPositionState()); } } @Override public void restoreState(Bundle savedState) { if (savedState.containsKey(REQUEST_PARCELABLE_KEY)) { mRequest = savedState.getParcelable(REQUEST_PARCELABLE_KEY); savedState.remove(REQUEST_PARCELABLE_KEY); } if (savedState.containsKey(INITIALIZED_PARCELABLE_KEY)) { mInitialized = savedState.getBoolean(INITIALIZED_PARCELABLE_KEY, false); savedState.remove(INITIALIZED_PARCELABLE_KEY); } if (savedState.containsKey(POSITION_PARCELABLE_KEY)) { mPositionSavedState = savedState.getParcelable(POSITION_PARCELABLE_KEY); savedState.remove(POSITION_PARCELABLE_KEY); } } @Override public void destroyAllSubscriptions() { if (mQueryBothMangaAndChaptersSubscription != null) { mQueryBothMangaAndChaptersSubscription.unsubscribe(); mQueryBothMangaAndChaptersSubscription = null; } if (mQueryFavouriteMangaSubscription != null) { mQueryFavouriteMangaSubscription.unsubscribe(); mQueryFavouriteMangaSubscription = null; } if (mUpdateSubscription != null) { mUpdateSubscription.unsubscribe(); mUpdateSubscription = null; } if (mQueryRecentChapterSubscription != null) { mQueryRecentChapterSubscription.unsubscribe(); mQueryRecentChapterSubscription = null; } } @Override public void releaseAllResources() { if (mChapterListingsAdapter != null) { mChapterListingsAdapter.setCursor(null); mChapterListingsAdapter = null; } } @Override public void onApplyColorChange(int color) { if (mChapterListingsAdapter != null) { mChapterListingsAdapter.setColor(color); } } @Override public void onSwipeRefresh() { updateDataFromUrl(); } @Override public void onChapterClick(int position) { if (mChapterListingsAdapter != null) { Chapter selectedChapter = (Chapter) mChapterListingsAdapter.getItem(position); if (selectedChapter != null) { String chapterSource = selectedChapter.getSource(); String chapterUrl = selectedChapter.getUrl(); final RequestWrapper chapterRequest = new RequestWrapper(chapterSource, chapterUrl); if (mQueryRecentChapterSubscription != null) { mQueryRecentChapterSubscription.unsubscribe(); mQueryRecentChapterSubscription = null; } mQueryRecentChapterSubscription = QueryManager .queryRecentChapterFromRequest(chapterRequest, false) .map(new Func1<Cursor, Cursor>() { @Override public Cursor call(Cursor incomingCursor) { if (incomingCursor != null && incomingCursor.getCount() != 0) { return incomingCursor; } return null; } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Cursor>() { @Override public void onCompleted() { // Do Nothing. } @Override public void onError(Throwable e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } @Override public void onNext(Cursor recentChapterCursor) { if (recentChapterCursor != null) { RecentChapter recentChapter = QueryManager.toObject(recentChapterCursor, RecentChapter.class); if (recentChapter != null) { if (((FragmentActivity) mMangaView.getContext()).getSupportFragmentManager().findFragmentByTag(ResumeChapterFragment.TAG) == null) { ResumeChapterFragment resumeChapterFragment = ResumeChapterFragment.newInstance(recentChapter); resumeChapterFragment.show(((FragmentActivity) mMangaView.getContext()).getSupportFragmentManager(), ResumeChapterFragment.TAG); } return; } } Intent chapterIntent = ChapterActivity.constructOnlineChapterActivityIntent(mMangaView.getContext(), chapterRequest, 0); mMangaView.getContext().startActivity(chapterIntent); } }); } } } @Override public void onFavourite() { try { if (mManga != null) { if (mFavouriteManga != null) { QueryManager.deleteObjectToApplicationDatabase(mFavouriteManga); mFavouriteManga = null; mMangaView.setFavouriteButton(false); } else { mFavouriteManga = DefaultFactory.FavouriteManga.constructDefault(); mFavouriteManga.setSource(mManga.getSource()); mFavouriteManga.setUrl(mManga.getUrl()); mFavouriteManga.setName(mManga.getName()); mFavouriteManga.setThumbnailUrl(mManga.getThumbnailUrl()); QueryManager.putObjectToApplicationDatabase(mFavouriteManga); mMangaView.setFavouriteButton(true); } } } catch (Throwable e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } } @Override public void onOptionRefresh() { updateDataFromUrl(); mMangaView.scrollToTop(); } @Override public void onOptionMarkRead() { if (((FragmentActivity) mMangaView.getContext()).getSupportFragmentManager().findFragmentByTag(MarkReadFragment.TAG) == null) { MarkReadFragment markReadFragment = MarkReadFragment.newOnlineInstance(mRequest); markReadFragment.show(((FragmentActivity) mMangaView.getContext()).getSupportFragmentManager(), MarkReadFragment.TAG); } } @Override public void onOptionDownload() { if (((FragmentActivity) mMangaView.getContext()).getSupportFragmentManager().findFragmentByTag(AddToQueueFragment.TAG) == null) { AddToQueueFragment addToQueueFragment = AddToQueueFragment.newInstance(mRequest); addToQueueFragment.show(((FragmentActivity) mMangaView.getContext()).getSupportFragmentManager(), AddToQueueFragment.TAG); } } @Override public void onOptionToTop() { mMangaView.scrollToTop(); } @Override public void onOptionDelete() { // Do Nothing. } @Override public void onOptionSelectAll() { // Do Nothing. } @Override public void onOptionClear() { // Do Nothing. } private void initializeFavouriteManga() { if (mQueryFavouriteMangaSubscription != null) { mQueryFavouriteMangaSubscription.unsubscribe(); mQueryFavouriteMangaSubscription = null; } if (mRequest != null) { mQueryFavouriteMangaSubscription = QueryManager .queryFavouriteMangaFromRequest(mRequest) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Cursor>() { @Override public void onCompleted() { if (mFavouriteManga != null) { mMangaView.initializeFavouriteButton(true); } else { mMangaView.initializeFavouriteButton(false); } } @Override public void onError(Throwable e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } @Override public void onNext(Cursor favouriteCursor) { if (favouriteCursor != null && favouriteCursor.getCount() != 0) { mFavouriteManga = QueryManager.toObject(favouriteCursor, com.jparkie.aizoban.models.databases.FavouriteManga.class); } } }); } } private void queryBothMangaAndChaptersFromUrl() { if (mQueryBothMangaAndChaptersSubscription != null) { mQueryBothMangaAndChaptersSubscription.unsubscribe(); mQueryBothMangaAndChaptersSubscription = null; } if (mRequest != null) { Observable<Cursor> queryMangaFromUrlObservable = QueryManager .queryMangaFromRequest(mRequest); Observable<Cursor> queryChaptersFromUrlObservable = QueryManager .queryChaptersOfMangaFromRequest(mRequest, false); Observable<List<String>> queryRecentChapterUrlsObservable = QueryManager .queryRecentChaptersOfMangaFromRequest(mRequest, false) .flatMap(new Func1<Cursor, Observable<RecentChapter>>() { @Override public Observable<RecentChapter> call(Cursor recentChaptersCursor) { List<RecentChapter> recentChapters = QueryManager.toList(recentChaptersCursor, RecentChapter.class); return Observable.from(recentChapters.toArray(new RecentChapter[recentChapters.size()])); } }) .flatMap(new Func1<RecentChapter, Observable<String>>() { @Override public Observable<String> call(RecentChapter recentChapter) { return Observable.just(recentChapter.getUrl()); } }) .toList(); mQueryBothMangaAndChaptersSubscription = Observable.zip(queryMangaFromUrlObservable, queryChaptersFromUrlObservable, queryRecentChapterUrlsObservable, new Func3<Cursor, Cursor, List<String>, Pair<Pair<Cursor, Cursor>, List<String>>>() { @Override public Pair<Pair<Cursor, Cursor>, List<String>> call(Cursor mangaCursor, Cursor chaptersCursor, List<String> recentChapterUrls) { Pair<Cursor, Cursor> cursorPair = Pair.create(mangaCursor, chaptersCursor); return Pair.create(cursorPair, recentChapterUrls); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Pair<Pair<Cursor, Cursor>, List<String>>>() { @Override public void onCompleted() { if (mManga != null) { if (mManga.isInitialized()) { mMangaView.setTitle(mManga.getName()); mMangaView.setName(mManga.getName()); mMangaView.setDescription(mManga.getDescription()); mMangaView.setAuthor(mManga.getAuthor()); mMangaView.setArtist(mManga.getArtist()); mMangaView.setGenre(mManga.getGenre()); mMangaView.setIsCompleted(mManga.isCompleted()); mMangaView.setThumbnail(mManga.getThumbnailUrl()); mMangaView.hideEmptyRelativeLayout(); mMangaView.showListViewIfHidden(); } } restorePosition(); } @Override public void onError(Throwable e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } @Override public void onNext(Pair<Pair<Cursor, Cursor>, List<String>> pairListPair) { Pair<Cursor, Cursor> cursorPair = pairListPair.first; List<String> recentChapterUrls = pairListPair.second; if (cursorPair != null) { Cursor mangaCursor = cursorPair.first; if (mangaCursor != null && mangaCursor.getCount() != 0) { mManga = QueryManager.toObject(mangaCursor, Manga.class); } Cursor chaptersCursor = cursorPair.second; if (chaptersCursor != null && chaptersCursor.getCount() != 0) { mMangaView.hideChapterStatusError(); } else { mMangaView.showChapterStatusError(); } if (mChapterListingsAdapter != null) { mChapterListingsAdapter.setCursor(chaptersCursor); } } if (recentChapterUrls != null) { if (mChapterListingsAdapter != null) { mChapterListingsAdapter.setRecentChapterUrls(recentChapterUrls); } } } }); } } private void updateDataFromUrl() { if (mUpdateSubscription != null) { mUpdateSubscription.unsubscribe(); mUpdateSubscription = null; } if (mRequest != null) { mMangaView.showRefreshing(); Observable<Manga> updateMangaFromUrl = AizobanManager .pullMangaFromNetwork(mRequest); Observable<List<Chapter>> updateChaptersFromUrl = AizobanManager .pullChaptersFromNetwork(mRequest) .onErrorReturn(new Func1<Throwable, List<Chapter>>() { @Override public List<Chapter> call(Throwable throwable) { // Swallow Error with Empty Chapter List. return null; } }); mUpdateSubscription = Observable.zip(updateMangaFromUrl, updateChaptersFromUrl, new Func2<Manga, List<Chapter>, Pair<Manga, List<Chapter>>>() { @Override public Pair<Manga, List<Chapter>> call(Manga manga, List<Chapter> chapterList) { return Pair.create(manga, chapterList); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Pair<Manga, List<Chapter>>>() { @Override public void onCompleted() { mMangaView.hideRefreshing(); queryBothMangaAndChaptersFromUrl(); mInitialized = true; } @Override public void onError(Throwable e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } mMangaView.hideRefreshing(); mMangaView.toastMangaError(); } @Override public void onNext(Pair<Manga, List<Chapter>> mangaListPair) { // Do Nothing. } }); } } private void restorePosition() { if (mPositionSavedState != null) { mMangaMapper.setPositionState(mPositionSavedState); mPositionSavedState = null; } } }