Use subscriptions in onCreate method in CataloguePresenter

This commit is contained in:
inorichi 2015-10-19 02:57:00 +02:00
parent 1719959bc8
commit 264d627dea
7 changed files with 130 additions and 122 deletions

View File

@ -27,15 +27,11 @@
android:name=".ui.activity.MangaDetailActivity" android:name=".ui.activity.MangaDetailActivity"
android:label="@string/title_activity_manga_detail" android:label="@string/title_activity_manga_detail"
android:parentActivityName=".ui.activity.MainActivity" > android:parentActivityName=".ui.activity.MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="eu.kanade.mangafeed.ui.activity.MainActivity" />
</activity> </activity>
<activity <activity
android:name=".ui.activity.CatalogueActivity" android:name=".ui.activity.CatalogueActivity"
android:label="@string/title_activity_catalogue_list" android:label="@string/title_activity_catalogue_list"
android:parentActivityName=".ui.activity.MainActivity" android:parentActivityName=".ui.activity.MainActivity"
android:launchMode="singleTop"
android:theme="@style/AppTheme" > android:theme="@style/AppTheme" >
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"

View File

@ -1,7 +1,6 @@
package eu.kanade.mangafeed.presenter; package eu.kanade.mangafeed.presenter;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -14,11 +13,13 @@ import eu.kanade.mangafeed.data.helpers.SourceManager;
import eu.kanade.mangafeed.data.models.Manga; import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.sources.Source; import eu.kanade.mangafeed.sources.Source;
import eu.kanade.mangafeed.ui.activity.CatalogueActivity; import eu.kanade.mangafeed.ui.activity.CatalogueActivity;
import eu.kanade.mangafeed.util.PageBundle;
import eu.kanade.mangafeed.util.RxPager;
import icepick.State;
import nucleus.presenter.RxPresenter; import nucleus.presenter.RxPresenter;
import rx.Observable; import rx.Observable;
import rx.Subscription; import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers; import rx.android.schedulers.AndroidSchedulers;
import rx.internal.util.SubscriptionList;
import rx.schedulers.Schedulers; import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject; import rx.subjects.PublishSubject;
import timber.log.Timber; import timber.log.Timber;
@ -33,28 +34,34 @@ public class CataloguePresenter extends RxPresenter<CatalogueActivity> {
private String mSearchName; private String mSearchName;
private boolean mSearchMode; private boolean mSearchMode;
private final int SEARCH_TIMEOUT = 1000; private final int SEARCH_TIMEOUT = 1000;
private int mCurrentPage = 1;
private Subscription mMangaFetchSubscription; @State protected int mCurrentPage;
private Subscription mMangaSearchSubscription; private RxPager pager;
private Subscription mSearchViewSubscription; private Subscription mSearchViewSubscription;
private Subscription mMangaDetailFetchSubscription; private Subscription mMangaDetailFetchSubscription;
private PublishSubject<Observable<String>> mSearchViewPublishSubject; private PublishSubject<Observable<String>> mSearchViewPublishSubject;
private PublishSubject<Observable<List<Manga>>> mMangaDetailPublishSubject; private PublishSubject<Observable<List<Manga>>> mMangaDetailPublishSubject;
private SubscriptionList mResultSubscriptions = new SubscriptionList();
private final String CURRENT_PAGE = "CATALOGUE_CURRENT_PAGE"; private static final int GET_MANGA_LIST = 1;
@Override @Override
protected void onCreate(Bundle savedState) { protected void onCreate(Bundle savedState) {
super.onCreate(savedState); super.onCreate(savedState);
if (savedState != null) { restartableReplay(GET_MANGA_LIST,
mCurrentPage = savedState.getInt(CURRENT_PAGE); () -> pager.pages().<PageBundle<List<Manga>>>concatMap(
} page -> getMangaObs(page + 1)
.map(mangas -> new PageBundle<>(page, mangas))
.observeOn(AndroidSchedulers.mainThread())
),
(view, page) -> {
view.hideProgressBar();
view.onAddPage(page);
if (mMangaDetailPublishSubject != null)
mMangaDetailPublishSubject.onNext(Observable.just(page.data));
});
selectedSource = sourceManager.getSelectedSource();
getMangasFromSource(mCurrentPage);
initializeSearch(); initializeSearch();
initializeMangaDetailsLoader(); initializeMangaDetailsLoader();
} }
@ -63,24 +70,40 @@ public class CataloguePresenter extends RxPresenter<CatalogueActivity> {
protected void onTakeView(CatalogueActivity view) { protected void onTakeView(CatalogueActivity view) {
super.onTakeView(view); super.onTakeView(view);
view.setScrollPage(mCurrentPage - 1);
view.setToolbarTitle(selectedSource.getName()); view.setToolbarTitle(selectedSource.getName());
if (view.getAdapter().getCount() == 0) if (view.getAdapter().getCount() == 0)
view.showProgressBar(); view.showProgressBar();
} }
@Override public void requestNext() {
protected void onSave(@NonNull Bundle state) { pager.requestNext(++mCurrentPage);
super.onSave(state);
state.putInt(CURRENT_PAGE, mCurrentPage);
} }
@Override public void initializeRequest(int source_id) {
protected void onDestroy() { this.selectedSource = sourceManager.get(source_id);
super.onDestroy(); restartRequest();
mResultSubscriptions.unsubscribe(); }
private void restartRequest() {
stop(GET_MANGA_LIST);
mCurrentPage = 1;
pager = new RxPager();
start(GET_MANGA_LIST);
}
private Observable<List<Manga>> getMangaObs(int page) {
Observable<List<Manga>> obs;
if (mSearchMode)
obs = selectedSource.searchMangasFromNetwork(mSearchName, page);
else
obs = selectedSource.pullPopularMangasFromNetwork(page);
return obs.subscribeOn(Schedulers.io())
.flatMap(Observable::from)
.map(this::networkToLocalManga)
.toList()
.observeOn(AndroidSchedulers.mainThread());
} }
private void initializeSearch() { private void initializeSearch() {
@ -134,36 +157,6 @@ public class CataloguePresenter extends RxPresenter<CatalogueActivity> {
add(mMangaDetailFetchSubscription); add(mMangaDetailFetchSubscription);
} }
public void getMangasFromSource(int page) {
mMangaFetchSubscription = getMangasSubscriber(
selectedSource.pullPopularMangasFromNetwork(page));
mResultSubscriptions.add(mMangaFetchSubscription);
}
public void getMangasFromSearch(int page) {
mMangaSearchSubscription = getMangasSubscriber(
selectedSource.searchMangasFromNetwork(mSearchName, page));
mResultSubscriptions.add(mMangaSearchSubscription);
}
private Subscription getMangasSubscriber(Observable<List<Manga>> mangas) {
return mangas
.subscribeOn(Schedulers.io())
.flatMap(Observable::from)
.map(this::networkToLocalManga)
.toList()
.observeOn(AndroidSchedulers.mainThread())
.compose(deliverReplay())
.subscribe(this.split((view, newMangas) -> {
view.hideProgressBar();
view.onMangasNext(newMangas);
if (mMangaDetailPublishSubject != null)
mMangaDetailPublishSubject.onNext(Observable.just(newMangas));
}));
}
private Manga networkToLocalManga(Manga networkManga) { private Manga networkToLocalManga(Manga networkManga) {
Manga localManga = db.getMangaBlock(networkManga.url); Manga localManga = db.getMangaBlock(networkManga.url);
if (localManga == null) { if (localManga == null) {
@ -186,31 +179,20 @@ public class CataloguePresenter extends RxPresenter<CatalogueActivity> {
// If going to search mode // If going to search mode
else if (mSearchName.equals("") && !query.equals("")) { else if (mSearchName.equals("") && !query.equals("")) {
mSearchMode = true; mSearchMode = true;
mResultSubscriptions.clear();
} }
// If going to normal mode // If going to normal mode
else if (!mSearchName.equals("") && query.equals("")) { else if (!mSearchName.equals("") && query.equals("")) {
mSearchMode = false; mSearchMode = false;
mResultSubscriptions.clear();
} }
mSearchName = query; mSearchName = query;
getView().getAdapter().getItems().clear(); if (getView() != null) {
getView().showProgressBar(); if (mCurrentPage == 1)
getView().resetScrollListener(); getView().showProgressBar();
loadMoreMangas(1); else
} getView().showGridProgressBar();
public void loadMoreMangas(int page) {
if (page > 1) {
getView().showGridProgressBar();
} }
if (mSearchMode) { restartRequest();
getMangasFromSearch(page);
} else {
getMangasFromSource(page);
}
mCurrentPage = page;
} }
} }

View File

@ -13,6 +13,7 @@ import android.widget.ProgressBar;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import butterknife.Bind; import butterknife.Bind;
@ -22,6 +23,7 @@ import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Manga; import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.presenter.CataloguePresenter; import eu.kanade.mangafeed.presenter.CataloguePresenter;
import eu.kanade.mangafeed.ui.adapter.CatalogueHolder; import eu.kanade.mangafeed.ui.adapter.CatalogueHolder;
import eu.kanade.mangafeed.util.PageBundle;
import eu.kanade.mangafeed.widget.EndlessScrollListener; import eu.kanade.mangafeed.widget.EndlessScrollListener;
import nucleus.factory.RequiresPresenter; import nucleus.factory.RequiresPresenter;
import uk.co.ribot.easyadapter.EasyAdapter; import uk.co.ribot.easyadapter.EasyAdapter;
@ -62,6 +64,11 @@ public class CatalogueActivity extends BaseActivity<CataloguePresenter> {
initializeAdapter(); initializeAdapter();
initializeScrollListener(); initializeScrollListener();
int source_id = getIntent().getIntExtra(SOURCE_ID, -1);
if (savedInstanceState == null)
getPresenter().initializeRequest(source_id);
} }
@Override @Override
@ -107,14 +114,7 @@ public class CatalogueActivity extends BaseActivity<CataloguePresenter> {
} }
public void initializeScrollListener() { public void initializeScrollListener() {
scroll_listener = new EndlessScrollListener() { scroll_listener = new EndlessScrollListener(getPresenter()::requestNext);
@Override
public boolean onLoadMore(int page, int totalItemsCount) {
getPresenter().loadMoreMangas(page);
return true;
}
};
manga_list.setOnScrollListener(scroll_listener); manga_list.setOnScrollListener(scroll_listener);
} }
@ -122,14 +122,6 @@ public class CatalogueActivity extends BaseActivity<CataloguePresenter> {
scroll_listener.resetScroll(); scroll_listener.resetScroll();
} }
public int getScrollPage() {
return scroll_listener.getCurrentPage();
}
public void setScrollPage(int page) {
scroll_listener.setCurrentPage(page);
}
public void showProgressBar() { public void showProgressBar() {
progress.setVisibility(ProgressBar.VISIBLE); progress.setVisibility(ProgressBar.VISIBLE);
} }
@ -143,8 +135,12 @@ public class CatalogueActivity extends BaseActivity<CataloguePresenter> {
progress_grid.setVisibility(ProgressBar.GONE); progress_grid.setVisibility(ProgressBar.GONE);
} }
public void onMangasNext(List<Manga> newMangas) { public void onAddPage(PageBundle<List<Manga>> page) {
adapter.addItems(newMangas); if (page.page == 0) {
adapter.setItems(new ArrayList<>());
resetScrollListener();
}
adapter.addItems(page.data);
} }
private int getMangaIndex(Manga manga) { private int getMangaIndex(Manga manga) {
@ -175,4 +171,5 @@ public class CatalogueActivity extends BaseActivity<CataloguePresenter> {
.into(imageView); .into(imageView);
} }
} }
} }

View File

@ -10,6 +10,7 @@ import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import butterknife.Bind; import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
@ -57,6 +58,16 @@ public class MangaDetailActivity extends BaseActivity<MangaDetailPresenter> {
setupViewPager(); setupViewPager();
} }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
private void disableToolbarElevation() { private void disableToolbarElevation() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
toolbar.setElevation(0); toolbar.setElevation(0);

View File

@ -0,0 +1,12 @@
package eu.kanade.mangafeed.util;
public class PageBundle<T> {
public final int page;
public final T data;
public PageBundle(int page, T data) {
this.page = page;
this.data = data;
}
}

View File

@ -0,0 +1,32 @@
package eu.kanade.mangafeed.util;
import rx.Observable;
import rx.subjects.PublishSubject;
public class RxPager {
private final int initialPageCount;
private final PublishSubject<Integer> requests = PublishSubject.create();
private int requestedCount;
public RxPager() {
this(1);
}
public RxPager(int initialPageCount) {
this.initialPageCount = initialPageCount;
}
public void requestNext(int page) {
requests.onNext(page);
}
public Observable<Integer> pages() {
return requests
.concatMap(targetPage -> targetPage <= requestedCount ?
Observable.<Integer>never() :
Observable.range(requestedCount, targetPage - requestedCount))
.startWith(Observable.range(0, initialPageCount))
.doOnNext(it -> requestedCount = it + 1);
}
}

View File

@ -2,35 +2,25 @@ package eu.kanade.mangafeed.widget;
import android.widget.AbsListView; import android.widget.AbsListView;
public abstract class EndlessScrollListener implements AbsListView.OnScrollListener { import rx.functions.Action0;
public class EndlessScrollListener implements AbsListView.OnScrollListener {
// The minimum amount of items to have below your current scroll position // The minimum amount of items to have below your current scroll position
// before loading more. // before loading more.
private int visibleThreshold = 5; private int visibleThreshold = 5;
// The current offset index of data you have loaded
private int currentPage = 0;
// The total number of items in the dataset after the last load // The total number of items in the dataset after the last load
private int previousTotalItemCount = 0; private int previousTotalItemCount = 0;
// True if we are still waiting for the last set of data to load. // True if we are still waiting for the last set of data to load.
private boolean loading = true; private boolean loading = true;
// Sets the starting page index
private int startingPageIndex = 0;
public EndlessScrollListener() { private Action0 requestNext;
}
public EndlessScrollListener(int visibleThreshold) { public EndlessScrollListener(Action0 requestNext) {
this.visibleThreshold = visibleThreshold; this.requestNext = requestNext;
}
public EndlessScrollListener(int visibleThreshold, int startPage) {
this.visibleThreshold = visibleThreshold;
this.startingPageIndex = startPage;
this.currentPage = startPage;
} }
public void resetScroll() { public void resetScroll() {
this.currentPage = 0; this.previousTotalItemCount = 0;
this.startingPageIndex = 0;
this.loading = true; this.loading = true;
} }
@ -43,7 +33,6 @@ public abstract class EndlessScrollListener implements AbsListView.OnScrollListe
// If the total item count is zero and the previous isn't, assume the // If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state // list is invalidated and should be reset back to initial state
if (totalItemCount < previousTotalItemCount) { if (totalItemCount < previousTotalItemCount) {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = totalItemCount; this.previousTotalItemCount = totalItemCount;
if (totalItemCount == 0) { this.loading = true; } if (totalItemCount == 0) { this.loading = true; }
} }
@ -53,31 +42,20 @@ public abstract class EndlessScrollListener implements AbsListView.OnScrollListe
if (loading && (totalItemCount > previousTotalItemCount)) { if (loading && (totalItemCount > previousTotalItemCount)) {
loading = false; loading = false;
previousTotalItemCount = totalItemCount; previousTotalItemCount = totalItemCount;
currentPage++;
} }
// If it isnt currently loading, we check to see if we have breached // If it isnt currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data. // the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data. // If we do need to reload some more data, we execute onLoadMore to fetch the data.
if (!loading && (totalItemCount - visibleItemCount)<=(firstVisibleItem + visibleThreshold)) { if (!loading && (totalItemCount - visibleItemCount)<=(firstVisibleItem + visibleThreshold)) {
loading = onLoadMore(currentPage + 1, totalItemCount); requestNext.call();
loading = true;
} }
} }
// Defines the process for actually loading more data based on page
// Returns true if more data is being loaded; returns false if there is no more data to load.
public abstract boolean onLoadMore(int page, int totalItemsCount);
@Override @Override
public void onScrollStateChanged(AbsListView view, int scrollState) { public void onScrollStateChanged(AbsListView view, int scrollState) {
// Don't take any action on changed // Don't take any action on changed
} }
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
} }