Load more mangas on scroll

This commit is contained in:
inorichi 2015-10-12 18:39:24 +02:00
parent 2aaaad7a24
commit e7116bdcab
8 changed files with 155 additions and 16 deletions

View file

@ -102,6 +102,11 @@ public class DatabaseHelper implements MangaManager, ChapterManager {
return mMangaManager.getManga(id); return mMangaManager.getManga(id);
} }
@Override
public Manga getMangaBlock(String url) {
return mMangaManager.getMangaBlock(url);
}
@Override @Override
public Observable<PutResult> insertManga(Manga manga) { public Observable<PutResult> insertManga(Manga manga) {
return mMangaManager.insertManga(manga); return mMangaManager.insertManga(manga);
@ -112,6 +117,11 @@ public class DatabaseHelper implements MangaManager, ChapterManager {
return mMangaManager.insertMangas(mangas); return mMangaManager.insertMangas(mangas);
} }
@Override
public PutResult insertMangaBlock(Manga manga) {
return mMangaManager.insertMangaBlock(manga);
}
@Override @Override
public Observable<DeleteResult> deleteManga(Manga manga) { public Observable<DeleteResult> deleteManga(Manga manga) {
return mMangaManager.deleteManga(manga); return mMangaManager.deleteManga(manga);

View file

@ -20,10 +20,14 @@ public interface MangaManager {
Observable<List<Manga>> getManga(int id); Observable<List<Manga>> getManga(int id);
Manga getMangaBlock(String url);
Observable<PutResult> insertManga(Manga manga); Observable<PutResult> insertManga(Manga manga);
Observable<PutResults<Manga>> insertMangas(List<Manga> mangas); Observable<PutResults<Manga>> insertMangas(List<Manga> mangas);
PutResult insertMangaBlock(Manga manga);
Observable<DeleteResult> deleteManga(Manga manga); Observable<DeleteResult> deleteManga(Manga manga);
Observable<DeleteResults<Manga>> deleteMangas(List<Manga> mangas); Observable<DeleteResults<Manga>> deleteMangas(List<Manga> mangas);

View file

@ -70,6 +70,24 @@ public class MangaManagerImpl extends BaseManager implements MangaManager {
return null; return null;
} }
@Override
public Manga getMangaBlock(String url) {
List<Manga> result = db.get()
.listOfObjects(Manga.class)
.withQuery(Query.builder()
.table(MangasTable.TABLE)
.where(MangasTable.COLUMN_URL + "=?")
.whereArgs(url)
.build())
.prepare()
.executeAsBlocking();
if (result.isEmpty())
return null;
return result.get(0);
}
public Observable<PutResult> insertManga(Manga manga) { public Observable<PutResult> insertManga(Manga manga) {
return db.put() return db.put()
.object(manga) .object(manga)
@ -84,6 +102,13 @@ public class MangaManagerImpl extends BaseManager implements MangaManager {
.createObservable(); .createObservable();
} }
public PutResult insertMangaBlock(Manga manga) {
return db.put()
.object(manga)
.prepare()
.executeAsBlocking();
}
public Observable<DeleteResult> deleteManga(Manga manga) { public Observable<DeleteResult> deleteManga(Manga manga) {
return db.delete() return db.delete()
.object(manga) .object(manga)

View file

@ -83,6 +83,8 @@ public class MangasTable {
+ COLUMN_INITIALIZED + " BOOLEAN NOT NULL, " + COLUMN_INITIALIZED + " BOOLEAN NOT NULL, "
+ COLUMN_VIEWER + " INTEGER NOT NULL, " + COLUMN_VIEWER + " INTEGER NOT NULL, "
+ COLUMN_CHAPTER_ORDER + " INTEGER NOT NULL" + COLUMN_CHAPTER_ORDER + " INTEGER NOT NULL"
+ ");"; + ");"
+ "CREATE INDEX " + TABLE + "_" + COLUMN_URL + "_index ON " + TABLE + "(" + COLUMN_URL + ");";
} }
} }

View file

@ -12,11 +12,12 @@ import eu.kanade.mangafeed.sources.Source;
import eu.kanade.mangafeed.ui.adapter.CatalogueListHolder; import eu.kanade.mangafeed.ui.adapter.CatalogueListHolder;
import eu.kanade.mangafeed.view.CatalogueListView; import eu.kanade.mangafeed.view.CatalogueListView;
import rx.Observable; import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers; import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers; import rx.schedulers.Schedulers;
import uk.co.ribot.easyadapter.EasyAdapter; import uk.co.ribot.easyadapter.EasyAdapter;
public class CatalogueListPresenter { public class CatalogueListPresenter extends BasePresenter {
CatalogueListView view; CatalogueListView view;
EasyAdapter<Manga> adapter; EasyAdapter<Manga> adapter;
@ -25,6 +26,8 @@ public class CatalogueListPresenter {
@Inject SourceManager sourceManager; @Inject SourceManager sourceManager;
@Inject DatabaseHelper db; @Inject DatabaseHelper db;
private Subscription mMangaFetchSubscription;
public CatalogueListPresenter(CatalogueListView view) { public CatalogueListPresenter(CatalogueListView view) {
this.view = view; this.view = view;
@ -38,24 +41,32 @@ public class CatalogueListPresenter {
adapter = new EasyAdapter<>(view.getActivity(), CatalogueListHolder.class); adapter = new EasyAdapter<>(view.getActivity(), CatalogueListHolder.class);
view.setAdapter(adapter); view.setAdapter(adapter);
view.setScrollListener();
getMangasFromSource(); getMangasFromSource(1);
} }
private void getMangasFromSource() { public void getMangasFromSource(int page) {
selectedSource.pullPopularMangasFromNetwork(1) subscriptions.remove(mMangaFetchSubscription);
mMangaFetchSubscription = selectedSource.pullPopularMangasFromNetwork(page)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.flatMap(Observable::from)
.flatMap(networkManga -> db.getManga(networkManga.url)
.flatMap(result -> {
if (result.size() == 0) {
return db.insertManga(networkManga)
.flatMap(i -> Observable.just(networkManga));
}
return Observable.just(networkManga);
}))
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(adapter::addItem); .flatMap(Observable::from)
.map(this::networkToLocalManga)
.toList()
.subscribe(adapter::addItems);
subscriptions.add(mMangaFetchSubscription);
}
private Manga networkToLocalManga(Manga networkManga) {
Manga localManga = db.getMangaBlock(networkManga.url);
if (localManga == null) {
db.insertMangaBlock(networkManga);
localManga = db.getMangaBlock(networkManga.url);
}
return localManga;
} }
} }

View file

@ -10,6 +10,7 @@ import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.presenter.CatalogueListPresenter; import eu.kanade.mangafeed.presenter.CatalogueListPresenter;
import eu.kanade.mangafeed.sources.Source; import eu.kanade.mangafeed.sources.Source;
import eu.kanade.mangafeed.view.CatalogueListView; import eu.kanade.mangafeed.view.CatalogueListView;
import eu.kanade.mangafeed.widget.EndlessScrollListener;
import uk.co.ribot.easyadapter.EasyAdapter; import uk.co.ribot.easyadapter.EasyAdapter;
public class CatalogueListActivity extends BaseActivity implements CatalogueListView { public class CatalogueListActivity extends BaseActivity implements CatalogueListView {
@ -24,7 +25,7 @@ public class CatalogueListActivity extends BaseActivity implements CatalogueList
private Source source; private Source source;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_catalogue_list); setContentView(R.layout.activity_catalogue_list);
ButterKnife.bind(this); ButterKnife.bind(this);
@ -35,6 +36,12 @@ public class CatalogueListActivity extends BaseActivity implements CatalogueList
presenter.initializeSource(); presenter.initializeSource();
} }
@Override
public void onDestroy() {
super.onDestroy();
presenter.destroySubscriptions();
}
public void setSource(Source source) { public void setSource(Source source) {
this.source = source; this.source = source;
setToolbarTitle(source.getName()); setToolbarTitle(source.getName());
@ -44,4 +51,14 @@ public class CatalogueListActivity extends BaseActivity implements CatalogueList
manga_list.setAdapter(adapter); manga_list.setAdapter(adapter);
} }
public void setScrollListener() {
manga_list.setOnScrollListener(new EndlessScrollListener() {
@Override
public boolean onLoadMore(int page, int totalItemsCount) {
presenter.getMangasFromSource(page);
return true;
}
});
}
} }

View file

@ -9,4 +9,5 @@ public interface CatalogueListView extends BaseView {
Intent getIntent(); Intent getIntent();
void setSource(Source source); void setSource(Source source);
void setAdapter(EasyAdapter adapter); void setAdapter(EasyAdapter adapter);
void setScrollListener();
} }

View file

@ -0,0 +1,69 @@
package eu.kanade.mangafeed.widget;
import android.widget.AbsListView;
public abstract class EndlessScrollListener implements AbsListView.OnScrollListener {
// The minimum amount of items to have below your current scroll position
// before loading more.
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
private int previousTotalItemCount = 0;
// True if we are still waiting for the last set of data to load.
private boolean loading = true;
// Sets the starting page index
private int startingPageIndex = 0;
public EndlessScrollListener() {
}
public EndlessScrollListener(int visibleThreshold) {
this.visibleThreshold = visibleThreshold;
}
public EndlessScrollListener(int visibleThreshold, int startPage) {
this.visibleThreshold = visibleThreshold;
this.startingPageIndex = startPage;
this.currentPage = startPage;
}
// This happens many times a second during a scroll, so be wary of the code you place here.
// We are given a few useful parameters to help us work out if we need to load some more data,
// but first we check if we are waiting for the previous load to finish.
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
// 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
if (totalItemCount < previousTotalItemCount) {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = totalItemCount;
if (totalItemCount == 0) { this.loading = true; }
}
// If its still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
if (loading && (totalItemCount > previousTotalItemCount)) {
loading = false;
previousTotalItemCount = totalItemCount;
currentPage++;
}
// If it isnt currently loading, we check to see if we have breached
// 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 (!loading && (totalItemCount - visibleItemCount)<=(firstVisibleItem + visibleThreshold)) {
loading = onLoadMore(currentPage + 1, totalItemCount);
}
}
// 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
public void onScrollStateChanged(AbsListView view, int scrollState) {
// Don't take any action on changed
}
}