Update to Kotlin 1.4.10, coroutines 1.3.9, Kotlinter 3.0.2 (#594)

pull/3963/head
arkon 4 years ago committed by GitHub
parent f10f8c6b64
commit 9f15f25f43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -75,7 +75,8 @@ class BackupCreateService : Service() {
startForeground(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build())
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "${javaClass.name}:WakeLock"
PowerManager.PARTIAL_WAKE_LOCK,
"${javaClass.name}:WakeLock"
)
wakeLock.acquire()
}

@ -37,8 +37,11 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
val interval = prefInterval ?: preferences.backupInterval().getOrDefault()
if (interval > 0) {
val request = PeriodicWorkRequestBuilder<BackupCreatorJob>(
interval.toLong(), TimeUnit.HOURS,
10, TimeUnit.MINUTES)
interval.toLong(),
TimeUnit.HOURS,
10,
TimeUnit.MINUTES
)
.addTag(TAG)
.build()

@ -103,7 +103,8 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
private fun initParser(): Gson = when (version) {
1 -> GsonBuilder().create()
2 -> GsonBuilder()
2 ->
GsonBuilder()
.registerTypeAdapter<MangaImpl>(MangaTypeAdapter.build())
.registerTypeHierarchyAdapter<ChapterImpl>(ChapterTypeAdapter.build())
.registerTypeAdapter<CategoryImpl>(CategoryTypeAdapter.build())

@ -121,7 +121,9 @@ class BackupRestoreService : Service() {
super.onCreate()
startForeground(Notifications.ID_RESTORE_PROGRESS, progressNotification.build())
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "BackupRestoreService:WakeLock")
PowerManager.PARTIAL_WAKE_LOCK,
"BackupRestoreService:WakeLock"
)
wakeLock.acquire(TimeUnit.HOURS.toMillis(3))
}
@ -382,12 +384,20 @@ class BackupRestoreService : Service() {
* @param total the total progress.
*/
private fun showProgressNotification(current: Int, total: Int, title: String) {
notificationManager.notify(Notifications.ID_RESTORE_PROGRESS, progressNotification
notificationManager.notify(
Notifications.ID_RESTORE_PROGRESS,
progressNotification
.setContentTitle(title.chop(30))
.setContentText(getString(R.string.restoring_progress, restoreProgress,
totalAmount))
.setContentText(
getString(
R.string.restoring_progress,
restoreProgress,
totalAmount
)
)
.setProgress(total, current, false)
.build())
.build()
)
}
/**
@ -395,8 +405,14 @@ class BackupRestoreService : Service() {
*/
private fun showResultNotification(path: String?, file: String?) {
val content = mutableListOf(getString(R.string.restore_completed_content, restoreProgress
.toString(), errors.size.toString()))
val content = mutableListOf(
getString(
R.string.restore_completed_content,
restoreProgress
.toString(),
errors.size.toString()
)
)
val sourceMissingCount = sourcesMissing.distinct().size
if (sourceMissingCount > 0) {
val sources = sourcesMissing.distinct().filter { it.toLongOrNull() == null }
@ -408,20 +424,29 @@ class BackupRestoreService : Service() {
if (sources.isEmpty()) {
content.add(
resources.getQuantityString(
R.plurals.sources_missing, sourceMissingCount, sourceMissingCount
R.plurals.sources_missing,
sourceMissingCount,
sourceMissingCount
)
)
} else {
content.add(
resources.getQuantityString(
R.plurals.sources_missing, sourceMissingCount, sourceMissingCount
R.plurals.sources_missing,
sourceMissingCount,
sourceMissingCount
) + ": " + missingSourcesString
)
}
}
if (lincensedManga > 0)
content.add(resources.getQuantityString(R.plurals.licensed_manga, lincensedManga,
lincensedManga))
content.add(
resources.getQuantityString(
R.plurals.licensed_manga,
lincensedManga,
lincensedManga
)
)
val trackingErrors = trackingErrors.distinct()
if (trackingErrors.isNotEmpty()) {
val trackingErrorsString = trackingErrors.distinct().joinToString("\n")
@ -440,8 +465,14 @@ class BackupRestoreService : Service() {
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
if (errors.size > 0 && !path.isNullOrEmpty() && !file.isNullOrEmpty()) {
resultNotification.addAction(R.drawable.ic_close_24dp, getString(R.string
.view_all_errors), getErrorLogIntent(path, file))
resultNotification.addAction(
R.drawable.ic_close_24dp,
getString(
R.string
.view_all_errors
),
getErrorLogIntent(path, file)
)
}
notificationManager.notify(Notifications.ID_RESTORE_COMPLETE, resultNotification.build())
}

@ -46,10 +46,12 @@ class ChapterCache(private val context: Context) {
private val gson: Gson by injectLazy()
/** Cache class used for cache management. */
private val diskCache = DiskLruCache.open(File(context.cacheDir, PARAMETER_CACHE_DIRECTORY),
private val diskCache = DiskLruCache.open(
File(context.cacheDir, PARAMETER_CACHE_DIRECTORY),
PARAMETER_APP_VERSION,
PARAMETER_VALUE_COUNT,
PARAMETER_CACHE_SIZE)
PARAMETER_CACHE_SIZE
)
/**
* Returns directory of cache.

@ -83,7 +83,8 @@ class CoverCache(val context: Context) {
withContext(Dispatchers.Main) {
context.toast(
context.getString(
R.string.deleted_, Formatter.formatFileSize(context, deletedSize)
R.string.deleted_,
Formatter.formatFileSize(context, deletedSize)
)
)
}
@ -111,7 +112,8 @@ class CoverCache(val context: Context) {
withContext(Dispatchers.Main) {
context.toast(
context.getString(
R.string.deleted_, Formatter.formatFileSize(context, deletedSize)
R.string.deleted_,
Formatter.formatFileSize(context, deletedSize)
)
)
}

@ -30,8 +30,13 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
* This class provides operations to manage the database through its interfaces.
*/
open class DatabaseHelper(context: Context) :
MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries,
HistoryQueries, SearchMetadataQueries {
MangaQueries,
ChapterQueries,
TrackQueries,
CategoryQueries,
MangaCategoryQueries,
HistoryQueries,
SearchMetadataQueries {
private val configuration = SupportSQLiteOpenHelper.Configuration.builder(context)
.name(DbOpenCallback.DATABASE_NAME)

@ -44,8 +44,10 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
db.execSQL(ChapterTable.sourceOrderUpdateQuery)
// Fix kissmanga covers after supporting cloudflare
db.execSQL("""UPDATE mangas SET thumbnail_url =
REPLACE(thumbnail_url, '93.174.95.110', 'kissmanga.com') WHERE source = 4""")
db.execSQL(
"""UPDATE mangas SET thumbnail_url =
REPLACE(thumbnail_url, '93.174.95.110', 'kissmanga.com') WHERE source = 4"""
)
}
if (oldVersion < 3) {
// Initialize history tables

@ -73,13 +73,14 @@ interface Manga : SManga {
genre?.split(",")?.map { it.trim().toLowerCase(Locale.US) } ?: emptyList()
return if (currentTags.any { tag -> tag.startsWith("japanese") || isMangaTag(tag) }) {
TYPE_MANGA
} else if (currentTags.any { tag -> tag.startsWith("english") || isComicTag(tag) } || isComicSource(
sourceName
)) {
} else if (currentTags.any { tag -> tag.startsWith("english") || isComicTag(tag) } ||
isComicSource(sourceName)
) {
TYPE_COMIC
} else if (currentTags.any { tag ->
tag.startsWith("chinese") || isManhuaTag(tag)
} || sourceName.contains("manhua", true)) {
} || sourceName.contains("manhua", true)
) {
TYPE_MANHUA
} else if (currentTags.any { tag -> isManhwaTag(tag) } || isWebtoonSource(sourceName)) {
TYPE_MANHWA

@ -74,7 +74,8 @@ open class MangaImpl : Manga {
override fun copyFrom(other: SManga) {
if (other is MangaImpl && other::ogTitle.isInitialized &&
!other.title.isBlank() && other.ogTitle != ogTitle) {
!other.title.isBlank() && other.ogTitle != ogTitle
) {
val oldTitle = ogTitle
title = other.ogTitle
val db: DownloadManager by injectLazy()

@ -11,18 +11,22 @@ interface CategoryQueries : DbProvider {
fun getCategories() = db.get()
.listOfObjects(Category::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(CategoryTable.TABLE)
.orderBy(CategoryTable.COL_ORDER)
.build())
.build()
)
.prepare()
fun getCategoriesForManga(manga: Manga) = db.get()
.listOfObjects(Category::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getCategoriesForMangaQuery())
.args(manga.id)
.build())
.build()
)
.prepare()
fun insertCategory(category: Category) = db.put().`object`(category).prepare()

@ -20,67 +20,81 @@ interface ChapterQueries : DbProvider {
fun getChapters(manga: Manga) = db.get()
.listOfObjects(Chapter::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_MANGA_ID} = ?")
.whereArgs(manga.id)
.build())
.build()
)
.prepare()
fun getRecentChapters(date: Date) = db.get()
.listOfObjects(MangaChapter::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getRecentsQuery())
.args(date.time)
.observesTables(ChapterTable.TABLE)
.build())
.build()
)
.withGetResolver(MangaChapterGetResolver.INSTANCE)
.prepare()
fun getUpdatedManga(date: Date, search: String = "", endless: Boolean) = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getRecentsQueryDistinct(search.sqLite, endless))
.args(date.time)
.observesTables(ChapterTable.TABLE)
.build())
.build()
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
fun getChapter(id: Long) = db.get()
.`object`(Chapter::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_ID} = ?")
.whereArgs(id)
.build())
.build()
)
.prepare()
fun getChapter(url: String) = db.get()
.`object`(Chapter::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ?")
.whereArgs(url)
.build())
.build()
)
.prepare()
fun getChapters(url: String) = db.get()
.listOfObjects(Chapter::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ?")
.whereArgs(url)
.build())
.build()
)
.prepare()
fun getChapter(url: String, mangaId: Long) = db.get()
.`object`(Chapter::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?")
.whereArgs(url, mangaId)
.build())
.build()
)
.prepare()
fun insertChapter(chapter: Chapter) = db.put().`object`(chapter).prepare()

@ -27,11 +27,13 @@ interface HistoryQueries : DbProvider {
*/
fun getRecentManga(date: Date, offset: Int = 0, search: String = "") = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getRecentMangasQuery(offset, search.sqLite))
.args(date.time)
.observesTables(HistoryTable.TABLE)
.build())
.build()
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
@ -42,11 +44,13 @@ interface HistoryQueries : DbProvider {
*/
fun getRecentlyAdded(date: Date, search: String = "", endless: Boolean) = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getRecentAdditionsQuery(search.sqLite, endless))
.args(date.time)
.observesTables(MangaTable.TABLE)
.build())
.build()
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
@ -57,11 +61,13 @@ interface HistoryQueries : DbProvider {
*/
fun getRecentMangaLimit(date: Date, limit: Int = 0, search: String = "") = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getRecentMangasLimitQuery(limit, search.sqLite))
.args(date.time)
.observesTables(HistoryTable.TABLE)
.build())
.build()
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
@ -72,30 +78,36 @@ interface HistoryQueries : DbProvider {
*/
fun getRecentsWithUnread(date: Date, search: String = "", endless: Boolean) = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getRecentReadWithUnreadChapters(search.sqLite, endless))
.args(date.time)
.observesTables(HistoryTable.TABLE)
.build())
.build()
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
fun getHistoryByMangaId(mangaId: Long) = db.get()
.listOfObjects(History::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getHistoryByMangaId())
.args(mangaId)
.observesTables(HistoryTable.TABLE)
.build())
.build()
)
.prepare()
fun getHistoryByChapterUrl(chapterUrl: String) = db.get()
.`object`(History::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getHistoryByChapterUrl())
.args(chapterUrl)
.observesTables(HistoryTable.TABLE)
.build())
.build()
)
.prepare()
/**
@ -119,16 +131,20 @@ interface HistoryQueries : DbProvider {
.prepare()
fun deleteHistory() = db.delete()
.byQuery(DeleteQuery.builder()
.byQuery(
DeleteQuery.builder()
.table(HistoryTable.TABLE)
.build())
.build()
)
.prepare()
fun deleteHistoryNoLastRead() = db.delete()
.byQuery(DeleteQuery.builder()
.byQuery(
DeleteQuery.builder()
.table(HistoryTable.TABLE)
.where("${HistoryTable.COL_LAST_READ} = ?")
.whereArgs(0)
.build())
.build()
)
.prepare()
}

@ -15,11 +15,13 @@ interface MangaCategoryQueries : DbProvider {
fun insertMangasCategories(mangasCategories: List<MangaCategory>) = db.put().objects(mangasCategories).prepare()
fun deleteOldMangasCategories(mangas: List<Manga>) = db.delete()
.byQuery(DeleteQuery.builder()
.byQuery(
DeleteQuery.builder()
.table(MangaCategoryTable.TABLE)
.where("${MangaCategoryTable.COL_MANGA_ID} IN (${Queries.placeholders(mangas.size)})")
.whereArgs(*mangas.map { it.id }.toTypedArray())
.build())
.build()
)
.prepare()
fun setMangaCategories(mangasCategories: List<MangaCategory>, mangas: List<Manga>) {

@ -23,46 +23,56 @@ interface MangaQueries : DbProvider {
fun getMangas() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.build())
.build()
)
.prepare()
fun getLibraryMangas() = db.get()
.listOfObjects(LibraryManga::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(libraryQuery)
.observesTables(MangaTable.TABLE, ChapterTable.TABLE, MangaCategoryTable.TABLE, CategoryTable.TABLE)
.build())
.build()
)
.withGetResolver(LibraryMangaGetResolver.INSTANCE)
.prepare()
fun getFavoriteMangas() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_FAVORITE} = ?")
.whereArgs(1)
.orderBy(MangaTable.COL_TITLE)
.build())
.build()
)
.prepare()
fun getManga(url: String, sourceId: Long) = db.get()
.`object`(Manga::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_URL} = ? AND ${MangaTable.COL_SOURCE} = ?")
.whereArgs(url, sourceId)
.build())
.build()
)
.prepare()
fun getManga(id: Long) = db.get()
.`object`(Manga::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(id)
.build())
.build()
)
.prepare()
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
@ -114,25 +124,31 @@ interface MangaQueries : DbProvider {
fun deleteMangas(mangas: List<Manga>) = db.delete().objects(mangas).prepare()
fun deleteMangasNotInLibrary() = db.delete()
.byQuery(DeleteQuery.builder()
.byQuery(
DeleteQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_FAVORITE} = ?")
.whereArgs(0)
.build())
.build()
)
.prepare()
fun deleteMangas() = db.delete()
.byQuery(DeleteQuery.builder()
.byQuery(
DeleteQuery.builder()
.table(MangaTable.TABLE)
.build())
.build()
)
.prepare()
fun getLastReadManga() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getLastReadMangaQuery())
.observesTables(MangaTable.TABLE)
.build())
.build()
)
.prepare()
fun getTotalChapterManga() = db.get().listOfObjects(Manga::class.java)

@ -9,7 +9,8 @@ import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga
/**
* Query to get the manga from the library, with their categories and unread count.
*/
val libraryQuery = """
val libraryQuery =
"""
SELECT M.*, COALESCE(MC.${MangaCategory.COL_CATEGORY_ID}, 0) AS ${Manga.COL_CATEGORY}
FROM (
SELECT ${Manga.TABLE}.*, COALESCE(C.unread, 0) AS ${Manga.COL_UNREAD}, COALESCE(R.hasread, 0) AS ${Manga.COL_HAS_READ}
@ -40,7 +41,8 @@ val libraryQuery = """
/**
* Query to get the recent chapters of manga from the library up to a date.
*/
fun getRecentsQuery() = """
fun getRecentsQuery() =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, * FROM ${Manga.TABLE} JOIN ${Chapter.TABLE}
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
WHERE ${Manga.COL_FAVORITE} = 1
@ -52,7 +54,8 @@ fun getRecentsQuery() = """
/**
* Query to get the recently added manga
*/
fun getRecentAdditionsQuery(search: String, endless: Boolean) = """
fun getRecentAdditionsQuery(search: String, endless: Boolean) =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, * FROM ${Manga.TABLE}
WHERE ${Manga.COL_FAVORITE} = 1
AND ${Manga.COL_DATE_ADDED} > ?
@ -64,7 +67,8 @@ fun getRecentAdditionsQuery(search: String, endless: Boolean) = """
/**
* Query to get the manga with recently uploaded chapters
*/
fun getRecentsQueryDistinct(search: String, endless: Boolean) = """
fun getRecentsQueryDistinct(search: String, endless: Boolean) =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
@ -92,7 +96,8 @@ fun getRecentsQueryDistinct(search: String, endless: Boolean) = """
* and are read after the given time period
* @return return limit is 25
*/
fun getRecentMangasQuery(offset: Int = 0, search: String = "") = """
fun getRecentMangasQuery(offset: Int = 0, search: String = "") =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
@ -118,7 +123,8 @@ fun getRecentMangasQuery(offset: Int = 0, search: String = "") = """
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
* and are read after the given time period
*/
fun getRecentMangasLimitQuery(limit: Int = 25, search: String = "") = """
fun getRecentMangasLimitQuery(limit: Int = 25, search: String = "") =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
@ -145,7 +151,8 @@ fun getRecentMangasLimitQuery(limit: Int = 25, search: String = "") = """
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
* and are read after the given time period
*/
fun getRecentReadWithUnreadChapters(search: String = "", endless: Boolean) = """
fun getRecentReadWithUnreadChapters(search: String = "", endless: Boolean) =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
FROM (
SELECT ${Manga.TABLE}.*
@ -178,7 +185,8 @@ fun getRecentReadWithUnreadChapters(search: String = "", endless: Boolean) = """
${if (endless) "" else "LIMIT 8"}
"""
fun getHistoryByMangaId() = """
fun getHistoryByMangaId() =
"""
SELECT ${History.TABLE}.*
FROM ${History.TABLE}
JOIN ${Chapter.TABLE}
@ -186,7 +194,8 @@ fun getHistoryByMangaId() = """
WHERE ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
"""
fun getHistoryByChapterUrl() = """
fun getHistoryByChapterUrl() =
"""
SELECT ${History.TABLE}.*
FROM ${History.TABLE}
JOIN ${Chapter.TABLE}
@ -194,7 +203,8 @@ fun getHistoryByChapterUrl() = """
WHERE ${Chapter.TABLE}.${Chapter.COL_URL} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
"""
fun getLastReadMangaQuery() = """
fun getLastReadMangaQuery() =
"""
SELECT ${Manga.TABLE}.*, MAX(${History.TABLE}.${History.COL_LAST_READ}) AS max
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
@ -206,7 +216,8 @@ fun getLastReadMangaQuery() = """
ORDER BY max DESC
"""
fun getTotalChapterMangaQuery() = """
fun getTotalChapterMangaQuery() =
"""
SELECT ${Manga.TABLE}.*
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
@ -218,7 +229,8 @@ fun getTotalChapterMangaQuery() = """
/**
* Query to get the categories for a manga.
*/
fun getCategoriesForMangaQuery() = """
fun getCategoriesForMangaQuery() =
"""
SELECT ${Category.TABLE}.* FROM ${Category.TABLE}
JOIN ${MangaCategory.TABLE} ON ${Category.TABLE}.${Category.COL_ID} =
${MangaCategory.TABLE}.${MangaCategory.COL_CATEGORY_ID}

@ -10,35 +10,43 @@ interface SearchMetadataQueries : DbProvider {
fun getSearchMetadataForManga(mangaId: Long) = db.get()
.`object`(SearchMetadata::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(SearchMetadataTable.TABLE)
.where("${SearchMetadataTable.COL_MANGA_ID} = ?")
.whereArgs(mangaId)
.build())
.build()
)
.prepare()
fun getSearchMetadata() = db.get()
.listOfObjects(SearchMetadata::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(SearchMetadataTable.TABLE)
.build())
.build()
)
.prepare()
fun getSearchMetadataByIndexedExtra(extra: String) = db.get()
.listOfObjects(SearchMetadata::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(SearchMetadataTable.TABLE)
.where("${SearchMetadataTable.COL_INDEXED_EXTRA} = ?")
.whereArgs(extra)
.build())
.build()
)
.prepare()
fun insertSearchMetadata(metadata: SearchMetadata) = db.put().`object`(metadata).prepare()
fun deleteSearchMetadata(metadata: SearchMetadata) = db.delete().`object`(metadata).prepare()
fun deleteAllSearchMetadata() = db.delete().byQuery(DeleteQuery.builder()
fun deleteAllSearchMetadata() = db.delete().byQuery(
DeleteQuery.builder()
.table(SearchMetadataTable.TABLE)
.build())
.build()
)
.prepare()
}

@ -12,11 +12,13 @@ interface TrackQueries : DbProvider {
fun getTracks(manga: Manga) = db.get()
.listOfObjects(Track::class.java)
.withQuery(Query.builder()
.withQuery(
Query.builder()
.table(TrackTable.TABLE)
.where("${TrackTable.COL_MANGA_ID} = ?")
.whereArgs(manga.id)
.build())
.build()
)
.prepare()
fun insertTrack(track: Track) = db.put().`object`(track).prepare()
@ -24,10 +26,12 @@ interface TrackQueries : DbProvider {
fun insertTracks(tracks: List<Track>) = db.put().objects(tracks).prepare()
fun deleteTrackForManga(manga: Manga, sync: TrackService) = db.delete()
.byQuery(DeleteQuery.builder()
.byQuery(
DeleteQuery.builder()
.table(TrackTable.TABLE)
.where("${TrackTable.COL_MANGA_ID} = ? AND ${TrackTable.COL_SYNC_ID} = ?")
.whereArgs(manga.id, sync.id)
.build())
.build()
)
.prepare()
}

@ -19,11 +19,13 @@ class HistoryLastReadPutResolver : HistoryPutResolver() {
override fun performPut(@NonNull db: StorIOSQLite, @NonNull history: History): PutResult = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(history)
val cursor = db.lowLevel().query(Query.builder()
val cursor = db.lowLevel().query(
Query.builder()
.table(updateQuery.table())
.where(updateQuery.where())
.whereArgs(updateQuery.whereArgs())
.build())
.build()
)
val putResult: PutResult

@ -15,7 +15,8 @@ object CategoryTable {
const val COL_MANGA_ORDER = "manga_order"
val createTableQuery: String
get() = """CREATE TABLE $TABLE(
get() =
"""CREATE TABLE $TABLE(
$COL_ID INTEGER NOT NULL PRIMARY KEY,
$COL_NAME TEXT NOT NULL,
$COL_ORDER INTEGER NOT NULL,

@ -31,7 +31,8 @@ object ChapterTable {
const val COL_SOURCE_ORDER = "source_order"
val createTableQuery: String
get() = """CREATE TABLE $TABLE(
get() =
"""CREATE TABLE $TABLE(
$COL_ID INTEGER NOT NULL PRIMARY KEY,
$COL_MANGA_ID INTEGER NOT NULL,
$COL_URL TEXT NOT NULL,

@ -31,7 +31,8 @@ object HistoryTable {
* query to create history table
*/
val createTableQuery: String
get() = """CREATE TABLE $TABLE(
get() =
"""CREATE TABLE $TABLE(
$COL_ID INTEGER NOT NULL PRIMARY KEY,
$COL_CHAPTER_ID INTEGER NOT NULL UNIQUE,
$COL_LAST_READ LONG,

@ -11,7 +11,8 @@ object MangaCategoryTable {
const val COL_CATEGORY_ID = "category_id"
val createTableQuery: String
get() = """CREATE TABLE $TABLE(
get() =
"""CREATE TABLE $TABLE(
$COL_ID INTEGER NOT NULL PRIMARY KEY,
$COL_MANGA_ID INTEGER NOT NULL,
$COL_CATEGORY_ID INTEGER NOT NULL,

@ -45,7 +45,8 @@ object MangaTable {
const val COL_DATE_ADDED = "date_added"
val createTableQuery: String
get() = """CREATE TABLE $TABLE(
get() =
"""CREATE TABLE $TABLE(
$COL_ID INTEGER NOT NULL PRIMARY KEY,
$COL_SOURCE INTEGER NOT NULL,
$COL_URL TEXT NOT NULL,

@ -15,7 +15,8 @@ object SearchMetadataTable {
// Insane foreign, primary key to avoid touch manga table
val createTableQuery: String
get() = """CREATE TABLE $TABLE(
get() =
"""CREATE TABLE $TABLE(
$COL_MANGA_ID INTEGER NOT NULL PRIMARY KEY,
$COL_UPLOADER TEXT,
$COL_EXTRA TEXT NOT NULL,

@ -27,7 +27,8 @@ object TrackTable {
const val COL_TRACKING_URL = "remote_url"
val createTableQuery: String
get() = """CREATE TABLE $TABLE(
get() =
"""CREATE TABLE $TABLE(
$COL_ID INTEGER NOT NULL PRIMARY KEY,
$COL_MANGA_ID INTEGER NOT NULL,
$COL_SYNC_ID INTEGER NOT NULL,

@ -251,7 +251,9 @@ class DownloadManager(val context: Context) {
queue.remove(chapters)
val chapterDirs =
provider.findChapterDirs(chapters, manga, source) + provider.findTempChapterDirs(
chapters, manga, source
chapters,
manga,
source
)
chapterDirs.forEach { it.delete() }
cache.removeChapters(chapters, manga)

@ -78,16 +78,21 @@ internal class DownloadNotifier(private val context: Context) {
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
isDownloading = true
// Pause action
addAction(R.drawable.ic_pause_24dp,
addAction(
R.drawable.ic_pause_24dp,
context.getString(R.string.pause),
NotificationReceiver.pauseDownloadsPendingBroadcast(context))
NotificationReceiver.pauseDownloadsPendingBroadcast(context)
)
}
if (download != null) {
val title = download.manga.title.chop(15)
val quotedTitle = Pattern.quote(title)
val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*"
.toRegex(RegexOption.IGNORE_CASE), "")
val chapter = download.chapter.name.replaceFirst(
"$quotedTitle[\\s]*[-]*[\\s]*"
.toRegex(RegexOption.IGNORE_CASE),
""
)
setContentTitle("$title - $chapter".chop(30))
setContentText(
context.getString(R.string.downloading)
@ -124,17 +129,21 @@ internal class DownloadNotifier(private val context: Context) {
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
isDownloading = true
// Pause action
addAction(R.drawable.ic_pause_24dp,
addAction(
R.drawable.ic_pause_24dp,
context.getString(R.string.pause),
NotificationReceiver.pauseDownloadsPendingBroadcast(context))
NotificationReceiver.pauseDownloadsPendingBroadcast(context)
)
}
val title = download.manga.title.chop(15)
val quotedTitle = Pattern.quote(title)
val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "")
setContentTitle("$title - $chapter".chop(30))
setContentText(context.getString(R.string.downloading_progress)
.format(download.downloadedImages, download.pages!!.size))
setContentText(
context.getString(R.string.downloading_progress)
.format(download.downloadedImages, download.pages!!.size)
)
setStyle(null)
setProgress(download.pages!!.size, download.downloadedImages, false)
}

@ -163,11 +163,15 @@ class DownloadService : Service() {
subscriptions += ReactiveNetwork.observeNetworkConnectivity(applicationContext)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ state -> onNetworkStateChanged(state)
}, {
.subscribe(
{ state ->
onNetworkStateChanged(state)
},
{
toast(R.string.could_not_download_chapter_can_try_again)
stopSelf()
})
}
)
}
/**

@ -13,7 +13,7 @@ class DownloadQueue(
private val store: DownloadStore,
private val queue: MutableList<Download> = CopyOnWriteArrayList<Download>()
) :
List<Download> by queue {
List<Download> by queue {
private val statusSubject = PublishSubject.create<Download>()

@ -20,7 +20,9 @@ class CoverViewTarget(
progress?.gone()
view.scaleType = ImageView.ScaleType.CENTER
val vector = VectorDrawableCompat.create(
view.context.resources, R.drawable.ic_broken_image_24dp, null
view.context.resources,
R.drawable.ic_broken_image_24dp,
null
)
vector?.setTint(view.context.getResourceColor(android.R.attr.textColorSecondary))
view.setImageDrawable(vector)

@ -70,7 +70,8 @@ class MangaFetcher : Fetcher<Manga> {
return fileLoader(coverFile)
}
val (_, body) = awaitGetCall(
manga, if (manga.favorite) {
manga,
if (manga.favorite) {
!options.networkCachePolicy.readEnabled
} else {
false

@ -29,7 +29,8 @@ class CustomMangaManager(val context: Context) {
val json = try {
Gson().fromJson(
Scanner(editJson).useDelimiter("\\Z").next(), JsonObject::class.java
Scanner(editJson).useDelimiter("\\Z").next(),
JsonObject::class.java
)
} catch (e: Exception) {
null
@ -83,7 +84,12 @@ class CustomMangaManager(val context: Context) {
fun Manga.toJson(): MangaJson {
return MangaJson(
id!!, title, author, artist, description, genre?.split(", ")?.toTypedArray()
id!!,
title,
author,
artist,
description,
genre?.split(", ")?.toTypedArray()
)
}

@ -42,8 +42,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
.build()
val request = PeriodicWorkRequestBuilder<LibraryUpdateJob>(
interval.toLong(), TimeUnit.HOURS,
10, TimeUnit.MINUTES)
interval.toLong(),
TimeUnit.HOURS,
10,
TimeUnit.MINUTES
)
.addTag(TAG)
.setConstraints(constraints)
.build()

@ -131,7 +131,9 @@ class LibraryUpdateNotifier(private val context: Context) {
val manga = it.key
val chapters = it.value
val chapterNames = chapters.map { chapter -> chapter.name }
notifications.add(Pair(context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
notifications.add(
Pair(
context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
setSmallIcon(R.drawable.ic_tachi)
try {
val request = GetRequest.Builder(context).data(manga)
@ -148,8 +150,8 @@ class LibraryUpdateNotifier(private val context: Context) {
setContentTitle(manga.title)
color = ContextCompat.getColor(context, R.color.colorAccent)
val chaptersNames = if (chapterNames.size > MAX_CHAPTERS) {
"${chapterNames.take(MAX_CHAPTERS - 1)
.joinToString(", ")}, " + context.resources.getQuantityString(
"${chapterNames.take(MAX_CHAPTERS - 1).joinToString(", ")}, " +
context.resources.getQuantityString(
R.plurals.notification_and_n_more,
(chapterNames.size - (MAX_CHAPTERS - 1)),
(chapterNames.size - (MAX_CHAPTERS - 1))
@ -161,30 +163,41 @@ class LibraryUpdateNotifier(private val context: Context) {
setGroup(Notifications.GROUP_NEW_CHAPTERS)
setContentIntent(
NotificationReceiver.openChapterPendingActivity(
context, manga, chapters.first()
context,
manga,
chapters.first()
)
)
addAction(
R.drawable.ic_eye_24dp,
context.getString(R.string.mark_as_read),
NotificationReceiver.markAsReadPendingBroadcast(
context, manga, chapters, Notifications.ID_NEW_CHAPTERS
context,
manga,
chapters,
Notifications.ID_NEW_CHAPTERS
)
)
addAction(
R.drawable.ic_book_24dp,
context.getString(R.string.view_chapters),
NotificationReceiver.openChapterPendingActivity(
context, manga, Notifications.ID_NEW_CHAPTERS
context,
manga,
Notifications.ID_NEW_CHAPTERS
)
)
setAutoCancel(true)
}, manga.id.hashCode()))
},
manga.id.hashCode()
)
)
}
NotificationManagerCompat.from(context).apply {
notify(Notifications.ID_NEW_CHAPTERS,
notify(
Notifications.ID_NEW_CHAPTERS,
context.notification(Notifications.CHANNEL_NEW_CHAPTERS) {
setSmallIcon(R.drawable.ic_tachi)
setLargeIcon(notificationBitmap)
@ -193,14 +206,18 @@ class LibraryUpdateNotifier(private val context: Context) {
if (updates.size > 1) {
setContentText(
context.resources.getQuantityString(
R.plurals.for_n_titles, updates.size, updates.size
R.plurals.for_n_titles,
updates.size,
updates.size
)
)
setStyle(
NotificationCompat.BigTextStyle()
.bigText(updates.keys.joinToString("\n") {
.bigText(
updates.keys.joinToString("\n") {
it.title.chop(45)
})
}
)
)
} else {
setContentText(updates.keys.first().title.chop(45))
@ -211,7 +228,8 @@ class LibraryUpdateNotifier(private val context: Context) {
setGroupSummary(true)
setContentIntent(getNotificationIntent())
setAutoCancel(true)
})
}
)
notifications.forEach {
notify(it.second, it.first)

@ -9,7 +9,8 @@ object LibraryUpdateRanker {
val rankingScheme = listOf(
(this::lexicographicRanking)(),
(this::latestFirstRanking)())
(this::latestFirstRanking)()
)
/**
* Provides a total ordering over all the Mangas.

@ -134,7 +134,8 @@ class LibraryUpdateService(
val selectedScheme = preferences.libraryUpdatePrioritization().getOrDefault()
val savedMangasList = intent.getLongArrayExtra(KEY_MANGAS)?.asList()
val mangaList = (if (savedMangasList != null) {
val mangaList = (
if (savedMangasList != null) {
val mangas = db.getLibraryMangas().executeAsBlocking().filter {
it.id in savedMangasList
}.distinctBy { it.id }
@ -143,7 +144,8 @@ class LibraryUpdateService(
mangas
} else {
getMangaToUpdate(intent, target)
}).sortedWith(rankingScheme[selectedScheme])
}
).sortedWith(rankingScheme[selectedScheme])
// Update favorite manga. Destroy service when completed or in case of an error.
launchTarget(target, mangaList, startId)
return START_REDELIVER_INTENT
@ -157,7 +159,8 @@ class LibraryUpdateService(
super.onCreate()
notifier = LibraryUpdateNotifier(this)
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "LibraryUpdateService:WakeLock"
PowerManager.PARTIAL_WAKE_LOCK,
"LibraryUpdateService:WakeLock"
)
wakeLock.acquire(TimeUnit.MINUTES.toMillis(30))
startForeground(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
@ -247,7 +250,9 @@ class LibraryUpdateService(
val hasDLs = try {
requestSemaphore.withPermit {
updateMangaInSource(
it.key, downloadNew, categoriesToDownload
it.key,
downloadNew,
categoriesToDownload
)
}
} catch (e: Exception) {
@ -351,13 +356,15 @@ class LibraryUpdateService(
var hasDownloads = false
while (count < mangaToUpdateMap[source]!!.size) {
val shouldDownload =
(downloadNew && (categoriesToDownload.isEmpty() || mangaToUpdateMap[source]!![count].category in categoriesToDownload || db.getCategoriesForManga(
mangaToUpdateMap[source]!![count]
).executeOnIO().any { (it.id ?: -1) in categoriesToDownload }))
if (updateMangaChapters(
mangaToUpdateMap[source]!![count], this.count.andIncrement, shouldDownload
(
downloadNew && (
categoriesToDownload.isEmpty() ||
mangaToUpdateMap[source]!![count].category in categoriesToDownload ||
db.getCategoriesForManga(mangaToUpdateMap[source]!![count])
.executeOnIO().any { (it.id ?: -1) in categoriesToDownload }
)
) {
)
if (updateMangaChapters(mangaToUpdateMap[source]!![count], this.count.andIncrement, shouldDownload)) {
hasDownloads = true
}
count++

@ -58,29 +58,41 @@ class NotificationReceiver : BroadcastReceiver() {
// Clear the download queue
ACTION_CLEAR_DOWNLOADS -> downloadManager.clearQueue(true)
// Launch share activity and dismiss notification
ACTION_SHARE_IMAGE -> shareImage(context, intent.getStringExtra(EXTRA_FILE_LOCATION),
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1))
ACTION_SHARE_IMAGE -> shareImage(
context,
intent.getStringExtra(EXTRA_FILE_LOCATION),
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
)
// Delete image from path and dismiss notification
ACTION_DELETE_IMAGE -> deleteImage(context, intent.getStringExtra(EXTRA_FILE_LOCATION),
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1))
ACTION_DELETE_IMAGE -> deleteImage(
context,
intent.getStringExtra(EXTRA_FILE_LOCATION),
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
)
// Cancel library update and dismiss notification
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context)
ACTION_CANCEL_RESTORE -> cancelRestoreUpdate(context)
// Share backup file
ACTION_SHARE_BACKUP ->
shareBackup(
context, intent.getParcelableExtra(EXTRA_URI),
context,
intent.getParcelableExtra(EXTRA_URI),
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
)
// Open reader activity
ACTION_OPEN_CHAPTER -> {
openChapter(context, intent.getLongExtra(EXTRA_MANGA_ID, -1),
intent.getLongExtra(EXTRA_CHAPTER_ID, -1))
openChapter(
context,
intent.getLongExtra(EXTRA_MANGA_ID, -1),
intent.getLongExtra(EXTRA_CHAPTER_ID, -1)
)
}
ACTION_MARK_AS_READ -> {
val notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
if (notificationId > -1) dismissNotification(
context, notificationId, intent.getIntExtra(EXTRA_GROUP_ID, 0)
context,
notificationId,
intent.getIntExtra(EXTRA_GROUP_ID, 0)
)
val urls = intent.getStringArrayExtra(EXTRA_CHAPTER_URL) ?: return
val mangaId = intent.getLongExtra(EXTRA_MANGA_ID, -1)
@ -377,8 +389,13 @@ class NotificationReceiver : BroadcastReceiver() {
clipData = ClipData.newRawUri(null, uri)
type = "image/*"
}
return PendingIntent.getActivity(context, 0, shareIntent, PendingIntent
.FLAG_CANCEL_CURRENT)
return PendingIntent.getActivity(
context,
0,
shareIntent,
PendingIntent
.FLAG_CANCEL_CURRENT
)
}
/**
@ -412,8 +429,13 @@ class NotificationReceiver : BroadcastReceiver() {
Chapter
): PendingIntent {
val newIntent = ReaderActivity.newIntent(context, manga, chapter)
return PendingIntent.getActivity(context, manga.id.hashCode(), newIntent, PendingIntent
.FLAG_UPDATE_CURRENT)
return PendingIntent.getActivity(
context,
manga.id.hashCode(),
newIntent,
PendingIntent
.FLAG_UPDATE_CURRENT
)
}
/**
@ -431,7 +453,10 @@ class NotificationReceiver : BroadcastReceiver() {
.putExtra("notificationId", manga.id.hashCode())
.putExtra("groupId", groupId)
return PendingIntent.getActivity(
context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT
context,
manga.id.hashCode(),
newIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
}
@ -462,7 +487,10 @@ class NotificationReceiver : BroadcastReceiver() {
Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_EXTENSIONS)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
return PendingIntent.getActivity(
context, 0, newIntent, PendingIntent.FLAG_UPDATE_CURRENT
context,
0,
newIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
}

@ -61,37 +61,44 @@ object Notifications {
fun createChannels(context: Context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
val channels = listOf(NotificationChannel(
val channels = listOf(
NotificationChannel(
CHANNEL_COMMON,
context.getString(R.string.common),
NotificationManager.IMPORTANCE_LOW
), NotificationChannel(
),
NotificationChannel(
CHANNEL_LIBRARY,
context.getString(R.string.updating_library),
NotificationManager.IMPORTANCE_LOW
).apply {
setShowBadge(false)
}, NotificationChannel(
},
NotificationChannel(
CHANNEL_DOWNLOADER,
context.getString(R.string.downloads),
NotificationManager.IMPORTANCE_LOW
).apply {
setShowBadge(false)
}, NotificationChannel(
},
NotificationChannel(
CHANNEL_UPDATES_TO_EXTS,
context.getString(R.string.extension_updates),
NotificationManager.IMPORTANCE_DEFAULT
), NotificationChannel(
),
NotificationChannel(
CHANNEL_NEW_CHAPTERS,
context.getString(R.string.new_chapters),
NotificationManager.IMPORTANCE_DEFAULT
), NotificationChannel(
),
NotificationChannel(
CHANNEL_BACKUP_RESTORE,
context.getString(R.string.restoring_backup),
NotificationManager.IMPORTANCE_LOW
).apply {
setShowBadge(false)
})
}
)
context.notificationManager.createNotificationChannels(channels)
}
}

@ -43,12 +43,20 @@ class PreferencesHelper(val context: Context) {
private val flowPrefs = FlowSharedPreferences(prefs)
private val defaultDownloadsDir = Uri.fromFile(
File(Environment.getExternalStorageDirectory().absolutePath + File.separator +
context.getString(R.string.app_name), "downloads"))
File(
Environment.getExternalStorageDirectory().absolutePath + File.separator +
context.getString(R.string.app_name),
"downloads"
)
)
private val defaultBackupDir = Uri.fromFile(
File(Environment.getExternalStorageDirectory().absolutePath + File.separator +
context.getString(R.string.app_name), "backup"))
File(
Environment.getExternalStorageDirectory().absolutePath + File.separator +
context.getString(R.string.app_name),
"backup"
)
)
fun getInt(key: String, default: Int?) = rxPrefs.getInteger(key, default)
fun getStringPref(key: String, default: String?) = rxPrefs.getString(key, default)

@ -237,7 +237,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
.appendQueryParameter("response_type", "token")
.build()!!
fun addToLibraryQuery() = """
fun addToLibraryQuery() =
"""
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
| id
@ -246,7 +247,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|}
|""".trimMargin()
fun deleteFromLibraryQuery() = """
fun deleteFromLibraryQuery() =
"""
|mutation DeleteManga(${'$'}listId: Int) {
|DeleteMediaListEntry (id: ${'$'}listId) {
|deleted
@ -254,7 +256,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|}
|}""".trimMargin()
fun updateInLibraryQuery() = """
fun updateInLibraryQuery() =
"""
|mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) {
|SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) {
|id
@ -264,7 +267,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|}
|""".trimMargin()
fun searchQuery() = """
fun searchQuery() =
"""
|query Search(${'$'}query: String) {
|Page (perPage: 50) {
|media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) {
@ -289,7 +293,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|}
|""".trimMargin()
fun findLibraryMangaQuery() = """
fun findLibraryMangaQuery() =
"""
|query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
|Page {
|mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) {
@ -320,7 +325,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|}
|""".trimMargin()
fun currentUserQuery() = """
fun currentUserQuery() =
"""
|query User {
|Viewer {
|id

@ -37,8 +37,10 @@ class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor {
val authRequest = if (originalRequest.method == "GET") originalRequest.newBuilder()
.header("User-Agent", "Tachiyomi")
.url(originalRequest.url.newBuilder()
.addQueryParameter("access_token", currAuth.access_token).build())
.url(
originalRequest.url.newBuilder()
.addQueryParameter("access_token", currAuth.access_token).build()
)
.build() else originalRequest.newBuilder()
.post(addTocken(currAuth.access_token, originalRequest.body as FormBody))
.header("User-Agent", "Tachiyomi")
@ -54,7 +56,8 @@ class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor {
System.currentTimeMillis() / 1000,
oauth.expires_in,
oauth.refresh_token,
this.oauth?.user_id)
this.oauth?.user_id
)
bangumi.saveToken(oauth)
}

@ -44,7 +44,10 @@ class UpdaterJob(private val context: Context, workerParams: WorkerParameters) :
android.R.drawable.stat_sys_download_done,
context.getString(R.string.download),
PendingIntent.getService(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
)
}
@ -66,8 +69,11 @@ class UpdaterJob(private val context: Context, workerParams: WorkerParameters) :
.build()
val request = PeriodicWorkRequestBuilder<UpdaterJob>(
1, TimeUnit.DAYS,
1, TimeUnit.HOURS)
1,
TimeUnit.DAYS,
1,
TimeUnit.HOURS
)
.addTag(TAG)
.setConstraints(constraints)
.build()

@ -75,13 +75,17 @@ internal class UpdaterNotifier(private val context: Context) {
setProgress(0, 0, false)
// Install action
setContentIntent(NotificationHandler.installApkPendingActivity(context, uri))
addAction(R.drawable.ic_system_update_24dp,
addAction(
R.drawable.ic_system_update_24dp,
context.getString(R.string.install),
NotificationHandler.installApkPendingActivity(context, uri))
NotificationHandler.installApkPendingActivity(context, uri)
)
// Cancel action
addAction(R.drawable.ic_close_24dp,
addAction(
R.drawable.ic_close_24dp,
context.getString(R.string.cancel),
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER))
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER)
)
}
notification.show()
}
@ -99,13 +103,17 @@ internal class UpdaterNotifier(private val context: Context) {
setProgress(0, 0, false)
color = ContextCompat.getColor(context, R.color.colorAccent)
// Retry action
addAction(R.drawable.ic_refresh_24dp,
addAction(
R.drawable.ic_refresh_24dp,
context.getString(R.string.retry),
UpdaterService.downloadApkPendingService(context, url))
UpdaterService.downloadApkPendingService(context, url)
)
// Cancel action
addAction(R.drawable.ic_close_24dp,
addAction(
R.drawable.ic_close_24dp,
context.getString(R.string.cancel),
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER))
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER)
)
}
notification.show(Notifications.ID_UPDATER)
}

@ -43,7 +43,8 @@ class UpdaterService : Service() {
startForeground(Notifications.ID_UPDATER, notifier.onDownloadStarted(getString(R.string.app_name)).build())
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "${javaClass.name}:WakeLock"
PowerManager.PARTIAL_WAKE_LOCK,
"${javaClass.name}:WakeLock"
)
wakeLock.acquire()
}

@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.data.updater.UpdateChecker
import eu.kanade.tachiyomi.data.updater.UpdateResult
class GithubUpdateChecker : UpdateChecker() {
class GithubUpdateChecker : UpdateChecker() {
private val service: GithubService = GithubService.create()

@ -80,8 +80,7 @@ class ExtensionManager(
context.packageManager.getApplicationIcon(pkgName)
} catch (e: Exception) {
null
}
else null
} else null
}
/**

@ -45,12 +45,15 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
val preferences: PreferencesHelper by injectLazy()
preferences.extensionUpdatesCount().set(names.size)
NotificationManagerCompat.from(context).apply {
notify(Notifications.ID_UPDATES_TO_EXTS,
notify(
Notifications.ID_UPDATES_TO_EXTS,
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
setContentTitle(
context.resources.getQuantityString(
R.plurals.extension_updates_available, names
.size, names.size
R.plurals.extension_updates_available,
names
.size,
names.size
)
)
val extNames = names.joinToString(", ")
@ -64,7 +67,8 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
)
)
setAutoCancel(true)
})
}
)
}
}
@ -80,8 +84,11 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
.build()
val request = PeriodicWorkRequestBuilder<ExtensionUpdateJob>(
12, TimeUnit.HOURS,
1, TimeUnit.HOURS)
12,
TimeUnit.HOURS,
1,
TimeUnit.HOURS
)
.addTag(TAG)
.setConstraints(constraints)
.build()

@ -103,8 +103,10 @@ internal object ExtensionLoader {
// Validate lib version
val majorLibVersion = versionName.substringBefore('.').toInt()
if (majorLibVersion < LIB_VERSION_MIN || majorLibVersion > LIB_VERSION_MAX) {
val exception = Exception("Lib version is $majorLibVersion, while only versions " +
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed")
val exception = Exception(
"Lib version is $majorLibVersion, while only versions " +
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed"
)
Timber.w(exception)
return LoadResult.Error(exception)
}

@ -58,7 +58,8 @@ fun Call.asObservable(): Observable<Response> {
// Based on https://github.com/gildor/kotlin-coroutines-okhttp
suspend fun Call.await(): Response {
return suspendCancellableCoroutine { continuation ->
enqueue(object : Callback {
enqueue(
object : Callback {
override fun onResponse(call: Call, response: Response) {
continuation.resume(response)
}
@ -68,7 +69,8 @@ suspend fun Call.await(): Response {
if (continuation.isCancelled) return
continuation.resumeWithException(e)
}
})
}
)
continuation.invokeOnCancellation {
try {

@ -241,10 +241,12 @@ class LocalSource(private val context: Context) : CatalogueSource {
date_upload = chapterFile.lastModified()
ChapterRecognition.parseChapterNumber(this, manga)
}
}.sortedWith(Comparator { c1, c2 ->
}.sortedWith(
Comparator { c1, c2 ->
val c = c2.chapter_number.compareTo(c1.chapter_number)
if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c
})
}
)
return Observable.just(chapters)
}
@ -289,18 +291,22 @@ class LocalSource(private val context: Context) : CatalogueSource {
return when (format) {
is Format.Directory -> {
val entry = format.file.listFiles()
?.sortedWith(Comparator<File> { f1, f2 ->
?.sortedWith(
Comparator<File> { f1, f2 ->
f1.name.compareToCaseInsensitiveNaturalOrder(f2.name)
})
}
)
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
entry?.let { updateCover(context, manga, it.inputStream()) }
}
is Format.Zip -> {
ZipFile(format.file).use { zip ->
val entry = zip.entries().toList().sortedWith(Comparator<ZipEntry> { f1, f2 ->
val entry = zip.entries().toList().sortedWith(
Comparator<ZipEntry> { f1, f2 ->
f1.name.compareToCaseInsensitiveNaturalOrder(f2.name)
}).find {
}
).find {
!it.isDirectory && ImageUtil.isImage(it.name) {
zip.getInputStream(it)
}
@ -311,9 +317,11 @@ class LocalSource(private val context: Context) : CatalogueSource {
}
is Format.Rar -> {
Archive(format.file).use { archive ->
val entry = archive.fileHeaders.sortedWith(Comparator<FileHeader> { f1, f2 ->
val entry = archive.fileHeaders.sortedWith(
Comparator<FileHeader> { f1, f2 ->
f1.fileNameString.compareToCaseInsensitiveNaturalOrder(f2.fileNameString)
}).find {
}
).find {
!it.isDirectory && ImageUtil.isImage(it.fileNameString) {
archive.getInputStream(it)
}

@ -21,13 +21,24 @@ open class SourceManager(private val context: Context) {
private val delegatedSources = listOf(
DelegatedSource(
"reader.kireicake.com", 5509224355268673176, KireiCake()
), DelegatedSource(
"jaiminisbox.com", 9064882169246918586, FoolSlide("jaiminis", "/reader")
), DelegatedSource(
"mangadex.org", 2499283573021220255, MangaDex()
), DelegatedSource(
"mangaplus.shueisha.co.jp", 1998944621602463790, MangaPlus()
"reader.kireicake.com",
5509224355268673176,
KireiCake()
),
DelegatedSource(
"jaiminisbox.com",
9064882169246918586,
FoolSlide("jaiminis", "/reader")
),
DelegatedSource(
"mangadex.org",
2499283573021220255,
MangaDex()
),
DelegatedSource(
"mangaplus.shueisha.co.jp",
1998944621602463790,
MangaPlus()
)
).associateBy { it.sourceId }
@ -92,8 +103,10 @@ open class SourceManager(private val context: Context) {
private fun getSourceNotInstalledException(): Exception {
return SourceNotFoundException(
context.getString(
R.string.source_not_installed_, id.toString()
), id
R.string.source_not_installed_,
id.toString()
),
id
)
}

@ -21,7 +21,7 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
open class FoolSlide(override val domainName: String, private val urlModifier: String = "") :
DelegatedHttpSource
DelegatedHttpSource
() {
override fun canOpenUrl(uri: Uri): Boolean = true
@ -34,7 +34,11 @@ DelegatedHttpSource
val chapterNumber = uri.pathSegments.getOrNull(4 + offset) ?: return null
val subChapterNumber = uri.pathSegments.getOrNull(5 + offset)?.toIntOrNull()?.toString()
return "$urlModifier/read/" + listOfNotNull(
mangaName, lang, volume, chapterNumber, subChapterNumber
mangaName,
lang,
volume,
chapterNumber,
subChapterNumber
).joinToString("/") + "/"
}
@ -95,8 +99,11 @@ DelegatedHttpSource
private fun allowAdult(request: Request) = allowAdult(request.url.toString())
private fun allowAdult(url: String): Request {
return POST(url, body = FormBody.Builder()
return POST(
url,
body = FormBody.Builder()
.add("adult", "true")
.build())
.build()
)
}
}

@ -17,9 +17,8 @@ class KireiCake : FoolSlide("kireicake") {
this.url = url
source = delegate?.id ?: -1
title = document.select("$mangaDetailsInfoSelector li:has(b:contains(title))").first()
?.ownText()?.substringAfter(":")?.trim() ?: url.split("/").last().replace(
"_", " " + ""
).capitalizeWords()
?.ownText()?.substringAfter(":")?.trim()
?: url.split("/").last().replace("_", " " + "").capitalizeWords()
description =
document.select("$mangaDetailsInfoSelector li:has(b:contains(description))").first()
?.ownText()?.substringAfter(":")

@ -61,9 +61,13 @@ class MangaPlus : DelegatedHttpSource() {
context.getString(R.string.chapter_not_found)
)
if (manga != null) {
Triple(trueChapter, manga.apply {
Triple(
trueChapter,
manga.apply {
this.title = trimmedTitle
}, chapters.orEmpty())
},
chapters.orEmpty()
)
} else null
}
}

@ -33,12 +33,16 @@ class CenteredToolbar@JvmOverloads constructor(context: Context, attrs: Attribut
}
fun showDropdown(down: Boolean = true) {
toolbar_title.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_blank_24dp, 0,
toolbar_title.setCompoundDrawablesRelativeWithIntrinsicBounds(
R.drawable.ic_blank_24dp,
0,
if (down) {
R.drawable.ic_arrow_drop_down_24dp
} else {
R.drawable.ic_arrow_drop_up_24dp
}, 0)
},
0
)
}
fun hideDropdown() {

@ -21,7 +21,9 @@ class MaterialFastScroll @JvmOverloads constructor(context: Context, attrs: Attr
var scrollOffset = 0
init {
setViewsToUse(
R.layout.material_fastscroll, R.id.fast_scroller_bubble, R.id.fast_scroller_handle
R.layout.material_fastscroll,
R.id.fast_scroller_bubble,
R.id.fast_scroller_handle
)
autoHideEnabled = true
ignoreTouchesOutsideHandle = false
@ -85,7 +87,8 @@ class MaterialFastScroll @JvmOverloads constructor(context: Context, attrs: Attr
val targetPos = getTargetPos(y)
if (layoutManager is StaggeredGridLayoutManager) {
(layoutManager as StaggeredGridLayoutManager).scrollToPositionWithOffset(
targetPos, scrollOffset
targetPos,
scrollOffset
)
} else {
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(targetPos, scrollOffset)

@ -119,7 +119,10 @@ class MaterialMenuSheet(
isElevated = elevate
elevationAnimator?.cancel()
elevationAnimator = ObjectAnimator.ofFloat(
title_layout, "elevation", title_layout.elevation, if (elevate) 10f else 0f
title_layout,
"elevation",
title_layout.elevation,
if (elevate) 10f else 0f
)
elevationAnimator?.start()
}

@ -14,11 +14,13 @@ import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.*
import timber.log.Timber
abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateController(bundle),
abstract class BaseController(bundle: Bundle? = null) :
RestoreViewOnCreateController(bundle),
LayoutContainer {
init {
addLifecycleListener(object : LifecycleListener() {
addLifecycleListener(
object : LifecycleListener() {
override fun postCreateView(controller: Controller, view: View) {
onViewCreated(view)
}
@ -38,7 +40,8 @@ abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateContr
override fun preDestroyView(controller: Controller, view: View) {
Timber.d("Destroy view for ${controller.instance()}")
}
})
}
)
}
override val containerView: View?
@ -96,7 +99,8 @@ abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateContr
*/
var expandActionViewFromInteraction = false
fun MenuItem.fixExpand(onExpand: ((MenuItem) -> Boolean)? = null, onCollapse: ((MenuItem) -> Boolean)? = null) {
setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
setOnActionExpandListener(
object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
return onExpand?.invoke(item) ?: true
}
@ -106,7 +110,8 @@ abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateContr
return onCollapse?.invoke(item) ?: true
}
})
}
)
if (expandActionViewFromInteraction) {
expandActionViewFromInteraction = false

@ -87,10 +87,12 @@ abstract class DialogController : RestoreViewOnCreateController {
*/
fun showDialog(router: Router, tag: String?) {
dismissed = false
router.pushController(RouterTransaction.with(this)
router.pushController(
RouterTransaction.with(this)
.pushChangeHandler(SimpleSwapChangeHandler(false))
.popChangeHandler(SimpleSwapChangeHandler(false))
.tag(tag))
.tag(tag)
)
}
/**

@ -7,7 +7,8 @@ import nucleus.factory.PresenterFactory
import nucleus.presenter.Presenter
@Suppress("LeakingThis")
abstract class NucleusController<P : Presenter<*>>(val bundle: Bundle? = null) : RxController(bundle),
abstract class NucleusController<P : Presenter<*>>(val bundle: Bundle? = null) :
RxController(bundle),
PresenterFactory<P> {
private val delegate = NucleusConductorDelegate(this)

@ -22,7 +22,8 @@ import kotlinx.android.synthetic.main.categories_controller.*
/**
* Controller to manage the categories for the users' library.
*/
class CategoryController(bundle: Bundle? = null) : BaseController(bundle),
class CategoryController(bundle: Bundle? = null) :
BaseController(bundle),
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemMoveListener,
CategoryAdapter.CategoryItemListener {
@ -147,12 +148,14 @@ class CategoryController(bundle: Bundle? = null) : BaseController(bundle),
adapter?.restoreDeletedItems()
undoing = true
}
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
addCallback(
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (!undoing) confirmDelete()
}
})
}
)
}
(activity as? MainActivity)?.setUndoSnackBar(snack)
}

@ -51,16 +51,22 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
createCategory = category.order == CREATE_CATEGORY_ORDER
if (createCategory) {
title.setTextColor(ContextCompat.getColor(itemView.context, R.color.text_color_hint))
regularDrawable = ContextCompat.getDrawable(itemView.context, R.drawable
.ic_add_24dp)
regularDrawable = ContextCompat.getDrawable(
itemView.context,
R.drawable
.ic_add_24dp
)
image.gone()
edit_button.setImageDrawable(null)
edit_text.setText("")
edit_text.hint = title.text
} else {
title.setTextColor(ContextCompat.getColor(itemView.context, R.color.textColorPrimary))
regularDrawable = ContextCompat.getDrawable(itemView.context, R.drawable
.ic_drag_handle_24dp)
regularDrawable = ContextCompat.getDrawable(
itemView.context,
R.drawable
.ic_drag_handle_24dp
)
image.visible()
edit_text.setText(title.text)
}
@ -80,7 +86,8 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
if (!createCategory) {
reorder.setImageDrawable(
ContextCompat.getDrawable(
itemView.context, R.drawable.ic_delete_24dp
itemView.context,
R.drawable.ic_delete_24dp
)
)
reorder.setOnClickListener {
@ -96,8 +103,13 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
reorder.setOnTouchListener { _, _ -> true }
}
edit_text.clearFocus()
edit_button.drawable?.mutate()?.setTint(ContextCompat.getColor(itemView.context, R
.color.gray_button))
edit_button.drawable?.mutate()?.setTint(
ContextCompat.getColor(
itemView.context,
R
.color.gray_button
)
)
reorder.setImageDrawable(regularDrawable)
}
}
@ -105,7 +117,8 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
private fun submitChanges() {
if (edit_text.visibility == View.VISIBLE) {
if (adapter.categoryItemListener
.onCategoryRename(adapterPosition, edit_text.text.toString())) {
.onCategoryRename(adapterPosition, edit_text.text.toString())
) {
isEditing(false)
edit_text.inputType = InputType.TYPE_NULL
if (!createCategory)
@ -119,8 +132,11 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
private fun showKeyboard() {
val inputMethodManager: InputMethodManager =
itemView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(edit_text, WindowManager.LayoutParams
.SOFT_INPUT_ADJUST_PAN)
inputMethodManager.showSoftInput(
edit_text,
WindowManager.LayoutParams
.SOFT_INPUT_ADJUST_PAN
)
}
/**

@ -70,7 +70,8 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
preferences.downloadNew().set(true)
}
if (preferences.libraryUpdateInterval().getOrDefault() > 0 &&
!updatePref(preferences.libraryUpdateCategories(), view.include_global)) {
!updatePref(preferences.libraryUpdateCategories(), view.include_global)
) {
preferences.libraryUpdateInterval().set(0)
LibraryUpdateJob.setupTask(0)
}
@ -99,9 +100,11 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
view.title.hint = category.name
view.title.append(category.name)
val downloadNew = preferences.downloadNew().getOrDefault()
setCheckbox(view.download_new,
setCheckbox(
view.download_new,
preferences.downloadNewCategories(),
true)
true
)
if (downloadNew && preferences.downloadNewCategories().getOrDefault().isEmpty())
view.download_new.gone()
else if (!downloadNew)

@ -8,8 +8,11 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
*
* @param context the context of the fragment containing this adapter.
*/
class DownloadAdapter(controller: DownloadItemListener) : FlexibleAdapter<DownloadItem>(null, controller,
true) {
class DownloadAdapter(controller: DownloadItemListener) : FlexibleAdapter<DownloadItem>(
null,
controller,
true
) {
/**
* Listener called when an item of the list is released.

@ -94,7 +94,8 @@ class DownloadBottomSheet @JvmOverloads constructor(
private fun updateDLTitle() {
val extCount = presenter.downloadQueue.firstOrNull()
title_text.text = if (extCount != null) resources.getString(
R.string.downloading_, extCount.chapter.name
R.string.downloading_,
extCount.chapter.name
)
else ""
}
@ -164,7 +165,8 @@ class DownloadBottomSheet @JvmOverloads constructor(
if (presenter.downloadQueue.isEmpty()) {
empty_view?.show(
R.drawable.ic_download_off_24dp,
R.string.nothing_is_downloading)
R.string.nothing_is_downloading
)
} else {
empty_view?.hide()
}

@ -17,22 +17,38 @@ class DownloadButton @JvmOverloads constructor(context: Context, attrs: Attribut
FrameLayout(context, attrs) {
private val activeColor = context.getResourceColor(R.attr.colorAccent)
private val progressBGColor = ContextCompat.getColor(context,
R.color.divider)
private val disabledColor = ContextCompat.getColor(context,
R.color.material_on_surface_disabled)
private val downloadedColor = ContextCompat.getColor(context,
R.color.download)
private val errorColor = ContextCompat.getColor(context,
R.color.material_red_500)
private val filledCircle = ContextCompat.getDrawable(context,
R.drawable.filled_circle)?.mutate()
private val borderCircle = ContextCompat.getDrawable(context,
R.drawable.border_circle)?.mutate()
private val downloadDrawable = ContextCompat.getDrawable(context,
R.drawable.ic_arrow_downward_24dp)?.mutate()
private val checkDrawable = ContextCompat.getDrawable(context,
R.drawable.ic_check_24dp)?.mutate()
private val progressBGColor = ContextCompat.getColor(
context,
R.color.divider
)
private val disabledColor = ContextCompat.getColor(
context,
R.color.material_on_surface_disabled
)
private val downloadedColor = ContextCompat.getColor(
context,
R.color.download
)
private val errorColor = ContextCompat.getColor(
context,
R.color.material_red_500
)
private val filledCircle = ContextCompat.getDrawable(
context,
R.drawable.filled_circle
)?.mutate()
private val borderCircle = ContextCompat.getDrawable(
context,
R.drawable.border_circle
)?.mutate()
private val downloadDrawable = ContextCompat.getDrawable(
context,
R.drawable.ic_arrow_downward_24dp
)?.mutate()
private val checkDrawable = ContextCompat.getDrawable(
context,
R.drawable.ic_check_24dp
)?.mutate()
private var isAnimating = false
private var iconAnimation: ObjectAnimator? = null
@ -42,8 +58,10 @@ class DownloadButton @JvmOverloads constructor(context: Context, attrs: Attribut
download_icon.alpha = 1f
isAnimating = false
}
download_icon.setImageDrawable(if (state == Download.CHECKED)
checkDrawable else downloadDrawable)
download_icon.setImageDrawable(
if (state == Download.CHECKED)
checkDrawable else downloadDrawable
)
when (state) {
Download.CHECKED -> {
download_progress.gone()

@ -54,8 +54,10 @@ class DownloadHolder(private val view: View, val adapter: DownloadAdapter) :
migration_menu.visibleIf(adapterPosition != 0 || adapterPosition != adapter.itemCount - 1)
migration_menu.setVectorCompat(
R.drawable.ic_more_vert_24dp, view.context
.getResourceColor(android.R.attr.textColorPrimary))
R.drawable.ic_more_vert_24dp,
view.context
.getResourceColor(android.R.attr.textColorPrimary)
)
}
/**

@ -83,9 +83,11 @@ class ExtensionBottomPresenter(
val untrustedSorted = untrusted.sortedBy { it.pkgName }
val availableSorted = available
// Filter out already installed extensions and disabled languages
.filter { avail -> installed.none { it.pkgName == avail.pkgName } &&
.filter { avail ->
installed.none { it.pkgName == avail.pkgName } &&
untrusted.none { it.pkgName == avail.pkgName } &&
(avail.lang in activeLangs || avail.lang == "all") }
(avail.lang in activeLangs || avail.lang == "all")
}
.sortedBy { it.pkgName }
if (installedSorted.isNotEmpty() || untrustedSorted.isNotEmpty()) {

@ -21,8 +21,8 @@ import eu.kanade.tachiyomi.util.view.withFadeTransaction
import kotlinx.android.synthetic.main.extensions_bottom_sheet.view.*
class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
LinearLayout(context, attrs),
ExtensionAdapter.OnButtonClickListener,
LinearLayout(context, attrs),
ExtensionAdapter.OnButtonClickListener,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
ExtensionTrustDialog.Listener {
@ -88,11 +88,17 @@ ExtensionAdapter.OnButtonClickListener,
fun updateExtTitle() {
val extCount = presenter.getExtensionUpdateCount()
title_text.text = if (extCount == 0) context.getString(R.string.extensions)
else resources.getQuantityString(R.plurals.extension_updates_available, extCount,
extCount)
else resources.getQuantityString(
R.plurals.extension_updates_available,
extCount,
extCount
)
title_text.setTextColor(context.getResourceColor(
if (extCount == 0) R.attr.actionBarTintColor else R.attr.colorAccent))
title_text.setTextColor(
context.getResourceColor(
if (extCount == 0) R.attr.actionBarTintColor else R.attr.colorAccent
)
)
}
override fun onButtonClick(position: Int) {
@ -153,7 +159,8 @@ ExtensionAdapter.OnButtonClickListener,
adapter?.updateDataSet(
extensions.filter {
it.extension.name.contains(controller.extQuery, ignoreCase = true)
})
}
)
} else {
adapter?.updateDataSet(extensions)
}

@ -44,9 +44,11 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
private var preferenceScreen: PreferenceScreen? = null
constructor(pkgName: String) : this(Bundle().apply {
constructor(pkgName: String) : this(
Bundle().apply {
putString(PKGNAME_KEY, pkgName)
})
}
)
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
val themedInflater = inflater.cloneInContext(getPreferenceThemeContext())
@ -107,8 +109,10 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
extension_prefs_recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
if (screen.preferenceCount == 0) {
extension_prefs_empty_view.show(R.drawable.ic_no_settings_24dp,
R.string.empty_preferences_for_extension)
extension_prefs_empty_view.show(
R.drawable.ic_no_settings_24dp,
R.string.empty_preferences_for_extension
)
}
}
@ -139,7 +143,8 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
source.preferences
} else {*/
context.getSharedPreferences("source_${source.id}", Context.MODE_PRIVATE)
/*}*/)
/*}*/
)
if (source is ConfigurableSource) {
if (multiSource) {
@ -193,14 +198,19 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
}
val f = when (preference) {
is EditTextPreference -> EditTextPreferenceDialogController
is EditTextPreference ->
EditTextPreferenceDialogController
.newInstance(preference.getKey())
is ListPreference -> ListPreferenceDialogController
is ListPreference ->
ListPreferenceDialogController
.newInstance(preference.getKey())
is MultiSelectListPreference -> MultiSelectListPreferenceDialogController
is MultiSelectListPreference ->
MultiSelectListPreferenceDialogController
.newInstance(preference.getKey())
else -> throw IllegalArgumentException("Tried to display dialog for unknown " +
"preference type. Did you forget to override onDisplayPreferenceDialog()?")
else -> throw IllegalArgumentException(
"Tried to display dialog for unknown " +
"preference type. Did you forget to override onDisplayPreferenceDialog()?"
)
}
f.targetController = this
f.showDialog(router)

@ -24,7 +24,8 @@ class ExtensionDividerItemDecoration(context: Context) : androidx.recyclerview.w
val child = parent.getChildAt(i)
val holder = parent.getChildViewHolder(child)
if (holder is ExtensionHolder &&
parent.getChildViewHolder(parent.getChildAt(i + 1)) is ExtensionHolder) {
parent.getChildViewHolder(parent.getChildAt(i + 1)) is ExtensionHolder
) {
val params = child.layoutParams as androidx.recyclerview.widget.RecyclerView.LayoutParams
val top = child.bottom + params.bottomMargin
val bottom = top + divider.intrinsicHeight

@ -63,13 +63,15 @@ class ExtensionHolder(view: View, val adapter: ExtensionAdapter) :
val extension = item.extension
val installStep = item.installStep
if (installStep != null) {
setText(when (installStep) {
setText(
when (installStep) {
InstallStep.Pending -> R.string.pending
InstallStep.Downloading -> R.string.downloading
InstallStep.Installing -> R.string.installing
InstallStep.Installed -> R.string.installed
InstallStep.Error -> R.string.retry
})
}
)
if (installStep != InstallStep.Error) {
isEnabled = false
isClickable = false
@ -79,7 +81,8 @@ class ExtensionHolder(view: View, val adapter: ExtensionAdapter) :
extension.hasUpdate -> {
isActivated = true
backgroundTintList = ColorStateList.valueOf(
context.getResourceColor(R.attr.colorAccent))
context.getResourceColor(R.attr.colorAccent)
)
strokeColor = ColorStateList.valueOf(Color.TRANSPARENT)
setText(R.string.update)
}

@ -10,10 +10,12 @@ class ExtensionTrustDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
where T : ExtensionTrustDialog.Listener {
lateinit var listener: Listener
constructor(target: T, signatureHash: String, pkgName: String) : this(Bundle().apply {
constructor(target: T, signatureHash: String, pkgName: String) : this(
Bundle().apply {
putString(SIGNATURE_KEY, signatureHash)
putString(PKGNAME_KEY, pkgName)
}) {
}
) {
listener = target
}

@ -24,7 +24,7 @@ import kotlinx.android.synthetic.main.display_bottom_sheet.*
import uy.kohesive.injekt.injectLazy
class DisplayBottomSheet(private val controller: LibraryController) : BottomSheetDialog
(controller.activity!!, R.style.BottomSheetDialogTheme) {
(controller.activity!!, R.style.BottomSheetDialogTheme) {
val activity = controller.activity!!
@ -45,7 +45,8 @@ class DisplayBottomSheet(private val controller: LibraryController) : BottomShee
val height = activity.window.decorView.rootWindowInsets.systemWindowInsetBottom
sheetBehavior.peekHeight = 220.dpToPx + height
sheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
sheetBehavior.addBottomSheetCallback(
object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, progress: Float) { }
override fun onStateChanged(p0: View, state: Int) {
@ -53,7 +54,8 @@ class DisplayBottomSheet(private val controller: LibraryController) : BottomShee
sheetBehavior.skipCollapsed = true
}
}
})
}
)
}
override fun onStart() {
@ -132,7 +134,7 @@ class DisplayBottomSheet(private val controller: LibraryController) : BottomShee
*/
private fun CompoundButton.bindToPreference(
pref: com.tfcporciuncula.flow
.Preference<Boolean>,
.Preference<Boolean>,
block: ((Boolean) -> Unit)? = null
) {
isChecked = pref.get()

@ -59,7 +59,8 @@ class LibraryCategoryAdapter(val controller: LibraryController) :
fun indexOf(categoryOrder: Int): Int {
return currentItems.indexOfFirst {
if (it is LibraryHeaderItem) it.category.order == categoryOrder
else false }
else false
}
}
/**
@ -70,13 +71,15 @@ class LibraryCategoryAdapter(val controller: LibraryController) :
fun indexOf(manga: Manga): Int {
return currentItems.indexOfFirst {
if (it is LibraryItem) it.manga.id == manga.id
else false }
else false
}
}
fun getHeaderPositions(): List<Int> {
return currentItems.mapIndexedNotNull { index, it ->
if (it is LibraryHeaderItem) index
else null }
else null
}
}
/**
@ -87,7 +90,8 @@ class LibraryCategoryAdapter(val controller: LibraryController) :
fun allIndexOf(manga: Manga): List<Int> {
return currentItems.mapIndexedNotNull { index, it ->
if (it is LibraryItem && it.manga.id == manga.id) index
else null }
else null
}
}
fun performFilter() {
@ -140,7 +144,8 @@ class LibraryCategoryAdapter(val controller: LibraryController) :
val last = history.maxBy { it.last_read }
if (last != null && last.last_read > 100) {
recyclerView.context.getString(
R.string.read_, last.last_read.timeSpanFromNow
R.string.read_,
last.last_read.timeSpanFromNow
)
} else {
"N/A"
@ -154,7 +159,9 @@ class LibraryCategoryAdapter(val controller: LibraryController) :
LibrarySort.TOTAL -> {
val total = item.manga.totalChapters
if (total > 0) recyclerView.resources.getQuantityString(
R.plurals.chapters, total, total
R.plurals.chapters,
total,
total
)
else {
"N/A"
@ -164,7 +171,8 @@ class LibraryCategoryAdapter(val controller: LibraryController) :
val lastUpdate = item.manga.last_update
if (lastUpdate > 0) {
recyclerView.context.getString(
R.string.updated_, lastUpdate.timeSpanFromNow
R.string.updated_,
lastUpdate.timeSpanFromNow
)
} else {
"N/A"

@ -108,10 +108,13 @@ class LibraryController(
) : BaseController(bundle),
ActionMode.Callback,
ChangeMangaCategoriesDialog.Listener,
FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener,
FlexibleAdapter.OnItemMoveListener, LibraryCategoryAdapter.LibraryListener,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
FlexibleAdapter.OnItemMoveListener,
LibraryCategoryAdapter.LibraryListener,
BottomSheetController,
RootSearchInterface, LibraryServiceListener {
RootSearchInterface,
LibraryServiceListener {
init {
setHasOptionsMenu(true)
@ -325,7 +328,8 @@ class LibraryController(
adapter = LibraryCategoryAdapter(this)
setRecyclerLayout()
recycler.manager.spanSizeLookup = (object : GridLayoutManager.SpanSizeLookup() {
recycler.manager.spanSizeLookup = (
object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
if (libraryLayout == 0) return 1
val item = this@LibraryController.adapter.getItem(position)
@ -335,7 +339,8 @@ class LibraryController(
1
}
}
})
}
)
recycler.setHasFixedSize(true)
recycler.adapter = adapter
@ -360,7 +365,10 @@ class LibraryController(
setUpHopper()
elevateAppBar =
scrollViewWith(recycler, swipeRefreshLayout = swipe_refresh, afterInsets = { insets ->
scrollViewWith(
recycler,
swipeRefreshLayout = swipe_refresh,
afterInsets = { insets ->
category_recycler?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = recycler?.paddingTop ?: 0
}
@ -368,9 +376,11 @@ class LibraryController(
topMargin = recycler?.paddingTop ?: 0
}
header_title?.updatePaddingRelative(top = insets.systemWindowInsetTop + 2.dpToPx)
}, onLeavingController = {
},
onLeavingController = {
header_title?.gone()
})
}
)
swipe_refresh.setOnRefreshListener {
swipe_refresh.isRefreshing = false
@ -387,19 +397,24 @@ class LibraryController(
preferences.updateOnRefresh().getOrDefault() == -1 -> {
MaterialDialog(activity!!).title(R.string.what_should_update)
.negativeButton(android.R.string.cancel)
.listItemsSingleChoice(items = listOf(
.listItemsSingleChoice(
items = listOf(
view.context.getString(
R.string.top_category,
presenter.allCategories.first().name
),
view.context.getString(
R.string.top_category, presenter.allCategories.first().name
), view.context.getString(
R.string.categories_in_global_update
)
), selection = { _, index, _ ->
),
selection = { _, index, _ ->
preferences.updateOnRefresh().set(index)
when (index) {
0 -> updateLibrary(presenter.allCategories.first())
else -> updateLibrary()
}
}).positiveButton(R.string.update).show()
}
).positiveButton(R.string.update).show()
}
else -> {
when (preferences.updateOnRefresh().getOrDefault()) {
@ -632,7 +647,8 @@ class LibraryController(
if (libraryLayout == 0) {
recycler.spanCount = 1
recycler.updatePaddingRelative(
start = 0, end = 0
start = 0,
end = 0
)
} else {
recycler.columnWidth = when (preferences.gridSize().getOrDefault()) {
@ -733,7 +749,8 @@ class LibraryController(
category_hopper_frame.visibleIf(!singleCategory && !preferences.hideHopper().get())
filter_bottom_sheet.updateButtons(
showExpand = !singleCategory && presenter.showAllCategories, groupType = presenter.groupType
showExpand = !singleCategory && presenter.showAllCategories,
groupType = presenter.groupType
)
adapter.isLongPressDragEnabled = canDrag()
category_recycler.setCategories(presenter.categories)
@ -783,9 +800,11 @@ class LibraryController(
)
animatorSet.playSequentially(animations)
animatorSet.startDelay = 1250
animatorSet.addListener(EndAnimatorListener {
animatorSet.addListener(
EndAnimatorListener {
isAnimatingHopper = false
})
}
)
animatorSet.start()
}
@ -836,16 +855,21 @@ class LibraryController(
if (headerPosition > -1) {
val appbar = activity?.appbar
recycler.suppressLayout(true)
val appbarOffset = if (appbar?.y ?: 0f > -20) 0 else (appbar?.y?.plus(
val appbarOffset = if (appbar?.y ?: 0f > -20) 0 else (
appbar?.y?.plus(
view?.rootWindowInsets?.systemWindowInsetTop ?: 0
) ?: 0f).roundToInt() + 30.dpToPx
) ?: 0f
).roundToInt() + 30.dpToPx
val previousHeader = adapter.getItem(adapter.indexOf(pos - 1)) as? LibraryHeaderItem
(recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
headerPosition, (when {
headerPosition,
(
when {
headerPosition == 0 -> 0
previousHeader?.category?.isHidden == true -> (-3).dpToPx
else -> (-30).dpToPx
}) + appbarOffset
}
) + appbarOffset
)
(adapter.getItem(headerPosition) as? LibraryHeaderItem)?.category?.let {
saveActiveCategory(it)
@ -1033,9 +1057,9 @@ class LibraryController(
val position = viewHolder?.adapterPosition ?: return
swipe_refresh.isEnabled = actionState != ItemTouchHelper.ACTION_STATE_DRAG
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
if (lastItemPosition != null && position != lastItemPosition && lastItem == adapter.getItem(
position
)
if (lastItemPosition != null &&
position != lastItemPosition &&
lastItem == adapter.getItem(position)
) {
// because for whatever reason you can repeatedly tap on a currently dragging manga
adapter.removeSelection(position)
@ -1063,10 +1087,11 @@ class LibraryController(
override fun onItemMove(fromPosition: Int, toPosition: Int) {
// Because padding a recycler causes it to scroll up we have to scroll it back down... wild
if ((adapter.getItem(fromPosition) is LibraryItem && adapter.getItem(fromPosition) is
LibraryItem) || adapter.getItem(
fromPosition
) == null
if ((
adapter.getItem(fromPosition) is LibraryItem &&
adapter.getItem(fromPosition) is LibraryItem
) ||
adapter.getItem(fromPosition) == null
) {
recycler.scrollBy(0, recycler.paddingTop)
}
@ -1079,9 +1104,12 @@ class LibraryController(
val item = adapter.getItem(fromPosition) as? LibraryItem ?: return false
val newHeader = adapter.getSectionHeader(toPosition) as? LibraryHeaderItem
if (toPosition < 1) return false
return (adapter.getItem(toPosition) !is LibraryHeaderItem) && (newHeader?.category?.id == item.manga.category || !presenter.mangaIsInCategory(
item.manga, newHeader?.category?.id
))
return (adapter.getItem(toPosition) !is LibraryHeaderItem) && (
newHeader?.category?.id == item.manga.category || !presenter.mangaIsInCategory(
item.manga,
newHeader?.category?.id
)
)
}
override fun onItemReleased(position: Int) {
@ -1110,7 +1138,9 @@ class LibraryController(
return
}
if (newHeader?.category != null) moveMangaToCategory(
item.manga, newHeader.category, mangaIds
item.manga,
newHeader.category,
mangaIds
)
}
lastItemPosition = null
@ -1147,8 +1177,10 @@ class LibraryController(
inQueue -> R.string._already_in_queue
LibraryUpdateService.isRunning() -> R.string.adding_category_to_queue
else -> R.string.updating_
}, category.name
), Snackbar.LENGTH_LONG
},
category.name
),
Snackbar.LENGTH_LONG
) {
anchorView = anchorView()
view.elevation = 15f.dpToPx
@ -1163,7 +1195,9 @@ class LibraryController(
}
}
if (!inQueue) LibraryUpdateService.start(
view!!.context, category, mangaToUse = if (category.isDynamic) {
view!!.context,
category,
mangaToUse = if (category.isDynamic) {
presenter.getMangaInCategories(category.id)
} else null
)
@ -1327,9 +1361,11 @@ class LibraryController(
}
R.id.action_migrate -> {
val skipPre = preferences.skipPreMigration().getOrDefault()
PreMigrationController.navigateToMigration(skipPre,
PreMigrationController.navigateToMigration(
skipPre,
router,
selectedMangas.filter { it.id != LocalSource.ID }.mapNotNull { it.id })
selectedMangas.filter { it.id != LocalSource.ID }.mapNotNull { it.id }
)
destroyActionModeIfNeeded()
}
else -> return false
@ -1356,7 +1392,8 @@ class LibraryController(
destroyActionModeIfNeeded()
snack?.dismiss()
snack = view?.snack(
activity?.getString(R.string.removed_from_library) ?: "", Snackbar.LENGTH_INDEFINITE
activity?.getString(R.string.removed_from_library) ?: "",
Snackbar.LENGTH_INDEFINITE
) {
anchorView = anchorView()
view.elevation = 15f.dpToPx
@ -1365,12 +1402,14 @@ class LibraryController(
presenter.reAddMangas(mangas)
undoing = true
}
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
addCallback(
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (!undoing) presenter.confirmDeletion(mangas)
}
})
}
)
}
(activity as? MainActivity)?.setUndoSnackBar(snack)
}

@ -26,38 +26,45 @@ class LibraryGestureDetector(private val controller: LibraryController) : Gestur
var result = false
val diffY = e2.y - e1.y
val diffX = e2.x - e1.x
if (abs(diffX) <= abs(diffY) && abs(diffY) > MainActivity.SWIPE_THRESHOLD && abs(velocityY) > MainActivity.SWIPE_VELOCITY_THRESHOLD) {
if (abs(diffX) <= abs(diffY) &&
abs(diffY) > MainActivity.SWIPE_THRESHOLD &&
abs(velocityY) > MainActivity.SWIPE_VELOCITY_THRESHOLD
) {
if (diffY <= 0) {
controller.showSheet()
} else {
controller.filter_bottom_sheet.sheetBehavior?.hide()
}
result = true
} else if (abs(diffX) >= abs(diffY) && abs(diffX) > MainActivity.SWIPE_THRESHOLD * 3 && abs(
velocityX
) > MainActivity.SWIPE_VELOCITY_THRESHOLD
} else if (abs(diffX) >= abs(diffY) &&
abs(diffX) > MainActivity.SWIPE_THRESHOLD * 3 &&
abs(velocityX) > MainActivity.SWIPE_VELOCITY_THRESHOLD
) {
if (diffX <= 0) {
controller.category_hopper_frame.updateLayoutParams<CoordinatorLayout.LayoutParams> {
anchorGravity =
Gravity.TOP or (if (anchorGravity == Gravity.TOP or Gravity.RIGHT) {
Gravity.TOP or (
if (anchorGravity == Gravity.TOP or Gravity.RIGHT) {
controller.preferences.hopperGravity().set(1)
Gravity.CENTER
} else {
controller.preferences.hopperGravity().set(0)
Gravity.LEFT
})
}
)
}
} else {
controller.category_hopper_frame.updateLayoutParams<CoordinatorLayout.LayoutParams> {
anchorGravity =
Gravity.TOP or Gravity.TOP or (if (anchorGravity == Gravity.TOP or Gravity.LEFT) {
Gravity.TOP or Gravity.TOP or (
if (anchorGravity == Gravity.TOP or Gravity.LEFT) {
controller.preferences.hopperGravity().set(1)
Gravity.CENTER
} else {
controller.preferences.hopperGravity().set(2)
Gravity.RIGHT
})
}
)
}
}
if (!controller.hasMovedHopper) {

@ -79,11 +79,13 @@ class LibraryHeaderHolder(val view: View, private val adapter: LibraryCategoryAd
}
val shorterMargin = adapter.headerItems.firstOrNull() == item
sectionText.updateLayoutParams<ConstraintLayout.LayoutParams> {
topMargin = (when {
topMargin = (
when {
shorterMargin -> 2
previousIsCollapsed -> 5
else -> 32
}).dpToPx
}
).dpToPx
}
val category = item.category
@ -117,7 +119,8 @@ class LibraryHeaderHolder(val view: View, private val adapter: LibraryCategoryAd
sortText.setText(category.sortRes())
expandImage.setImageResource(
if (category.isHidden) R.drawable.ic_expand_more_24dp
else R.drawable.ic_expand_less_24dp)
else R.drawable.ic_expand_less_24dp
)
when {
adapter.mode == SelectableAdapter.Mode.MULTI -> {
checkboxImage.visibleIf(!category.isHidden)
@ -160,22 +163,31 @@ class LibraryHeaderHolder(val view: View, private val adapter: LibraryCategoryAd
adapter.controller.activity?.let { activity ->
val items = mutableListOf(
MaterialMenuSheet.MenuSheetItem(
LibrarySort.ALPHA, R.drawable.ic_sort_by_alpha_24dp, R.string.title
), MaterialMenuSheet.MenuSheetItem(
LibrarySort.ALPHA,
R.drawable.ic_sort_by_alpha_24dp,
R.string.title
),
MaterialMenuSheet.MenuSheetItem(
LibrarySort.LAST_READ,
R.drawable.ic_recent_read_outline_24dp,
R.string.last_read
), MaterialMenuSheet.MenuSheetItem(
),
MaterialMenuSheet.MenuSheetItem(
LibrarySort.LATEST_CHAPTER,
R.drawable.ic_new_releases_24dp,
R.string.latest_chapter
), MaterialMenuSheet.MenuSheetItem(
LibrarySort.UNREAD, R.drawable.ic_eye_24dp, R.string.unread
), MaterialMenuSheet.MenuSheetItem(
),
MaterialMenuSheet.MenuSheetItem(
LibrarySort.UNREAD,
R.drawable.ic_eye_24dp,
R.string.unread
),
MaterialMenuSheet.MenuSheetItem(
LibrarySort.TOTAL,
R.drawable.ic_sort_by_numeric_24dp,
R.string.total_chapters
), MaterialMenuSheet.MenuSheetItem(
),
MaterialMenuSheet.MenuSheetItem(
LibrarySort.DATE_ADDED,
R.drawable.ic_heart_outline_24dp,
R.string.date_added
@ -192,7 +204,10 @@ class LibraryHeaderHolder(val view: View, private val adapter: LibraryCategoryAd
}
val sortingMode = category.sortingMode()
val sheet = MaterialMenuSheet(
activity, items, activity.getString(R.string.sort_by), sortingMode
activity,
items,
activity.getString(R.string.sort_by),
sortingMode
) { sheet, item ->
onCatSortClicked(category, item)
val nCategory = (adapter.getItem(adapterPosition) as? LibraryHeaderItem)?.category
@ -212,7 +227,8 @@ class LibraryHeaderHolder(val view: View, private val adapter: LibraryCategoryAd
sortingMode == LibrarySort.DRAG_AND_DROP -> R.drawable.ic_check_24dp
if (sortingMode == LibrarySort.DATE_ADDED ||
sortingMode == LibrarySort.LATEST_CHAPTER ||
sortingMode == LibrarySort.LAST_READ) !isAscending else isAscending ->
sortingMode == LibrarySort.LAST_READ
) !isAscending else isAscending ->
R.drawable.ic_arrow_downward_24dp
else -> R.drawable.ic_arrow_upward_24dp
}
@ -257,7 +273,8 @@ class LibraryHeaderHolder(val view: View, private val adapter: LibraryCategoryAd
val tintedDrawable = drawable?.mutate()
tintedDrawable?.setTint(
ContextCompat.getColor(
contentView.context, if (allSelected) R.color.colorAccent
contentView.context,
if (allSelected) R.color.colorAccent
else R.color.gray_button
)
)

@ -39,7 +39,8 @@ abstract class LibraryHolder(
item.manga.source == LocalSource.ID -> -2
else -> item.downloadCount
},
showTotal)
showTotal
)
}
fun setReadingButton(item: LibraryItem) {

@ -66,18 +66,21 @@ class LibraryItem(
gradient.layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
(coverHeight * 0.66f).toInt(),
Gravity.BOTTOM)
Gravity.BOTTOM
)
card.updateLayoutParams<ConstraintLayout.LayoutParams> {
bottomMargin = 6.dpToPx
}
} else if (libraryLayout == 2) {
constraint_layout.background = ContextCompat.getDrawable(
context, R.drawable.library_item_selector
context,
R.drawable.library_item_selector
)
}
if (isFixedSize) {
constraint_layout.layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
cover_thumbnail.maxHeight = Int.MAX_VALUE
cover_thumbnail.minimumHeight = 0
@ -162,7 +165,8 @@ class LibraryItem(
} == null
else
genres?.find {
it.trim().toLowerCase() == tag.toLowerCase() } != null
it.trim().toLowerCase() == tag.toLowerCase()
} != null
}
override fun equals(other: Any?): Boolean {

@ -129,7 +129,8 @@ class LibraryPresenter(
private fun blankItem(id: Int = currentCategory): List<LibraryItem> {
return listOf(
LibraryItem(
LibraryManga.createBlank(id), LibraryHeaderItem({ getCategory(id) }, id)
LibraryManga.createBlank(id),
LibraryHeaderItem({ getCategory(id) }, id)
)
)
}
@ -146,7 +147,8 @@ class LibraryPresenter(
view.onNextLibraryUpdate(
if (!show) sectionedLibraryItems[currentCategory]
?: sectionedLibraryItems[categories.first().id] ?: blankItem()
else libraryItems, true
else libraryItems,
true
)
}
@ -169,7 +171,8 @@ class LibraryPresenter(
view.onNextLibraryUpdate(
if (!showAll) sectionedLibraryItems[currentCategory]
?: sectionedLibraryItems[categories.first().id] ?: blankItem()
else libraryItems, freshStart
else libraryItems,
freshStart
)
}
}
@ -454,15 +457,19 @@ class LibraryPresenter(
)
val catItemAll = LibraryHeaderItem({ categoryAll }, -1)
val categorySet = mutableSetOf<Int>()
val headerItems = (categories.mapNotNull { category ->
val headerItems = (
categories.mapNotNull { category ->
val id = category.id
if (id == null) null
else id to LibraryHeaderItem({ getCategory(id) }, id)
} + (-1 to catItemAll) + (0 to LibraryHeaderItem({ getCategory(0) }, 0))).toMap()
} + (-1 to catItemAll) + (0 to LibraryHeaderItem({ getCategory(0) }, 0))
).toMap()
val items = libraryManga.mapNotNull {
val headerItem = (if (!libraryIsGrouped) catItemAll
else headerItems[it.category]) ?: return@mapNotNull null
val headerItem = (
if (!libraryIsGrouped) catItemAll
else headerItems[it.category]
) ?: return@mapNotNull null
categorySet.add(it.category)
LibraryItem(it, headerItem)
}.toMutableList()
@ -475,8 +482,11 @@ class LibraryPresenter(
if (libraryIsGrouped) {
categories.forEach { category ->
val catId = category.id ?: return@forEach
if (catId > 0 && !categorySet.contains(catId) && (catId !in categoriesHidden ||
!showAll)) {
if (catId > 0 && !categorySet.contains(catId) && (
catId !in categoriesHidden ||
!showAll
)
) {
val headerItem = headerItems[catId]
if (headerItem != null) items.add(
LibraryItem(LibraryManga.createBlank(catId), headerItem)
@ -592,18 +602,21 @@ class LibraryPresenter(
mapTrackingOrder(it.name)
} else {
it.name
} }
}
}
headers.forEachIndexed { index, category -> category.order = index }
return items to headers
}
private fun mapStatus(status: Int): String {
return context.getString(when (status) {
return context.getString(
when (status) {
SManga.LICENSED -> R.string.licensed
SManga.ONGOING -> R.string.ongoing
SManga.COMPLETED -> R.string.completed
else -> R.string.unknown
})
}
)
}
private fun mapTrackingOrder(status: String): String {

@ -33,7 +33,8 @@ class CategoryRecyclerView @JvmOverloads constructor(
fun setCategories(items: List<Category>) {
itemAdapter.set(items.map(::CategoryItem))
fastAdapter.onBindViewHolderListener =
(object : OnBindViewHolderListenerImpl<CategoryItem>() {
(
object : OnBindViewHolderListenerImpl<CategoryItem>() {
override fun onBindViewHolder(
viewHolder: ViewHolder,
position: Int,
@ -43,7 +44,8 @@ class CategoryRecyclerView @JvmOverloads constructor(
(viewHolder as? CategoryItem.ViewHolder)?.categoryTitle?.isSelected =
selectedCategory == position
}
})
}
)
fastAdapter.onClickListener = { _, _, item, _ ->
if (item.category.id != -1)
onCategoryClicked(item.category.order)

@ -104,7 +104,8 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
pager = controller.recycler
val shadow2: View = controller.shadow2
val shadow: View = controller.shadow
sheetBehavior?.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
sheetBehavior?.addBottomSheetCallback(
object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, progress: Float) {
pill.alpha = (1 - max(0f, progress)) * 0.25f
shadow2.alpha = (1 - max(0f, progress)) * 0.25f
@ -115,7 +116,8 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
override fun onStateChanged(p0: View, state: Int) {
stateChanged(state)
}
})
}
)
if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
second_layout.removeView(view_options)
@ -284,13 +286,15 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
unreadProgress.state = unreadP - 3
}
tracked?.setState(preferences.filterTracked())
mangaType?.setState(when (preferences.filterMangaType().getOrDefault()) {
mangaType?.setState(
when (preferences.filterMangaType().getOrDefault()) {
Manga.TYPE_MANGA -> context.getString(R.string.manga)
Manga.TYPE_MANHUA -> context.getString(R.string.manhua)
Manga.TYPE_MANHWA -> context.getString(R.string.manhwa)
Manga.TYPE_COMIC -> context.getString(R.string.comic)
else -> ""
})
}
)
reorderFilters()
reSortViews()
}
@ -361,8 +365,11 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
val recycler = RecyclerView(context)
if (filterOrder.count() != 6)
filterOrder = "urdcmt"
val adapter = FlexibleAdapter(filterOrder.toCharArray().map(::ManageFilterItem),
this, true)
val adapter = FlexibleAdapter(
filterOrder.toCharArray().map(::ManageFilterItem),
this,
true
)
recycler.layoutManager = LinearLayoutManager(context)
recycler.adapter = adapter
adapter.isHandleDragEnabled = true

@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.util.view.visibleIf
import kotlinx.android.synthetic.main.filter_buttons.view.*
class FilterTagGroup@JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout
(context, attrs) {
(context, attrs) {
private var listener: FilterTagGroupListener? = null
@ -83,19 +83,23 @@ class FilterTagGroup@JvmOverloads constructor(context: Context, attrs: Attribute
private fun toggleButton(index: Int, callBack: Boolean = true) {
if (index < 0 || itemCount == 0 ||
(isActivated && index != buttons.indexOfFirst { it.isActivated }))
(isActivated && index != buttons.indexOfFirst { it.isActivated })
)
return
if (callBack) {
val transition = androidx.transition.AutoTransition()
transition.duration = 150
androidx.transition.TransitionManager.beginDelayedTransition(
parent.parent as ViewGroup, transition
parent.parent as ViewGroup,
transition
)
}
if (itemCount == 1) {
firstButton.isActivated = !firstButton.isActivated
firstButton.setTextColor(if (firstButton.isActivated) Color.WHITE else context
.getResourceColor(android.R.attr.textColorPrimary))
firstButton.setTextColor(
if (firstButton.isActivated) Color.WHITE else context
.getResourceColor(android.R.attr.textColorPrimary)
)
listener?.onFilterClicked(this, if (firstButton.isActivated) index else -1, callBack)
return
}
@ -118,8 +122,10 @@ class FilterTagGroup@JvmOverloads constructor(context: Context, attrs: Attribute
buttons.forEach { if (it != mainButton) it.gone() }
separators.forEach { it.gone() }
}
mainButton.setTextColor(if (mainButton.isActivated) Color.WHITE else context
.getResourceColor(android.R.attr.textColorPrimary))
mainButton.setTextColor(
if (mainButton.isActivated) Color.WHITE else context
.getResourceColor(android.R.attr.textColorPrimary)
)
}
}

@ -144,10 +144,12 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
drawerArrow = DrawerArrowDrawable(this)
drawerArrow?.color = getResourceColor(R.attr.actionBarTintColor)
searchDrawable = ContextCompat.getDrawable(
this, R.drawable.ic_search_24dp
this,
R.drawable.ic_search_24dp
)
dismissDrawable = ContextCompat.getDrawable(
this, R.drawable.ic_close_24dp
this,
R.drawable.ic_close_24dp
)
var continueSwitchingTabs = false
@ -182,18 +184,22 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
if (!currentController.canChangeTabs {
continueSwitchingTabs = true
this@MainActivity.bottom_nav.selectedItemId = id
}) return@setOnNavigationItemSelectedListener false
}
) return@setOnNavigationItemSelectedListener false
}
continueSwitchingTabs = false
if (item.itemId != R.id.nav_browse)
preferences.lastTab().set(item.itemId)
val currentRoot = router.backstack.firstOrNull()
if (currentRoot?.tag()?.toIntOrNull() != id) {
setRoot(when (id) {
setRoot(
when (id) {
R.id.nav_library -> LibraryController()
R.id.nav_recents -> RecentsController()
else -> SourceController()
}, id)
},
id
)
} else if (currentRoot.tag()?.toIntOrNull() == id) {
if (router.backstackSize == 1) {
val controller =
@ -225,7 +231,8 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
// Consume any horizontal insets and pad all content in. There's not much we can do
// with horizontal insets
v.updatePadding(
left = insets.systemWindowInsetLeft, right = insets.systemWindowInsetRight
left = insets.systemWindowInsetLeft,
right = insets.systemWindowInsetRight
)
appbar.updatePadding(
top = insets.systemWindowInsetTop
@ -251,7 +258,8 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
bottom_nav.visibleIf(!hideBottomNav)
bottom_nav.alpha = if (hideBottomNav) 0f else 1f
router.addChangeListener(object : ControllerChangeHandler.ControllerChangeListener {
router.addChangeListener(
object : ControllerChangeHandler.ControllerChangeListener {
override fun onChangeStarted(
to: Controller?,
from: Controller?,
@ -275,7 +283,8 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
appbar.y = 0f
showDLQueueTutorial()
}
})
}
)
syncActivityViewWithController(router.backstack.lastOrNull()?.controller())
@ -322,7 +331,8 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
// if in portrait with 2/3 button mode, translucent nav bar
else {
ColorUtils.setAlphaComponent(
getResourceColor(R.attr.colorPrimaryVariant), 179
getResourceColor(R.attr.colorPrimaryVariant),
179
)
}
}
@ -335,7 +345,9 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
override fun onSupportActionModeFinished(mode: androidx.appcompat.view.ActionMode) {
launchUI {
val scale = Settings.Global.getFloat(
contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f
contentResolver,
Settings.Global.ANIMATOR_DURATION_SCALE,
1.0f
)
val duration = resources.getInteger(android.R.integer.config_mediumAnimTime) * scale
delay(duration.toLong())
@ -372,7 +384,8 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
) {
val recentsItem = bottom_nav.getItemView(R.id.nav_recents) ?: return
preferences.shownDownloadQueueTutorial().set(true)
TapTargetView.showFor(this,
TapTargetView.showFor(
this,
TapTarget.forView(
recentsItem,
getString(R.string.manage_whats_downloading),
@ -387,7 +400,8 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
super.onTargetClick(view)
bottom_nav.selectedItemId = R.id.nav_recents
}
})
}
)
}
}
@ -398,7 +412,8 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
private fun getAppUpdates() {
if (isUpdaterEnabled &&
Date().time >= preferences.lastAppCheck().get() + TimeUnit.DAYS.toMillis(1)) {
Date().time >= preferences.lastAppCheck().get() + TimeUnit.DAYS.toMillis(1)
) {
lifecycleScope.launch(Dispatchers.IO) {
try {
val result = updateChecker.checkForUpdate()
@ -441,7 +456,9 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
protected open fun handleIntentAction(intent: Intent): Boolean {
val notificationId = intent.getIntExtra("notificationId", -1)
if (notificationId > -1) NotificationReceiver.dismissNotification(
applicationContext, notificationId, intent.getIntExtra("groupId", 0)
applicationContext,
notificationId,
intent.getIntExtra("groupId", 0)
)
when (intent.action) {
SHORTCUT_LIBRARY -> bottom_nav.selectedItemId = R.id.nav_library
@ -528,10 +545,9 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
extraViewForUndo?.getGlobalVisibleRect(extRect)
// This way the snackbar will only be dismissed if
// the user clicks outside it.
if (canDismissSnackBar && !sRect.contains(
ev.x.toInt(),
ev.y.toInt()
) && (extRect == null || !extRect.contains(ev.x.toInt(), ev.y.toInt()))
if (canDismissSnackBar &&
!sRect.contains(ev.x.toInt(), ev.y.toInt()) &&
(extRect == null || !extRect.contains(ev.x.toInt(), ev.y.toInt()))
) {
snackBar?.dismiss()
snackBar = null
@ -567,14 +583,17 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
animationSet?.cancel()
animationSet = AnimatorSet()
val alphaAnimation = ValueAnimator.ofFloat(
bottom_nav.alpha, if (hideBottomNav) 0f else 1f
bottom_nav.alpha,
if (hideBottomNav) 0f else 1f
)
alphaAnimation.addUpdateListener { valueAnimator ->
bottom_nav.alpha = valueAnimator.animatedValue as Float
}
alphaAnimation.addListener(EndAnimatorListener {
alphaAnimation.addListener(
EndAnimatorListener {
bottom_nav.visibility = if (hideBottomNav) View.GONE else View.VISIBLE
})
}
)
alphaAnimation.duration = 200
alphaAnimation.startDelay = 50
animationSet?.playTogether(alphaAnimation)
@ -610,9 +629,10 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
if (abs(diffX) <= abs(diffY)) {
val sheetRect = Rect()
bottom_nav.getGlobalVisibleRect(sheetRect)
if (sheetRect.contains(
e1.x.toInt(), e1.y.toInt()
) && abs(diffY) > Companion.SWIPE_THRESHOLD && abs(velocityY) > Companion.SWIPE_VELOCITY_THRESHOLD && diffY <= 0
if (sheetRect.contains(e1.x.toInt(), e1.y.toInt()) &&
abs(diffY) > Companion.SWIPE_THRESHOLD &&
abs(velocityY) > Companion.SWIPE_VELOCITY_THRESHOLD &&
diffY <= 0
) {
val bottomSheetController =
router.backstack.lastOrNull()?.controller() as? BottomSheetController

@ -64,7 +64,9 @@ class SearchActivity : MainActivity() {
override fun handleIntentAction(intent: Intent): Boolean {
val notificationId = intent.getIntExtra("notificationId", -1)
if (notificationId > -1) NotificationReceiver.dismissNotification(
applicationContext, notificationId, intent.getIntExtra("groupId", 0)
applicationContext,
notificationId,
intent.getIntExtra("groupId", 0)
)
when (intent.action) {
Intent.ACTION_SEARCH, "com.google.android.gms.actions.SEARCH_ACTION" -> {
@ -92,7 +94,8 @@ class SearchActivity : MainActivity() {
router.replaceTopController(
RouterTransaction.with(MangaDetailsController(extras))
.pushChangeHandler(SimpleSwapChangeHandler())
.popChangeHandler(FadeChangeHandler()))
.popChangeHandler(FadeChangeHandler())
)
}
else -> return false
}
@ -100,8 +103,11 @@ class SearchActivity : MainActivity() {
}
companion object {
fun openMangaIntent(context: Context, id: Long) = Intent(context, SearchActivity::class
.java)
fun openMangaIntent(context: Context, id: Long) = Intent(
context,
SearchActivity::class
.java
)
.apply {
action = SHORTCUT_MANGA
putExtra(MangaDetailsController.MANGA_EXTRA, id)

@ -33,10 +33,12 @@ class EditMangaDialog : DialogController {
private val infoController
get() = targetController as MangaDetailsController
constructor(target: MangaDetailsController, manga: Manga) : super(Bundle()
constructor(target: MangaDetailsController, manga: Manga) : super(
Bundle()
.apply {
putLong(KEY_MANGA, manga.id!!)
}) {
}
) {
targetController = target
this.manga = manga
}
@ -99,7 +101,8 @@ class EditMangaDialog : DialogController {
if (manga.originalDescription != null) {
view.manga_description.hint =
"${resources?.getString(R.string.description)}: ${manga.originalDescription?.replace(
"\n", " "
"\n",
" "
)?.chop(20)}"
}
}
@ -110,9 +113,12 @@ class EditMangaDialog : DialogController {
view.reset_tags.setOnClickListener { resetTags() }
view.reset_cover.visibleIf(!isLocal)
view.reset_cover.setOnClickListener {
view.manga_cover.loadAny(manga, builder = {
view.manga_cover.loadAny(
manga,
builder = {
parameters(Parameters.Builder().set(MangaFetcher.realCover, true).build())
})
}
)
willResetCover = true
}
}
@ -136,10 +142,15 @@ class EditMangaDialog : DialogController {
}
private fun onPositiveButtonClick() {
infoController.presenter.updateManga(dialogView?.title?.text.toString(),
dialogView?.manga_author?.text.toString(), dialogView?.manga_artist?.text.toString(),
customCoverUri, dialogView?.manga_description?.text.toString(),
dialogView?.manga_genres_tags?.tags, willResetCover)
infoController.presenter.updateManga(
dialogView?.title?.text.toString(),
dialogView?.manga_author?.text.toString(),
dialogView?.manga_artist?.text.toString(),
customCoverUri,
dialogView?.manga_description?.text.toString(),
dialogView?.manga_genres_tags?.tags,
willResetCover
)
}
private companion object {

@ -26,8 +26,11 @@ class MangaDetailsAdapter(
val delegate: MangaDetailsInterface = controller
val presenter = controller.presenter
val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols()
.apply { decimalSeparator = '.' })
val decimalFormat = DecimalFormat(
"#.###",
DecimalFormatSymbols()
.apply { decimalSeparator = '.' }
)
fun setChapters(items: List<ChapterItem>?) {
this.items = items ?: emptyList()
@ -47,10 +50,12 @@ class MangaDetailsAdapter(
if (s.isNullOrBlank()) {
updateDataSet(items)
} else {
updateDataSet(items.filter {
updateDataSet(
items.filter {
it.name.contains(s, true) ||
it.scanlator?.contains(s, true) == true
})
}
)
}
}
@ -71,14 +76,16 @@ class MangaDetailsAdapter(
if (volume != null) {
recyclerView.context.getString(
if (scrollType == MangaDetailsPresenter.MULTIPLE_SEASONS) R.string.season_
else R.string.volume_, volume
else R.string.volume_,
volume
)
} else {
getChapterName(chapter)
}
}
MangaDetailsPresenter.TENS_OF_CHAPTERS -> recyclerView.context.getString(
R.string.chapters_, get10sRange(
R.string.chapters_,
get10sRange(
chapter.chapter_number
)
)
@ -89,7 +96,8 @@ class MangaDetailsAdapter(
private fun getChapterName(item: ChapterItem): String {
return if (item.chapter_number > 0) {
recyclerView.context.getString(
R.string.chapter_, decimalFormat.format(item.chapter_number)
R.string.chapter_,
decimalFormat.format(item.chapter_number)
)
} else {
item.name

@ -110,7 +110,8 @@ import java.io.IOException
import java.util.Locale
import kotlin.math.max
class MangaDetailsController : BaseController,
class MangaDetailsController :
BaseController,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
ActionMode.Callback,
@ -124,12 +125,14 @@ class MangaDetailsController : BaseController,
fromCatalogue: Boolean = false,
smartSearchConfig: SourceController.SmartSearchConfig? = null,
update: Boolean = false
) : super(Bundle().apply {
) : super(
Bundle().apply {
putLong(MANGA_EXTRA, manga?.id ?: 0)
putBoolean(FROM_CATALOGUE_EXTRA, fromCatalogue)
putParcelable(SMART_SEARCH_CONFIG_EXTRA, smartSearchConfig)
putBoolean(UPDATE_EXTRA, update)
}) {
}
) {
this.manga = manga
if (manga != null) {
source = Injekt.get<SourceManager>().getOrStub(manga.source)
@ -145,7 +148,9 @@ class MangaDetailsController : BaseController,
val notificationId = bundle.getInt("notificationId", -1)
val context = applicationContext ?: return
if (notificationId > -1) NotificationReceiver.dismissNotification(
context, notificationId, bundle.getInt("groupId", 0)
context,
notificationId,
bundle.getInt("groupId", 0)
)
}
@ -227,13 +232,20 @@ class MangaDetailsController : BaseController,
swipe_refresh.setDistanceToTriggerSync(70.dpToPx)
activity!!.appbar.elevation = 0f
scrollViewWith(recycler, padBottom = true, customPadding = true, afterInsets = { insets ->
scrollViewWith(
recycler,
padBottom = true,
customPadding = true,
afterInsets = { insets ->
setInsets(insets, appbarHeight, offset)
}, liftOnScroll = {
},
liftOnScroll = {
colorToolbar(it)
})
}
)
recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
recycler.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val atTop = !recyclerView.canScrollVertically(-1)
@ -246,7 +258,8 @@ class MangaDetailsController : BaseController,
val atTop = !recyclerView.canScrollVertically(-1)
if (atTop) getHeader()?.backdrop?.translationY = 0f
}
})
}
)
}
private fun setInsets(insets: WindowInsets, appbarHeight: Int, offset: Int) {
@ -280,15 +293,19 @@ class MangaDetailsController : BaseController,
if (colorAnimator?.isRunning == true) activity?.window?.statusBarColor
?: color
else ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 0 else 175
color,
if (toolbarIsColored) 0 else 175
)
val colorTo = ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 175 else 0
color,
if (toolbarIsColored) 175 else 0
)
colorAnimator?.cancel()
if (animate) {
colorAnimator = ValueAnimator.ofObject(
android.animation.ArgbEvaluator(), colorFrom, colorTo
android.animation.ArgbEvaluator(),
colorFrom,
colorTo
)
colorAnimator?.duration = 250 // milliseconds
colorAnimator?.addUpdateListener { animator ->
@ -307,7 +324,8 @@ class MangaDetailsController : BaseController,
val view = view ?: return
val request = LoadRequest.Builder(view.context).data(presenter.manga).allowHardware(false)
.target(onSuccess = { drawable ->
.target(
onSuccess = { drawable ->
val bitmap = (drawable as BitmapDrawable).bitmap
// Generate the Palette on a background thread.
Palette.from(bitmap).generate {
@ -329,13 +347,15 @@ class MangaDetailsController : BaseController,
}
manga_cover_full.setImageDrawable(drawable)
getHeader()?.updateCover(manga!!)
}, onError = {
},
onError = {
val file = presenter.coverCache.getCoverFile(manga!!)
if (file.exists()) {
file.delete()
setPaletteColor()
}
}).build()
}
).build()
Coil.imageLoader(view.context).execute(request)
}
@ -343,10 +363,7 @@ class MangaDetailsController : BaseController,
private fun setActionBar(forThis: Boolean) {
val activity = activity ?: return
// if the theme is using inverted toolbar color
if (!activity.isInNightMode() && ThemeUtil.isBlueTheme(
presenter.preferences.theme()
)
) {
if (!activity.isInNightMode() && ThemeUtil.isBlueTheme(presenter.preferences.theme())) {
if (forThis) (activity as MainActivity).appbar.context.setTheme(
R.style.ThemeOverlay_AppCompat_DayNight_ActionBar
)
@ -481,10 +498,12 @@ class MangaDetailsController : BaseController,
1 -> return
else -> {
MaterialDialog(context).title(R.string.chapters_removed).message(
text = context.resources.getQuantityString(R.plurals.deleted_chapters,
text = context.resources.getQuantityString(
R.plurals.deleted_chapters,
deletedChapters.size,
deletedChapters.size,
deletedChapters.joinToString("\n") { "${it.name}" })
deletedChapters.joinToString("\n") { "${it.name}" }
)
).positiveButton(R.string.delete) {
presenter.deleteChapters(deletedChapters, false)
if (it.isCheckPromptChecked()) deleteRemovedPref.set(2)
@ -502,7 +521,8 @@ class MangaDetailsController : BaseController,
//region Recycler methods
fun updateChapterDownload(download: Download) {
getHolder(download.chapter)?.notifyStatus(
download.status, presenter.isLockedFromSearch,
download.status,
presenter.isLockedFromSearch,
download.progress
)
}
@ -638,7 +658,8 @@ class MangaDetailsController : BaseController,
snack?.dismiss()
snack = view?.snack(
if (bookmarked) R.string.removed_bookmark
else R.string.bookmarked, Snackbar.LENGTH_INDEFINITE
else R.string.bookmarked,
Snackbar.LENGTH_INDEFINITE
) {
setAction(R.string.undo) {
bookmarkChapters(listOf(item), bookmarked)
@ -657,21 +678,24 @@ class MangaDetailsController : BaseController,
snack?.dismiss()
snack = view?.snack(
if (read) R.string.marked_as_unread
else R.string.marked_as_read, Snackbar.LENGTH_INDEFINITE
else R.string.marked_as_read,
Snackbar.LENGTH_INDEFINITE
) {
var undoing = false
setAction(R.string.undo) {
presenter.markChaptersRead(listOf(item), read, true, lastRead, pagesLeft)
undoing = true
}
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
addCallback(
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (!undoing && !read && presenter.preferences.removeAfterMarkedAsRead()) {
presenter.deleteChapters(listOf(item))
}
}
})
}
)
}
(activity as? MainActivity)?.setUndoSnackBar(snack)
}
@ -748,7 +772,8 @@ class MangaDetailsController : BaseController,
when (item.itemId) {
R.id.action_edit -> {
editMangaDialog = EditMangaDialog(
this, presenter.manga
this,
presenter.manga
)
editMangaDialog?.showDialog(router)
}
@ -781,11 +806,14 @@ class MangaDetailsController : BaseController,
override fun prepareToShareManga() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val request = LoadRequest.Builder(activity!!).data(manga).target(onError = {
val request = LoadRequest.Builder(activity!!).data(manga).target(
onError = {
shareManga()
}, onSuccess = {
},
onSuccess = {
presenter.shareManga((it as BitmapDrawable).bitmap)
}).build()
}
).build()
Coil.imageLoader(activity!!).execute(request)
} else {
shareManga()
@ -825,7 +853,10 @@ class MangaDetailsController : BaseController,
val activity = activity ?: return
val intent = WebViewActivity.newIntent(
activity.applicationContext, source.id, url, presenter.manga
activity.applicationContext,
source.id,
url,
presenter.manga
.title
)
startActivity(intent)
@ -847,7 +878,9 @@ class MangaDetailsController : BaseController,
val context = view?.context ?: return
MaterialDialog(context).message(
text = context.resources.getQuantityString(
R.plurals.remove_n_chapters, chapters.size, chapters.size
R.plurals.remove_n_chapters,
chapters.size,
chapters.size
)
).positiveButton(R.string.remove) {
presenter.deleteChapters(chapters)
@ -901,22 +934,27 @@ class MangaDetailsController : BaseController,
val view = view ?: return
presenter.downloadChapters(chapters)
val text = view.context.getString(
R.string.add_x_to_library, presenter.manga.mangaType
R.string.add_x_to_library,
presenter.manga.mangaType
(view.context).toLowerCase(Locale.ROOT)
)
if (!presenter.manga.favorite && (snack == null ||
snack?.getText() != text)
if (!presenter.manga.favorite && (
snack == null ||
snack?.getText() != text
)
) {
snack = view.snack(text, Snackbar.LENGTH_INDEFINITE) {
setAction(R.string.add) {
presenter.setFavorite(true)
}
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
addCallback(
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (snack == transientBottomBar) snack = null
}
})
}
)
}
(activity as? MainActivity)?.setUndoSnackBar(snack)
}
@ -935,17 +973,18 @@ class MangaDetailsController : BaseController,
val item = presenter.getNextUnreadChapter()
if (item != null) {
openChapter(item.chapter)
} else if (snack == null || snack?.getText() != view?.context?.getString(
R.string.next_chapter_not_found
)
} else if (snack == null ||
snack?.getText() != view?.context?.getString(R.string.next_chapter_not_found)
) {
snack = view?.snack(R.string.next_chapter_not_found, Snackbar.LENGTH_LONG) {
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
addCallback(
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (snack == transientBottomBar) snack = null
}
})
}
)
}
}
}
@ -1023,7 +1062,10 @@ class MangaDetailsController : BaseController,
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
}.toTypedArray()
ChangeMangaCategoriesDialog(
this, listOf(manga), categories, preselected
this,
listOf(manga),
categories,
preselected
).showDialog(
router
)
@ -1080,12 +1122,14 @@ class MangaDetailsController : BaseController,
setAction(R.string.undo) {
presenter.setFavorite(true)
}
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
addCallback(
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (!presenter.manga.favorite) presenter.confirmDeletion()
}
})
}
)
}
val favButton = getHeader()?.favorite_button
(activity as? MainActivity)?.setUndoSnackBar(snack, favButton)
@ -1204,7 +1248,9 @@ class MangaDetailsController : BaseController,
if (startingDLChapterPos != null) {
val item = adapter?.getItem(startingDLChapterPos!!) as? ChapterItem
(recycler.findViewHolderForAdapterPosition(startingDLChapterPos!!) as? ChapterHolder)?.notifyStatus(
item?.status ?: Download.NOT_DOWNLOADED, false, 0
item?.status ?: Download.NOT_DOWNLOADED,
false,
0
)
}
startingDLChapterPos = null
@ -1308,7 +1354,8 @@ class MangaDetailsController : BaseController,
)
duration = shortAnimationDuration.toLong()
interpolator = DecelerateInterpolator()
addListener(object : AnimatorListenerAdapter() {
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
TransitionManager.endTransitions(frame_layout)
@ -1319,7 +1366,8 @@ class MangaDetailsController : BaseController,
TransitionManager.endTransitions(frame_layout)
currentAnimator = null
}
})
}
)
start()
}
@ -1351,7 +1399,8 @@ class MangaDetailsController : BaseController,
play(ObjectAnimator.ofFloat(fullBackdrop, View.ALPHA, 0f))
duration = shortAnimationDuration.toLong()
interpolator = DecelerateInterpolator()
addListener(object : AnimatorListenerAdapter() {
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
thumbView.alpha = 1f
@ -1368,7 +1417,8 @@ class MangaDetailsController : BaseController,
swipe_refresh.isEnabled = true
currentAnimator = null
}
})
}
)
start()
}
}

@ -353,8 +353,10 @@ class MangaDetailsPresenter(
val categoriesToDownload = preferences.downloadNewCategories().getOrDefault().map(String::toInt)
val shouldDownload = categoriesToDownload.isEmpty() || getMangaCategoryIds().any { it in categoriesToDownload }
if (shouldDownload) {
downloadChapters(newChapters.first.sortedBy { it.chapter_number }
.map { it.toModel() })
downloadChapters(
newChapters.first.sortedBy { it.chapter_number }
.map { it.toModel() }
)
}
}
}
@ -419,9 +421,11 @@ class MangaDetailsPresenter(
}
private fun trimException(e: java.lang.Exception): String {
return (if (e.message?.contains(": ") == true) e.message?.split(": ")?.drop(1)
return (
if (e.message?.contains(": ") == true) e.message?.split(": ")?.drop(1)
?.joinToString(": ")
else e.message) ?: preferences.context.getString(R.string.unknown_error)
else e.message
) ?: preferences.context.getString(R.string.unknown_error)
}
/**

@ -160,11 +160,13 @@ class MangaHeaderHolder(
else expand()
}
manga_summary_label.text = itemView.context.getString(
R.string.about_this_, manga.mangaType(itemView.context)
R.string.about_this_,
manga.mangaType(itemView.context)
)
with(favorite_button) {
icon = ContextCompat.getDrawable(
itemView.context, when {
itemView.context,
when {
item.isLocked -> R.drawable.ic_lock_24dp
manga.favorite -> R.drawable.ic_heart_24dp
else -> R.drawable.ic_heart_outline_24dp
@ -210,7 +212,8 @@ class MangaHeaderHolder(
val number = adapter.decimalFormat.format(nextChapter.chapter_number.toDouble())
if (nextChapter.chapter_number > 0) resources.getString(
if (nextChapter.last_page_read > 0) R.string.continue_reading_chapter_
else R.string.start_reading_chapter_, number
else R.string.start_reading_chapter_,
number
)
else {
resources.getString(
@ -231,14 +234,16 @@ class MangaHeaderHolder(
}
manga_status.visibleIf(manga.status != 0)
manga_status.text = (itemView.context.getString(
manga_status.text = (
itemView.context.getString(
when (manga.status) {
SManga.ONGOING -> R.string.ongoing
SManga.COMPLETED -> R.string.completed
SManga.LICENSED -> R.string.licensed
else -> R.string.unknown_status
}
))
)
)
manga_source.text = presenter.source.toString()
filters_text.text = presenter.currentFilters()
@ -256,7 +261,8 @@ class MangaHeaderHolder(
if (checked) {
backgroundTintList = ColorStateList.valueOf(
ColorUtils.setAlphaComponent(
context.getResourceColor(R.attr.colorAccent), 75
context.getResourceColor(R.attr.colorAccent),
75
)
)
strokeColor = ColorStateList.valueOf(Color.TRANSPARENT)
@ -287,7 +293,8 @@ class MangaHeaderHolder(
)
icon = ContextCompat.getDrawable(
itemView.context, if (tracked) R.drawable
itemView.context,
if (tracked) R.drawable
.ic_check_24dp else R.drawable.ic_sync_24dp
)
checked(tracked)
@ -308,16 +315,22 @@ class MangaHeaderHolder(
fun updateCover(manga: Manga) {
if (!manga.initialized) return
val drawable = adapter.controller.manga_cover_full?.drawable
manga_cover.loadAny(manga, builder = {
manga_cover.loadAny(
manga,
builder = {
placeholder(drawable)
error(drawable)
if (manga.favorite) networkCachePolicy(CachePolicy.DISABLED)
})
backdrop.loadAny(manga, builder = {
}
)
backdrop.loadAny(
manga,
builder = {
placeholder(drawable)
error(drawable)
if (manga.favorite) networkCachePolicy(CachePolicy.DISABLED)
})
}
)
}
fun expand() {

@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.model.Page
abstract class BaseChapterItem<T : BaseChapterHolder, H : AbstractHeaderItem<*>>(
val chapter:
Chapter,
Chapter,
header: H? = null
) :
AbstractSectionableItem<T, H?>(header),

@ -44,7 +44,9 @@ class ChapterFilterLayout @JvmOverloads constructor(context: Context, attrs: Att
show_download.isChecked = manga.downloadedFilter == Manga.SHOW_DOWNLOADED
show_bookmark.isChecked = manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED
show_all.isChecked = !(show_read.isChecked || show_unread.isChecked ||
show_download.isChecked || show_bookmark.isChecked)
show_all.isChecked = !(
show_read.isChecked || show_unread.isChecked ||
show_download.isChecked || show_bookmark.isChecked
)
}
}

@ -58,13 +58,16 @@ class ChapterHolder(
if (showPagesLeft && chapter.pages_left > 0) {
statuses.add(
itemView.resources.getQuantityString(
R.plurals.pages_left, chapter.pages_left, chapter.pages_left
R.plurals.pages_left,
chapter.pages_left,
chapter.pages_left
)
)
} else if (showPagesLeft) {
statuses.add(
itemView.context.getString(
R.string.page_, chapter.last_page_read + 1
R.string.page_,
chapter.last_page_read + 1
)
)
}
@ -81,7 +84,10 @@ class ChapterHolder(
}
// this will color the scanlator the same bookmarks
ChapterUtil.setTextViewForChapter(
chapter_scanlator, item, showBookmark = false, hideStatus = isLocked
chapter_scanlator,
item,
showBookmark = false,
hideStatus = isLocked
)
chapter_scanlator.text = statuses.joinToString("")
@ -115,9 +121,11 @@ class ChapterHolder(
val anim3 = slideAnimation(-slide, 0f)
anim3.startDelay = 750
animatorSet.playSequentially(anim1, anim2, anim3)
animatorSet.addListener(EndAnimatorListener {
animatorSet.addListener(
EndAnimatorListener {
adapter.hasShownSwipeTut.set(true)
})
}
)
animatorSet.start()
}

@ -19,7 +19,7 @@ import kotlinx.android.synthetic.main.chapter_sort_bottom_sheet.*
import kotlin.math.max
class ChaptersSortBottomSheet(controller: MangaDetailsController) : BottomSheetDialog
(controller.activity!!, R.style.BottomSheetDialogTheme) {
(controller.activity!!, R.style.BottomSheetDialogTheme) {
val activity = controller.activity!!
@ -37,7 +37,8 @@ class ChaptersSortBottomSheet(controller: MangaDetailsController) : BottomSheetD
val height = activity.window.decorView.rootWindowInsets.systemWindowInsetBottom
sheetBehavior.peekHeight = 415.dpToPx + height
sheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
sheetBehavior.addBottomSheetCallback(
object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, progress: Float) {
if (progress.isNaN())
pill.alpha = 0f
@ -50,7 +51,8 @@ class ChaptersSortBottomSheet(controller: MangaDetailsController) : BottomSheetD
sheetBehavior.skipCollapsed = true
}
}
})
}
)
}
override fun onStart() {
@ -89,19 +91,27 @@ class ChaptersSortBottomSheet(controller: MangaDetailsController) : BottomSheetD
chapter_filter_layout.setCheckboxes(presenter.manga)
var defPref = presenter.globalSort()
sort_group.check(if (presenter.manga.sortDescending(defPref)) R.id.sort_newest else
R.id.sort_oldest)
sort_group.check(
if (presenter.manga.sortDescending(defPref)) R.id.sort_newest else
R.id.sort_oldest
)
hide_titles.isChecked = presenter.manga.displayMode != Manga.DISPLAY_NAME
sort_method_group.check(if (presenter.manga.sorting == Manga.SORTING_SOURCE) R.id.sort_by_source else
R.id.sort_by_number)
sort_method_group.check(
if (presenter.manga.sorting == Manga.SORTING_SOURCE) R.id.sort_by_source else
R.id.sort_by_number
)
set_as_default_sort.visInvisIf(defPref != presenter.manga.sortDescending() &&
presenter.manga.usesLocalSort())
set_as_default_sort.visInvisIf(
defPref != presenter.manga.sortDescending() &&
presenter.manga.usesLocalSort()
)
sort_group.setOnCheckedChangeListener { _, checkedId ->
presenter.setSortOrder(checkedId == R.id.sort_newest)
set_as_default_sort.visInvisIf(defPref != presenter.manga.sortDescending() &&
presenter.manga.usesLocalSort())
set_as_default_sort.visInvisIf(
defPref != presenter.manga.sortDescending() &&
presenter.manga.usesLocalSort()
)
}
set_as_default_sort.setOnClickListener {

@ -19,9 +19,11 @@ class SetTrackChaptersDialog<T> : DialogController
private val item: TrackItem
private lateinit var listener: Listener
constructor(target: T, item: TrackItem) : super(Bundle().apply {
constructor(target: T, item: TrackItem) : super(
Bundle().apply {
putSerializable(KEY_ITEM_TRACK, item.track)
}) {
}
) {
listener = target
this.item = item
}

@ -18,9 +18,11 @@ class SetTrackScoreDialog<T> : DialogController where T : SetTrackScoreDialog.Li
private val item: TrackItem
private lateinit var listener: Listener
constructor(target: T, item: TrackItem) : super(Bundle().apply {
constructor(target: T, item: TrackItem) : super(
Bundle().apply {
putSerializable(KEY_ITEM_TRACK, item.track)
}) {
}
) {
listener = target
this.item = item
}

@ -17,9 +17,11 @@ class SetTrackStatusDialog<T> : DialogController
private val item: TrackItem
private lateinit var listener: Listener
constructor(target: T, item: TrackItem) : super(Bundle().apply {
constructor(target: T, item: TrackItem) : super(
Bundle().apply {
putSerializable(KEY_ITEM_TRACK, item.track)
}) {
}
) {
listener = target
this.item = item
}
@ -40,8 +42,11 @@ class SetTrackStatusDialog<T> : DialogController
return MaterialDialog(activity!!)
.title(R.string.status)
.negativeButton(android.R.string.cancel)
.listItemsSingleChoice(items = statusString, initialSelection = selectedIndex,
waitForPositiveButton = false) { dialog, position, _ ->
.listItemsSingleChoice(
items = statusString,
initialSelection = selectedIndex,
waitForPositiveButton = false
) { dialog, position, _ ->
listener.setStatus(item, position)
dialog.dismiss()
}

@ -42,10 +42,13 @@ class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) {
R.string.all_chapters_read
)
track.total_chapters > 0 -> context.getString(
R.string.chapter_x_of_y, track.last_chapter_read, track.total_chapters
R.string.chapter_x_of_y,
track.last_chapter_read,
track.total_chapters
)
track.last_chapter_read > 0 -> context.getString(
R.string.chapter_, track.last_chapter_read.toString()
R.string.chapter_,
track.last_chapter_read.toString()
)
else -> context.getString(R.string.not_started)
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save