Linting fixes

This commit is contained in:
arkon
2020-04-25 14:24:45 -04:00
parent 4da760d614
commit 3f63b320c4
272 changed files with 4167 additions and 3602 deletions
app/src
main
java
eu
kanade
tachiyomi
App.ktAppModule.kt
data
extension
network
source
ui
base
category
download
extension
library
main
manga
migration
more
reader
recent
security
setting
source
util
widget
test
java
eu
kanade
tachiyomi

@@ -22,7 +22,6 @@ import uy.kohesive.injekt.api.get
class AppModule(val app: Application) : InjektModule {
override fun InjektRegistrar.registerInjectables() {
addSingleton(app)
addSingletonFactory { PreferencesHelper(app) }

@@ -33,7 +33,8 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
if (interval > 0) {
val request = PeriodicWorkRequestBuilder<BackupCreatorJob>(
interval.toLong(), TimeUnit.HOURS,
10, TimeUnit.MINUTES)
10, TimeUnit.MINUTES
)
.addTag(TAG)
.build()

@@ -85,7 +85,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())
@@ -442,8 +443,9 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
val dbChapters = databaseHelper.getChapters(manga).executeAsBlocking()
// Return if fetch is needed
if (dbChapters.isEmpty() || dbChapters.size < chapters.size)
if (dbChapters.isEmpty() || dbChapters.size < chapters.size) {
return false
}
for (chapter in chapters) {
val pos = dbChapters.indexOf(chapter)

@@ -143,7 +143,8 @@ class BackupRestoreService : Service() {
startForeground(Notifications.ID_RESTORE, notifier.showRestoreProgress().build())
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "BackupRestoreService:WakeLock")
PowerManager.PARTIAL_WAKE_LOCK, "BackupRestoreService:WakeLock"
)
wakeLock.acquire()
}
@@ -184,7 +185,8 @@ class BackupRestoreService : Service() {
subscription = Observable.using(
{ db.lowLevel().beginTransaction() },
{ getRestoreObservable(uri).doOnNext { db.lowLevel().setTransactionSuccessful() } },
{ executor.execute { db.lowLevel().endTransaction() } })
{ executor.execute { db.lowLevel().endTransaction() } }
)
.doAfterTerminate { stopSelf(startId) }
.subscribeOn(Schedulers.from(executor))
.subscribe()
@@ -231,14 +233,22 @@ class BackupRestoreService : Service() {
.concatMap {
val obj = it.asJsonObject
val manga = backupManager.parser.fromJson<MangaImpl>(obj.get(MANGA))
val chapters = backupManager.parser.fromJson<List<ChapterImpl>>(obj.get(CHAPTERS)
?: JsonArray())
val categories = backupManager.parser.fromJson<List<String>>(obj.get(CATEGORIES)
?: JsonArray())
val history = backupManager.parser.fromJson<List<DHistory>>(obj.get(HISTORY)
?: JsonArray())
val tracks = backupManager.parser.fromJson<List<TrackImpl>>(obj.get(TRACK)
?: JsonArray())
val chapters = backupManager.parser.fromJson<List<ChapterImpl>>(
obj.get(CHAPTERS)
?: JsonArray()
)
val categories = backupManager.parser.fromJson<List<String>>(
obj.get(CATEGORIES)
?: JsonArray()
)
val history = backupManager.parser.fromJson<List<DHistory>>(
obj.get(HISTORY)
?: JsonArray()
)
val tracks = backupManager.parser.fromJson<List<TrackImpl>>(
obj.get(TRACK)
?: JsonArray()
)
val observable = getMangaRestoreObservable(manga, chapters, categories, history, tracks)
if (observable != null) {
@@ -379,7 +389,6 @@ class BackupRestoreService : Service() {
history: List<DHistory>,
tracks: List<Track>
): Observable<Manga> {
return Observable.just(backupManga)
.flatMap { manga ->
if (!backupManager.restoreChaptersForManga(manga, chapters)) {

@@ -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.
@@ -77,8 +79,9 @@ class ChapterCache(private val context: Context) {
*/
fun removeFileFromCache(file: String): Boolean {
// Make sure we don't delete the journal file (keeps track of cache).
if (file == "journal" || file.startsWith("journal."))
if (file == "journal" || file.startsWith("journal.")) {
return false
}
return try {
// Remove the extension from the file to get the key of the cache

@@ -56,8 +56,9 @@ class CoverCache(private val context: Context) {
*/
fun deleteFromCache(thumbnailUrl: String?): Boolean {
// Check if url is empty.
if (thumbnailUrl.isNullOrEmpty())
if (thumbnailUrl.isNullOrEmpty()) {
return false
}
// Remove file.
val file = getCoverFile(thumbnailUrl)

@@ -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

@@ -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()

@@ -17,48 +17,58 @@ 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 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 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()

@@ -24,30 +24,36 @@ interface HistoryQueries : DbProvider {
*/
fun getRecentManga(date: Date) = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getRecentMangasQuery())
.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()
/**
@@ -71,16 +77,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>) {

@@ -21,46 +21,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()
@@ -97,40 +107,50 @@ 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)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getTotalChapterMangaQuery())
.observesTables(MangaTable.TABLE)
.build())
.build()
)
.prepare()
fun getLatestChapterManga() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(RawQuery.builder()
.withQuery(
RawQuery.builder()
.query(getLatestChapterMangaQuery())
.observesTables(MangaTable.TABLE)
.build())
.build()
)
.prepare()
}

@@ -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}
@@ -33,7 +34,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 AND ${Chapter.COL_DATE_UPLOAD} > ?
@@ -47,7 +49,8 @@ fun getRecentsQuery() = """
* and are read after the given time period
* @return return limit is 25
*/
fun getRecentMangasQuery() = """
fun getRecentMangasQuery() =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
@@ -65,7 +68,8 @@ fun getRecentMangasQuery() = """
LIMIT 25
"""
fun getHistoryByMangaId() = """
fun getHistoryByMangaId() =
"""
SELECT ${History.TABLE}.*
FROM ${History.TABLE}
JOIN ${Chapter.TABLE}
@@ -73,7 +77,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}
@@ -81,7 +86,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}
@@ -93,7 +99,8 @@ fun getLastReadMangaQuery() = """
ORDER BY max DESC
"""
fun getTotalChapterMangaQuery() = """
fun getTotalChapterMangaQuery() =
"""
SELECT ${Manga.TABLE}.*
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
@@ -102,7 +109,8 @@ fun getTotalChapterMangaQuery() = """
ORDER by COUNT(*)
"""
fun getLatestChapterMangaQuery() = """
fun getLatestChapterMangaQuery() =
"""
SELECT ${Manga.TABLE}.*, MAX(${Chapter.TABLE}.${Chapter.COL_DATE_UPLOAD}) AS max
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
@@ -114,7 +122,8 @@ fun getLatestChapterMangaQuery() = """
/**
* 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}

@@ -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

@@ -13,7 +13,8 @@ object CategoryTable {
const val COL_FLAGS = "flags"
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,

@@ -29,7 +29,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,

@@ -39,7 +39,8 @@ object MangaTable {
const val COL_CATEGORY = "category"
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,

@@ -31,7 +31,8 @@ object TrackTable {
const val COL_FINISH_DATE = "finish_date"
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,

@@ -87,9 +87,11 @@ 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.action_pause),
NotificationReceiver.pauseDownloadsPendingBroadcast(context))
NotificationReceiver.pauseDownloadsPendingBroadcast(context)
)
}
val downloadingProgressText = context.getString(R.string.chapter_downloading_progress)
@@ -126,13 +128,17 @@ internal class DownloadNotifier(private val context: Context) {
// Open download manager when clicked
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
// Resume action
addAction(R.drawable.ic_play_arrow_24dp,
addAction(
R.drawable.ic_play_arrow_24dp,
context.getString(R.string.action_resume),
NotificationReceiver.resumeDownloadsPendingBroadcast(context))
NotificationReceiver.resumeDownloadsPendingBroadcast(context)
)
// Clear action
addAction(R.drawable.ic_close_24dp,
addAction(
R.drawable.ic_close_24dp,
context.getString(R.string.action_cancel_all),
NotificationReceiver.clearDownloadsPendingBroadcast(context))
NotificationReceiver.clearDownloadsPendingBroadcast(context)
)
}
// Show notification.
@@ -173,8 +179,10 @@ internal class DownloadNotifier(private val context: Context) {
fun onError(error: String? = null, chapter: String? = null) {
// Create notification
with(notificationBuilder) {
setContentTitle(chapter
?: context.getString(R.string.download_notifier_downloader_title))
setContentTitle(
chapter
?: context.getString(R.string.download_notifier_downloader_title)
)
setContentText(error ?: context.getString(R.string.download_notifier_unknown_error))
setSmallIcon(android.R.drawable.stat_sys_warning)
clearActions()

@@ -125,12 +125,15 @@ class DownloadService : Service() {
subscriptions += ReactiveNetwork.observeNetworkConnectivity(applicationContext)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ state ->
.subscribe(
{ state ->
onNetworkStateChanged(state)
}, {
},
{
toast(R.string.download_queue_error)
stopSelf()
})
}
)
}
/**
@@ -162,12 +165,13 @@ class DownloadService : Service() {
*/
private fun listenDownloaderState() {
subscriptions += downloadManager.runningRelay.subscribe { running ->
if (running)
if (running) {
wakeLock.acquireIfNeeded()
else
} else {
wakeLock.releaseIfNeeded()
}
}
}
/**
* Releases the wake lock if it's held.

@@ -100,11 +100,13 @@ class Downloader(
* @return true if the downloader is started, false otherwise.
*/
fun start(): Boolean {
if (isRunning || queue.isEmpty())
if (isRunning || queue.isEmpty()) {
return false
}
if (!subscriptions.hasSubscriptions())
if (!subscriptions.hasSubscriptions()) {
initializeSubscriptions()
}
val pending = queue.filter { it.status != Download.DOWNLOADED }
pending.forEach { if (it.status != Download.QUEUE) it.status = Download.QUEUE }
@@ -177,13 +179,16 @@ class Downloader(
.concatMap { downloadChapter(it).subscribeOn(Schedulers.io()) }
.onBackpressureBuffer()
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
.subscribe(
{
completeDownload(it)
}, { error ->
},
{ error ->
DownloadService.stop(context)
Timber.e(error)
notifier.onError(error.message)
})
}
)
}
/**
@@ -304,8 +309,9 @@ class Downloader(
*/
private fun getOrDownloadImage(page: Page, download: Download, tmpDir: UniFile): Observable<Page> {
// If the image URL is empty, do nothing
if (page.imageUrl == null)
if (page.imageUrl == null) {
return Observable.just(page)
}
val filename = String.format("%03d", page.number)
val tmpFile = tmpDir.findFile("$filename.tmp")
@@ -317,10 +323,11 @@ class Downloader(
val imageFile = tmpDir.listFiles()!!.find { it.name!!.startsWith("$filename.") }
// If the image is already downloaded, do nothing. Otherwise download from network
val pageObservable = if (imageFile != null)
val pageObservable = if (imageFile != null) {
Observable.just(imageFile)
else
} else {
downloadImage(page, download.source, tmpDir, filename)
}
return pageObservable
// When the image is ready, set image path, progress (just in case) and status
@@ -400,7 +407,6 @@ class Downloader(
tmpDir: UniFile,
dirname: String
) {
// Ensure that the chapter folder has all the images.
val downloadedImages = tmpDir.listFiles().orEmpty().filterNot { it.name!!.endsWith(".tmp") }

@@ -25,7 +25,9 @@ class LibraryMangaUrlFetcher(
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
if (!file.exists()) {
networkFetcher.loadData(priority, object : DataFetcher.DataCallback<InputStream> {
networkFetcher.loadData(
priority,
object : DataFetcher.DataCallback<InputStream> {
override fun onDataReady(data: InputStream?) {
if (data != null) {
val tmpFile = File(file.path + ".tmp")
@@ -54,7 +56,8 @@ class LibraryMangaUrlFetcher(
override fun onLoadFailed(e: Exception) {
callback.onLoadFailed(e)
}
})
}
)
} else {
loadFromFile(callback)
}

@@ -27,8 +27,10 @@ class TachiGlideModule : AppGlideModule() {
override fun applyOptions(context: Context, builder: GlideBuilder) {
builder.setDiskCache(InternalCacheDiskCacheFactory(context, 50 * 1024 * 1024))
builder.setDefaultRequestOptions(RequestOptions().format(DecodeFormat.PREFER_RGB_565))
builder.setDefaultTransitionOptions(Drawable::class.java,
DrawableTransitionOptions.withCrossFade())
builder.setDefaultTransitionOptions(
Drawable::class.java,
DrawableTransitionOptions.withCrossFade()
)
}
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
@@ -36,7 +38,10 @@ class TachiGlideModule : AppGlideModule() {
registry.replace(GlideUrl::class.java, InputStream::class.java, networkFactory)
registry.append(MangaThumbnail::class.java, InputStream::class.java, MangaThumbnailModelLoader.Factory())
registry.append(InputStream::class.java, InputStream::class.java, PassthroughModelLoader
.Factory())
registry.append(
InputStream::class.java, InputStream::class.java,
PassthroughModelLoader
.Factory()
)
}
}

@@ -30,10 +30,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
if (interval > 0) {
val restrictions = preferences.libraryUpdateRestriction()!!
val acRestriction = "ac" in restrictions
val wifiRestriction = if ("wifi" in restrictions)
val wifiRestriction = if ("wifi" in restrictions) {
NetworkType.UNMETERED
else
} else {
NetworkType.CONNECTED
}
val constraints = Constraints.Builder()
.setRequiredNetworkType(wifiRestriction)
@@ -42,7 +43,8 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
val request = PeriodicWorkRequestBuilder<LibraryUpdateJob>(
interval.toLong(), TimeUnit.HOURS,
10, TimeUnit.MINUTES)
10, TimeUnit.MINUTES
)
.addTag(TAG)
.setConstraints(constraints)
.build()

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

@@ -184,7 +184,8 @@ class LibraryUpdateService(
super.onCreate()
startForeground(Notifications.ID_LIBRARY_PROGRESS, progressNotificationBuilder.build())
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "LibraryUpdateService:WakeLock")
PowerManager.PARTIAL_WAKE_LOCK, "LibraryUpdateService:WakeLock"
)
wakeLock.acquire()
}
@@ -238,13 +239,17 @@ class LibraryUpdateService(
}
}
.subscribeOn(Schedulers.io())
.subscribe({
}, {
.subscribe(
{
},
{
Timber.e(it)
stopSelf(startId)
}, {
},
{
stopSelf(startId)
})
}
)
return START_REDELIVER_INTENT
}
@@ -259,17 +264,18 @@ class LibraryUpdateService(
fun getMangaToUpdate(intent: Intent, target: Target): List<LibraryManga> {
val categoryId = intent.getIntExtra(KEY_CATEGORY, -1)
var listToUpdate = if (categoryId != -1)
var listToUpdate = if (categoryId != -1) {
db.getLibraryMangas().executeAsBlocking().filter { it.category == categoryId }
else {
} else {
val categoriesToUpdate = preferences.libraryUpdateCategories().get().map(String::toInt)
if (categoriesToUpdate.isNotEmpty())
if (categoriesToUpdate.isNotEmpty()) {
db.getLibraryMangas().executeAsBlocking()
.filter { it.category in categoriesToUpdate }
.distinctBy { it.id }
else
} else {
db.getLibraryMangas().executeAsBlocking().distinctBy { it.id }
}
}
if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) {
listToUpdate = listToUpdate.filter { it.status != SManga.COMPLETED }
}
@@ -315,9 +321,11 @@ class LibraryUpdateService(
// Filter out mangas without new chapters (or failed).
.filter { pair -> pair.first.isNotEmpty() }
.doOnNext {
if (downloadNew && (categoriesToDownload.isEmpty() ||
manga.category in categoriesToDownload)) {
if (downloadNew && (
categoriesToDownload.isEmpty() ||
manga.category in categoriesToDownload
)
) {
downloadChapters(manga, it.first)
hasDownloads = true
}
@@ -453,15 +461,19 @@ class LibraryUpdateService(
* @param total the total progress.
*/
private fun showProgressNotification(manga: Manga, current: Int, total: Int) {
val title = if (preferences.hideNotificationContent())
val title = if (preferences.hideNotificationContent()) {
getString(R.string.notification_check_updates)
else
} else {
manga.title
}
notificationManager.notify(Notifications.ID_LIBRARY_PROGRESS, progressNotificationBuilder
notificationManager.notify(
Notifications.ID_LIBRARY_PROGRESS,
progressNotificationBuilder
.setContentTitle(title)
.setProgress(total, current, false)
.build())
.build()
)
}
/**
@@ -476,7 +488,9 @@ class LibraryUpdateService(
NotificationManagerCompat.from(this).apply {
// Parent group notification
notify(Notifications.ID_NEW_CHAPTERS, notification(Notifications.CHANNEL_NEW_CHAPTERS) {
notify(
Notifications.ID_NEW_CHAPTERS,
notification(Notifications.CHANNEL_NEW_CHAPTERS) {
setContentTitle(getString(R.string.notification_new_chapters))
if (updates.size == 1 && !preferences.hideNotificationContent()) {
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
@@ -484,9 +498,13 @@ class LibraryUpdateService(
setContentText(resources.getQuantityString(R.plurals.notification_new_chapters_summary, updates.size, updates.size))
if (!preferences.hideNotificationContent()) {
setStyle(NotificationCompat.BigTextStyle().bigText(updates.joinToString("\n") {
setStyle(
NotificationCompat.BigTextStyle().bigText(
updates.joinToString("\n") {
it.first.title.chop(NOTIF_TITLE_MAX_LEN)
}))
}
)
)
}
}
@@ -500,7 +518,8 @@ class LibraryUpdateService(
setContentIntent(getNotificationIntent())
setAutoCancel(true)
})
}
)
// Per-manga notification
if (!preferences.hideNotificationContent()) {
@@ -536,13 +555,21 @@ class LibraryUpdateService(
setAutoCancel(true)
// Mark chapters as read action
addAction(R.drawable.ic_glasses_black_24dp, getString(R.string.action_mark_as_read),
NotificationReceiver.markAsReadPendingBroadcast(this@LibraryUpdateService,
manga, chapters, Notifications.ID_NEW_CHAPTERS))
addAction(
R.drawable.ic_glasses_black_24dp, getString(R.string.action_mark_as_read),
NotificationReceiver.markAsReadPendingBroadcast(
this@LibraryUpdateService,
manga, chapters, Notifications.ID_NEW_CHAPTERS
)
)
// View chapters action
addAction(R.drawable.ic_book_24dp, getString(R.string.action_view_chapters),
NotificationReceiver.openChapterPendingActivity(this@LibraryUpdateService,
manga, Notifications.ID_NEW_CHAPTERS))
addAction(
R.drawable.ic_book_24dp, getString(R.string.action_view_chapters),
NotificationReceiver.openChapterPendingActivity(
this@LibraryUpdateService,
manga, Notifications.ID_NEW_CHAPTERS
)
)
}
}
@@ -570,8 +597,11 @@ class LibraryUpdateService(
}
private fun getNewChaptersDescription(chapters: Array<Chapter>): String {
val formatter = DecimalFormat("#.###", DecimalFormatSymbols()
.apply { decimalSeparator = '.' })
val formatter = DecimalFormat(
"#.###",
DecimalFormatSymbols()
.apply { decimalSeparator = '.' }
)
val displayableChapterNumbers = chapters
.filter { it.isRecognizedNumber }

@@ -54,22 +54,35 @@ 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)
)
// Share backup file
ACTION_SHARE_BACKUP -> shareBackup(context, intent.getParcelableExtra(EXTRA_URI),
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1))
ACTION_CANCEL_RESTORE -> cancelRestore(context,
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1))
ACTION_SHARE_BACKUP ->
shareBackup(
context, intent.getParcelableExtra(EXTRA_URI),
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
)
ACTION_CANCEL_RESTORE -> cancelRestore(
context,
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
)
// Cancel library update and dismiss notification
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context, Notifications.ID_LIBRARY_PROGRESS)
// 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)
)
}
// Mark updated manga chapters as read
ACTION_MARK_AS_READ -> {

@@ -61,22 +61,34 @@ object Notifications {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
val channels = listOf(
NotificationChannel(CHANNEL_COMMON, context.getString(R.string.channel_common),
NotificationManager.IMPORTANCE_LOW),
NotificationChannel(CHANNEL_LIBRARY, context.getString(R.string.channel_library),
NotificationManager.IMPORTANCE_LOW).apply {
NotificationChannel(
CHANNEL_COMMON, context.getString(R.string.channel_common),
NotificationManager.IMPORTANCE_LOW
),
NotificationChannel(
CHANNEL_LIBRARY, context.getString(R.string.channel_library),
NotificationManager.IMPORTANCE_LOW
).apply {
setShowBadge(false)
},
NotificationChannel(CHANNEL_DOWNLOADER, context.getString(R.string.channel_downloader),
NotificationManager.IMPORTANCE_LOW).apply {
NotificationChannel(
CHANNEL_DOWNLOADER, context.getString(R.string.channel_downloader),
NotificationManager.IMPORTANCE_LOW
).apply {
setShowBadge(false)
},
NotificationChannel(CHANNEL_NEW_CHAPTERS, context.getString(R.string.channel_new_chapters),
NotificationManager.IMPORTANCE_DEFAULT),
NotificationChannel(CHANNEL_UPDATES_TO_EXTS, context.getString(R.string.channel_ext_updates),
NotificationManager.IMPORTANCE_DEFAULT),
NotificationChannel(CHANNEL_BACKUP_RESTORE, context.getString(R.string.channel_backup_restore),
NotificationManager.IMPORTANCE_HIGH).apply {
NotificationChannel(
CHANNEL_NEW_CHAPTERS, context.getString(R.string.channel_new_chapters),
NotificationManager.IMPORTANCE_DEFAULT
),
NotificationChannel(
CHANNEL_UPDATES_TO_EXTS, context.getString(R.string.channel_ext_updates),
NotificationManager.IMPORTANCE_DEFAULT
),
NotificationChannel(
CHANNEL_BACKUP_RESTORE, context.getString(R.string.channel_backup_restore),
NotificationManager.IMPORTANCE_HIGH
).apply {
setShowBadge(false)
}
)

@@ -45,12 +45,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 startScreen() = prefs.getInt(Keys.startScreen, 1)

@@ -25,7 +25,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
fun addLibManga(track: Track): Observable<Track> {
val query = """
val query =
"""
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
| id
@@ -62,7 +63,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
}
fun updateLibManga(track: Track): Observable<Track> {
val query = """
val query =
"""
|mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) {
|SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) {
|id
@@ -94,7 +96,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
}
fun search(search: String): Observable<List<TrackSearch>> {
val query = """
val query =
"""
|query Search(${'$'}query: String) {
|Page (perPage: 50) {
|media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) {
@@ -147,7 +150,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
}
fun findLibManga(track: Track, userid: Int): Observable<Track?> {
val query = """
val query =
"""
|query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
|Page {
|mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) {
@@ -216,7 +220,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
}
fun getCurrentUser(): Observable<Pair<Int, String>> {
val query = """
val query =
"""
|query User {
|Viewer {
|id
@@ -251,17 +256,24 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
private fun jsonToALManga(struct: JsonObject): ALManga {
val date = try {
val date = Calendar.getInstance()
date.set(struct["startDate"]["year"].nullInt ?: 0, (struct["startDate"]["month"].nullInt
?: 0) - 1,
struct["startDate"]["day"].nullInt ?: 0)
date.set(
struct["startDate"]["year"].nullInt ?: 0,
(
struct["startDate"]["month"].nullInt
?: 0
) - 1,
struct["startDate"]["day"].nullInt ?: 0
)
date.timeInMillis
} catch (_: Exception) {
0L
}
return ALManga(struct["id"].asInt, struct["title"]["romaji"].asString, struct["coverImage"]["large"].asString,
return ALManga(
struct["id"].asInt, struct["title"]["romaji"].asString, struct["coverImage"]["large"].asString,
struct["description"].nullString.orEmpty(), struct["type"].asString, struct["status"].asString,
date, struct["chapters"].nullInt ?: 0)
date, struct["chapters"].nullInt ?: 0
)
}
private fun jsonToALUserManga(struct: JsonObject): ALUserManga {

@@ -73,7 +73,8 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
fun search(search: String): Observable<List<TrackSearch>> {
val url = Uri.parse(
"$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}").buildUpon()
"$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}"
).buildUpon()
.appendQueryParameter("max_results", "20")
.build()
val request = Request.Builder()
@@ -109,9 +110,15 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
return Track.create(TrackManager.BANGUMI).apply {
title = mangas["name"].asString
media_id = mangas["id"].asInt
score = if (mangas["rating"] != null)
(if (mangas["rating"].isJsonObject) mangas["rating"].obj["score"].asFloat else 0f)
else 0f
score = if (mangas["rating"] != null) {
if (mangas["rating"].isJsonObject) {
mangas["rating"].obj["score"].asFloat
} else {
0f
}
} else {
0f
}
status = Bangumi.DEFAULT_STATUS
tracking_url = mangas["url"].asString
}
@@ -163,7 +170,8 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
}
}
private fun accessTokenRequest(code: String) = POST(oauthUrl,
private fun accessTokenRequest(code: String) = POST(
oauthUrl,
body = FormBody.Builder()
.add("grant_type", "authorization_code")
.add("client_id", clientId)
@@ -196,13 +204,15 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
.appendQueryParameter("redirect_uri", redirectUrl)
.build()
fun refreshTokenRequest(token: String) = POST(oauthUrl,
fun refreshTokenRequest(token: String) = POST(
oauthUrl,
body = FormBody.Builder()
.add("grant_type", "refresh_token")
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("refresh_token", token)
.add("redirect_uri", redirectUrl)
.build())
.build()
)
}
}

@@ -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)
}

@@ -242,12 +242,14 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
return baseMangaUrl + remoteId
}
fun refreshTokenRequest(token: String) = POST("${loginUrl}oauth/token",
fun refreshTokenRequest(token: String) = POST(
"${loginUrl}oauth/token",
body = FormBody.Builder()
.add("grant_type", "refresh_token")
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("refresh_token", token)
.build())
.build()
)
}
}

@@ -152,9 +152,10 @@ class MyAnimeList(private val context: Context, id: Int) : TrackService(id) {
var ckCount = 0
val url = BASE_URL.toHttpUrlOrNull()!!
for (ck in networkService.cookieManager.get(url)) {
if (ck.name == USER_SESSION_COOKIE || ck.name == LOGGED_IN_COOKIE)
if (ck.name == USER_SESSION_COOKIE || ck.name == LOGGED_IN_COOKIE) {
ckCount++
}
}
return ckCount == 2
}

@@ -46,10 +46,12 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
client.newCall(GET(searchUrl(query)))
.asObservable()
.flatMap { response ->
Observable.from(Jsoup.parse(response.consumeBody())
Observable.from(
Jsoup.parse(response.consumeBody())
.select("div.js-categories-seasonal.js-block-list.list")
.select("table").select("tbody")
.select("tr").drop(1))
.select("tr").drop(1)
)
}
.filter { row ->
row.select(TD)[2].text() != "Novel"
@@ -349,8 +351,9 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
private fun Element.searchDateXml(field: String): Long {
val text = selectText(field, "0000-00-00")!!
// MAL sets the data to 0000-00-00 when date is invalid or missing
if (text == "0000-00-00")
if (text == "0000-00-00") {
return 0L
}
return SimpleDateFormat("yyyy-MM-dd", Locale.US).parse(text)?.time ?: 0L
}
@@ -359,8 +362,9 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
val month = select(id + "_month > option[selected]").`val`().toIntOrNull()
val day = select(id + "_day > option[selected]").`val`().toIntOrNull()
val year = select(id + "_year > option[selected]").`val`().toIntOrNull()
if (year == null || month == null || day == null)
if (year == null || month == null || day == null) {
return 0L
}
return GregorianCalendar(year, month - 1, day).timeInMillis
}
@@ -472,8 +476,9 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
fun copyPersonalFrom(track: Track) {
num_read_chapters = track.last_chapter_read.toString()
val numScore = track.score.toInt()
if (numScore in 1..9)
if (numScore in 1..9) {
score = numScore.toString()
}
status = track.status.toString()
if (track.started_reading_date == 0L) {
start_date_month = ""

@@ -159,7 +159,8 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
}
}
private fun accessTokenRequest(code: String) = POST(oauthUrl,
private fun accessTokenRequest(code: String) = POST(
oauthUrl,
body = FormBody.Builder()
.add("grant_type", "authorization_code")
.add("client_id", clientId)
@@ -192,12 +193,14 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
.appendQueryParameter("response_type", "code")
.build()
fun refreshTokenRequest(token: String) = POST(oauthUrl,
fun refreshTokenRequest(token: String) = POST(
oauthUrl,
body = FormBody.Builder()
.add("grant_type", "refresh_token")
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("refresh_token", token)
.build())
.build()
)
}
}

@@ -37,9 +37,11 @@ class UpdaterJob(private val context: Context, workerParams: WorkerParameters) :
setContentText(context.getString(R.string.update_check_notification_update_available))
setSmallIcon(android.R.drawable.stat_sys_download_done)
// Download action
addAction(android.R.drawable.stat_sys_download_done,
addAction(
android.R.drawable.stat_sys_download_done,
context.getString(R.string.action_download),
PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
)
}
}
Result.success()
@@ -64,7 +66,8 @@ class UpdaterJob(private val context: Context, workerParams: WorkerParameters) :
val request = PeriodicWorkRequestBuilder<UpdaterJob>(
3, TimeUnit.DAYS,
3, TimeUnit.HOURS)
3, TimeUnit.HOURS
)
.addTag(TAG)
.setConstraints(constraints)
.build()

@@ -69,13 +69,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_alt_white_24dp,
addAction(
R.drawable.ic_system_update_alt_white_24dp,
context.getString(R.string.action_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.action_cancel),
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER))
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER)
)
}
notificationBuilder.show()
}
@@ -92,13 +96,17 @@ internal class UpdaterNotifier(private val context: Context) {
setOnlyAlertOnce(false)
setProgress(0, 0, false)
// Retry action
addAction(R.drawable.ic_refresh_24dp,
addAction(
R.drawable.ic_refresh_24dp,
context.getString(R.string.action_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.action_cancel),
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER))
NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER)
)
}
notificationBuilder.show(Notifications.ID_UPDATER)
}

@@ -40,7 +40,8 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
private fun createUpdateNotification(names: List<String>) {
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(
@@ -55,7 +56,8 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
setSmallIcon(R.drawable.ic_extension_24dp)
setContentIntent(NotificationReceiver.openExtensionsPendingActivity(context))
setAutoCancel(true)
})
}
)
}
}
@@ -72,7 +74,8 @@ class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParam
val request = PeriodicWorkRequestBuilder<ExtensionUpdateJob>(
12, TimeUnit.HOURS,
1, TimeUnit.HOURS)
1, TimeUnit.HOURS
)
.addTag(TAG)
.setConstraints(constraints)
.build()

@@ -107,8 +107,10 @@ internal object ExtensionLoader {
// Validate lib version
val libVersion = versionName.substringBeforeLast('.').toDouble()
if (libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) {
val exception = Exception("Lib version is $libVersion, while only versions " +
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed")
val exception = Exception(
"Lib version is $libVersion, while only versions " +
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed"
)
Timber.w(exception)
return LoadResult.Error(exception)
}
@@ -129,11 +131,12 @@ internal object ExtensionLoader {
.split(";")
.map {
val sourceClass = it.trim()
if (sourceClass.startsWith("."))
if (sourceClass.startsWith(".")) {
pkgInfo.packageName + sourceClass
else
} else {
sourceClass
}
}
.flatMap {
try {
when (val obj = Class.forName(it, false, classLoader).newInstance()) {

@@ -83,18 +83,20 @@ class LocalSource(private val context: Context) : CatalogueSource {
val state = ((if (filters.isEmpty()) POPULAR_FILTERS else filters)[0] as OrderBy).state
when (state?.index) {
0 -> {
mangaDirs = if (state.ascending)
mangaDirs = if (state.ascending) {
mangaDirs.sortedBy { it.name.toLowerCase(Locale.ENGLISH) }
else
} else {
mangaDirs.sortedByDescending { it.name.toLowerCase(Locale.ENGLISH) }
}
}
1 -> {
mangaDirs = if (state.ascending)
mangaDirs = if (state.ascending) {
mangaDirs.sortedBy(File::lastModified)
else
} else {
mangaDirs.sortedByDescending(File::lastModified)
}
}
}
val mangas = mangaDirs.map { mangaDir ->
SManga.create().apply {
@@ -167,10 +169,12 @@ class LocalSource(private val context: Context) : CatalogueSource {
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
})
}
)
.toList()
return Observable.just(chapters)

@@ -23,26 +23,32 @@ interface SManga : Serializable {
var initialized: Boolean
fun copyFrom(other: SManga) {
if (other.author != null)
if (other.author != null) {
author = other.author
}
if (other.artist != null)
if (other.artist != null) {
artist = other.artist
}
if (other.description != null)
if (other.description != null) {
description = other.description
}
if (other.genre != null)
if (other.genre != null) {
genre = other.genre
}
if (other.thumbnail_url != null)
if (other.thumbnail_url != null) {
thumbnail_url = other.thumbnail_url
}
status = other.status
if (!initialized)
if (!initialized) {
initialized = other.initialized
}
}
companion object {
const val UNKNOWN = 0

@@ -343,10 +343,12 @@ abstract class HttpSource : CatalogueSource {
return try {
val uri = URI(orig)
var out = uri.path
if (uri.query != null)
if (uri.query != null) {
out += "?" + uri.query
if (uri.fragment != null)
}
if (uri.fragment != null) {
out += "#" + uri.fragment
}
out
} catch (e: URISyntaxException) {
orig

@@ -59,7 +59,8 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
}
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(when (preferences.themeMode().get()) {
setTheme(
when (preferences.themeMode().get()) {
Values.THEME_MODE_SYSTEM -> {
if (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) {
darkTheme
@@ -69,7 +70,8 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
}
Values.THEME_MODE_DARK -> darkTheme
else -> lightTheme
})
}
)
super.onCreate(savedInstanceState)

@@ -15,7 +15,8 @@ import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.clearFindViewByIdCache
import timber.log.Timber
abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) : RestoreViewOnCreateController(bundle),
abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) :
RestoreViewOnCreateController(bundle),
LayoutContainer {
lateinit var binding: VB

@@ -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)
)
}
/**

@@ -44,12 +44,10 @@ abstract class RxController<VB : ViewBinding>(bundle: Bundle? = null) : BaseCont
}
fun <T> Observable<T>.subscribeUntilDetach(): Subscription {
return subscribe().also { untilDetachSubscriptions.add(it) }
}
fun <T> Observable<T>.subscribeUntilDetach(onNext: (T) -> Unit): Subscription {
return subscribe(onNext).also { untilDetachSubscriptions.add(it) }
}
@@ -57,7 +55,6 @@ abstract class RxController<VB : ViewBinding>(bundle: Bundle? = null) : BaseCont
onNext: (T) -> Unit,
onError: (Throwable) -> Unit
): Subscription {
return subscribe(onNext, onError).also { untilDetachSubscriptions.add(it) }
}
@@ -66,17 +63,14 @@ abstract class RxController<VB : ViewBinding>(bundle: Bundle? = null) : BaseCont
onError: (Throwable) -> Unit,
onCompleted: () -> Unit
): Subscription {
return subscribe(onNext, onError, onCompleted).also { untilDetachSubscriptions.add(it) }
}
fun <T> Observable<T>.subscribeUntilDestroy(): Subscription {
return subscribe().also { untilDestroySubscriptions.add(it) }
}
fun <T> Observable<T>.subscribeUntilDestroy(onNext: (T) -> Unit): Subscription {
return subscribe(onNext).also { untilDestroySubscriptions.add(it) }
}
@@ -84,7 +78,6 @@ abstract class RxController<VB : ViewBinding>(bundle: Bundle? = null) : BaseCont
onNext: (T) -> Unit,
onError: (Throwable) -> Unit
): Subscription {
return subscribe(onNext, onError).also { untilDestroySubscriptions.add(it) }
}
@@ -93,7 +86,6 @@ abstract class RxController<VB : ViewBinding>(bundle: Bundle? = null) : BaseCont
onError: (Throwable) -> Unit,
onCompleted: () -> Unit
): Subscription {
return subscribe(onNext, onError, onCompleted).also { untilDestroySubscriptions.add(it) }
}
}

@@ -25,7 +25,8 @@ import reactivecircus.flowbinding.android.view.clicks
/**
* Controller to manage the categories for the users' library.
*/
class CategoryController : NucleusController<CategoriesControllerBinding, CategoryPresenter>(),
class CategoryController :
NucleusController<CategoriesControllerBinding, CategoryPresenter>(),
ActionMode.Callback,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
@@ -176,8 +177,10 @@ class CategoryController : NucleusController<CategoriesControllerBinding, Catego
when (item.itemId) {
R.id.action_delete -> {
undoHelper = UndoHelper(adapter, this)
undoHelper?.start(adapter.selectedPositions, view!!,
R.string.snack_categories_deleted, R.string.action_undo, 3000)
undoHelper?.start(
adapter.selectedPositions, view!!,
R.string.snack_categories_deleted, R.string.action_undo, 3000
)
mode.finish()
}

@@ -49,7 +49,6 @@ class CategoryItem(val category: Category) : AbstractFlexibleItem<CategoryHolder
position: Int,
payloads: List<Any?>?
) {
holder.bind(category)
}

@@ -23,7 +23,8 @@ import rx.android.schedulers.AndroidSchedulers
* Controller that shows the currently active downloads.
* Uses R.layout.fragment_download_queue.
*/
class DownloadController : NucleusController<DownloadControllerBinding, DownloadPresenter>(),
class DownloadController :
NucleusController<DownloadControllerBinding, DownloadPresenter>(),
DownloadAdapter.DownloadItemListener {
/**
@@ -123,8 +124,9 @@ class DownloadController : NucleusController<DownloadControllerBinding, Download
val adapter = adapter ?: return false
val items = adapter.currentItems.sortedBy { it.download.chapter.date_upload }
.toMutableList()
if (item.itemId == R.id.newest)
if (item.itemId == R.id.newest) {
items.reverse()
}
adapter.updateDataSet(items)
val downloads = items.mapNotNull { it.download }
presenter.reorder(downloads)
@@ -279,10 +281,11 @@ class DownloadController : NucleusController<DownloadControllerBinding, Download
val items = adapter?.currentItems?.toMutableList() ?: return
val item = items[position]
items.remove(item)
if (menuItem.itemId == R.id.move_to_top)
if (menuItem.itemId == R.id.move_to_top) {
items.add(0, item)
else
} else {
items.add(item)
}
val adapter = adapter ?: return
adapter.updateDataSet(items)

@@ -80,13 +80,17 @@ class DownloadHolder(private val view: View, val adapter: DownloadAdapter) :
}
private fun showPopupMenu(view: View) {
view.popupMenu(R.menu.download_single, {
view.popupMenu(
R.menu.download_single,
{
findItem(R.id.move_to_top).isVisible = bindingAdapterPosition != 0
findItem(R.id.move_to_bottom).isVisible =
bindingAdapterPosition != adapter.itemCount - 1
}, {
},
{
adapter.downloadItemListener.onMenuItemClick(bindingAdapterPosition, this)
true
})
}
)
}
}

@@ -32,7 +32,8 @@ import uy.kohesive.injekt.api.get
/**
* Controller to manage the catalogues available in the app.
*/
open class ExtensionController : NucleusController<ExtensionControllerBinding, ExtensionPresenter>(),
open class ExtensionController :
NucleusController<ExtensionControllerBinding, ExtensionPresenter>(),
ExtensionAdapter.OnButtonClickListener,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
@@ -92,9 +93,11 @@ open class ExtensionController : NucleusController<ExtensionControllerBinding, E
when (item.itemId) {
R.id.action_search -> expandActionViewFromInteraction = true
R.id.action_settings -> {
router.pushController((RouterTransaction.with(ExtensionFilterController()))
router.pushController(
(RouterTransaction.with(ExtensionFilterController()))
.popChangeHandler(SettingsExtensionsFadeChangeHandler())
.pushChangeHandler(FadeChangeHandler()))
.pushChangeHandler(FadeChangeHandler())
)
}
R.id.action_auto_check -> {
item.isChecked = !item.isChecked
@@ -198,7 +201,8 @@ open class ExtensionController : NucleusController<ExtensionControllerBinding, E
adapter?.updateDataSet(
extensions.filter {
it.extension.name.contains(query, ignoreCase = true)
})
}
)
} else {
adapter?.updateDataSet(extensions)
}

@@ -46,9 +46,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())
@@ -137,7 +139,8 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
source.preferences
} else {*/
context.getSharedPreferences("source_${source.id}", Context.MODE_PRIVATE)
/*}*/)
/*}*/
)
if (source is ConfigurableSource) {
if (multiSource) {
@@ -178,14 +181,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)

@@ -23,7 +23,8 @@ class ExtensionDividerItemDecoration(context: Context) : RecyclerView.ItemDecora
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 RecyclerView.LayoutParams
val top = child.bottom + params.bottomMargin
val bottom = top + divider.intrinsicHeight

@@ -38,7 +38,6 @@ data class ExtensionGroupItem(val name: String, val size: Int, val showSize: Boo
position: Int,
payloads: List<Any?>?
) {
holder.bind(this)
}

@@ -69,13 +69,15 @@ class ExtensionHolder(view: View, override val adapter: ExtensionAdapter) :
val installStep = item.installStep
if (installStep != null) {
setText(when (installStep) {
setText(
when (installStep) {
InstallStep.Pending -> R.string.ext_pending
InstallStep.Downloading -> R.string.ext_downloading
InstallStep.Installing -> R.string.ext_installing
InstallStep.Installed -> R.string.ext_installed
InstallStep.Error -> R.string.action_retry
})
}
)
if (installStep != InstallStep.Error) {
isEnabled = false
isClickable = false

@@ -46,7 +46,6 @@ data class ExtensionItem(
position: Int,
payloads: List<Any?>?
) {
if (payloads == null || payloads.isEmpty()) {
holder.bind(this)
} else {

@@ -10,10 +10,12 @@ import eu.kanade.tachiyomi.ui.base.controller.DialogController
class ExtensionTrustDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
where T : Controller, T : ExtensionTrustDialog.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)
}) {
}
) {
targetController = target
}

@@ -255,9 +255,11 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
controller.createActionModeIfNeeded()
when {
lastClickPosition == -1 -> setSelection(position)
lastClickPosition > position -> for (i in position until lastClickPosition)
lastClickPosition > position ->
for (i in position until lastClickPosition)
setSelection(i)
lastClickPosition < position -> for (i in lastClickPosition + 1..position)
lastClickPosition < position ->
for (i in lastClickPosition + 1..position)
setSelection(i)
else -> setSelection(position)
}

@@ -230,10 +230,11 @@ class LibraryController(
}
// Get the current active category.
val activeCat = if (adapter.categories.isNotEmpty())
val activeCat = if (adapter.categories.isNotEmpty()) {
binding.libraryPager.currentItem
else
} else {
activeCategory
}
// Set the categories
adapter.categories = categories
@@ -260,11 +261,12 @@ class LibraryController(
* @return the preference.
*/
private fun getColumnsPreferenceForCurrentOrientation(): Preference<Int> {
return if (resources?.configuration?.orientation == Configuration.ORIENTATION_PORTRAIT)
return if (resources?.configuration?.orientation == Configuration.ORIENTATION_PORTRAIT) {
preferences.portraitColumns()
else
} else {
preferences.landscapeColumns()
}
}
/**
* Called when a filter is changed.
@@ -510,8 +512,13 @@ class LibraryController(
if (manga.favorite) {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
startActivityForResult(Intent.createChooser(intent,
resources?.getString(R.string.file_select_cover)), REQUEST_IMAGE_OPEN)
startActivityForResult(
Intent.createChooser(
intent,
resources?.getString(R.string.file_select_cover)
),
REQUEST_IMAGE_OPEN
)
} else {
activity?.toast(R.string.notification_first_add_to_library)
}

@@ -27,11 +27,12 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference
var downloadCount = -1
override fun getLayoutRes(): Int {
return if (libraryAsList.get())
return if (libraryAsList.get()) {
R.layout.source_list_item
else
} else {
R.layout.source_grid_item
}
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): LibraryHolder {
val parent = adapter.recyclerView
@@ -40,7 +41,8 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference
val coverHeight = parent.itemWidth / 3 * 4
card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight)
gradient.layoutParams = FrameLayout.LayoutParams(
MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM)
MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM
)
}
LibraryGridHolder(view, adapter)
} else {
@@ -76,15 +78,16 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference
}
private fun containsGenre(tag: String, genres: List<String>?): Boolean {
return if (tag.startsWith("-"))
return if (tag.startsWith("-")) {
genres?.find {
it.trim().toLowerCase() == tag.substringAfter("-").toLowerCase()
} == null
else
} else {
genres?.find {
it.trim().toLowerCase() == tag.toLowerCase()
} != null
}
}
override fun equals(other: Any?): Boolean {
if (other is LibraryItem) {

@@ -214,10 +214,11 @@ class LibraryPresenter(
}
}
val comparator = if (preferences.librarySortingAscending().get())
val comparator = if (preferences.librarySortingAscending().get()) {
Comparator(sortFn)
else
} else {
Collections.reverseOrder(sortFn)
}
return map.mapValues { entry -> entry.value.sortedWith(comparator) }
}
@@ -229,10 +230,11 @@ class LibraryPresenter(
*/
private fun getLibraryObservable(): Observable<Library> {
return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable()) { dbCategories, libraryManga ->
val categories = if (libraryManga.containsKey(0))
val categories = if (libraryManga.containsKey(0)) {
arrayListOf(Category.createDefault()) + dbCategories
else
} else {
dbCategories
}
this.categories = categories
Library(categories, libraryManga)

@@ -129,8 +129,11 @@ class LibrarySettingsSheet(
override fun initModels() {
val sorting = preferences.librarySortingMode().get()
val order = if (preferences.librarySortingAscending().get())
Item.MultiSort.SORT_ASC else Item.MultiSort.SORT_DESC
val order = if (preferences.librarySortingAscending().get()) {
Item.MultiSort.SORT_ASC
} else {
Item.MultiSort.SORT_DESC
}
alphabetically.state =
if (sorting == LibrarySort.ALPHA) order else Item.MultiSort.SORT_NONE

@@ -29,7 +29,7 @@ import eu.kanade.tachiyomi.ui.more.MoreController
import eu.kanade.tachiyomi.ui.recent.history.HistoryController
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
import eu.kanade.tachiyomi.ui.source.SourceController
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchController
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.WebViewUtil
import eu.kanade.tachiyomi.util.system.toast

@@ -35,10 +35,12 @@ import uy.kohesive.injekt.api.get
class MangaController : RxController<MangaControllerBinding>, TabbedController {
constructor(manga: Manga?, fromSource: Boolean = false) : super(Bundle().apply {
constructor(manga: Manga?, fromSource: Boolean = false) : super(
Bundle().apply {
putLong(MANGA_EXTRA, manga?.id ?: 0)
putBoolean(FROM_SOURCE_EXTRA, fromSource)
}) {
}
) {
this.manga = manga
if (manga != null) {
source = Injekt.get<SourceManager>().getOrStub(manga.source)
@@ -46,7 +48,8 @@ class MangaController : RxController<MangaControllerBinding>, TabbedController {
}
constructor(mangaId: Long) : this(
Injekt.get<DatabaseHelper>().getManga(mangaId).executeAsBlocking())
Injekt.get<DatabaseHelper>().getManga(mangaId).executeAsBlocking()
)
@Suppress("unused")
constructor(bundle: Bundle) : this(bundle.getLong(MANGA_EXTRA))
@@ -87,9 +90,10 @@ class MangaController : RxController<MangaControllerBinding>, TabbedController {
binding.mangaPager.offscreenPageLimit = 3
binding.mangaPager.adapter = adapter
if (!fromSource)
if (!fromSource) {
binding.mangaPager.currentItem = CHAPTERS_CONTROLLER
}
}
override fun onDestroyView(view: View) {
super.onDestroyView(view)
@@ -130,9 +134,11 @@ class MangaController : RxController<MangaControllerBinding>, TabbedController {
private fun setTrackingIconInternal(visible: Boolean) {
val tab = activity?.tabs?.getTabAt(TRACK_CONTROLLER) ?: return
val drawable = if (visible)
val drawable = if (visible) {
VectorDrawableCompat.create(resources!!, R.drawable.ic_done_white_18dp, null)
else null
} else {
null
}
tab.icon = drawable
}
@@ -144,7 +150,8 @@ class MangaController : RxController<MangaControllerBinding>, TabbedController {
private val tabTitles = listOf(
R.string.manga_detail_tab,
R.string.manga_chapters_tab,
R.string.manga_tracking_tab)
R.string.manga_tracking_tab
)
.map { resources!!.getString(it) }
override fun getCount(): Int {

@@ -10,7 +10,8 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download
class ChapterItem(val chapter: Chapter, val manga: Manga) : AbstractFlexibleItem<ChapterHolder>(),
class ChapterItem(val chapter: Chapter, val manga: Manga) :
AbstractFlexibleItem<ChapterHolder>(),
Chapter by chapter {
private var _status: Int = 0

@@ -26,8 +26,11 @@ class ChaptersAdapter(
val bookmarkedColor = context.getResourceColor(R.attr.colorAccent)
val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols()
.apply { decimalSeparator = '.' })
val decimalFormat = DecimalFormat(
"#.###",
DecimalFormatSymbols()
.apply { decimalSeparator = '.' }
)
val dateFormat: DateFormat = preferences.dateFormat().getOrDefault()

@@ -39,7 +39,8 @@ import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.swiperefreshlayout.refreshes
import timber.log.Timber
class ChaptersController : NucleusController<ChaptersControllerBinding, ChaptersPresenter>(),
class ChaptersController :
NucleusController<ChaptersControllerBinding, ChaptersPresenter>(),
ActionMode.Callback,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
@@ -168,11 +169,13 @@ class ChaptersController : NucleusController<ChaptersControllerBinding, Chapters
menuFilterEmpty.isVisible = filterSet
// Disable unread filter option if read filter is enabled.
if (presenter.onlyRead())
if (presenter.onlyRead()) {
menuFilterUnread.isEnabled = false
}
// Disable read filter option if unread filter is enabled.
if (presenter.onlyUnread())
if (presenter.onlyUnread()) {
menuFilterRead.isEnabled = false
}
// Display mode submenu
if (presenter.manga.displayMode == Manga.DISPLAY_NAME) {
@@ -320,9 +323,11 @@ class ChaptersController : NucleusController<ChaptersControllerBinding, Chapters
createActionModeIfNeeded()
when {
lastClickPosition == -1 -> setSelection(position)
lastClickPosition > position -> for (i in position until lastClickPosition)
lastClickPosition > position ->
for (i in position until lastClickPosition)
setSelection(i)
lastClickPosition < position -> for (i in lastClickPosition + 1..position)
lastClickPosition < position ->
for (i in lastClickPosition + 1..position)
setSelection(i)
else -> setSelection(position)
}

@@ -71,7 +71,8 @@ class ChaptersPresenter(
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
// changes, and sends the list of chapters to the relay.
add(db.getChapters(manga).asRxObservable()
add(
db.getChapters(manga).asRxObservable()
.map { chapters ->
// Convert every chapter to a model.
chapters.map { it.toModel() }
@@ -86,7 +87,8 @@ class ChaptersPresenter(
// Listen for download status changes
observeDownloads()
}
.subscribe { chaptersRelay.call(it) })
.subscribe { chaptersRelay.call(it) }
)
}
private fun observeDownloads() {
@@ -141,9 +143,12 @@ class ChaptersPresenter(
.subscribeOn(Schedulers.io())
.map { syncChaptersWithSource(db, it, manga, source) }
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst({ view, _ ->
.subscribeFirst(
{ view, _ ->
view.onFetchChaptersDone()
}, ChaptersController::onFetchChaptersError)
},
ChaptersController::onFetchChaptersError
)
}
/**
@@ -200,9 +205,10 @@ class ChaptersPresenter(
}
// Force UI update if downloaded filter active and download finished.
if (onlyDownloaded() && download.status == Download.DOWNLOADED)
if (onlyDownloaded() && download.status == Download.DOWNLOADED) {
refreshChapters()
}
}
/**
* Returns the next unread chapter or null if everything is read.
@@ -263,9 +269,12 @@ class ChaptersPresenter(
.doOnNext { if (onlyDownloaded()) refreshChapters() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst({ view, _ ->
.subscribeFirst(
{ view, _ ->
view.onChaptersDeleted(chapters)
}, ChaptersController::onChaptersDeletedError)
},
ChaptersController::onChaptersDeletedError
)
}
/**

@@ -24,10 +24,12 @@ class DownloadCustomChaptersDialog<T> : DialogController
* Initialize dialog.
* @param maxChapters maximal number of chapters that user can download.
*/
constructor(target: T, maxChapters: Int) : super(Bundle().apply {
constructor(target: T, maxChapters: Int) : super(
Bundle().apply {
// Add maximum number of chapters to download value to bundle.
putInt(KEY_ITEM_MAX, maxChapters)
}) {
}
) {
targetController = target
this.maxChapters = maxChapters
}

@@ -31,7 +31,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.recent.history.HistoryController
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchController
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.lang.truncateCenter
import eu.kanade.tachiyomi.util.system.toast
@@ -217,12 +217,14 @@ class MangaInfoController(private val fromSource: Boolean = false) :
}
// Update status TextView.
binding.mangaStatus.setText(when (manga.status) {
binding.mangaStatus.setText(
when (manga.status) {
SManga.ONGOING -> R.string.ongoing
SManga.COMPLETED -> R.string.completed
SManga.LICENSED -> R.string.licensed
else -> R.string.unknown
})
}
)
// Set the favorite drawable to the correct one.
setFavoriteButtonState(manga.favorite)
@@ -291,24 +293,27 @@ class MangaInfoController(private val fromSource: Boolean = false) :
val isExpanded = binding.mangaInfoToggle.text == context.getString(R.string.manga_info_collapse)
binding.mangaInfoToggle.text =
if (isExpanded)
if (isExpanded) {
context.getString(R.string.manga_info_expand)
else
} else {
context.getString(R.string.manga_info_collapse)
}
with(binding.mangaSummary) {
maxLines =
if (isExpanded)
if (isExpanded) {
3
else
} else {
Int.MAX_VALUE
}
ellipsize =
if (isExpanded)
if (isExpanded) {
TextUtils.TruncateAt.END
else
} else {
null
}
}
binding.mangaGenresTagsCompact.visibleIf { isExpanded }
binding.mangaGenresTagsFullChips.visibleIf { !isExpanded }
@@ -492,8 +497,10 @@ class MangaInfoController(private val fromSource: Boolean = false) :
val clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(ClipData.newPlainText(label, content))
activity.toast(view.context.getString(R.string.copied_to_clipboard, content.truncateCenter(20)),
Toast.LENGTH_SHORT)
activity.toast(
view.context.getString(R.string.copied_to_clipboard, content.truncateCenter(20)),
Toast.LENGTH_SHORT
)
}
/**

@@ -76,9 +76,12 @@ class MangaInfoPresenter(
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { sendMangaToView() }
.subscribeFirst({ view, _ ->
.subscribeFirst(
{ view, _ ->
view.onFetchMangaDone()
}, MangaInfoController::onFetchMangaError)
},
MangaInfoController::onFetchMangaError
)
}
/**

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

@@ -20,9 +20,11 @@ class SetTrackReadingDatesDialog<T> : DialogController
private val dateToUpdate: ReadingDate
constructor(target: T, dateToUpdate: ReadingDate, item: TrackItem) : super(Bundle().apply {
constructor(target: T, dateToUpdate: ReadingDate, item: TrackItem) : super(
Bundle().apply {
putSerializable(SetTrackReadingDatesDialog.KEY_ITEM_TRACK, item.track)
}) {
}
) {
targetController = target
this.item = item
this.dateToUpdate = dateToUpdate
@@ -40,10 +42,12 @@ class SetTrackReadingDatesDialog<T> : DialogController
val listener = (targetController as? Listener)
return MaterialDialog(activity!!)
.title(when (dateToUpdate) {
.title(
when (dateToUpdate) {
ReadingDate.Start -> R.string.track_started_reading_date
ReadingDate.Finish -> R.string.track_finished_reading_date
})
}
)
.datePicker(currentDate = getCurrentDate()) { _, date ->
listener?.setReadingDate(item, dateToUpdate, date.timeInMillis)
}
@@ -60,11 +64,12 @@ class SetTrackReadingDatesDialog<T> : DialogController
ReadingDate.Start -> it.started_reading_date
ReadingDate.Finish -> it.finished_reading_date
}
if (date != 0L)
if (date != 0L) {
timeInMillis = date
}
}
}
}
interface Listener {
fun setReadingDate(item: TrackItem, type: ReadingDate, date: Long)

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

@@ -17,9 +17,11 @@ class SetTrackStatusDialog<T> : DialogController
private val item: TrackItem
constructor(target: T, item: TrackItem) : super(Bundle().apply {
constructor(target: T, item: TrackItem) : super(
Bundle().apply {
putSerializable(KEY_ITEM_TRACK, item.track)
}) {
}
) {
targetController = target
this.item = item
}

@@ -17,7 +17,8 @@ import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.swiperefreshlayout.refreshes
import timber.log.Timber
class TrackController : NucleusController<TrackControllerBinding, TrackPresenter>(),
class TrackController :
NucleusController<TrackControllerBinding, TrackPresenter>(),
TrackAdapter.OnClickListener,
SetTrackStatusDialog.Listener,
SetTrackChaptersDialog.Listener,

@@ -67,8 +67,10 @@ class TrackPresenter(
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst({ view, _ -> view.onRefreshDone() },
TrackController::onRefreshError)
.subscribeFirst(
{ view, _ -> view.onRefreshDone() },
TrackController::onRefreshError
)
}
fun search(query: String, service: TrackService) {
@@ -76,19 +78,25 @@ class TrackPresenter(
searchSubscription = service.search(query)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(TrackController::onSearchResults,
TrackController::onSearchResultsError)
.subscribeLatestCache(
TrackController::onSearchResults,
TrackController::onSearchResultsError
)
}
fun registerTracking(item: Track?, service: TrackService) {
if (item != null) {
item.manga_id = manga.id!!
add(service.bind(item)
add(
service.bind(item)
.flatMap { db.insertTrack(item).asRxObservable() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ },
{ error -> context.toast(error.message) }))
.subscribe(
{ },
{ error -> context.toast(error.message) }
)
)
} else {
unregisterTracking(service)
}
@@ -103,13 +111,15 @@ class TrackPresenter(
.flatMap { db.insertTrack(track).asRxObservable() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst({ view, _ -> view.onRefreshDone() },
.subscribeFirst(
{ view, _ -> view.onRefreshDone() },
{ view, error ->
view.onRefreshError(error)
// Restart on error to set old values
fetchTrackings()
})
}
)
}
fun setStatus(item: TrackItem, index: Int) {

@@ -40,9 +40,11 @@ class TrackSearchDialog : DialogController {
private val trackController
get() = targetController as TrackController
constructor(target: TrackController, service: TrackService) : super(Bundle().apply {
constructor(target: TrackController, service: TrackService) : super(
Bundle().apply {
putInt(KEY_SERVICE, service.id)
}) {
}
) {
targetController = target
this.service = service
}

@@ -24,7 +24,6 @@ class MangaItem(val manga: Manga) : AbstractFlexibleItem<MangaHolder>() {
position: Int,
payloads: List<Any?>?
) {
holder.bind(this)
}

@@ -11,7 +11,8 @@ import eu.kanade.tachiyomi.databinding.MigrationControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
class MigrationController : NucleusController<MigrationControllerBinding, MigrationPresenter>(),
class MigrationController :
NucleusController<MigrationControllerBinding, MigrationPresenter>(),
FlexibleAdapter.OnItemClickListener,
SourceAdapter.OnSelectClickListener {

@@ -34,7 +34,8 @@ class MigrationPresenter(
.asRxObservable()
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { state = state.copy(sourcesWithManga = findSourcesWithManga(it)) }
.combineLatest(stateRelay.map { it.selectedSource }
.combineLatest(
stateRelay.map { it.selectedSource }
.distinctUntilChanged()
) { library, source -> library to source }
.filter { (_, source) -> source != null }

@@ -8,8 +8,8 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchPresenter
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchController
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchPresenter
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.visible
import uy.kohesive.injekt.injectLazy

@@ -8,9 +8,9 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchCardItem
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchItem
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchPresenter
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchCardItem
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchItem
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchPresenter
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import rx.Observable
import rx.android.schedulers.AndroidSchedulers

@@ -40,7 +40,6 @@ data class SourceItem(val source: Source, val header: SelectionHeader? = null) :
position: Int,
payloads: List<Any?>?
) {
holder.bind(this)
}
}

@@ -50,11 +50,12 @@ class AboutController : SettingsController() {
preference {
titleRes = R.string.version
summary = if (BuildConfig.DEBUG)
summary = if (BuildConfig.DEBUG) {
"Preview r${BuildConfig.COMMIT_COUNT} (${BuildConfig.COMMIT_SHA})"
else
} else {
"Stable ${BuildConfig.VERSION_NAME}"
}
}
preference {
titleRes = R.string.build_time
summary = getFormattedBuildTime()
@@ -157,10 +158,12 @@ class AboutController : SettingsController() {
class NewUpdateDialogController(bundle: Bundle? = null) : DialogController(bundle) {
constructor(body: String, url: String) : this(Bundle().apply {
constructor(body: String, url: String) : this(
Bundle().apply {
putString(BODY_KEY, body)
putString(URL_KEY, url)
})
}
)
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialDialog(activity!!)
@@ -190,7 +193,8 @@ class AboutController : SettingsController() {
val buildTime = inputDf.parse(BuildConfig.BUILD_TIME)
val outputDf = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault())
DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault()
)
outputDf.timeZone = TimeZone.getDefault()
buildTime.toDateTimestampString(dateFormat)

@@ -26,7 +26,8 @@ class ChapterLoadByNumber {
// If there is only one chapter for this number, use it
chaptersForNumber.size == 1 -> chaptersForNumber.first()
// Prefer a chapter of the same scanlator as the selected
else -> chaptersForNumber.find { it.scanlator == selectedChapter.scanlator }
else ->
chaptersForNumber.find { it.scanlator == selectedChapter.scanlator }
?: chaptersForNumber.first()
}
chapters.add(preferredChapter)

@@ -127,10 +127,12 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
* Called when the activity is created. Initializes the presenter and configuration.
*/
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(when (preferences.readerTheme().get()) {
setTheme(
when (preferences.readerTheme().get()) {
0 -> R.style.Theme_Reader_Light
else -> R.style.Theme_Reader
})
}
)
super.onCreate(savedInstanceState)
binding = ReaderActivityBinding.inflate(layoutInflater)
@@ -278,7 +280,8 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop,
insets.systemWindowInsetRight,
insets.systemWindowInsetBottom)
insets.systemWindowInsetBottom
)
}
insets
}
@@ -293,20 +296,22 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
})
binding.leftChapter.setOnClickListener {
if (viewer != null) {
if (viewer is R2LPagerViewer)
if (viewer is R2LPagerViewer) {
loadNextChapter()
else
} else {
loadPreviousChapter()
}
}
}
binding.rightChapter.setOnClickListener {
if (viewer != null) {
if (viewer is R2LPagerViewer)
if (viewer is R2LPagerViewer) {
loadPreviousChapter()
else
} else {
loadNextChapter()
}
}
}
// Set initial visibility
setMenuVisibility(menuVisible)
@@ -590,11 +595,13 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
* depending on the [result].
*/
fun onSetAsCoverResult(result: ReaderPresenter.SetAsCoverResult) {
toast(when (result) {
toast(
when (result) {
Success -> R.string.cover_updated
AddToLibraryFirst -> R.string.notification_first_add_to_library
Error -> R.string.notification_cover_update_failed
})
}
)
}
/**
@@ -700,11 +707,12 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
* Sets the 32-bit color mode according to [enabled].
*/
private fun setTrueColor(enabled: Boolean) {
if (enabled)
if (enabled) {
SubsamplingScaleImageView.setPreferredBitmapConfig(Bitmap.Config.ARGB_8888)
else
} else {
SubsamplingScaleImageView.setPreferredBitmapConfig(Bitmap.Config.RGB_565)
}
}
@TargetApi(Build.VERSION_CODES.P)
private fun setCutoutShort(enabled: Boolean) {

@@ -214,9 +214,10 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BottomSheet
brightness_overlay.gone()
}
if (!isDisabled)
if (!isDisabled) {
txt_brightness_seekbar_value.text = value.toString()
}
}
/**
* Manages the color filter value subscription

@@ -17,14 +17,16 @@ class ReaderColorFilterView(
fun setFilterColor(color: Int, filterMode: Int) {
colorFilterPaint.color = color
colorFilterPaint.xfermode = PorterDuffXfermode(when (filterMode) {
colorFilterPaint.xfermode = PorterDuffXfermode(
when (filterMode) {
1 -> PorterDuff.Mode.MULTIPLY
2 -> PorterDuff.Mode.SCREEN
3 -> PorterDuff.Mode.OVERLAY
4 -> PorterDuff.Mode.LIGHTEN
5 -> PorterDuff.Mode.DARKEN
else -> PorterDuff.Mode.SRC_OVER
})
}
)
invalidate()
}

@@ -100,8 +100,10 @@ class ReaderPresenter(
if (
(manga.readFilter == Manga.SHOW_READ && !it.read) ||
(manga.readFilter == Manga.SHOW_UNREAD && it.read) ||
(manga.downloadedFilter == Manga.SHOW_DOWNLOADED &&
!downloadManager.isChapterDownloaded(it, manga)) ||
(
manga.downloadedFilter == Manga.SHOW_DOWNLOADED &&
!downloadManager.isChapterDownloaded(it, manga)
) ||
(manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED && !it.bookmark)
) {
return@filter false
@@ -201,9 +203,12 @@ class ReaderPresenter(
.first()
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { init(it, initialChapterId) }
.subscribeFirst({ _, _ ->
.subscribeFirst(
{ _, _ ->
// Ignore onNext event
}, ReaderActivity::setInitialChapterError)
},
ReaderActivity::setInitialChapterError
)
}
/**
@@ -245,9 +250,12 @@ class ReaderPresenter(
.flatMap { getLoadObservable(loader!!, it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst({ _, _ ->
.subscribeFirst(
{ _, _ ->
// Ignore onNext event
}, ReaderActivity::setInitialChapterError)
},
ReaderActivity::setInitialChapterError
)
}
/**
@@ -262,13 +270,17 @@ class ReaderPresenter(
chapter: ReaderChapter
): Observable<ViewerChapters> {
return loader.loadChapter(chapter)
.andThen(Observable.fromCallable {
.andThen(
Observable.fromCallable {
val chapterPos = chapterList.indexOf(chapter)
ViewerChapters(chapter,
ViewerChapters(
chapter,
chapterList.getOrNull(chapterPos - 1),
chapterList.getOrNull(chapterPos + 1))
})
chapterList.getOrNull(chapterPos + 1)
)
}
)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { newChapters ->
val oldChapters = viewerChaptersRelay.value
@@ -312,11 +324,14 @@ class ReaderPresenter(
activeChapterSubscription = getLoadObservable(loader, chapter)
.doOnSubscribe { isLoadingAdjacentChapterRelay.call(true) }
.doOnUnsubscribe { isLoadingAdjacentChapterRelay.call(false) }
.subscribeFirst({ view, _ ->
.subscribeFirst(
{ view, _ ->
view.moveToPageIndex(0)
}, { _, _ ->
},
{ _, _ ->
// Ignore onError event, viewers handle that state
})
}
)
}
/**
@@ -509,9 +524,11 @@ class ReaderPresenter(
notifier.onClear()
// Pictures directory.
val destDir = File(Environment.getExternalStorageDirectory().absolutePath +
val destDir = File(
Environment.getExternalStorageDirectory().absolutePath +
File.separator + Environment.DIRECTORY_PICTURES +
File.separator + "Tachiyomi")
File.separator + "Tachiyomi"
)
// Copy file in background.
Observable.fromCallable { saveImage(page, destDir, manga) }
@@ -614,7 +631,8 @@ class ReaderPresenter(
db.getTracks(manga).asRxSingle()
.flatMapCompletable { trackList ->
Completable.concat(trackList.map { track ->
Completable.concat(
trackList.map { track ->
val service = trackManager.getService(track.sync_id)
if (service != null && service.isLogged && chapterRead > track.last_chapter_read) {
track.last_chapter_read = chapterRead
@@ -628,7 +646,8 @@ class ReaderPresenter(
} else {
Completable.complete()
}
})
}
)
}
.onErrorComplete()
.subscribeOn(Schedulers.io())

@@ -66,13 +66,17 @@ class SaveImageNotifier(private val context: Context) {
setContentIntent(NotificationHandler.openImagePendingActivity(context, file))
// Share action
addAction(R.drawable.ic_share_24dp,
addAction(
R.drawable.ic_share_24dp,
context.getString(R.string.action_share),
NotificationReceiver.shareImagePendingBroadcast(context, file.absolutePath, notificationId))
NotificationReceiver.shareImagePendingBroadcast(context, file.absolutePath, notificationId)
)
// Delete action
addAction(R.drawable.ic_delete_24dp,
addAction(
R.drawable.ic_delete_24dp,
context.getString(R.string.action_delete),
NotificationReceiver.deleteImagePendingBroadcast(context, file.absolutePath, notificationId))
NotificationReceiver.deleteImagePendingBroadcast(context, file.absolutePath, notificationId)
)
updateNotification()
}

@@ -44,10 +44,12 @@ class EpubPageLoader(file: File) : PageLoader() {
* Returns an observable that emits a ready state unless the loader was recycled.
*/
override fun getPage(page: ReaderPage): Observable<Int> {
return Observable.just(if (isRecycled) {
return Observable.just(
if (isRecycled) {
Page.ERROR
} else {
Page.READY
})
}
)
}
}

@@ -46,12 +46,15 @@ class HttpPageLoader(
.concatMap { source.fetchImageFromCacheThenNet(it) }
.repeat()
.subscribeOn(Schedulers.io())
.subscribe({
}, { error ->
.subscribe(
{
},
{ error ->
if (error !is InterruptedException) {
Timber.e(error)
}
})
}
)
}
/**
@@ -186,11 +189,12 @@ class HttpPageLoader(
* @param page the page whose source image has to be downloaded.
*/
private fun HttpSource.fetchImageFromCacheThenNet(page: ReaderPage): Observable<ReaderPage> {
return if (page.imageUrl.isNullOrEmpty())
return if (page.imageUrl.isNullOrEmpty()) {
getImageUrl(page).flatMap { getCachedImage(it) }
else
} else {
getCachedImage(page)
}
}
private fun HttpSource.getImageUrl(page: ReaderPage): Observable<ReaderPage> {
page.status = Page.LOAD_PAGE

@@ -60,11 +60,13 @@ class RarPageLoader(file: File) : PageLoader() {
* Returns an observable that emits a ready state unless the loader was recycled.
*/
override fun getPage(page: ReaderPage): Observable<Int> {
return Observable.just(if (isRecycled) {
return Observable.just(
if (isRecycled) {
Page.ERROR
} else {
Page.READY
})
}
)
}
/**

@@ -49,10 +49,12 @@ class ZipPageLoader(file: File) : PageLoader() {
* Returns an observable that emits a ready state unless the loader was recycled.
*/
override fun getPage(page: ReaderPage): Observable<Int> {
return Observable.just(if (isRecycled) {
return Observable.just(
if (isRecycled) {
Page.ERROR
} else {
Page.READY
})
}
)
}
}

@@ -61,7 +61,8 @@ class ReaderProgressBar @JvmOverloads constructor(
* The rotation animation to use while the progress bar is visible.
*/
private val rotationAnimation by lazy {
RotateAnimation(0f, 360f,
RotateAnimation(
0f, 360f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
).apply {

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