Use interfaces for database managers, create a source manager

This commit is contained in:
inorichi 2015-10-08 13:15:29 +02:00
parent 7e3d7cb4c4
commit 67a2e99cc1
13 changed files with 415 additions and 214 deletions

View file

@ -10,6 +10,7 @@ import eu.kanade.mangafeed.data.caches.CacheManager;
import eu.kanade.mangafeed.data.helpers.DatabaseHelper; import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
import eu.kanade.mangafeed.data.helpers.NetworkHelper; import eu.kanade.mangafeed.data.helpers.NetworkHelper;
import eu.kanade.mangafeed.data.helpers.PreferencesHelper; import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
import eu.kanade.mangafeed.data.helpers.SourceManager;
import rx.Scheduler; import rx.Scheduler;
import rx.schedulers.Schedulers; import rx.schedulers.Schedulers;
@ -49,4 +50,9 @@ public class DataModule {
return new NetworkHelper(); return new NetworkHelper();
} }
@Provides
@Singleton
SourceManager provideSourceManager(NetworkHelper networkHelper, CacheManager cacheManager) {
return new SourceManager(networkHelper, cacheManager);
}
} }

View file

@ -5,9 +5,17 @@ import android.content.Context;
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping; import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping;
import com.pushtorefresh.storio.sqlite.StorIOSQLite; import com.pushtorefresh.storio.sqlite.StorIOSQLite;
import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite; import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResult;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResults;
import com.pushtorefresh.storio.sqlite.operations.put.PutResult;
import com.pushtorefresh.storio.sqlite.operations.put.PutResults;
import java.util.List;
import eu.kanade.mangafeed.data.managers.ChapterManager; import eu.kanade.mangafeed.data.managers.ChapterManager;
import eu.kanade.mangafeed.data.managers.ChapterManagerImpl;
import eu.kanade.mangafeed.data.managers.MangaManager; import eu.kanade.mangafeed.data.managers.MangaManager;
import eu.kanade.mangafeed.data.managers.MangaManagerImpl;
import eu.kanade.mangafeed.data.models.Chapter; import eu.kanade.mangafeed.data.models.Chapter;
import eu.kanade.mangafeed.data.models.ChapterStorIOSQLiteDeleteResolver; import eu.kanade.mangafeed.data.models.ChapterStorIOSQLiteDeleteResolver;
import eu.kanade.mangafeed.data.models.ChapterStorIOSQLiteGetResolver; import eu.kanade.mangafeed.data.models.ChapterStorIOSQLiteGetResolver;
@ -16,16 +24,17 @@ import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.data.models.MangaStorIOSQLiteDeleteResolver; import eu.kanade.mangafeed.data.models.MangaStorIOSQLiteDeleteResolver;
import eu.kanade.mangafeed.data.models.MangaStorIOSQLitePutResolver; import eu.kanade.mangafeed.data.models.MangaStorIOSQLitePutResolver;
import eu.kanade.mangafeed.data.resolvers.MangaWithUnreadGetResolver; import eu.kanade.mangafeed.data.resolvers.MangaWithUnreadGetResolver;
import rx.Observable;
public class DatabaseHelper { public class DatabaseHelper implements MangaManager, ChapterManager {
private StorIOSQLite db; private StorIOSQLite mDb;
public MangaManager manga; private MangaManagerImpl mMangaManager;
public ChapterManager chapter; private ChapterManagerImpl mChapterManager;
public DatabaseHelper(Context context) { public DatabaseHelper(Context context) {
db = DefaultStorIOSQLite.builder() mDb = DefaultStorIOSQLite.builder()
.sqliteOpenHelper(new DbOpenHelper(context)) .sqliteOpenHelper(new DbOpenHelper(context))
.addTypeMapping(Manga.class, SQLiteTypeMapping.<Manga>builder() .addTypeMapping(Manga.class, SQLiteTypeMapping.<Manga>builder()
.putResolver(new MangaStorIOSQLitePutResolver()) .putResolver(new MangaStorIOSQLitePutResolver())
@ -39,8 +48,67 @@ public class DatabaseHelper {
.build()) .build())
.build(); .build();
manga = new MangaManager(db); mMangaManager = new MangaManagerImpl(mDb);
chapter = new ChapterManager(db); mChapterManager = new ChapterManagerImpl(mDb);
} }
@Override
public Observable<List<Chapter>> getChapters(Manga manga) {
return mChapterManager.getChapters(manga);
}
@Override
public Observable<PutResult> insertChapter(Chapter chapter) {
return mChapterManager.insertChapter(chapter);
}
@Override
public Observable<PutResults<Chapter>> insertChapters(List<Chapter> chapters) {
return mChapterManager.insertChapters(chapters);
}
@Override
public Observable insertOrRemoveChapters(Manga manga, List<Chapter> chapters) {
return mChapterManager.insertOrRemoveChapters(manga, chapters);
}
@Override
public Observable<DeleteResult> deleteChapter(Chapter chapter) {
return mChapterManager.deleteChapter(chapter);
}
@Override
public Observable<DeleteResults<Chapter>> deleteChapters(List<Chapter> chapters) {
return mChapterManager.deleteChapters(chapters);
}
@Override
public Observable<List<Manga>> getMangas() {
return mMangaManager.getMangas();
}
@Override
public Observable<List<Manga>> getMangasWithUnread() {
return mMangaManager.getMangasWithUnread();
}
@Override
public Observable<PutResult> insertManga(Manga manga) {
return mMangaManager.insertManga(manga);
}
@Override
public Observable<PutResults<Manga>> insertMangas(List<Manga> mangas) {
return mMangaManager.insertMangas(mangas);
}
@Override
public Observable<DeleteResult> deleteManga(Manga manga) {
return mMangaManager.deleteManga(manga);
}
@Override
public Observable<DeleteResults<Manga>> deleteMangas(List<Manga> mangas) {
return mMangaManager.deleteMangas(mangas);
}
} }

View file

@ -0,0 +1,38 @@
package eu.kanade.mangafeed.data.helpers;
import java.util.HashMap;
import eu.kanade.mangafeed.data.caches.CacheManager;
import eu.kanade.mangafeed.sources.Batoto;
import eu.kanade.mangafeed.sources.Source;
public class SourceManager {
public static final int BATOTO = 1;
private HashMap<Integer, Source> mSourcesMap;
private NetworkHelper mNetworkHelper;
private CacheManager mCacheManager;
public SourceManager(NetworkHelper networkHelper, CacheManager cacheManager) {
mSourcesMap = new HashMap<>();
mNetworkHelper = networkHelper;
mCacheManager = cacheManager;
}
public Source get(int sourceKey) {
if (!mSourcesMap.containsKey(sourceKey)) {
mSourcesMap.put(sourceKey, createSource(sourceKey));
}
return mSourcesMap.get(sourceKey);
}
private Source createSource(int sourceKey) {
switch (sourceKey) {
case BATOTO:
return new Batoto(mNetworkHelper, mCacheManager);
}
return null;
}
}

View file

@ -1,107 +1,28 @@
package eu.kanade.mangafeed.data.managers; package eu.kanade.mangafeed.data.managers;
import com.pushtorefresh.storio.sqlite.StorIOSQLite;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResult; import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResult;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResults; import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResults;
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetListOfObjects;
import com.pushtorefresh.storio.sqlite.operations.put.PutResult; import com.pushtorefresh.storio.sqlite.operations.put.PutResult;
import com.pushtorefresh.storio.sqlite.operations.put.PutResults; import com.pushtorefresh.storio.sqlite.operations.put.PutResults;
import com.pushtorefresh.storio.sqlite.queries.Query;
import java.util.List; import java.util.List;
import eu.kanade.mangafeed.data.models.Chapter; import eu.kanade.mangafeed.data.models.Chapter;
import eu.kanade.mangafeed.data.models.Manga; import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.data.tables.ChaptersTable;
import rx.Observable; import rx.Observable;
public class ChapterManager extends BaseManager { public interface ChapterManager {
public ChapterManager(StorIOSQLite db) { Observable<List<Chapter>> getChapters(Manga manga);
super(db);
}
private PreparedGetListOfObjects<Chapter> prepareGet(Manga manga) { Observable<PutResult> insertChapter(Chapter chapter);
return db.get()
.listOfObjects(Chapter.class)
.withQuery(Query.builder()
.table(ChaptersTable.TABLE)
.where(ChaptersTable.COLUMN_MANGA_ID + "=?")
.whereArgs(manga.id)
.build())
.prepare();
}
public Observable<List<Chapter>> get(Manga manga) { Observable<PutResults<Chapter>> insertChapters(List<Chapter> chapters);
return prepareGet(manga).createObservable();
}
public Observable<PutResult> insert(Chapter chapter) { Observable insertOrRemoveChapters(Manga manga, List<Chapter> chapters);
return db.put()
.object(chapter)
.prepare()
.createObservable();
}
public Observable<PutResults<Chapter>> insert(List<Chapter> chapters) { Observable<DeleteResult> deleteChapter(Chapter chapter);
return db.put()
.objects(chapters)
.prepare()
.createObservable();
}
// Add new chapters or delete if the source deletes them Observable<DeleteResults<Chapter>> deleteChapters(List<Chapter> chapters);
public Observable insertOrRemove(Manga manga, List<Chapter> chapters) {
// I don't know a better approach
// TODO Fix this method
return Observable.create(subscriber -> {
List<Chapter> dbChapters = prepareGet(manga).executeAsBlocking();
Observable<List<Chapter>> newChaptersObs =
Observable.from(chapters)
.filter(c -> !dbChapters.contains(c))
.toList();
Observable<List<Chapter>> deletedChaptersObs =
Observable.from(dbChapters)
.filter(c -> !chapters.contains(c))
.toList();
Observable.zip(newChaptersObs, deletedChaptersObs,
(newChapters, deletedChapters) -> {
insert(newChapters).subscribe();
delete(deletedChapters).subscribe();
subscriber.onCompleted();
return null;
}).subscribe();
});
}
public void createDummyChapters() {
Chapter c;
for (int i = 1; i < 100; i++) {
c = new Chapter();
c.manga_id = 1L;
c.name = "Chapter " + i;
c.url = "http://example.com/1";
insert(c).subscribe();
}
}
public Observable<DeleteResults<Chapter>> delete(List<Chapter> chapters) {
return db.delete()
.objects(chapters)
.prepare()
.createObservable();
}
public Observable<DeleteResult> delete(Chapter chapter) {
return db.delete()
.object(chapter)
.prepare()
.createObservable();
}
} }

View file

@ -0,0 +1,101 @@
package eu.kanade.mangafeed.data.managers;
import com.pushtorefresh.storio.sqlite.StorIOSQLite;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResult;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResults;
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetListOfObjects;
import com.pushtorefresh.storio.sqlite.operations.put.PutResult;
import com.pushtorefresh.storio.sqlite.operations.put.PutResults;
import com.pushtorefresh.storio.sqlite.queries.Query;
import java.util.ArrayList;
import java.util.List;
import eu.kanade.mangafeed.data.models.Chapter;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.data.tables.ChaptersTable;
import rx.Observable;
public class ChapterManagerImpl extends BaseManager implements ChapterManager {
public ChapterManagerImpl(StorIOSQLite db) {
super(db);
}
private PreparedGetListOfObjects<Chapter> prepareGet(Manga manga) {
return db.get()
.listOfObjects(Chapter.class)
.withQuery(Query.builder()
.table(ChaptersTable.TABLE)
.where(ChaptersTable.COLUMN_MANGA_ID + "=?")
.whereArgs(manga.id)
.build())
.prepare();
}
@Override
public Observable<List<Chapter>> getChapters(Manga manga) {
return prepareGet(manga).createObservable();
}
@Override
public Observable<PutResult> insertChapter(Chapter chapter) {
return db.put()
.object(chapter)
.prepare()
.createObservable();
}
@Override
public Observable<PutResults<Chapter>> insertChapters(List<Chapter> chapters) {
return db.put()
.objects(chapters)
.prepare()
.createObservable();
}
// Add new chapters or delete if the source deletes them
@Override
public Observable insertOrRemoveChapters(Manga manga, List<Chapter> chapters) {
// I don't know a better approach
// TODO Fix this method
return Observable.create(subscriber -> {
List<Chapter> dbChapters = prepareGet(manga).executeAsBlocking();
Observable<List<Chapter>> newChaptersObs =
Observable.from(chapters)
.filter(c -> !dbChapters.contains(c))
.toList();
Observable<List<Chapter>> deletedChaptersObs =
Observable.from(dbChapters)
.filter(c -> !chapters.contains(c))
.toList();
Observable.zip(newChaptersObs, deletedChaptersObs,
(newChapters, deletedChapters) -> {
insertChapters(newChapters).subscribe();
deleteChapters(deletedChapters).subscribe();
subscriber.onCompleted();
return null;
}).subscribe();
});
}
@Override
public Observable<DeleteResult> deleteChapter(Chapter chapter) {
return db.delete()
.object(chapter)
.prepare()
.createObservable();
}
@Override
public Observable<DeleteResults<Chapter>> deleteChapters(List<Chapter> chapters) {
return db.delete()
.objects(chapters)
.prepare()
.createObservable();
}
}

View file

@ -1,97 +1,27 @@
package eu.kanade.mangafeed.data.managers; package eu.kanade.mangafeed.data.managers;
import com.pushtorefresh.storio.sqlite.StorIOSQLite;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResult; import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResult;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResults; import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResults;
import com.pushtorefresh.storio.sqlite.operations.put.PutResult; import com.pushtorefresh.storio.sqlite.operations.put.PutResult;
import com.pushtorefresh.storio.sqlite.queries.Query; import com.pushtorefresh.storio.sqlite.operations.put.PutResults;
import com.pushtorefresh.storio.sqlite.queries.RawQuery;
import java.util.List; import java.util.List;
import eu.kanade.mangafeed.data.models.Manga; import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.data.tables.ChaptersTable;
import eu.kanade.mangafeed.data.tables.MangasTable;
import rx.Observable; import rx.Observable;
public class MangaManager extends BaseManager { public interface MangaManager {
public MangaManager(StorIOSQLite db) { Observable<List<Manga>> getMangas();
super(db);
}
private final String mangasWithUnreadQuery = String.format( Observable<List<Manga>> getMangasWithUnread();
"SELECT %1$s.*, COUNT(C.%4$s) AS %5$s FROM %1$s LEFT JOIN " +
"(SELECT %4$s FROM %2$s WHERE %6$s = 0) AS C ON %3$s = C.%4$s " +
"GROUP BY %3$s",
MangasTable.TABLE,
ChaptersTable.TABLE,
MangasTable.TABLE + "." + MangasTable.COLUMN_ID,
ChaptersTable.COLUMN_MANGA_ID,
MangasTable.COLUMN_UNREAD,
ChaptersTable.COLUMN_READ
);
public Observable<List<Manga>> get() { Observable<PutResult> insertManga(Manga manga);
return db.get()
.listOfObjects(Manga.class)
.withQuery(Query.builder()
.table(MangasTable.TABLE)
.build())
.prepare()
.createObservable();
}
public Observable<List<Manga>> getWithUnread() { Observable<PutResults<Manga>> insertMangas(List<Manga> mangas);
return db.get()
.listOfObjects(Manga.class)
.withQuery(RawQuery.builder()
.query(mangasWithUnreadQuery)
.observesTables(MangasTable.TABLE, ChaptersTable.TABLE)
.build())
.prepare()
.createObservable();
}
public Observable<PutResult> insert(Manga manga) { Observable<DeleteResult> deleteManga(Manga manga);
return db.put()
.object(manga)
.prepare()
.createObservable();
}
public void createDummyManga() { Observable<DeleteResults<Manga>> deleteMangas(List<Manga> mangas);
insert(createDummyManga("One Piece")).subscribe();
insert(createDummyManga("Übel Blatt")).subscribe();
insert(createDummyManga("Berserk")).subscribe();
insert(createDummyManga("Horimiya")).subscribe();
}
private Manga createDummyManga(String title) {
Manga m = new Manga();
m.title = title;
m.url="http://example.com";
m.artist="Eiichiro Oda";
m.author="Eiichiro Oda";
m.description="...";
m.genre="Action, Drama";
m.status="Ongoing";
m.thumbnail_url="http://example.com/pic.png";
return m;
}
public Observable<DeleteResult> delete(Manga manga) {
return db.delete()
.object(manga)
.prepare()
.createObservable();
}
public Observable<DeleteResults<Manga>> delete(List<Manga> mangas) {
return db.delete()
.objects(mangas)
.prepare()
.createObservable();
}
} }

View file

@ -0,0 +1,85 @@
package eu.kanade.mangafeed.data.managers;
import com.pushtorefresh.storio.sqlite.StorIOSQLite;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResult;
import com.pushtorefresh.storio.sqlite.operations.delete.DeleteResults;
import com.pushtorefresh.storio.sqlite.operations.put.PutResult;
import com.pushtorefresh.storio.sqlite.operations.put.PutResults;
import com.pushtorefresh.storio.sqlite.queries.Query;
import com.pushtorefresh.storio.sqlite.queries.RawQuery;
import java.util.ArrayList;
import java.util.List;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.data.tables.ChaptersTable;
import eu.kanade.mangafeed.data.tables.MangasTable;
import rx.Observable;
public class MangaManagerImpl extends BaseManager implements MangaManager {
public MangaManagerImpl(StorIOSQLite db) {
super(db);
}
private final String mangasWithUnreadQuery = String.format(
"SELECT %1$s.*, COUNT(C.%4$s) AS %5$s FROM %1$s LEFT JOIN " +
"(SELECT %4$s FROM %2$s WHERE %6$s = 0) AS C ON %3$s = C.%4$s " +
"GROUP BY %3$s",
MangasTable.TABLE,
ChaptersTable.TABLE,
MangasTable.TABLE + "." + MangasTable.COLUMN_ID,
ChaptersTable.COLUMN_MANGA_ID,
MangasTable.COLUMN_UNREAD,
ChaptersTable.COLUMN_READ
);
public Observable<List<Manga>> getMangas() {
return db.get()
.listOfObjects(Manga.class)
.withQuery(Query.builder()
.table(MangasTable.TABLE)
.build())
.prepare()
.createObservable();
}
public Observable<List<Manga>> getMangasWithUnread() {
return db.get()
.listOfObjects(Manga.class)
.withQuery(RawQuery.builder()
.query(mangasWithUnreadQuery)
.observesTables(MangasTable.TABLE, ChaptersTable.TABLE)
.build())
.prepare()
.createObservable();
}
public Observable<PutResult> insertManga(Manga manga) {
return db.put()
.object(manga)
.prepare()
.createObservable();
}
public Observable<PutResults<Manga>> insertMangas(List<Manga> mangas) {
return db.put()
.objects(mangas)
.prepare()
.createObservable();
}
public Observable<DeleteResult> deleteManga(Manga manga) {
return db.delete()
.object(manga)
.prepare()
.createObservable();
}
public Observable<DeleteResults<Manga>> deleteMangas(List<Manga> mangas) {
return db.delete()
.objects(mangas)
.prepare()
.createObservable();
}
}

View file

@ -40,7 +40,8 @@ public class ChaptersTable {
+ COLUMN_DATE_UPLOAD + " LONG NOT NULL, " + COLUMN_DATE_UPLOAD + " LONG NOT NULL, "
+ "FOREIGN KEY(" + COLUMN_MANGA_ID + ") REFERENCES " + MangasTable.TABLE + "(" + MangasTable.COLUMN_ID + ") " + "FOREIGN KEY(" + COLUMN_MANGA_ID + ") REFERENCES " + MangasTable.TABLE + "(" + MangasTable.COLUMN_ID + ") "
+ "ON DELETE CASCADE" + "ON DELETE CASCADE"
+ ");"; + ");"
+ "CREATE INDEX " + TABLE + "_" + COLUMN_MANGA_ID + "_index ON " + TABLE + "(" + COLUMN_MANGA_ID + ");";
} }
} }

View file

@ -11,6 +11,7 @@ import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
import eu.kanade.mangafeed.data.models.Manga; import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.ui.activity.MangaDetailActivity; import eu.kanade.mangafeed.ui.activity.MangaDetailActivity;
import eu.kanade.mangafeed.ui.adapter.LibraryAdapter; import eu.kanade.mangafeed.ui.adapter.LibraryAdapter;
import eu.kanade.mangafeed.util.DummyDataUtil;
import eu.kanade.mangafeed.view.LibraryView; import eu.kanade.mangafeed.view.LibraryView;
import rx.Observable; import rx.Observable;
import rx.schedulers.Schedulers; import rx.schedulers.Schedulers;
@ -32,8 +33,8 @@ public class LibraryPresenter extends BasePresenter {
//TODO remove, only for testing //TODO remove, only for testing
if (prefs.isFirstRun()) { if (prefs.isFirstRun()) {
db.manga.createDummyManga(); db.insertMangas(DummyDataUtil.createDummyManga()).toBlocking().single();
db.chapter.createDummyChapters(); db.insertChapters(DummyDataUtil.createDummyChapters()).subscribe();
prefs.setNotFirstRun(); prefs.setNotFirstRun();
} }
@ -52,10 +53,11 @@ public class LibraryPresenter extends BasePresenter {
view.setAdapter(adapter); view.setAdapter(adapter);
view.setMangaClickListener(); view.setMangaClickListener();
subscriptions.add(db.manga.getWithUnread() subscriptions.add(db.getMangasWithUnread()
.observeOn(mainThread()) .observeOn(mainThread())
.subscribe(adapter::setNewItems) .subscribe(adapter::setNewItems)
); );
} }
public void onQueryTextChange(String query) { public void onQueryTextChange(String query) {
@ -68,7 +70,7 @@ public class LibraryPresenter extends BasePresenter {
.map(checkedItems::keyAt) .map(checkedItems::keyAt)
.map(adapter::getItem) .map(adapter::getItem)
.toList() .toList()
.flatMap(db.manga::delete) .flatMap(db::deleteMangas)
.subscribe(); .subscribe();
} }

View file

@ -30,7 +30,7 @@ public class MangaDetailPresenter extends BasePresenter {
} }
public void initializeChapters(Manga manga) { public void initializeChapters(Manga manga) {
db.chapter.get(manga) db.getChapters(manga)
.subscribe(view::setChapters); .subscribe(view::setChapters);
} }
} }

View file

@ -1,7 +1,6 @@
package eu.kanade.mangafeed.sources; package eu.kanade.mangafeed.sources;
import com.squareup.okhttp.Headers; import com.squareup.okhttp.Headers;
import com.squareup.okhttp.Response;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
@ -18,18 +17,17 @@ import java.util.concurrent.atomic.AtomicInteger;
import eu.kanade.mangafeed.data.caches.CacheManager; import eu.kanade.mangafeed.data.caches.CacheManager;
import eu.kanade.mangafeed.data.helpers.NetworkHelper; import eu.kanade.mangafeed.data.helpers.NetworkHelper;
import eu.kanade.mangafeed.data.helpers.SourceManager;
import eu.kanade.mangafeed.data.models.Chapter; import eu.kanade.mangafeed.data.models.Chapter;
import eu.kanade.mangafeed.data.models.Manga; import eu.kanade.mangafeed.data.models.Manga;
import rx.Observable; import rx.Observable;
import rx.functions.Func1;
import rx.schedulers.Schedulers; import rx.schedulers.Schedulers;
import timber.log.Timber;
public class Batoto { public class Batoto extends Source {
public static final String NAME = "Batoto (EN)"; public static final String NAME = "Batoto (EN)";
public static final String BASE_URL = "www.bato.to"; public static final String BASE_URL = "www.bato.to";
public static final String INITIAL_UPDATE_URL = "http://bato.to/search_ajax?order_cond=update&order=desc&p=1"; public static final String INITIAL_UPDATE_URL = "http://bato.to/search_ajax?order_cond=views&order=desc&p=1";
private static final Headers REQUEST_HEADERS = constructRequestHeaders(); private static final Headers REQUEST_HEADERS = constructRequestHeaders();
private static Headers constructRequestHeaders() { private static Headers constructRequestHeaders() {
@ -105,38 +103,32 @@ public class Batoto {
return Observable.just(genres); return Observable.just(genres);
} }
/* public String getUrlFromPageNumber(int page) {
public Observable<UpdatePageMarker> pullLatestUpdatesFromNetwork(final UpdatePageMarker newUpdate) { if (page == 1)
return mNetworkService return INITIAL_UPDATE_URL;
.getResponse(newUpdate.getNextPageUrl(), NetworkModule.NULL_CACHE_CONTROL, REQUEST_HEADERS)
.flatMap(new Func1<Response, Observable<String>>() { return INITIAL_UPDATE_URL.substring(0, INITIAL_UPDATE_URL.length() - 1) + page;
@Override
public Observable<String> call(Response response) {
return mNetworkService.mapResponseToString(response);
}
})
.flatMap(new Func1<String, Observable<UpdatePageMarker>>() {
@Override
public Observable<UpdatePageMarker> call(String unparsedHtml) {
return Observable.just(parseHtmlToLatestUpdates(newUpdate.getNextPageUrl(), unparsedHtml));
}
});
} }
private UpdatePageMarker parseHtmlToLatestUpdates(String requestUrl, String unparsedHtml) { public Observable<List<Manga>> pullPopularMangasFromNetwork(int page) {
String url = getUrlFromPageNumber(page);
return mNetworkService
.getStringResponse(url, mNetworkService.NULL_CACHE_CONTROL, REQUEST_HEADERS)
.flatMap(response -> Observable.just(parseHtmlToLatestUpdates(response)));
}
private List<Manga> parseHtmlToLatestUpdates(String unparsedHtml) {
Document parsedDocument = Jsoup.parse(unparsedHtml); Document parsedDocument = Jsoup.parse(unparsedHtml);
List<Manga> updatedMangaList = scrapeUpdateMangasFromParsedDocument(parsedDocument); List<Manga> updatedMangaList = scrapeUpdateMangasFromParsedDocument(parsedDocument);
updateLibraryInDatabase(updatedMangaList); //updateLibraryInDatabase(updatedMangaList);
String nextPageUrl = findNextUrlFromParsedDocument(requestUrl, unparsedHtml); return updatedMangaList;
int lastMangaPostion = updatedMangaList.size();
return new UpdatePageMarker(nextPageUrl, lastMangaPostion);
} }
private List<Manga> scrapeUpdateMangasFromParsedDocument(Document parsedDocument) { private List<Manga> scrapeUpdateMangasFromParsedDocument(Document parsedDocument) {
List<Manga> updatedMangaList = new ArrayList<Manga>(); List<Manga> updatedMangaList = new ArrayList<>();
Elements updatedHtmlBlocks = parsedDocument.select("tr:not([id]):not([class])"); Elements updatedHtmlBlocks = parsedDocument.select("tr:not([id]):not([class])");
for (Element currentHtmlBlock : updatedHtmlBlocks) { for (Element currentHtmlBlock : updatedHtmlBlocks) {
@ -149,29 +141,27 @@ public class Batoto {
} }
private Manga constructMangaFromHtmlBlock(Element htmlBlock) { private Manga constructMangaFromHtmlBlock(Element htmlBlock) {
Manga mangaFromHtmlBlock = DefaultFactory.Manga.constructDefault(); Manga mangaFromHtmlBlock = new Manga();
mangaFromHtmlBlock.setSource(NAME);
Element urlElement = htmlBlock.select("a[href^=http://bato.to]").first(); Element urlElement = htmlBlock.select("a[href^=http://bato.to]").first();
Element nameElement = urlElement; Element nameElement = urlElement;
Element updateElement = htmlBlock.select("td").get(5); Element updateElement = htmlBlock.select("td").get(5);
mangaFromHtmlBlock.source = SourceManager.BATOTO;
if (urlElement != null) { if (urlElement != null) {
String fieldUrl = urlElement.attr("href"); String fieldUrl = urlElement.attr("href");
mangaFromHtmlBlock.setUrl(fieldUrl); mangaFromHtmlBlock.url = fieldUrl;
} }
if (nameElement != null) { if (nameElement != null) {
String fieldName = nameElement.text().trim(); String fieldName = nameElement.text().trim();
mangaFromHtmlBlock.setName(fieldName); mangaFromHtmlBlock.title = fieldName;
} }
if (updateElement != null) { if (updateElement != null) {
long fieldUpdate = parseUpdateFromElement(updateElement); long fieldUpdate = parseUpdateFromElement(updateElement);
mangaFromHtmlBlock.setUpdated(fieldUpdate); mangaFromHtmlBlock.last_update = fieldUpdate;
} }
int updateCount = 1;
mangaFromHtmlBlock.setUpdateCount(updateCount);
return mangaFromHtmlBlock; return mangaFromHtmlBlock;
} }
@ -186,9 +176,11 @@ public class Batoto {
// Do Nothing. // Do Nothing.
} }
return DefaultFactory.Manga.DEFAULT_UPDATED; return 0;
} }
/*
private void updateLibraryInDatabase(List<Manga> mangaList) { private void updateLibraryInDatabase(List<Manga> mangaList) {
mQueryManager.beginLibraryTransaction(); mQueryManager.beginLibraryTransaction();
try { try {

View file

@ -0,0 +1,5 @@
package eu.kanade.mangafeed.sources;
public class Source {
}

View file

@ -0,0 +1,52 @@
package eu.kanade.mangafeed.util;
import java.util.ArrayList;
import java.util.List;
import eu.kanade.mangafeed.data.models.Chapter;
import eu.kanade.mangafeed.data.models.Manga;
/**
* Created by len on 8/10/15.
*/
public class DummyDataUtil {
public static List<Manga> createDummyManga() {
ArrayList<Manga> mangas = new ArrayList<>();
mangas.add(createDummyManga("One Piece"));
mangas.add(createDummyManga("Berserk"));
mangas.add(createDummyManga("Horimiya"));
mangas.add(createDummyManga("Übel Blatt"));
return mangas;
}
private static Manga createDummyManga(String title) {
Manga m = new Manga();
m.title = title;
m.url="http://example.com";
m.artist="Eiichiro Oda";
m.author="Eiichiro Oda";
m.description="...";
m.genre="Action, Drama";
m.status="Ongoing";
m.thumbnail_url="http://example.com/pic.png";
return m;
}
public static List<Chapter> createDummyChapters() {
List<Chapter> chapters = new ArrayList<>();
Chapter c;
for (int i = 1; i < 50; i++) {
c = new Chapter();
c.manga_id = 1L;
c.name = "Chapter " + i;
c.url = "http://example.com/1";
chapters.add(c);
}
return chapters;
}
}