Show reverse progress and chapters in lists #904
This commit is contained in:
@@ -18,178 +18,178 @@ import org.koitharu.kotatsu.tracker.data.TrackEntity
|
||||
import javax.inject.Inject
|
||||
|
||||
class MigrateUseCase
|
||||
@Inject
|
||||
constructor(
|
||||
private val mangaRepositoryFactory: MangaRepository.Factory,
|
||||
private val mangaDataRepository: MangaDataRepository,
|
||||
private val database: MangaDatabase,
|
||||
private val progressUpdateUseCase: ProgressUpdateUseCase,
|
||||
private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>,
|
||||
@Inject
|
||||
constructor(
|
||||
private val mangaRepositoryFactory: MangaRepository.Factory,
|
||||
private val mangaDataRepository: MangaDataRepository,
|
||||
private val database: MangaDatabase,
|
||||
private val progressUpdateUseCase: ProgressUpdateUseCase,
|
||||
private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>,
|
||||
) {
|
||||
suspend operator fun invoke(
|
||||
oldManga: Manga,
|
||||
newManga: Manga,
|
||||
) {
|
||||
suspend operator fun invoke(
|
||||
oldManga: Manga,
|
||||
newManga: Manga,
|
||||
) {
|
||||
val oldDetails =
|
||||
if (oldManga.chapters.isNullOrEmpty()) {
|
||||
runCatchingCancellable {
|
||||
mangaRepositoryFactory.create(oldManga.source).getDetails(oldManga)
|
||||
}.getOrDefault(oldManga)
|
||||
} else {
|
||||
oldManga
|
||||
}
|
||||
val newDetails =
|
||||
if (newManga.chapters.isNullOrEmpty()) {
|
||||
mangaRepositoryFactory.create(newManga.source).getDetails(newManga)
|
||||
} else {
|
||||
newManga
|
||||
}
|
||||
mangaDataRepository.storeManga(newDetails)
|
||||
database.withTransaction {
|
||||
// replace favorites
|
||||
val favoritesDao = database.getFavouritesDao()
|
||||
val oldFavourites = favoritesDao.findAllRaw(oldDetails.id)
|
||||
if (oldFavourites.isNotEmpty()) {
|
||||
favoritesDao.delete(oldManga.id)
|
||||
for (f in oldFavourites) {
|
||||
val e =
|
||||
f.copy(
|
||||
mangaId = newManga.id,
|
||||
)
|
||||
favoritesDao.upsert(e)
|
||||
}
|
||||
}
|
||||
// replace history
|
||||
val historyDao = database.getHistoryDao()
|
||||
val oldHistory = historyDao.find(oldDetails.id)
|
||||
val newHistory =
|
||||
if (oldHistory != null) {
|
||||
val newHistory = makeNewHistory(oldDetails, newDetails, oldHistory)
|
||||
historyDao.delete(oldDetails.id)
|
||||
historyDao.upsert(newHistory)
|
||||
newHistory
|
||||
} else {
|
||||
null
|
||||
}
|
||||
// track
|
||||
val tracksDao = database.getTracksDao()
|
||||
val oldTrack = tracksDao.find(oldDetails.id)
|
||||
if (oldTrack != null) {
|
||||
val lastChapter = newDetails.chapters?.lastOrNull()
|
||||
val newTrack =
|
||||
TrackEntity(
|
||||
mangaId = newDetails.id,
|
||||
lastChapterId = lastChapter?.id ?: 0L,
|
||||
newChapters = 0,
|
||||
lastCheckTime = System.currentTimeMillis(),
|
||||
lastChapterDate = lastChapter?.uploadDate ?: 0L,
|
||||
lastResult = TrackEntity.RESULT_EXTERNAL_MODIFICATION,
|
||||
lastError = null,
|
||||
val oldDetails =
|
||||
if (oldManga.chapters.isNullOrEmpty()) {
|
||||
runCatchingCancellable {
|
||||
mangaRepositoryFactory.create(oldManga.source).getDetails(oldManga)
|
||||
}.getOrDefault(oldManga)
|
||||
} else {
|
||||
oldManga
|
||||
}
|
||||
val newDetails =
|
||||
if (newManga.chapters.isNullOrEmpty()) {
|
||||
mangaRepositoryFactory.create(newManga.source).getDetails(newManga)
|
||||
} else {
|
||||
newManga
|
||||
}
|
||||
mangaDataRepository.storeManga(newDetails)
|
||||
database.withTransaction {
|
||||
// replace favorites
|
||||
val favoritesDao = database.getFavouritesDao()
|
||||
val oldFavourites = favoritesDao.findAllRaw(oldDetails.id)
|
||||
if (oldFavourites.isNotEmpty()) {
|
||||
favoritesDao.delete(oldManga.id)
|
||||
for (f in oldFavourites) {
|
||||
val e =
|
||||
f.copy(
|
||||
mangaId = newManga.id,
|
||||
)
|
||||
tracksDao.delete(oldDetails.id)
|
||||
tracksDao.upsert(newTrack)
|
||||
favoritesDao.upsert(e)
|
||||
}
|
||||
// scrobbling
|
||||
for (scrobbler in scrobblers) {
|
||||
if (!scrobbler.isEnabled) {
|
||||
continue
|
||||
}
|
||||
val prevInfo = scrobbler.getScrobblingInfoOrNull(oldDetails.id) ?: continue
|
||||
scrobbler.unregisterScrobbling(oldDetails.id)
|
||||
scrobbler.linkManga(newDetails.id, prevInfo.targetId)
|
||||
scrobbler.updateScrobblingInfo(
|
||||
}
|
||||
// replace history
|
||||
val historyDao = database.getHistoryDao()
|
||||
val oldHistory = historyDao.find(oldDetails.id)
|
||||
val newHistory =
|
||||
if (oldHistory != null) {
|
||||
val newHistory = makeNewHistory(oldDetails, newDetails, oldHistory)
|
||||
historyDao.delete(oldDetails.id)
|
||||
historyDao.upsert(newHistory)
|
||||
newHistory
|
||||
} else {
|
||||
null
|
||||
}
|
||||
// track
|
||||
val tracksDao = database.getTracksDao()
|
||||
val oldTrack = tracksDao.find(oldDetails.id)
|
||||
if (oldTrack != null) {
|
||||
val lastChapter = newDetails.chapters?.lastOrNull()
|
||||
val newTrack =
|
||||
TrackEntity(
|
||||
mangaId = newDetails.id,
|
||||
rating = prevInfo.rating,
|
||||
status =
|
||||
prevInfo.status ?: when {
|
||||
newHistory == null -> ScrobblingStatus.PLANNED
|
||||
newHistory.percent == 1f -> ScrobblingStatus.COMPLETED
|
||||
else -> ScrobblingStatus.READING
|
||||
},
|
||||
comment = prevInfo.comment,
|
||||
lastChapterId = lastChapter?.id ?: 0L,
|
||||
newChapters = 0,
|
||||
lastCheckTime = System.currentTimeMillis(),
|
||||
lastChapterDate = lastChapter?.uploadDate ?: 0L,
|
||||
lastResult = TrackEntity.RESULT_EXTERNAL_MODIFICATION,
|
||||
lastError = null,
|
||||
)
|
||||
if (newHistory != null) {
|
||||
scrobbler.scrobble(
|
||||
manga = newDetails,
|
||||
chapterId = newHistory.chapterId,
|
||||
)
|
||||
}
|
||||
}
|
||||
tracksDao.delete(oldDetails.id)
|
||||
tracksDao.upsert(newTrack)
|
||||
}
|
||||
progressUpdateUseCase(newManga)
|
||||
}
|
||||
|
||||
private fun makeNewHistory(
|
||||
oldManga: Manga,
|
||||
newManga: Manga,
|
||||
history: HistoryEntity,
|
||||
): HistoryEntity {
|
||||
if (oldManga.chapters.isNullOrEmpty()) { // probably broken manga/source
|
||||
val branch = newManga.getPreferredBranch(null)
|
||||
val chapters = checkNotNull(newManga.getChapters(branch))
|
||||
val currentChapter =
|
||||
if (history.percent in 0f..1f) {
|
||||
chapters[(chapters.lastIndex * history.percent).toInt()]
|
||||
} else {
|
||||
chapters.first()
|
||||
}
|
||||
return HistoryEntity(
|
||||
mangaId = newManga.id,
|
||||
createdAt = history.createdAt,
|
||||
updatedAt = System.currentTimeMillis(),
|
||||
chapterId = currentChapter.id,
|
||||
page = history.page,
|
||||
scroll = history.scroll,
|
||||
percent = history.percent,
|
||||
deletedAt = 0,
|
||||
chaptersCount = chapters.size,
|
||||
// scrobbling
|
||||
for (scrobbler in scrobblers) {
|
||||
if (!scrobbler.isEnabled) {
|
||||
continue
|
||||
}
|
||||
val prevInfo = scrobbler.getScrobblingInfoOrNull(oldDetails.id) ?: continue
|
||||
scrobbler.unregisterScrobbling(oldDetails.id)
|
||||
scrobbler.linkManga(newDetails.id, prevInfo.targetId)
|
||||
scrobbler.updateScrobblingInfo(
|
||||
mangaId = newDetails.id,
|
||||
rating = prevInfo.rating,
|
||||
status =
|
||||
prevInfo.status ?: when {
|
||||
newHistory == null -> ScrobblingStatus.PLANNED
|
||||
newHistory.percent == 1f -> ScrobblingStatus.COMPLETED
|
||||
else -> ScrobblingStatus.READING
|
||||
},
|
||||
comment = prevInfo.comment,
|
||||
)
|
||||
}
|
||||
val branch = oldManga.getPreferredBranch(history.toMangaHistory())
|
||||
val oldChapters = checkNotNull(oldManga.getChapters(branch))
|
||||
var index = oldChapters.indexOfFirst { it.id == history.chapterId }
|
||||
if (index < 0) {
|
||||
index =
|
||||
if (history.percent in 0f..1f) {
|
||||
(oldChapters.lastIndex * history.percent).toInt()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
val newChapters = checkNotNull(newManga.chapters).groupBy { it.branch }
|
||||
val newBranch =
|
||||
if (newChapters.containsKey(branch)) {
|
||||
branch
|
||||
} else {
|
||||
newManga.getPreferredBranch(null)
|
||||
if (newHistory != null) {
|
||||
scrobbler.scrobble(
|
||||
manga = newDetails,
|
||||
chapterId = newHistory.chapterId,
|
||||
)
|
||||
}
|
||||
val newChapterId =
|
||||
checkNotNull(newChapters[newBranch])
|
||||
.let {
|
||||
val oldChapter = oldChapters[index]
|
||||
it.findByNumber(oldChapter.volume, oldChapter.number) ?: it.getOrNull(index) ?: it.last()
|
||||
}.id
|
||||
}
|
||||
}
|
||||
progressUpdateUseCase(newManga)
|
||||
}
|
||||
|
||||
private fun makeNewHistory(
|
||||
oldManga: Manga,
|
||||
newManga: Manga,
|
||||
history: HistoryEntity,
|
||||
): HistoryEntity {
|
||||
if (oldManga.chapters.isNullOrEmpty()) { // probably broken manga/source
|
||||
val branch = newManga.getPreferredBranch(null)
|
||||
val chapters = checkNotNull(newManga.getChapters(branch))
|
||||
val currentChapter =
|
||||
if (history.percent in 0f..1f) {
|
||||
chapters[(chapters.lastIndex * history.percent).toInt()]
|
||||
} else {
|
||||
chapters.first()
|
||||
}
|
||||
return HistoryEntity(
|
||||
mangaId = newManga.id,
|
||||
createdAt = history.createdAt,
|
||||
updatedAt = System.currentTimeMillis(),
|
||||
chapterId = newChapterId,
|
||||
chapterId = currentChapter.id,
|
||||
page = history.page,
|
||||
scroll = history.scroll,
|
||||
percent = PROGRESS_NONE,
|
||||
percent = history.percent,
|
||||
deletedAt = 0,
|
||||
chaptersCount = checkNotNull(newChapters[newBranch]).size,
|
||||
chaptersCount = chapters.count { it.branch == currentChapter.branch },
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<MangaChapter>.findByNumber(
|
||||
volume: Int,
|
||||
number: Float,
|
||||
): MangaChapter? =
|
||||
if (number <= 0f) {
|
||||
null
|
||||
val branch = oldManga.getPreferredBranch(history.toMangaHistory())
|
||||
val oldChapters = checkNotNull(oldManga.getChapters(branch))
|
||||
var index = oldChapters.indexOfFirst { it.id == history.chapterId }
|
||||
if (index < 0) {
|
||||
index =
|
||||
if (history.percent in 0f..1f) {
|
||||
(oldChapters.lastIndex * history.percent).toInt()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
val newChapters = checkNotNull(newManga.chapters).groupBy { it.branch }
|
||||
val newBranch =
|
||||
if (newChapters.containsKey(branch)) {
|
||||
branch
|
||||
} else {
|
||||
firstOrNull { it.volume == volume && it.number == number }
|
||||
newManga.getPreferredBranch(null)
|
||||
}
|
||||
val newChapterId =
|
||||
checkNotNull(newChapters[newBranch])
|
||||
.let {
|
||||
val oldChapter = oldChapters[index]
|
||||
it.findByNumber(oldChapter.volume, oldChapter.number) ?: it.getOrNull(index) ?: it.last()
|
||||
}.id
|
||||
|
||||
return HistoryEntity(
|
||||
mangaId = newManga.id,
|
||||
createdAt = history.createdAt,
|
||||
updatedAt = System.currentTimeMillis(),
|
||||
chapterId = newChapterId,
|
||||
page = history.page,
|
||||
scroll = history.scroll,
|
||||
percent = PROGRESS_NONE,
|
||||
deletedAt = 0,
|
||||
chaptersCount = checkNotNull(newChapters[newBranch]).size,
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<MangaChapter>.findByNumber(
|
||||
volume: Int,
|
||||
number: Float,
|
||||
): MangaChapter? =
|
||||
if (number <= 0f) {
|
||||
null
|
||||
} else {
|
||||
firstOrNull { it.volume == volume && it.number == number }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ fun alternativeAD(
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.progressView.setPercent(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads)
|
||||
binding.progressView.setProgress(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads)
|
||||
binding.chipSource.also { chip ->
|
||||
chip.text = item.manga.source.getTitle(chip.context)
|
||||
ImageRequest.Builder(context)
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.core.util.ext.require
|
||||
import org.koitharu.kotatsu.history.data.HistoryRepository
|
||||
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
|
||||
@@ -89,11 +89,7 @@ class AlternativesViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getProgress(mangaId: Long): Float {
|
||||
return if (settings.isReadingIndicatorsEnabled) {
|
||||
historyRepository.getProgress(mangaId)
|
||||
} else {
|
||||
PROGRESS_NONE
|
||||
}
|
||||
private suspend fun getProgress(mangaId: Long): ReadingProgress? {
|
||||
return historyRepository.getProgress(mangaId, settings.progressIndicatorMode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package org.koitharu.kotatsu.alternatives.ui
|
||||
|
||||
import org.koitharu.kotatsu.core.model.chaptersCount
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
|
||||
data class MangaAlternativeModel(
|
||||
val manga: Manga,
|
||||
val progress: Float,
|
||||
val progress: ReadingProgress?,
|
||||
private val referenceChapters: Int,
|
||||
) : ListModel {
|
||||
|
||||
|
||||
@@ -37,6 +37,6 @@ fun bookmarkLargeAD(
|
||||
source(item.manga.source)
|
||||
enqueueWith(coil)
|
||||
}
|
||||
binding.progressView.percent = item.percent
|
||||
binding.progressView.setProgress(item.percent, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,8 +192,8 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
get() = prefs.getBoolean(KEY_FEED_HEADER, true)
|
||||
set(value) = prefs.edit { putBoolean(KEY_FEED_HEADER, value) }
|
||||
|
||||
val isReadingIndicatorsEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_READING_INDICATORS, true)
|
||||
val progressIndicatorMode: ProgressIndicatorMode
|
||||
get() = prefs.getEnumValue(KEY_PROGRESS_INDICATORS, ProgressIndicatorMode.PERCENT_READ)
|
||||
|
||||
val isHistoryExcludeNsfw: Boolean
|
||||
get() = prefs.getBoolean(KEY_HISTORY_EXCLUDE_NSFW, false)
|
||||
@@ -619,7 +619,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_BACKUP_PERIODICAL_LAST = "backup_periodic_last"
|
||||
const val KEY_HISTORY_GROUPING = "history_grouping"
|
||||
const val KEY_UPDATED_GROUPING = "updated_grouping"
|
||||
const val KEY_READING_INDICATORS = "reading_indicators"
|
||||
const val KEY_PROGRESS_INDICATORS = "progress_indicators"
|
||||
const val KEY_REVERSE_CHAPTERS = "reverse_chapters"
|
||||
const val KEY_GRID_VIEW_CHAPTERS = "grid_view_chapters"
|
||||
const val KEY_HISTORY_EXCLUDE_NSFW = "history_exclude_nsfw"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
enum class ProgressIndicatorMode {
|
||||
|
||||
NONE, PERCENT_READ, PERCENT_LEFT, CHAPTERS_READ, CHAPTERS_LEFT;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ class RelatedListViewModel @Inject constructor(
|
||||
|
||||
override val content = combine(
|
||||
mangaList,
|
||||
listMode,
|
||||
observeListModeWithTriggers(),
|
||||
listError,
|
||||
) { list, mode, error ->
|
||||
when {
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.koitharu.kotatsu.explore.domain.ExploreRepository
|
||||
import org.koitharu.kotatsu.explore.ui.model.ExploreButtons
|
||||
import org.koitharu.kotatsu.explore.ui.model.MangaSourceItem
|
||||
import org.koitharu.kotatsu.explore.ui.model.RecommendationsItem
|
||||
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyHint
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
@@ -197,7 +196,8 @@ class ExploreViewModel @Inject constructor(
|
||||
coverUrl = manga.coverUrl,
|
||||
manga = manga,
|
||||
counter = 0,
|
||||
progress = PROGRESS_NONE,
|
||||
progress = null,
|
||||
isFavorite = false,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,9 @@ abstract class FavouritesDao {
|
||||
@Query("SELECT DISTINCT category_id FROM favourites WHERE manga_id IN (:mangaIds) AND deleted_at = 0 ORDER BY favourites.created_at ASC")
|
||||
abstract suspend fun findCategoriesIds(mangaIds: Collection<Long>): List<Long>
|
||||
|
||||
@Query("SELECT COUNT(category_id) FROM favourites WHERE manga_id = :mangaId AND deleted_at = 0")
|
||||
abstract suspend fun findCategoriesCount(mangaId: Long): Int
|
||||
|
||||
/** INSERT **/
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
|
||||
@@ -115,6 +115,10 @@ class FavouritesRepository @Inject constructor(
|
||||
return db.getFavouriteCategoriesDao().find(id.toInt()).toFavouriteCategory()
|
||||
}
|
||||
|
||||
suspend fun isFavorite(mangaId: Long): Boolean {
|
||||
return db.getFavouritesDao().findCategoriesCount(mangaId) != 0
|
||||
}
|
||||
|
||||
suspend fun getCategoriesIds(mangaIds: Collection<Long>): Set<Long> {
|
||||
return db.getFavouritesDao().findCategoriesIds(mangaIds).toSet()
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class FavouritesListViewModel @Inject constructor(
|
||||
|
||||
override val content = combine(
|
||||
observeFavorites(),
|
||||
listMode,
|
||||
observeListModeWithTriggers(),
|
||||
refreshTrigger,
|
||||
) { list, mode, _ ->
|
||||
when {
|
||||
|
||||
@@ -88,7 +88,7 @@ abstract class HistoryDao {
|
||||
abstract suspend fun insert(entity: HistoryEntity): Long
|
||||
|
||||
@Query(
|
||||
"UPDATE history SET page = :page, chapter_id = :chapterId, scroll = :scroll, percent = :percent, updated_at = :updatedAt, deleted_at = 0 WHERE manga_id = :mangaId",
|
||||
"UPDATE history SET page = :page, chapter_id = :chapterId, scroll = :scroll, percent = :percent, updated_at = :updatedAt, chapters = :chapters, deleted_at = 0 WHERE manga_id = :mangaId",
|
||||
)
|
||||
abstract suspend fun update(
|
||||
mangaId: Long,
|
||||
@@ -96,6 +96,7 @@ abstract class HistoryDao {
|
||||
chapterId: Long,
|
||||
scroll: Float,
|
||||
percent: Float,
|
||||
chapters: Int,
|
||||
updatedAt: Long,
|
||||
): Int
|
||||
|
||||
@@ -116,6 +117,7 @@ abstract class HistoryDao {
|
||||
chapterId = entity.chapterId,
|
||||
scroll = entity.scroll,
|
||||
percent = entity.percent,
|
||||
chapters = entity.chaptersCount,
|
||||
updatedAt = entity.updatedAt,
|
||||
)
|
||||
|
||||
|
||||
@@ -19,10 +19,12 @@ import org.koitharu.kotatsu.core.model.isLocal
|
||||
import org.koitharu.kotatsu.core.model.isNsfw
|
||||
import org.koitharu.kotatsu.core.parser.MangaDataRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleHandle
|
||||
import org.koitharu.kotatsu.core.util.ext.mapItems
|
||||
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
|
||||
import org.koitharu.kotatsu.list.domain.ListSortOrder
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler
|
||||
@@ -102,6 +104,7 @@ class HistoryRepository @Inject constructor(
|
||||
assert(manga.chapters != null)
|
||||
db.withTransaction {
|
||||
mangaRepository.storeManga(manga)
|
||||
val branch = manga.chapters?.findById(chapterId)?.branch
|
||||
db.getHistoryDao().upsert(
|
||||
HistoryEntity(
|
||||
mangaId = manga.id,
|
||||
@@ -111,7 +114,7 @@ class HistoryRepository @Inject constructor(
|
||||
page = page,
|
||||
scroll = scroll.toFloat(), // we migrate to int, but decide to not update database
|
||||
percent = percent,
|
||||
chaptersCount = manga.chapters?.size ?: -1,
|
||||
chaptersCount = manga.chapters?.count { it.branch == branch } ?: 0,
|
||||
deletedAt = 0L,
|
||||
),
|
||||
)
|
||||
@@ -124,8 +127,13 @@ class HistoryRepository @Inject constructor(
|
||||
return db.getHistoryDao().find(manga.id)?.recoverIfNeeded(manga)?.toMangaHistory()
|
||||
}
|
||||
|
||||
suspend fun getProgress(mangaId: Long): Float {
|
||||
return db.getHistoryDao().findProgress(mangaId) ?: PROGRESS_NONE
|
||||
suspend fun getProgress(mangaId: Long, mode: ProgressIndicatorMode): ReadingProgress? {
|
||||
val entity = db.getHistoryDao().find(mangaId) ?: return null
|
||||
return ReadingProgress(
|
||||
percent = entity.percent,
|
||||
totalChapters = entity.chaptersCount,
|
||||
mode = mode,
|
||||
).takeIf { it.isValid() }
|
||||
}
|
||||
|
||||
suspend fun clear() {
|
||||
|
||||
@@ -88,7 +88,7 @@ class HistoryListViewModel @Inject constructor(
|
||||
override val content = combine(
|
||||
observeHistory(),
|
||||
isGroupingEnabled,
|
||||
listMode,
|
||||
observeListModeWithTriggers(),
|
||||
networkState,
|
||||
settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) { isIncognitoModeEnabled },
|
||||
) { list, grouped, mode, online, incognito ->
|
||||
|
||||
@@ -26,7 +26,6 @@ class ReadingProgressDrawable(
|
||||
private val outlineColor: Int
|
||||
private val backgroundColor: Int
|
||||
private val textColor: Int
|
||||
private val textPattern = context.getString(R.string.percent_string_pattern)
|
||||
private val textBounds = Rect()
|
||||
private val tempRect = Rect()
|
||||
private val hasBackground: Boolean
|
||||
@@ -36,14 +35,18 @@ class ReadingProgressDrawable(
|
||||
private val desiredWidth: Int
|
||||
private val autoFitTextSize: Boolean
|
||||
|
||||
var progress: Float = PROGRESS_NONE
|
||||
var percent: Float = PROGRESS_NONE
|
||||
set(value) {
|
||||
field = value
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
var text = ""
|
||||
set(value) {
|
||||
field = value
|
||||
text = textPattern.format((value * 100f).toInt().toString())
|
||||
paint.getTextBounds(text, 0, text.length, textBounds)
|
||||
invalidateSelf()
|
||||
}
|
||||
private var text = ""
|
||||
|
||||
init {
|
||||
val ta = context.obtainStyledAttributes(styleResId, R.styleable.ProgressDrawable)
|
||||
@@ -79,7 +82,7 @@ class ReadingProgressDrawable(
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
if (progress < 0f) {
|
||||
if (percent < 0f) {
|
||||
return
|
||||
}
|
||||
val cx = bounds.exactCenterX()
|
||||
@@ -103,12 +106,12 @@ class ReadingProgressDrawable(
|
||||
cx + innerRadius,
|
||||
cy + innerRadius,
|
||||
-90f,
|
||||
360f * progress,
|
||||
360f * percent,
|
||||
false,
|
||||
paint,
|
||||
)
|
||||
if (hasText) {
|
||||
if (checkDrawable != null && progress >= 1f - Math.ulp(progress)) {
|
||||
if (checkDrawable != null && percent >= 1f - Math.ulp(percent)) {
|
||||
tempRect.set(bounds)
|
||||
tempRect.scale(0.6)
|
||||
checkDrawable.bounds = tempRect
|
||||
|
||||
@@ -11,8 +11,14 @@ import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.StyleRes
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.CHAPTERS_LEFT
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.CHAPTERS_READ
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.NONE
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.PERCENT_LEFT
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.PERCENT_READ
|
||||
import org.koitharu.kotatsu.core.util.ext.getAnimationDuration
|
||||
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
|
||||
class ReadingProgressView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
@@ -20,17 +26,30 @@ class ReadingProgressView @JvmOverloads constructor(
|
||||
@AttrRes defStyleAttr: Int = 0,
|
||||
) : View(context, attrs, defStyleAttr), ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
|
||||
|
||||
private val percentPattern = context.getString(R.string.percent_string_pattern)
|
||||
private var percentAnimator: ValueAnimator? = null
|
||||
private val animationDuration = context.getAnimationDuration(android.R.integer.config_shortAnimTime)
|
||||
|
||||
@StyleRes
|
||||
private val drawableStyle: Int
|
||||
|
||||
var percent: Float
|
||||
get() = peekProgressDrawable()?.progress ?: PROGRESS_NONE
|
||||
var progress: ReadingProgress? = null
|
||||
set(value) {
|
||||
field = value
|
||||
cancelAnimation()
|
||||
getProgressDrawable().progress = value
|
||||
getProgressDrawable().also {
|
||||
it.percent = value?.percent ?: PROGRESS_NONE
|
||||
it.text = when (value?.mode) {
|
||||
null,
|
||||
NONE -> ""
|
||||
|
||||
PERCENT_READ -> percentPattern.format((value.percent * 100f).toInt().toString())
|
||||
PERCENT_LEFT -> "-" + percentPattern.format((value.percentLeft * 100f).toInt().toString())
|
||||
|
||||
CHAPTERS_READ -> value.chapters.toString()
|
||||
CHAPTERS_LEFT -> "-" + value.chaptersLeft.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -39,7 +58,11 @@ class ReadingProgressView @JvmOverloads constructor(
|
||||
ta.recycle()
|
||||
outlineProvider = OutlineProvider()
|
||||
if (isInEditMode) {
|
||||
percent = 0.27f
|
||||
progress = ReadingProgress(
|
||||
percent = 0.27f,
|
||||
totalChapters = 20,
|
||||
mode = PERCENT_READ,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +76,7 @@ class ReadingProgressView @JvmOverloads constructor(
|
||||
|
||||
override fun onAnimationUpdate(animation: ValueAnimator) {
|
||||
val p = animation.animatedValue as Float
|
||||
getProgressDrawable().progress = p
|
||||
getProgressDrawable().percent = p
|
||||
}
|
||||
|
||||
override fun onAnimationStart(animation: Animator) = Unit
|
||||
@@ -68,16 +91,25 @@ class ReadingProgressView @JvmOverloads constructor(
|
||||
|
||||
override fun onAnimationRepeat(animation: Animator) = Unit
|
||||
|
||||
fun setPercent(value: Float, animate: Boolean) {
|
||||
fun setProgress(percent: Float, animate: Boolean) {
|
||||
setProgress(
|
||||
value = ReadingProgress(percent, 1, PERCENT_READ),
|
||||
animate = animate,
|
||||
)
|
||||
}
|
||||
|
||||
fun setProgress(value: ReadingProgress?, animate: Boolean) {
|
||||
val currentDrawable = peekProgressDrawable()
|
||||
if (!animate || currentDrawable == null || value == PROGRESS_NONE) {
|
||||
percent = value
|
||||
if (!animate || currentDrawable == null || value == null) {
|
||||
progress = value
|
||||
return
|
||||
}
|
||||
percentAnimator?.cancel()
|
||||
val currentPercent = currentDrawable.percent.coerceAtLeast(0f)
|
||||
progress = value.copy(percent = currentPercent)
|
||||
percentAnimator = ValueAnimator.ofFloat(
|
||||
currentDrawable.progress.coerceAtLeast(0f),
|
||||
value,
|
||||
currentDrawable.percent.coerceAtLeast(0f),
|
||||
value.percent,
|
||||
).apply {
|
||||
duration = animationDuration
|
||||
interpolator = AccelerateDecelerateInterpolator()
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.history.data.HistoryRepository
|
||||
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.list.ui.model.MangaCompactListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.MangaDetailedListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
|
||||
@@ -59,6 +58,7 @@ class MangaListMapper @Inject constructor(
|
||||
manga = manga,
|
||||
counter = getCounter(manga.id),
|
||||
progress = getProgress(manga.id),
|
||||
isFavorite = isFavorite(manga.id),
|
||||
)
|
||||
|
||||
suspend fun toDetailedListModel(manga: Manga) = MangaDetailedListModel(
|
||||
@@ -69,6 +69,7 @@ class MangaListMapper @Inject constructor(
|
||||
manga = manga,
|
||||
counter = getCounter(manga.id),
|
||||
progress = getProgress(manga.id),
|
||||
isFavorite = isFavorite(manga.id),
|
||||
tags = mapTags(manga.tags),
|
||||
)
|
||||
|
||||
@@ -79,6 +80,7 @@ class MangaListMapper @Inject constructor(
|
||||
manga = manga,
|
||||
counter = getCounter(manga.id),
|
||||
progress = getProgress(manga.id),
|
||||
isFavorite = isFavorite(manga.id),
|
||||
)
|
||||
|
||||
fun mapTags(tags: Collection<MangaTag>) = tags.map {
|
||||
@@ -97,12 +99,12 @@ class MangaListMapper @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getProgress(mangaId: Long): Float {
|
||||
return if (settings.isReadingIndicatorsEnabled) {
|
||||
historyRepository.getProgress(mangaId)
|
||||
} else {
|
||||
PROGRESS_NONE
|
||||
}
|
||||
private suspend fun getProgress(mangaId: Long): ReadingProgress? {
|
||||
return historyRepository.getProgress(mangaId, settings.progressIndicatorMode)
|
||||
}
|
||||
|
||||
private fun isFavorite(mangaId: Long): Boolean {
|
||||
return false // TODO favouritesRepository.isFavorite(mangaId)
|
||||
}
|
||||
|
||||
@ColorRes
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.koitharu.kotatsu.list.domain
|
||||
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.CHAPTERS_LEFT
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.CHAPTERS_READ
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.NONE
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.PERCENT_LEFT
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode.PERCENT_READ
|
||||
|
||||
data class ReadingProgress(
|
||||
val percent: Float,
|
||||
val totalChapters: Int,
|
||||
val mode: ProgressIndicatorMode,
|
||||
) {
|
||||
|
||||
val percentLeft: Float
|
||||
get() = 1f - percent
|
||||
|
||||
val chapters: Int
|
||||
get() = (totalChapters * percent).toInt()
|
||||
|
||||
val chaptersLeft: Int
|
||||
get() = (totalChapters * percentLeft).toInt()
|
||||
|
||||
fun isValid() = when (mode) {
|
||||
NONE -> false
|
||||
PERCENT_READ,
|
||||
PERCENT_LEFT -> percent in 0f..1f
|
||||
|
||||
CHAPTERS_READ,
|
||||
CHAPTERS_LEFT -> totalChapters > 0 && percent in 0f..1f
|
||||
}
|
||||
|
||||
fun isReversed() = mode == PERCENT_LEFT || mode == CHAPTERS_LEFT
|
||||
}
|
||||
@@ -2,11 +2,16 @@ package org.koitharu.kotatsu.list.ui
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
@@ -55,4 +60,13 @@ abstract class MangaListViewModel(
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
protected fun observeListModeWithTriggers(): Flow<ListMode> = combine(
|
||||
listMode,
|
||||
settings.observe().filter { key ->
|
||||
key == AppSettings.KEY_PROGRESS_INDICATORS || key == AppSettings.KEY_TRACKER_ENABLED
|
||||
}.onStart { emit("") }
|
||||
) { mode, _ ->
|
||||
mode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.koitharu.kotatsu.list.ui.adapter
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
@@ -14,7 +15,7 @@ import org.koitharu.kotatsu.core.util.ext.newImageRequest
|
||||
import org.koitharu.kotatsu.core.util.ext.setOnContextClickListenerCompat
|
||||
import org.koitharu.kotatsu.core.util.ext.source
|
||||
import org.koitharu.kotatsu.databinding.ItemMangaGridBinding
|
||||
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
|
||||
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback.Companion.PAYLOAD_PROGRESS_CHANGED
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
|
||||
import org.koitharu.kotatsu.list.ui.size.ItemSizeResolver
|
||||
@@ -41,7 +42,8 @@ fun mangaGridItemAD(
|
||||
|
||||
bind { payloads ->
|
||||
binding.textViewTitle.text = item.title
|
||||
binding.progressView.setPercent(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads)
|
||||
binding.progressView.setProgress(item.progress, PAYLOAD_PROGRESS_CHANGED in payloads)
|
||||
binding.imageViewFavorite.isVisible = item.isFavorite
|
||||
binding.imageViewCover.newImageRequest(lifecycleOwner, item.coverUrl)?.run {
|
||||
size(CoverSizeResolver(binding.imageViewCover))
|
||||
defaultPlaceholders(context)
|
||||
|
||||
@@ -39,7 +39,10 @@ fun mangaListDetailedItemAD(
|
||||
bind { payloads ->
|
||||
binding.textViewTitle.text = item.title
|
||||
binding.textViewAuthor.textAndVisible = item.manga.author
|
||||
binding.progressView.setPercent(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads)
|
||||
binding.progressView.setProgress(
|
||||
value = item.progress,
|
||||
animate = ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads,
|
||||
)
|
||||
binding.imageViewCover.newImageRequest(lifecycleOwner, item.coverUrl)?.run {
|
||||
size(CoverSizeResolver(binding.imageViewCover))
|
||||
defaultPlaceholders(context)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.list.ui.model
|
||||
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
|
||||
data class MangaCompactListModel(
|
||||
@@ -9,5 +10,6 @@ data class MangaCompactListModel(
|
||||
override val coverUrl: String,
|
||||
override val manga: Manga,
|
||||
override val counter: Int,
|
||||
override val progress: Float,
|
||||
override val progress: ReadingProgress?,
|
||||
override val isFavorite: Boolean,
|
||||
) : MangaListModel()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.koitharu.kotatsu.list.ui.model
|
||||
|
||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
|
||||
data class MangaDetailedListModel(
|
||||
@@ -10,6 +11,7 @@ data class MangaDetailedListModel(
|
||||
override val coverUrl: String,
|
||||
override val manga: Manga,
|
||||
override val counter: Int,
|
||||
override val progress: Float,
|
||||
override val progress: ReadingProgress?,
|
||||
override val isFavorite: Boolean,
|
||||
val tags: List<ChipsView.ChipModel>,
|
||||
) : MangaListModel()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.list.ui.model
|
||||
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
|
||||
data class MangaGridModel(
|
||||
@@ -8,5 +9,6 @@ data class MangaGridModel(
|
||||
override val coverUrl: String,
|
||||
override val manga: Manga,
|
||||
override val counter: Int,
|
||||
override val progress: Float,
|
||||
override val progress: ReadingProgress?,
|
||||
override val isFavorite: Boolean,
|
||||
) : MangaListModel()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.koitharu.kotatsu.list.ui.model
|
||||
|
||||
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback.Companion.PAYLOAD_ANYTHING_CHANGED
|
||||
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback.Companion.PAYLOAD_PROGRESS_CHANGED
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
|
||||
@@ -11,7 +13,8 @@ sealed class MangaListModel : ListModel {
|
||||
abstract val title: String
|
||||
abstract val coverUrl: String
|
||||
abstract val counter: Int
|
||||
abstract val progress: Float
|
||||
abstract val isFavorite: Boolean
|
||||
abstract val progress: ReadingProgress?
|
||||
|
||||
val source: MangaSource
|
||||
get() = manga.source
|
||||
@@ -20,12 +23,12 @@ sealed class MangaListModel : ListModel {
|
||||
return other is MangaListModel && other.javaClass == javaClass && id == other.id
|
||||
}
|
||||
|
||||
override fun getChangePayload(previousState: ListModel): Any? {
|
||||
return when {
|
||||
previousState !is MangaListModel -> super.getChangePayload(previousState)
|
||||
progress != previousState.progress -> ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED
|
||||
counter != previousState.counter -> ListModelDiffCallback.PAYLOAD_ANYTHING_CHANGED
|
||||
else -> null
|
||||
}
|
||||
override fun getChangePayload(previousState: ListModel): Any? = when {
|
||||
previousState !is MangaListModel || previousState.manga != manga -> null
|
||||
|
||||
previousState.progress != progress -> PAYLOAD_PROGRESS_CHANGED
|
||||
previousState.isFavorite != isFavorite || previousState.counter != counter -> PAYLOAD_ANYTHING_CHANGED
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ open class RemoteListViewModel @Inject constructor(
|
||||
|
||||
override val content = combine(
|
||||
mangaList.map { it?.skipNsfwIfNeeded() },
|
||||
listMode,
|
||||
observeListModeWithTriggers(),
|
||||
listError,
|
||||
hasNextPage,
|
||||
) { list, mode, error, hasNext ->
|
||||
|
||||
@@ -50,7 +50,7 @@ class SearchViewModel @Inject constructor(
|
||||
|
||||
override val content = combine(
|
||||
mangaList.map { it?.skipNsfwIfNeeded() },
|
||||
listMode,
|
||||
observeListModeWithTriggers(),
|
||||
listError,
|
||||
hasNextPage,
|
||||
) { list, mode, error, hasNext ->
|
||||
|
||||
@@ -14,6 +14,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode
|
||||
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.ui.util.ActivityRecreationHandle
|
||||
import org.koitharu.kotatsu.core.util.LocaleComparator
|
||||
@@ -44,6 +45,10 @@ class AppearanceSettingsFragment :
|
||||
entryValues = ListMode.entries.names()
|
||||
setDefaultValueCompat(ListMode.GRID.name)
|
||||
}
|
||||
findPreference<ListPreference>(AppSettings.KEY_PROGRESS_INDICATORS)?.run {
|
||||
entryValues = ProgressIndicatorMode.entries.names()
|
||||
setDefaultValueCompat(ProgressIndicatorMode.PERCENT_READ.name)
|
||||
}
|
||||
findPreference<ActivityListPreference>(AppSettings.KEY_APP_LOCALE)?.run {
|
||||
initLocalePicker(this)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
|
||||
@@ -36,7 +36,7 @@ class SuggestionsViewModel @Inject constructor(
|
||||
|
||||
override val content = combine(
|
||||
repository.observeAll(),
|
||||
listMode,
|
||||
observeListModeWithTriggers(),
|
||||
) { list, mode ->
|
||||
when {
|
||||
list.isEmpty() -> listOf(
|
||||
|
||||
@@ -39,7 +39,7 @@ class UpdatesViewModel @Inject constructor(
|
||||
override val content = combine(
|
||||
repository.observeUpdatedManga(),
|
||||
settings.observeAsFlow(AppSettings.KEY_UPDATED_GROUPING) { isUpdatedGroupingEnabled },
|
||||
listMode,
|
||||
observeListModeWithTriggers(),
|
||||
) { mangaList, grouping, mode ->
|
||||
when {
|
||||
mangaList.isEmpty() -> listOf(
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/custom_selectable_item_background"
|
||||
android:layout_margin="2dp"
|
||||
android:padding="6dp"
|
||||
android:background="@drawable/custom_selectable_item_background"
|
||||
android:clipChildren="false"
|
||||
android:orientation="vertical"
|
||||
android:padding="6dp"
|
||||
tools:layout_width="140dp">
|
||||
|
||||
<FrameLayout
|
||||
@@ -34,6 +34,17 @@
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/card_indicator_offset" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_favorite"
|
||||
android:layout_width="@dimen/card_indicator_size"
|
||||
android:layout_height="@dimen/card_indicator_size"
|
||||
android:layout_gravity="bottom|start"
|
||||
android:layout_margin="@dimen/card_indicator_offset"
|
||||
android:contentDescription="@string/favourites"
|
||||
android:scaleType="centerInside"
|
||||
app:srcCompat="@drawable/ic_heart"
|
||||
app:tint="?colorSurfaceBright" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
|
||||
@@ -101,4 +101,11 @@
|
||||
<item>@string/pages</item>
|
||||
<item>@string/webtoon</item>
|
||||
</string-array>
|
||||
<string-array name="progress_indicators" translatable="false">
|
||||
<item>@string/disabled</item>
|
||||
<item>@string/percent_read</item>
|
||||
<item>@string/percent_left</item>
|
||||
<item>@string/chapters_read</item>
|
||||
<item>@string/chapters_left</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -664,4 +664,8 @@
|
||||
<string name="sources_unpinned">Sources unpinned</string>
|
||||
<string name="sources_pinned">Sources pinned</string>
|
||||
<string name="recent_sources">Recent sources</string>
|
||||
<string name="percent_read">Percent read</string>
|
||||
<string name="percent_left">Percent left</string>
|
||||
<string name="chapters_read">Chapters read</string>
|
||||
<string name="chapters_left">Chapters left</string>
|
||||
</resources>
|
||||
|
||||
@@ -38,11 +38,11 @@
|
||||
android:valueTo="150"
|
||||
app:defaultValue="100" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="reading_indicators"
|
||||
android:summary="@string/show_reading_indicators_summary"
|
||||
android:title="@string/show_reading_indicators" />
|
||||
<ListPreference
|
||||
android:entries="@array/progress_indicators"
|
||||
android:key="progress_indicators"
|
||||
android:title="@string/show_reading_indicators"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user