Show suggestions on the shelf
This commit is contained in:
@@ -5,8 +5,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import dagger.hilt.android.testing.HiltAndroidRule
|
import dagger.hilt.android.testing.HiltAndroidRule
|
||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
import java.io.File
|
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.*
|
||||||
@@ -20,6 +18,8 @@ import org.koitharu.kotatsu.core.db.MangaDatabase
|
|||||||
import org.koitharu.kotatsu.core.db.entity.toMangaTags
|
import org.koitharu.kotatsu.core.db.entity.toMangaTags
|
||||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltAndroidTest
|
@HiltAndroidTest
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@@ -52,6 +52,7 @@ class AppBackupAgentTest {
|
|||||||
title = SampleData.favouriteCategory.title,
|
title = SampleData.favouriteCategory.title,
|
||||||
sortOrder = SampleData.favouriteCategory.order,
|
sortOrder = SampleData.favouriteCategory.order,
|
||||||
isTrackerEnabled = SampleData.favouriteCategory.isTrackingEnabled,
|
isTrackerEnabled = SampleData.favouriteCategory.isTrackingEnabled,
|
||||||
|
isVisibleOnShelf = SampleData.favouriteCategory.isVisibleInLibrary,
|
||||||
)
|
)
|
||||||
favouritesRepository.addToCategory(categoryId = category.id, mangas = listOf(SampleData.manga))
|
favouritesRepository.addToCategory(categoryId = category.id, mangas = listOf(SampleData.manga))
|
||||||
historyRepository.addOrUpdate(
|
historyRepository.addOrUpdate(
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import android.text.format.DateUtils
|
|||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.app.PendingIntentCompat
|
import androidx.core.app.PendingIntentCompat
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
@@ -155,8 +154,7 @@ class DownloadNotificationFactory @AssistedInject constructor(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
if (state.error != null) {
|
if (state.error != null) {
|
||||||
builder.setContentText(state.error)
|
builder.setContentText(context.getString(R.string.download_summary_pattern, percent, state.error))
|
||||||
builder.setSubText(percent)
|
|
||||||
} else {
|
} else {
|
||||||
builder.setContentText(percent)
|
builder.setContentText(percent)
|
||||||
}
|
}
|
||||||
@@ -183,22 +181,7 @@ class DownloadNotificationFactory @AssistedInject constructor(
|
|||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
builder.setProgress(state.max, state.progress, false)
|
builder.setProgress(state.max, state.progress, false)
|
||||||
val percent = if (state.percent >= 0f) {
|
builder.setContentText(getProgressString(state.percent, state.eta))
|
||||||
context.getString(R.string.percent_string_pattern, (state.percent * 100).format())
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
if (state.eta > 0L) {
|
|
||||||
val eta = DateUtils.getRelativeTimeSpanString(
|
|
||||||
state.eta,
|
|
||||||
System.currentTimeMillis(),
|
|
||||||
DateUtils.SECOND_IN_MILLIS,
|
|
||||||
)
|
|
||||||
builder.setContentText(eta)
|
|
||||||
builder.setSubText(percent)
|
|
||||||
} else {
|
|
||||||
builder.setContentText(percent)
|
|
||||||
}
|
|
||||||
builder.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
builder.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||||
builder.setStyle(null)
|
builder.setStyle(null)
|
||||||
builder.setOngoing(true)
|
builder.setOngoing(true)
|
||||||
@@ -209,6 +192,29 @@ class DownloadNotificationFactory @AssistedInject constructor(
|
|||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getProgressString(percent: Float, eta: Long): CharSequence? {
|
||||||
|
val percentString = if (percent >= 0f) {
|
||||||
|
context.getString(R.string.percent_string_pattern, (percent * 100).format())
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
val etaString = if (eta > 0L) {
|
||||||
|
DateUtils.getRelativeTimeSpanString(
|
||||||
|
eta,
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
DateUtils.SECOND_IN_MILLIS,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
return when {
|
||||||
|
percentString == null && etaString == null -> null
|
||||||
|
percentString != null && etaString == null -> percentString
|
||||||
|
percentString == null && etaString != null -> etaString
|
||||||
|
else -> context.getString(R.string.download_summary_pattern, percentString, etaString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createMangaIntent(context: Context, manga: Manga?) = PendingIntentCompat.getActivity(
|
private fun createMangaIntent(context: Context, manga: Manga?) = PendingIntentCompat.getActivity(
|
||||||
context,
|
context,
|
||||||
manga.hashCode(),
|
manga.hashCode(),
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class ShelfContent(
|
|||||||
val favourites: Map<FavouriteCategory, List<Manga>>,
|
val favourites: Map<FavouriteCategory, List<Manga>>,
|
||||||
val updated: Map<Manga, Int>,
|
val updated: Map<Manga, Int>,
|
||||||
val local: List<Manga>,
|
val local: List<Manga>,
|
||||||
|
val suggestions: List<Manga>,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
@@ -21,8 +22,7 @@ class ShelfContent(
|
|||||||
if (favourites != other.favourites) return false
|
if (favourites != other.favourites) return false
|
||||||
if (updated != other.updated) return false
|
if (updated != other.updated) return false
|
||||||
if (local != other.local) return false
|
if (local != other.local) return false
|
||||||
|
return suggestions == other.suggestions
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
@@ -30,6 +30,7 @@ class ShelfContent(
|
|||||||
result = 31 * result + favourites.hashCode()
|
result = 31 * result + favourites.hashCode()
|
||||||
result = 31 * result + updated.hashCode()
|
result = 31 * result + updated.hashCode()
|
||||||
result = 31 * result + local.hashCode()
|
result = 31 * result + local.hashCode()
|
||||||
|
result = 31 * result + suggestions.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
|||||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||||
|
import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
|
||||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||||
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
|
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -34,6 +35,7 @@ class ShelfRepository @Inject constructor(
|
|||||||
private val localMangaRepository: LocalMangaRepository,
|
private val localMangaRepository: LocalMangaRepository,
|
||||||
private val historyRepository: HistoryRepository,
|
private val historyRepository: HistoryRepository,
|
||||||
private val trackingRepository: TrackingRepository,
|
private val trackingRepository: TrackingRepository,
|
||||||
|
private val suggestionRepository: SuggestionRepository,
|
||||||
private val db: MangaDatabase,
|
private val db: MangaDatabase,
|
||||||
@LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>,
|
@LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>,
|
||||||
) {
|
) {
|
||||||
@@ -43,8 +45,9 @@ class ShelfRepository @Inject constructor(
|
|||||||
observeLocalManga(SortOrder.UPDATED),
|
observeLocalManga(SortOrder.UPDATED),
|
||||||
observeFavourites(),
|
observeFavourites(),
|
||||||
trackingRepository.observeUpdatedManga(),
|
trackingRepository.observeUpdatedManga(),
|
||||||
) { history, local, favorites, updated ->
|
suggestionRepository.observeAll(16),
|
||||||
ShelfContent(history, favorites, updated, local)
|
) { history, local, favorites, updated, suggestions ->
|
||||||
|
ShelfContent(history, favorites, updated, local, suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeLocalManga(sortOrder: SortOrder): Flow<List<Manga>> {
|
private fun observeLocalManga(sortOrder: SortOrder): Flow<List<Manga>> {
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ package org.koitharu.kotatsu.shelf.domain
|
|||||||
|
|
||||||
enum class ShelfSection {
|
enum class ShelfSection {
|
||||||
|
|
||||||
HISTORY, LOCAL, UPDATED, FAVORITES;
|
HISTORY, LOCAL, UPDATED, FAVORITES, SUGGESTIONS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import org.koitharu.kotatsu.search.ui.MangaListActivity
|
|||||||
import org.koitharu.kotatsu.shelf.ui.adapter.ShelfAdapter
|
import org.koitharu.kotatsu.shelf.ui.adapter.ShelfAdapter
|
||||||
import org.koitharu.kotatsu.shelf.ui.adapter.ShelfListEventListener
|
import org.koitharu.kotatsu.shelf.ui.adapter.ShelfListEventListener
|
||||||
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
|
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
|
||||||
|
import org.koitharu.kotatsu.suggestions.ui.SuggestionsActivity
|
||||||
import org.koitharu.kotatsu.tracker.ui.updates.UpdatesActivity
|
import org.koitharu.kotatsu.tracker.ui.updates.UpdatesActivity
|
||||||
import org.koitharu.kotatsu.utils.ext.addMenuProvider
|
import org.koitharu.kotatsu.utils.ext.addMenuProvider
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -118,6 +119,7 @@ class ShelfFragment :
|
|||||||
is ShelfSectionModel.Favourites -> FavouritesActivity.newIntent(view.context, section.category)
|
is ShelfSectionModel.Favourites -> FavouritesActivity.newIntent(view.context, section.category)
|
||||||
is ShelfSectionModel.Updated -> UpdatesActivity.newIntent(view.context)
|
is ShelfSectionModel.Updated -> UpdatesActivity.newIntent(view.context)
|
||||||
is ShelfSectionModel.Local -> MangaListActivity.newIntent(view.context, MangaSource.LOCAL)
|
is ShelfSectionModel.Local -> MangaListActivity.newIntent(view.context, MangaSource.LOCAL)
|
||||||
|
is ShelfSectionModel.Suggestions -> SuggestionsActivity.newIntent(view.context)
|
||||||
}
|
}
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ class ShelfSelectionCallback(
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
val checkedIds = controller.peekCheckedIds().entries
|
val checkedIds = controller.peekCheckedIds().entries
|
||||||
val singleKey = checkedIds.singleOrNull { (_, ids) -> ids.isNotEmpty() }?.key
|
val singleKey = checkedIds.singleOrNull { (_, ids) -> ids.isNotEmpty() }?.key
|
||||||
menu.findItem(R.id.action_remove)?.isVisible = singleKey != null && singleKey !is ShelfSectionModel.Updated
|
menu.findItem(R.id.action_remove)?.isVisible = singleKey != null &&
|
||||||
|
singleKey !is ShelfSectionModel.Updated &&
|
||||||
|
singleKey !is ShelfSectionModel.Suggestions
|
||||||
menu.findItem(R.id.action_save)?.isVisible = singleKey !is ShelfSectionModel.Local
|
menu.findItem(R.id.action_save)?.isVisible = singleKey !is ShelfSectionModel.Local
|
||||||
return super.onPrepareActionMode(controller, mode, menu)
|
return super.onPrepareActionMode(controller, mode, menu)
|
||||||
}
|
}
|
||||||
@@ -82,6 +84,8 @@ class ShelfSelectionCallback(
|
|||||||
showDeletionConfirm(ids, mode)
|
showDeletionConfirm(ids, mode)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ShelfSectionModel.Suggestions -> return false
|
||||||
}
|
}
|
||||||
mode.finish()
|
mode.finish()
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -58,10 +58,11 @@ class ShelfViewModel @Inject constructor(
|
|||||||
val content: LiveData<List<ListModel>> = combine(
|
val content: LiveData<List<ListModel>> = combine(
|
||||||
settings.observeAsFlow(AppSettings.KEY_SHELF_SECTIONS) { shelfSections },
|
settings.observeAsFlow(AppSettings.KEY_SHELF_SECTIONS) { shelfSections },
|
||||||
settings.observeAsFlow(AppSettings.KEY_TRACKER_ENABLED) { isTrackerEnabled },
|
settings.observeAsFlow(AppSettings.KEY_TRACKER_ENABLED) { isTrackerEnabled },
|
||||||
|
settings.observeAsFlow(AppSettings.KEY_SUGGESTIONS) { isSuggestionsEnabled },
|
||||||
networkState,
|
networkState,
|
||||||
repository.observeShelfContent(),
|
repository.observeShelfContent(),
|
||||||
) { sections, isTrackerEnabled, isConnected, content ->
|
) { sections, isTrackerEnabled, isSuggestionsEnabled, isConnected, content ->
|
||||||
mapList(content, isTrackerEnabled, sections, isConnected)
|
mapList(content, isTrackerEnabled, isSuggestionsEnabled, sections, isConnected)
|
||||||
}.catch { e ->
|
}.catch { e ->
|
||||||
emit(listOf(e.toErrorState(canRetry = false)))
|
emit(listOf(e.toErrorState(canRetry = false)))
|
||||||
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
||||||
@@ -157,6 +158,7 @@ class ShelfViewModel @Inject constructor(
|
|||||||
private suspend fun mapList(
|
private suspend fun mapList(
|
||||||
content: ShelfContent,
|
content: ShelfContent,
|
||||||
isTrackerEnabled: Boolean,
|
isTrackerEnabled: Boolean,
|
||||||
|
isSuggestionsEnabled: Boolean,
|
||||||
sections: List<ShelfSection>,
|
sections: List<ShelfSection>,
|
||||||
isNetworkAvailable: Boolean,
|
isNetworkAvailable: Boolean,
|
||||||
): List<ListModel> {
|
): List<ListModel> {
|
||||||
@@ -171,6 +173,9 @@ class ShelfViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShelfSection.FAVORITES -> mapFavourites(result, content.favourites)
|
ShelfSection.FAVORITES -> mapFavourites(result, content.favourites)
|
||||||
|
ShelfSection.SUGGESTIONS -> if (isSuggestionsEnabled) {
|
||||||
|
mapSuggestions(result, content.suggestions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -190,6 +195,7 @@ class ShelfViewModel @Inject constructor(
|
|||||||
ShelfSection.LOCAL -> mapLocal(result, content.local)
|
ShelfSection.LOCAL -> mapLocal(result, content.local)
|
||||||
ShelfSection.UPDATED -> Unit
|
ShelfSection.UPDATED -> Unit
|
||||||
ShelfSection.FAVORITES -> Unit
|
ShelfSection.FAVORITES -> Unit
|
||||||
|
ShelfSection.SUGGESTIONS -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,6 +263,19 @@ class ShelfViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun mapSuggestions(
|
||||||
|
destination: MutableList<in ShelfSectionModel.Suggestions>,
|
||||||
|
suggestions: List<Manga>,
|
||||||
|
) {
|
||||||
|
if (suggestions.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
destination += ShelfSectionModel.Suggestions(
|
||||||
|
items = suggestions.toUi(ListMode.GRID, this, null),
|
||||||
|
showAllButtonText = R.string.show_all,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun mapFavourites(
|
private suspend fun mapFavourites(
|
||||||
destination: MutableList<in ShelfSectionModel.Favourites>,
|
destination: MutableList<in ShelfSectionModel.Favourites>,
|
||||||
favourites: Map<FavouriteCategory, List<Manga>>,
|
favourites: Map<FavouriteCategory, List<Manga>>,
|
||||||
|
|||||||
@@ -73,4 +73,5 @@ private val ShelfSection.titleResId: Int
|
|||||||
ShelfSection.LOCAL -> R.string.local_storage
|
ShelfSection.LOCAL -> R.string.local_storage
|
||||||
ShelfSection.UPDATED -> R.string.updated
|
ShelfSection.UPDATED -> R.string.updated
|
||||||
ShelfSection.FAVORITES -> R.string.favourites
|
ShelfSection.FAVORITES -> R.string.favourites
|
||||||
|
ShelfSection.SUGGESTIONS -> R.string.suggestions
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ sealed interface ShelfSectionModel : ListModel {
|
|||||||
other as History
|
other as History
|
||||||
|
|
||||||
if (showAllButtonText != other.showAllButtonText) return false
|
if (showAllButtonText != other.showAllButtonText) return false
|
||||||
if (items != other.items) return false
|
return items == other.items
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
@@ -67,9 +65,7 @@ sealed interface ShelfSectionModel : ListModel {
|
|||||||
|
|
||||||
if (category != other.category) return false
|
if (category != other.category) return false
|
||||||
if (showAllButtonText != other.showAllButtonText) return false
|
if (showAllButtonText != other.showAllButtonText) return false
|
||||||
if (items != other.items) return false
|
return items == other.items
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
@@ -98,9 +94,7 @@ sealed interface ShelfSectionModel : ListModel {
|
|||||||
other as Updated
|
other as Updated
|
||||||
|
|
||||||
if (items != other.items) return false
|
if (items != other.items) return false
|
||||||
if (showAllButtonText != other.showAllButtonText) return false
|
return showAllButtonText == other.showAllButtonText
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
@@ -128,9 +122,35 @@ sealed interface ShelfSectionModel : ListModel {
|
|||||||
other as Local
|
other as Local
|
||||||
|
|
||||||
if (items != other.items) return false
|
if (items != other.items) return false
|
||||||
if (showAllButtonText != other.showAllButtonText) return false
|
return showAllButtonText == other.showAllButtonText
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
override fun hashCode(): Int {
|
||||||
|
var result = items.hashCode()
|
||||||
|
result = 31 * result + showAllButtonText
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = key
|
||||||
|
}
|
||||||
|
|
||||||
|
class Suggestions(
|
||||||
|
override val items: List<MangaItemModel>,
|
||||||
|
override val showAllButtonText: Int,
|
||||||
|
) : ShelfSectionModel {
|
||||||
|
|
||||||
|
override val key = "suggestions"
|
||||||
|
|
||||||
|
override fun getTitle(resources: Resources) = resources.getString(R.string.suggestions)
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Suggestions
|
||||||
|
|
||||||
|
if (items != other.items) return false
|
||||||
|
return showAllButtonText == other.showAllButtonText
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
package org.koitharu.kotatsu.suggestions.data
|
package org.koitharu.kotatsu.suggestions.data
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Transaction
|
||||||
|
import androidx.room.Update
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
@@ -10,6 +15,10 @@ abstract class SuggestionDao {
|
|||||||
@Query("SELECT * FROM suggestions ORDER BY relevance DESC")
|
@Query("SELECT * FROM suggestions ORDER BY relevance DESC")
|
||||||
abstract fun observeAll(): Flow<List<SuggestionWithManga>>
|
abstract fun observeAll(): Flow<List<SuggestionWithManga>>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM suggestions ORDER BY relevance DESC LIMIT :limit")
|
||||||
|
abstract fun observeAll(limit: Int): Flow<List<SuggestionWithManga>>
|
||||||
|
|
||||||
@Query("SELECT COUNT(*) FROM suggestions")
|
@Query("SELECT COUNT(*) FROM suggestions")
|
||||||
abstract suspend fun count(): Int
|
abstract suspend fun count(): Int
|
||||||
|
|
||||||
@@ -28,4 +37,4 @@ abstract class SuggestionDao {
|
|||||||
insert(entity)
|
insert(entity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ class SuggestionRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun observeAll(limit: Int): Flow<List<Manga>> {
|
||||||
|
return db.suggestionDao.observeAll(limit).mapItems {
|
||||||
|
it.manga.toManga(it.tags.toMangaTags())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun clear() {
|
suspend fun clear() {
|
||||||
db.suggestionDao.deleteAll()
|
db.suggestionDao.deleteAll()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import androidx.work.impl.foreground.SystemForegroundService
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
||||||
|
|
||||||
@@ -27,8 +29,10 @@ class WorkServiceStopHelper(
|
|||||||
WorkManager.getInstance(context)
|
WorkManager.getInstance(context)
|
||||||
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.RUNNING))
|
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.RUNNING))
|
||||||
.asFlow()
|
.asFlow()
|
||||||
|
.map { it.isEmpty() }
|
||||||
|
.distinctUntilChanged()
|
||||||
.collectLatest {
|
.collectLatest {
|
||||||
if (it.isEmpty()) {
|
if (it) {
|
||||||
delay(1_000)
|
delay(1_000)
|
||||||
stopWorkerService()
|
stopWorkerService()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
|
android:tint="?colorControlNormal"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
|
android:tint="?colorControlNormal"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -361,7 +361,7 @@
|
|||||||
<string name="options">Options</string>
|
<string name="options">Options</string>
|
||||||
<string name="not_found_404">Content not found or removed</string>
|
<string name="not_found_404">Content not found or removed</string>
|
||||||
<string name="downloading_manga">Downloading manga</string>
|
<string name="downloading_manga">Downloading manga</string>
|
||||||
<string name="download_summary_pattern" translatable="false"><b>%1$s</b> %2$s</string>
|
<string name="download_summary_pattern" translatable="false">%1$s · %2$s</string>
|
||||||
<string name="incognito_mode">Incognito mode</string>
|
<string name="incognito_mode">Incognito mode</string>
|
||||||
<string name="app_update_available_s">Application update available: %s</string>
|
<string name="app_update_available_s">Application update available: %s</string>
|
||||||
<string name="no_chapters">No chapters</string>
|
<string name="no_chapters">No chapters</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user