Skip to content

Commit ec61837

Browse files
committed
Add reading bookmark to sura and bookmarks
1 parent e29d070 commit ec61837

10 files changed

Lines changed: 290 additions & 91 deletions

File tree

app/src/main/java/com/quran/labs/androidquran/dao/bookmark/BookmarkRowData.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package com.quran.labs.androidquran.dao.bookmark
22

33
import com.quran.data.model.bookmark.Bookmark
4+
import com.quran.data.model.bookmark.ReadingBookmark
45
import com.quran.data.model.bookmark.Tag
56

67
sealed class BookmarkRowData {
8+
data object ReadingBookmarkHeader : BookmarkRowData()
9+
data class ReadingBookmarkItem(val readingBookmark: ReadingBookmark) : BookmarkRowData()
10+
711
data class RecentPageHeader(val count: Int) : BookmarkRowData()
812
data class RecentPage(val recentPage: com.quran.data.model.bookmark.RecentPage) : BookmarkRowData()
913
data class TagHeader(val tag: Tag) : BookmarkRowData()

app/src/main/java/com/quran/labs/androidquran/presenter/bookmark/BookmarkPresenter.kt

Lines changed: 103 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
package com.quran.labs.androidquran.presenter.bookmark
22

3-
import android.annotation.SuppressLint
43
import androidx.annotation.VisibleForTesting
54
import com.google.android.material.snackbar.BaseTransientBottomBar
65
import com.quran.data.dao.BookmarksDao
6+
import com.quran.data.dao.ReadingBookmarksDao
77
import com.quran.data.dao.RecentPagesDao
88
import com.quran.data.model.bookmark.Bookmark
99
import com.quran.data.model.bookmark.BookmarkData
10+
import com.quran.data.model.bookmark.ReadingBookmark
1011
import com.quran.data.model.bookmark.RecentPage
1112
import com.quran.data.model.bookmark.Tag
1213
import com.quran.labs.androidquran.dao.bookmark.BookmarkRawResult
1314
import com.quran.labs.androidquran.dao.bookmark.BookmarkRowData
1415
import com.quran.labs.androidquran.dao.bookmark.BookmarkRowData.BookmarkItem
1516
import com.quran.labs.androidquran.dao.bookmark.BookmarkRowData.NotTaggedHeader
17+
import com.quran.labs.androidquran.dao.bookmark.BookmarkRowData.ReadingBookmarkHeader
18+
import com.quran.labs.androidquran.dao.bookmark.BookmarkRowData.ReadingBookmarkItem
1619
import com.quran.labs.androidquran.dao.bookmark.BookmarkRowData.RecentPageHeader
1720
import com.quran.labs.androidquran.dao.bookmark.BookmarkRowData.TagHeader
1821
import com.quran.labs.androidquran.model.translation.ArabicDatabaseUtils
@@ -27,16 +30,21 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
2730
import io.reactivex.rxjava3.core.Single
2831
import io.reactivex.rxjava3.observers.DisposableSingleObserver
2932
import io.reactivex.rxjava3.schedulers.Schedulers
33+
import kotlinx.coroutines.Dispatchers
3034
import kotlinx.coroutines.MainScope
35+
import kotlinx.coroutines.async
36+
import kotlinx.coroutines.coroutineScope
3137
import kotlinx.coroutines.flow.drop
3238
import kotlinx.coroutines.launch
3339
import kotlinx.coroutines.runBlocking
40+
import kotlinx.coroutines.withContext
3441
import timber.log.Timber
3542
import java.util.concurrent.TimeUnit
3643

3744
open class BookmarkPresenter @Inject internal constructor(
3845
private val bookmarksDao: BookmarksDao,
3946
private val recentPagesDao: RecentPagesDao,
47+
private val readingBookmarksDao: ReadingBookmarksDao,
4048
private val quranSettings: QuranSettings,
4149
private val arabicDatabaseUtils: Provider<ArabicDatabaseUtils>,
4250
) : Presenter<BookmarksFragment> {
@@ -81,6 +89,18 @@ open class BookmarkPresenter @Inject internal constructor(
8189
Timber.e(throwable, "Error observing recent page changes")
8290
}
8391
}
92+
93+
presenterScope.launch {
94+
try {
95+
readingBookmarksDao.readingBookmarkFlow()
96+
.drop(1)
97+
.collect {
98+
onObservedDataChanged()
99+
}
100+
} catch (throwable: Throwable) {
101+
Timber.e(throwable, "Error observing reading bookmark changes")
102+
}
103+
}
84104
}
85105

86106
private fun onObservedDataChanged() {
@@ -294,36 +314,11 @@ open class BookmarkPresenter @Inject internal constructor(
294314

295315
return Single.fromCallable {
296316
runBlocking {
297-
val tagsToDelete = mutableListOf<Tag>()
298-
val bookmarksToDelete = mutableListOf<Bookmark>()
299-
val bookmarksToUntag = mutableListOf<Pair<Bookmark, String>>()
300-
301-
items.forEach { row ->
302-
val tagId = row.userTagId()
303-
when {
304-
row.isBookmarkHeader && tagId != null -> {
305-
tagsToDelete += Tag(tagId, row.text)
306-
}
307-
308-
row.isBookmark && row.bookmark != null -> {
309-
if (tagId != null) {
310-
bookmarksToUntag += row.bookmark to tagId
311-
} else {
312-
bookmarksToDelete += row.bookmark
313-
}
314-
}
315-
}
316-
}
317-
318-
bookmarksDao.removeTags(tagsToDelete)
319-
bookmarksToUntag.forEach { (bookmark, tagId) ->
320-
bookmarksDao.removeBookmarkFromTag(bookmark, tagId)
321-
}
322-
bookmarksDao.removeBookmarks(bookmarksToDelete)
317+
removeItems(items)
318+
getBookmarksList(sortOrder, isGroupedByTags)
323319
}
324-
}.flatMap {
325-
getBookmarksListObservable(sortOrder, isGroupedByTags)
326320
}
321+
.subscribeOn(Schedulers.io())
327322
}
328323

329324
fun cancelDeletion() {
@@ -332,57 +327,89 @@ open class BookmarkPresenter @Inject internal constructor(
332327
itemsToRemove = null
333328
}
334329

335-
private fun getBookmarkDataObservable(sortOrder: Int): Single<BookmarkData> {
336-
return Single.fromCallable {
337-
runBlocking {
338-
BookmarkData(
339-
tags = bookmarksDao.tags(),
340-
bookmarks = bookmarksDao.bookmarks(sortOrder)
341-
)
330+
private suspend fun removeItems(items: List<QuranRow>) {
331+
withContext(Dispatchers.IO) {
332+
val tagsToDelete = mutableListOf<Tag>()
333+
val bookmarksToDelete = mutableListOf<Bookmark>()
334+
val bookmarksToUntag = mutableListOf<Pair<Bookmark, String>>()
335+
336+
items.forEach { row ->
337+
val tagId = row.userTagId()
338+
when {
339+
row.isBookmarkHeader && tagId != null -> {
340+
tagsToDelete += Tag(tagId, row.text)
341+
}
342+
343+
row.isBookmark && row.bookmark != null -> {
344+
if (tagId != null) {
345+
bookmarksToUntag += row.bookmark to tagId
346+
} else {
347+
bookmarksToDelete += row.bookmark
348+
}
349+
}
350+
}
342351
}
343-
}.subscribeOn(Schedulers.io())
352+
353+
bookmarksDao.removeTags(tagsToDelete)
354+
bookmarksToUntag.forEach { (bookmark, tagId) ->
355+
bookmarksDao.removeBookmarkFromTag(bookmark, tagId)
356+
}
357+
bookmarksDao.removeBookmarks(bookmarksToDelete)
358+
}
344359
}
345360

346-
private fun getBookmarksWithAyatObservable(sortOrder: Int): Single<BookmarkData> {
347-
return Single.zip(
348-
getBookmarkDataObservable(sortOrder),
349-
getRecentPagesObservable()
350-
) { bookmarkData: BookmarkData, recentPages: List<RecentPage> ->
351-
bookmarkData.copy(recentPages = recentPages)
361+
private suspend fun getBookmarkData(sortOrder: Int): BookmarkData {
362+
return withContext(Dispatchers.IO) {
363+
BookmarkData(
364+
tags = bookmarksDao.tags(),
365+
bookmarks = bookmarksDao.bookmarks(sortOrder)
366+
)
352367
}
353-
.map { bookmarkData ->
354-
try {
355-
bookmarkData.copy(
356-
bookmarks = arabicDatabaseUtils().hydrateAyahText(bookmarkData.bookmarks.toMutableList())
357-
)
358-
} catch (exception: Exception) {
359-
bookmarkData
360-
}
368+
}
369+
370+
private suspend fun getBookmarksWithAyat(sortOrder: Int): BookmarkData {
371+
return coroutineScope {
372+
val bookmarkData = async { getBookmarkData(sortOrder) }
373+
val recentPages = async { getRecentPages() }
374+
375+
hydrateAyahText(
376+
bookmarkData.await().copy(recentPages = recentPages.await())
377+
)
378+
}
379+
}
380+
381+
private suspend fun hydrateAyahText(bookmarkData: BookmarkData): BookmarkData {
382+
return withContext(Dispatchers.IO) {
383+
try {
384+
bookmarkData.copy(
385+
bookmarks = arabicDatabaseUtils().hydrateAyahText(bookmarkData.bookmarks.toMutableList())
386+
)
387+
} catch (exception: Exception) {
388+
bookmarkData
361389
}
390+
}
362391
}
363392

364-
private fun getRecentPagesObservable(): Single<List<RecentPage>> {
365-
return Single.fromCallable {
366-
runBlocking { recentPagesDao.recentPages() }
367-
}.subscribeOn(Schedulers.io())
393+
private suspend fun getRecentPages(): List<RecentPage> {
394+
return withContext(Dispatchers.IO) { recentPagesDao.recentPages() }
368395
}
369396

370397
@VisibleForTesting
371-
fun getBookmarksListObservable(sortOrder: Int, groupByTags: Boolean): Single<BookmarkRawResult> {
372-
return getBookmarksWithAyatObservable(sortOrder)
373-
.map { bookmarkData ->
374-
val rows = getBookmarkRowData(bookmarkData, groupByTags)
375-
val tagMap = generateTagMap(bookmarkData.tags)
376-
BookmarkRawResult(rows, tagMap)
377-
}
378-
.subscribeOn(Schedulers.io())
398+
suspend fun getBookmarksList(sortOrder: Int, groupByTags: Boolean): BookmarkRawResult {
399+
return coroutineScope {
400+
val bookmarkData = async { getBookmarksWithAyat(sortOrder) }
401+
val readingBookmark = async { readingBookmarksDao.readingBookmark() }
402+
val data = bookmarkData.await()
403+
val rows = getBookmarkRowData(data, groupByTags, readingBookmark.await())
404+
val tagMap = generateTagMap(data.tags)
405+
BookmarkRawResult(rows, tagMap)
406+
}
379407
}
380408

381-
@SuppressLint("CheckResult")
382409
private fun getBookmarks(sortOrder: Int, groupByTags: Boolean) {
383-
getBookmarksListObservable(sortOrder, groupByTags)
384-
.observeOn(AndroidSchedulers.mainThread())
385-
.subscribe({ result ->
410+
presenterScope.launch {
411+
try {
412+
val result = getBookmarksList(sortOrder, groupByTags)
386413
cachedData = result
387414
val fragment = fragment
388415
if (fragment != null) {
@@ -394,14 +421,16 @@ open class BookmarkPresenter @Inject internal constructor(
394421
fragment.onNewRawData(result)
395422
}
396423
}
397-
}, { throwable ->
424+
} catch (throwable: Throwable) {
398425
Timber.e(throwable, "Unable to load bookmarks")
399-
})
426+
}
427+
}
400428
}
401429

402430
private fun getBookmarkRowData(
403431
data: BookmarkData,
404-
groupByTags: Boolean
432+
groupByTags: Boolean,
433+
readingBookmark: ReadingBookmark?
405434
): MutableList<BookmarkRowData> {
406435
val rows = if (groupByTags) {
407436
getRowDataSortedByTags(data.tags, data.bookmarks)
@@ -420,6 +449,10 @@ open class BookmarkPresenter @Inject internal constructor(
420449
rows.add(i + 1, BookmarkRowData.RecentPage(recentPages[i]))
421450
}
422451
}
452+
if (readingBookmark != null) {
453+
rows.add(0, ReadingBookmarkHeader)
454+
rows.add(1, ReadingBookmarkItem(readingBookmark))
455+
}
423456
return rows
424457
}
425458

app/src/main/java/com/quran/labs/androidquran/ui/fragment/BookmarksFragment.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ class BookmarksFragment : Fragment(), QuranTouchListener {
180180
val bookmarksAdapter = bookmarksAdapter
181181
val bookmarkPresenter = bookmarkPresenter
182182
if (bookmarksAdapter != null && items != null) {
183+
if (bookmarksContextualModePresenter.isInActionMode()) {
184+
bookmarksContextualModePresenter.finishActionMode()
185+
}
183186
bookmarksAdapter.setShowTags(bookmarkPresenter.shouldShowInlineTags())
184187
bookmarksAdapter.setShowDate(bookmarkPresenter.isDateShowing)
185188
bookmarksAdapter.setElements(

0 commit comments

Comments
 (0)