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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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