DatabaseHelper.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. package eu.kanade.mangafeed.data.database;
  2. import android.content.Context;
  3. import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping;
  4. import com.pushtorefresh.storio.sqlite.StorIOSQLite;
  5. import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite;
  6. import com.pushtorefresh.storio.sqlite.operations.delete.PreparedDeleteCollectionOfObjects;
  7. import com.pushtorefresh.storio.sqlite.operations.delete.PreparedDeleteObject;
  8. import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetListOfObjects;
  9. import com.pushtorefresh.storio.sqlite.operations.put.PreparedPutCollectionOfObjects;
  10. import com.pushtorefresh.storio.sqlite.operations.put.PreparedPutObject;
  11. import com.pushtorefresh.storio.sqlite.operations.put.PutResults;
  12. import com.pushtorefresh.storio.sqlite.queries.Query;
  13. import com.pushtorefresh.storio.sqlite.queries.RawQuery;
  14. import java.util.List;
  15. import eu.kanade.mangafeed.data.database.models.Category;
  16. import eu.kanade.mangafeed.data.database.models.CategoryStorIOSQLiteDeleteResolver;
  17. import eu.kanade.mangafeed.data.database.models.CategoryStorIOSQLiteGetResolver;
  18. import eu.kanade.mangafeed.data.database.models.CategoryStorIOSQLitePutResolver;
  19. import eu.kanade.mangafeed.data.database.models.Chapter;
  20. import eu.kanade.mangafeed.data.database.models.ChapterStorIOSQLiteDeleteResolver;
  21. import eu.kanade.mangafeed.data.database.models.ChapterStorIOSQLiteGetResolver;
  22. import eu.kanade.mangafeed.data.database.models.ChapterStorIOSQLitePutResolver;
  23. import eu.kanade.mangafeed.data.database.models.Manga;
  24. import eu.kanade.mangafeed.data.database.models.MangaCategory;
  25. import eu.kanade.mangafeed.data.database.models.MangaCategoryStorIOSQLiteDeleteResolver;
  26. import eu.kanade.mangafeed.data.database.models.MangaCategoryStorIOSQLiteGetResolver;
  27. import eu.kanade.mangafeed.data.database.models.MangaCategoryStorIOSQLitePutResolver;
  28. import eu.kanade.mangafeed.data.database.models.MangaStorIOSQLiteDeleteResolver;
  29. import eu.kanade.mangafeed.data.database.models.MangaStorIOSQLiteGetResolver;
  30. import eu.kanade.mangafeed.data.database.models.MangaStorIOSQLitePutResolver;
  31. import eu.kanade.mangafeed.data.database.models.MangaSync;
  32. import eu.kanade.mangafeed.data.database.models.MangaSyncStorIOSQLiteDeleteResolver;
  33. import eu.kanade.mangafeed.data.database.models.MangaSyncStorIOSQLiteGetResolver;
  34. import eu.kanade.mangafeed.data.database.models.MangaSyncStorIOSQLitePutResolver;
  35. import eu.kanade.mangafeed.data.database.resolvers.LibraryMangaGetResolver;
  36. import eu.kanade.mangafeed.data.database.tables.CategoryTable;
  37. import eu.kanade.mangafeed.data.database.tables.ChapterTable;
  38. import eu.kanade.mangafeed.data.database.tables.MangaSyncTable;
  39. import eu.kanade.mangafeed.data.database.tables.MangaTable;
  40. import eu.kanade.mangafeed.data.mangasync.base.MangaSyncService;
  41. import eu.kanade.mangafeed.util.ChapterRecognition;
  42. import eu.kanade.mangafeed.util.PostResult;
  43. import rx.Observable;
  44. public class DatabaseHelper {
  45. private StorIOSQLite db;
  46. public DatabaseHelper(Context context) {
  47. db = DefaultStorIOSQLite.builder()
  48. .sqliteOpenHelper(new DbOpenHelper(context))
  49. .addTypeMapping(Manga.class, SQLiteTypeMapping.<Manga>builder()
  50. .putResolver(new MangaStorIOSQLitePutResolver())
  51. .getResolver(new MangaStorIOSQLiteGetResolver())
  52. .deleteResolver(new MangaStorIOSQLiteDeleteResolver())
  53. .build())
  54. .addTypeMapping(Chapter.class, SQLiteTypeMapping.<Chapter>builder()
  55. .putResolver(new ChapterStorIOSQLitePutResolver())
  56. .getResolver(new ChapterStorIOSQLiteGetResolver())
  57. .deleteResolver(new ChapterStorIOSQLiteDeleteResolver())
  58. .build())
  59. .addTypeMapping(MangaSync.class, SQLiteTypeMapping.<MangaSync>builder()
  60. .putResolver(new MangaSyncStorIOSQLitePutResolver())
  61. .getResolver(new MangaSyncStorIOSQLiteGetResolver())
  62. .deleteResolver(new MangaSyncStorIOSQLiteDeleteResolver())
  63. .build())
  64. .addTypeMapping(Category.class, SQLiteTypeMapping.<Category>builder()
  65. .putResolver(new CategoryStorIOSQLitePutResolver())
  66. .getResolver(new CategoryStorIOSQLiteGetResolver())
  67. .deleteResolver(new CategoryStorIOSQLiteDeleteResolver())
  68. .build())
  69. .addTypeMapping(MangaCategory.class, SQLiteTypeMapping.<MangaCategory>builder()
  70. .putResolver(new MangaCategoryStorIOSQLitePutResolver())
  71. .getResolver(new MangaCategoryStorIOSQLiteGetResolver())
  72. .deleteResolver(new MangaCategoryStorIOSQLiteDeleteResolver())
  73. .build())
  74. .build();
  75. }
  76. // Mangas related queries
  77. public PreparedGetListOfObjects<Manga> getMangas() {
  78. return db.get()
  79. .listOfObjects(Manga.class)
  80. .withQuery(Query.builder()
  81. .table(MangaTable.TABLE)
  82. .build())
  83. .prepare();
  84. }
  85. public PreparedGetListOfObjects<Manga> getLibraryMangas() {
  86. return db.get()
  87. .listOfObjects(Manga.class)
  88. .withQuery(RawQuery.builder()
  89. .query(LibraryMangaGetResolver.QUERY)
  90. .observesTables(MangaTable.TABLE, ChapterTable.TABLE, CategoryTable.TABLE)
  91. .build())
  92. .withGetResolver(LibraryMangaGetResolver.INSTANCE)
  93. .prepare();
  94. }
  95. public PreparedGetListOfObjects<Manga> getFavoriteMangas() {
  96. return db.get()
  97. .listOfObjects(Manga.class)
  98. .withQuery(Query.builder()
  99. .table(MangaTable.TABLE)
  100. .where(MangaTable.COLUMN_FAVORITE + "=?")
  101. .whereArgs(1)
  102. .build())
  103. .prepare();
  104. }
  105. public PreparedGetListOfObjects<Manga> getManga(String url, int sourceId) {
  106. return db.get()
  107. .listOfObjects(Manga.class)
  108. .withQuery(Query.builder()
  109. .table(MangaTable.TABLE)
  110. .where(MangaTable.COLUMN_URL + "=? AND " + MangaTable.COLUMN_SOURCE + "=?")
  111. .whereArgs(url, sourceId)
  112. .build())
  113. .prepare();
  114. }
  115. public PreparedGetListOfObjects<Manga> getManga(long id) {
  116. return db.get()
  117. .listOfObjects(Manga.class)
  118. .withQuery(Query.builder()
  119. .table(MangaTable.TABLE)
  120. .where(MangaTable.COLUMN_ID + "=?")
  121. .whereArgs(id)
  122. .build())
  123. .prepare();
  124. }
  125. public PreparedPutObject<Manga> insertManga(Manga manga) {
  126. return db.put()
  127. .object(manga)
  128. .prepare();
  129. }
  130. public PreparedPutCollectionOfObjects<Manga> insertMangas(List<Manga> mangas) {
  131. return db.put()
  132. .objects(mangas)
  133. .prepare();
  134. }
  135. public PreparedDeleteObject<Manga> deleteManga(Manga manga) {
  136. return db.delete()
  137. .object(manga)
  138. .prepare();
  139. }
  140. public PreparedDeleteCollectionOfObjects<Manga> deleteMangas(List<Manga> mangas) {
  141. return db.delete()
  142. .objects(mangas)
  143. .prepare();
  144. }
  145. // Chapters related queries
  146. public PreparedGetListOfObjects<Chapter> getChapters(Manga manga) {
  147. return db.get()
  148. .listOfObjects(Chapter.class)
  149. .withQuery(Query.builder()
  150. .table(ChapterTable.TABLE)
  151. .where(ChapterTable.COLUMN_MANGA_ID + "=?")
  152. .whereArgs(manga.id)
  153. .build())
  154. .prepare();
  155. }
  156. public PreparedGetListOfObjects<Chapter> getChapters(long manga_id, boolean sortAToZ, boolean onlyUnread) {
  157. Query.CompleteBuilder query = Query.builder()
  158. .table(ChapterTable.TABLE)
  159. .orderBy(ChapterTable.COLUMN_CHAPTER_NUMBER + (sortAToZ ? " ASC" : " DESC"));
  160. if (onlyUnread) {
  161. query = query.where(ChapterTable.COLUMN_MANGA_ID + "=? AND " + ChapterTable.COLUMN_READ + "=?")
  162. .whereArgs(manga_id, 0);
  163. } else {
  164. query = query.where(ChapterTable.COLUMN_MANGA_ID + "=?")
  165. .whereArgs(manga_id);
  166. }
  167. return db.get()
  168. .listOfObjects(Chapter.class)
  169. .withQuery(query.build())
  170. .prepare();
  171. }
  172. public PreparedGetListOfObjects<Chapter> getNextChapter(Chapter chapter) {
  173. // Add a delta to the chapter number, because binary decimal representation
  174. // can retrieve the same chapter again
  175. double chapterNumber = chapter.chapter_number + 0.00001;
  176. return db.get()
  177. .listOfObjects(Chapter.class)
  178. .withQuery(Query.builder()
  179. .table(ChapterTable.TABLE)
  180. .where(ChapterTable.COLUMN_MANGA_ID + "=? AND " +
  181. ChapterTable.COLUMN_CHAPTER_NUMBER + ">? AND " +
  182. ChapterTable.COLUMN_CHAPTER_NUMBER + "<=?")
  183. .whereArgs(chapter.manga_id, chapterNumber, chapterNumber + 1)
  184. .orderBy(ChapterTable.COLUMN_CHAPTER_NUMBER)
  185. .limit(1)
  186. .build())
  187. .prepare();
  188. }
  189. public PreparedGetListOfObjects<Chapter> getPreviousChapter(Chapter chapter) {
  190. // Add a delta to the chapter number, because binary decimal representation
  191. // can retrieve the same chapter again
  192. double chapterNumber = chapter.chapter_number - 0.00001;
  193. return db.get()
  194. .listOfObjects(Chapter.class)
  195. .withQuery(Query.builder()
  196. .table(ChapterTable.TABLE)
  197. .where(ChapterTable.COLUMN_MANGA_ID + "=? AND " +
  198. ChapterTable.COLUMN_CHAPTER_NUMBER + "<? AND " +
  199. ChapterTable.COLUMN_CHAPTER_NUMBER + ">=?")
  200. .whereArgs(chapter.manga_id, chapterNumber, chapterNumber - 1)
  201. .orderBy(ChapterTable.COLUMN_CHAPTER_NUMBER + " DESC")
  202. .limit(1)
  203. .build())
  204. .prepare();
  205. }
  206. public PreparedGetListOfObjects<Chapter> getNextUnreadChapter(Manga manga) {
  207. return db.get()
  208. .listOfObjects(Chapter.class)
  209. .withQuery(Query.builder()
  210. .table(ChapterTable.TABLE)
  211. .where(ChapterTable.COLUMN_MANGA_ID + "=? AND " +
  212. ChapterTable.COLUMN_READ + "=? AND " +
  213. ChapterTable.COLUMN_CHAPTER_NUMBER + ">=?")
  214. .whereArgs(manga.id, 0, 0)
  215. .orderBy(ChapterTable.COLUMN_CHAPTER_NUMBER)
  216. .limit(1)
  217. .build())
  218. .prepare();
  219. }
  220. public PreparedPutObject<Chapter> insertChapter(Chapter chapter) {
  221. return db.put()
  222. .object(chapter)
  223. .prepare();
  224. }
  225. public PreparedPutCollectionOfObjects<Chapter> insertChapters(List<Chapter> chapters) {
  226. return db.put()
  227. .objects(chapters)
  228. .prepare();
  229. }
  230. // Add new chapters or delete if the source deletes them
  231. public Observable<PostResult> insertOrRemoveChapters(Manga manga, List<Chapter> chapters) {
  232. for (Chapter chapter : chapters) {
  233. chapter.manga_id = manga.id;
  234. }
  235. Observable<List<Chapter>> chapterList = Observable.create(subscriber -> {
  236. subscriber.onNext(getChapters(manga).executeAsBlocking());
  237. subscriber.onCompleted();
  238. });
  239. Observable<Integer> newChaptersObs = chapterList
  240. .flatMap(dbChapters -> Observable.from(chapters)
  241. .filter(c -> !dbChapters.contains(c))
  242. .map(c -> {
  243. ChapterRecognition.parseChapterNumber(c, manga);
  244. return c;
  245. })
  246. .toList()
  247. .flatMap(newChapters -> insertChapters(newChapters).createObservable())
  248. .map(PutResults::numberOfInserts));
  249. Observable<Integer> deletedChaptersObs = chapterList
  250. .flatMap(dbChapters -> Observable.from(dbChapters)
  251. .filter(c -> !chapters.contains(c))
  252. .toList()
  253. .flatMap(deletedChapters -> deleteChapters(deletedChapters).createObservable())
  254. .map(d -> d.results().size()));
  255. return Observable.zip(newChaptersObs, deletedChaptersObs,
  256. (insertions, deletions) -> new PostResult(0, insertions, deletions)
  257. );
  258. }
  259. public PreparedDeleteObject<Chapter> deleteChapter(Chapter chapter) {
  260. return db.delete()
  261. .object(chapter)
  262. .prepare();
  263. }
  264. public PreparedDeleteCollectionOfObjects<Chapter> deleteChapters(List<Chapter> chapters) {
  265. return db.delete()
  266. .objects(chapters)
  267. .prepare();
  268. }
  269. // Manga sync related queries
  270. public PreparedGetListOfObjects<MangaSync> getMangaSync(Manga manga, MangaSyncService sync) {
  271. return db.get()
  272. .listOfObjects(MangaSync.class)
  273. .withQuery(Query.builder()
  274. .table(MangaSyncTable.TABLE)
  275. .where(MangaSyncTable.COLUMN_MANGA_ID + "=? AND " +
  276. MangaSyncTable.COLUMN_SYNC_ID + "=?")
  277. .whereArgs(manga.id, sync.getId())
  278. .build())
  279. .prepare();
  280. }
  281. public PreparedGetListOfObjects<MangaSync> getMangaSync(Manga manga) {
  282. return db.get()
  283. .listOfObjects(MangaSync.class)
  284. .withQuery(Query.builder()
  285. .table(MangaSyncTable.TABLE)
  286. .where(MangaSyncTable.COLUMN_MANGA_ID + "=?")
  287. .whereArgs(manga.id)
  288. .build())
  289. .prepare();
  290. }
  291. public PreparedPutObject<MangaSync> insertMangaSync(MangaSync manga) {
  292. return db.put()
  293. .object(manga)
  294. .prepare();
  295. }
  296. public PreparedDeleteObject<MangaSync> deleteMangaSync(MangaSync manga) {
  297. return db.delete()
  298. .object(manga)
  299. .prepare();
  300. }
  301. // Categories related queries
  302. public PreparedGetListOfObjects<Category> getCategories() {
  303. return db.get()
  304. .listOfObjects(Category.class)
  305. .withQuery(Query.builder()
  306. .table(CategoryTable.TABLE)
  307. .build())
  308. .prepare();
  309. }
  310. public PreparedPutObject<Category> insertCategory(Category category) {
  311. return db.put()
  312. .object(category)
  313. .prepare();
  314. }
  315. public PreparedDeleteObject<Category> deleteCategory(Category category) {
  316. return db.delete()
  317. .object(category)
  318. .prepare();
  319. }
  320. public PreparedPutObject<MangaCategory> insertMangaCategory(MangaCategory mangaCategory) {
  321. return db.put()
  322. .object(mangaCategory)
  323. .prepare();
  324. }
  325. public PreparedPutCollectionOfObjects<MangaCategory> insertMangasCategory(List<MangaCategory> mangasCategory) {
  326. return db.put()
  327. .objects(mangasCategory)
  328. .prepare();
  329. }
  330. }