mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 06:17:57 +01:00 
			
		
		
		
	Run default Android Studio formatter on code
This commit is contained in:
		| @@ -1,4 +1,5 @@ | ||||
| package eu.kanade.tachiyomi.data.backup | ||||
|  | ||||
| import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID | ||||
|  | ||||
|  | ||||
| @@ -20,4 +21,4 @@ object BackupConst { | ||||
|     const val EXTRA_TIME = "$ID.$INTENT_FILTER.EXTRA_TIME" | ||||
|     const val EXTRA_ERROR_FILE_PATH = "$ID.$INTENT_FILTER.EXTRA_ERROR_FILE_PATH" | ||||
|     const val EXTRA_ERROR_FILE = "$ID.$INTENT_FILTER.EXTRA_ERROR_FILE" | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -217,10 +217,14 @@ 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) { | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| package eu.kanade.tachiyomi.data.backup.models | ||||
|  | ||||
| data class DHistory(val url: String,val lastRead: Long) | ||||
| data class DHistory(val url: String, val lastRead: Long) | ||||
|   | ||||
| @@ -20,8 +20,8 @@ class CoverCache(private val context: Context) { | ||||
|     /** | ||||
|      * Cache directory used for cache management. | ||||
|      */ | ||||
|     private val cacheDir = context.getExternalFilesDir("covers") ?: | ||||
|             File(context.filesDir, "covers").also { it.mkdirs() } | ||||
|     private val cacheDir = context.getExternalFilesDir("covers") | ||||
|             ?: File(context.filesDir, "covers").also { it.mkdirs() } | ||||
|  | ||||
|     /** | ||||
|      * Returns the cover from cache. | ||||
|   | ||||
| @@ -12,12 +12,12 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory | ||||
|  * This class provides operations to manage the database through its interfaces. | ||||
|  */ | ||||
| open class DatabaseHelper(context: Context) | ||||
| : MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries, HistoryQueries { | ||||
|     : MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries, HistoryQueries { | ||||
|  | ||||
|     private val configuration = SupportSQLiteOpenHelper.Configuration.builder(context) | ||||
|         .name(DbOpenCallback.DATABASE_NAME) | ||||
|         .callback(DbOpenCallback()) | ||||
|         .build() | ||||
|             .name(DbOpenCallback.DATABASE_NAME) | ||||
|             .callback(DbOpenCallback()) | ||||
|             .build() | ||||
|  | ||||
|     override val db = DefaultStorIOSQLite.builder() | ||||
|             .sqliteOpenHelper(RequerySQLiteOpenHelperFactory().create(configuration)) | ||||
|   | ||||
| @@ -35,7 +35,7 @@ interface History : Serializable { | ||||
|          * @param chapter chapter object | ||||
|          * @return history object | ||||
|          */ | ||||
|         fun create(chapter: Chapter): History =  HistoryImpl().apply { | ||||
|         fun create(chapter: Chapter): History = HistoryImpl().apply { | ||||
|             this.chapter_id = chapter.id!! | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -53,13 +53,13 @@ interface ChapterQueries : DbProvider { | ||||
|             .prepare() | ||||
|  | ||||
|     fun getChapter(url: String, mangaId: Long) = db.get() | ||||
|         .`object`(Chapter::class.java) | ||||
|         .withQuery(Query.builder() | ||||
|             .table(ChapterTable.TABLE) | ||||
|             .where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?") | ||||
|             .whereArgs(url, mangaId) | ||||
|             .build()) | ||||
|         .prepare() | ||||
|             .`object`(Chapter::class.java) | ||||
|             .withQuery(Query.builder() | ||||
|                     .table(ChapterTable.TABLE) | ||||
|                     .where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?") | ||||
|                     .whereArgs(url, mangaId) | ||||
|                     .build()) | ||||
|             .prepare() | ||||
|  | ||||
|     fun insertChapter(chapter: Chapter) = db.put().`object`(chapter).prepare() | ||||
|  | ||||
|   | ||||
| @@ -161,7 +161,8 @@ 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_unkown_error)) | ||||
|             setSmallIcon(android.R.drawable.stat_sys_warning) | ||||
|             clearActions() | ||||
|   | ||||
| @@ -131,7 +131,8 @@ class DownloadService : Service() { | ||||
|         subscriptions += ReactiveNetwork.observeNetworkConnectivity(applicationContext) | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe({ state -> onNetworkStateChanged(state) | ||||
|                 .subscribe({ state -> | ||||
|                     onNetworkStateChanged(state) | ||||
|                 }, { | ||||
|                     toast(R.string.download_queue_error) | ||||
|                     stopSelf() | ||||
| @@ -156,7 +157,9 @@ class DownloadService : Service() { | ||||
|             DISCONNECTED -> { | ||||
|                 downloadManager.stopDownloads(getString(R.string.download_notifier_no_network)) | ||||
|             } | ||||
|             else -> { /* Do nothing */ } | ||||
|             else -> { | ||||
|                 /* Do nothing */ | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -82,7 +82,8 @@ class Downloader( | ||||
|     /** | ||||
|      * Whether the downloader is running. | ||||
|      */ | ||||
|     @Volatile private var isRunning: Boolean = false | ||||
|     @Volatile | ||||
|     private var isRunning: Boolean = false | ||||
|  | ||||
|     init { | ||||
|         launchNow { | ||||
| @@ -175,7 +176,8 @@ class Downloader( | ||||
|                 .concatMap { downloadChapter(it).subscribeOn(Schedulers.io()) } | ||||
|                 .onBackpressureBuffer() | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe({ completeDownload(it) | ||||
|                 .subscribe({ | ||||
|                     completeDownload(it) | ||||
|                 }, { error -> | ||||
|                     DownloadService.stop(context) | ||||
|                     Timber.e(error) | ||||
| @@ -376,10 +378,10 @@ class Downloader( | ||||
|     private fun getImageExtension(response: Response, file: UniFile): String { | ||||
|         // Read content type if available. | ||||
|         val mime = response.body?.contentType()?.let { ct -> "${ct.type}/${ct.subtype}" } | ||||
|             // Else guess from the uri. | ||||
|             ?: context.contentResolver.getType(file.uri) | ||||
|             // Else read magic numbers. | ||||
|             ?: ImageUtil.findImageType { file.openInputStream() }?.mime | ||||
|                 // Else guess from the uri. | ||||
|                 ?: context.contentResolver.getType(file.uri) | ||||
|                 // Else read magic numbers. | ||||
|                 ?: ImageUtil.findImageType { file.openInputStream() }?.mime | ||||
|  | ||||
|         return MimeTypeMap.getSingleton().getExtensionFromMimeType(mime) ?: "jpg" | ||||
|     } | ||||
|   | ||||
| @@ -10,17 +10,24 @@ class Download(val source: HttpSource, val manga: Manga, val chapter: Chapter) { | ||||
|  | ||||
|     var pages: List<Page>? = null | ||||
|  | ||||
|     @Volatile @Transient var totalProgress: Int = 0 | ||||
|     @Volatile | ||||
|     @Transient | ||||
|     var totalProgress: Int = 0 | ||||
|  | ||||
|     @Volatile @Transient var downloadedImages: Int = 0 | ||||
|     @Volatile | ||||
|     @Transient | ||||
|     var downloadedImages: Int = 0 | ||||
|  | ||||
|     @Volatile @Transient var status: Int = 0 | ||||
|     @Volatile | ||||
|     @Transient | ||||
|     var status: Int = 0 | ||||
|         set(status) { | ||||
|             field = status | ||||
|             statusSubject?.onNext(this) | ||||
|         } | ||||
|  | ||||
|     @Transient private var statusSubject: PublishSubject<Download>? = null | ||||
|     @Transient | ||||
|     private var statusSubject: PublishSubject<Download>? = null | ||||
|  | ||||
|     fun setStatusSubject(subject: PublishSubject<Download>?) { | ||||
|         statusSubject = subject | ||||
| @@ -34,4 +41,4 @@ class Download(val source: HttpSource, val manga: Manga, val chapter: Chapter) { | ||||
|         const val DOWNLOADED = 3 | ||||
|         const val ERROR = 4 | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import java.util.concurrent.CopyOnWriteArrayList | ||||
| class DownloadQueue( | ||||
|         private val store: DownloadStore, | ||||
|         private val queue: MutableList<Download> = CopyOnWriteArrayList<Download>()) | ||||
| : List<Download> by queue { | ||||
|     : List<Download> by queue { | ||||
|  | ||||
|     private val statusSubject = PublishSubject.create<Download>() | ||||
|  | ||||
| @@ -42,7 +42,9 @@ class DownloadQueue( | ||||
|     } | ||||
|  | ||||
|     fun remove(chapters: List<Chapter>) { | ||||
|         for (chapter in chapters) { remove(chapter) } | ||||
|         for (chapter in chapters) { | ||||
|             remove(chapter) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun remove(manga: Manga) { | ||||
| @@ -59,7 +61,7 @@ class DownloadQueue( | ||||
|     } | ||||
|  | ||||
|     fun getActiveDownloads(): Observable<Download> = | ||||
|         Observable.from(this).filter { download -> download.status == Download.DOWNLOADING } | ||||
|             Observable.from(this).filter { download -> download.status == Download.DOWNLOADING } | ||||
|  | ||||
|     fun getStatusObservable(): Observable<Download> = statusSubject.onBackpressureBuffer() | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,7 @@ import java.io.InputStream | ||||
| class LibraryMangaUrlFetcher(private val networkFetcher: DataFetcher<InputStream>, | ||||
|                              private val manga: Manga, | ||||
|                              private val file: File) | ||||
| : FileFetcher(file) { | ||||
|     : FileFetcher(file) { | ||||
|  | ||||
|     override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) { | ||||
|         if (!file.exists()) { | ||||
| @@ -69,4 +69,4 @@ class LibraryMangaUrlFetcher(private val networkFetcher: DataFetcher<InputStream | ||||
|         networkFetcher.cancel() | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -38,6 +38,6 @@ class TachiGlideModule : AppGlideModule() { | ||||
|         registry.replace(GlideUrl::class.java, InputStream::class.java, networkFactory) | ||||
|         registry.append(Manga::class.java, InputStream::class.java, MangaModelLoader.Factory()) | ||||
|         registry.append(InputStream::class.java, InputStream::class.java, PassthroughModelLoader | ||||
|             .Factory()) | ||||
|                 .Factory()) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -35,9 +35,9 @@ object LibraryUpdateRanker { | ||||
|      */ | ||||
|     fun lexicographicRanking(): Comparator<Manga> { | ||||
|         return Comparator { mangaFirst: Manga, | ||||
|                                    mangaSecond: Manga -> | ||||
|                             mangaSecond: Manga -> | ||||
|             compareValues(mangaFirst.title, mangaSecond.title) | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -312,7 +312,7 @@ class LibraryUpdateService( | ||||
|                             .filter { pair -> pair.first.isNotEmpty() } | ||||
|                             .doOnNext { | ||||
|                                 if (downloadNew && (categoriesToDownload.isEmpty() || | ||||
|                                         manga.category in categoriesToDownload)) { | ||||
|                                                 manga.category in categoriesToDownload)) { | ||||
|  | ||||
|                                     downloadChapters(manga, it.first) | ||||
|                                     hasDownloads = true | ||||
| @@ -321,8 +321,8 @@ class LibraryUpdateService( | ||||
|                             // Convert to the manga that contains new chapters. | ||||
|                             .map { | ||||
|                                 Pair( | ||||
|                                     manga, | ||||
|                                     (it.first.sortedByDescending { ch -> ch.source_order }.toTypedArray()) | ||||
|                                         manga, | ||||
|                                         (it.first.sortedByDescending { ch -> ch.source_order }.toTypedArray()) | ||||
|                                 ) | ||||
|                             } | ||||
|                 } | ||||
| @@ -573,7 +573,7 @@ class LibraryUpdateService( | ||||
|  | ||||
|         var description = resources.getQuantityString(R.plurals.notification_chapters, chapters.size, chaptersDescription) | ||||
|         if (shouldTruncate) { | ||||
|            description += " ${resources.getString(R.string.notification_and_n_more, (chapterNumbers.size - (NOTIF_MAX_CHAPTERS - 1)))}" | ||||
|             description += " ${resources.getString(R.string.notification_and_n_more, (chapterNumbers.size - (NOTIF_MAX_CHAPTERS - 1)))}" | ||||
|         } | ||||
|  | ||||
|         return description | ||||
|   | ||||
| @@ -393,11 +393,11 @@ class NotificationReceiver : BroadcastReceiver() { | ||||
|          */ | ||||
|         internal fun openChapterPendingActivity(context: Context, manga: Manga, groupId: Int): PendingIntent { | ||||
|             val newIntent = | ||||
|                 Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_MANGA) | ||||
|                     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) | ||||
|                     .putExtra(MangaController.MANGA_EXTRA, manga.id) | ||||
|                     .putExtra("notificationId", manga.id.hashCode()) | ||||
|                     .putExtra("groupId", groupId) | ||||
|                     Intent(context, MainActivity::class.java).setAction(MainActivity.SHORTCUT_MANGA) | ||||
|                             .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) | ||||
|                             .putExtra(MangaController.MANGA_EXTRA, manga.id) | ||||
|                             .putExtra("notificationId", manga.id.hashCode()) | ||||
|                             .putExtra("groupId", groupId) | ||||
|             return PendingIntent.getActivity(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT) | ||||
|         } | ||||
|  | ||||
| @@ -408,8 +408,8 @@ class NotificationReceiver : BroadcastReceiver() { | ||||
|          * @param manga manga of chapter | ||||
|          */ | ||||
|         internal fun markAsReadPendingBroadcast(context: Context, manga: Manga, chapters: | ||||
|             Array<Chapter>, groupId: Int): | ||||
|             PendingIntent { | ||||
|         Array<Chapter>, groupId: Int): | ||||
|                 PendingIntent { | ||||
|             val newIntent = Intent(context, NotificationReceiver::class.java).apply { | ||||
|                 action = ACTION_MARK_AS_READ | ||||
|                 putExtra(EXTRA_CHAPTER_URL, chapters.map { it.url }.toTypedArray()) | ||||
|   | ||||
| @@ -137,7 +137,7 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) { | ||||
|             track.status = COMPLETED | ||||
|         } | ||||
|         // If user was using API v1 fetch library_id | ||||
|         if (track.library_id == null || track.library_id!! == 0L){ | ||||
|         if (track.library_id == null || track.library_id!! == 0L) { | ||||
|             return api.findLibManga(track, getUsername().toInt()).flatMap { | ||||
|                 if (it == null) { | ||||
|                     throw Exception("$track not found on user library") | ||||
| @@ -187,7 +187,7 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) { | ||||
|         return api.getCurrentUser().map { (username, scoreType) -> | ||||
|             scorePreference.set(scoreType) | ||||
|             saveCredentials(username.toString(), oauth.access_token) | ||||
|          }.doOnError{ | ||||
|         }.doOnError { | ||||
|             logout() | ||||
|         }.toCompletable() | ||||
|     } | ||||
|   | ||||
| @@ -250,7 +250,8 @@ 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, | ||||
|             date.set(struct["startDate"]["year"].nullInt ?: 0, (struct["startDate"]["month"].nullInt | ||||
|                     ?: 0) - 1, | ||||
|                     struct["startDate"]["day"].nullInt ?: 0) | ||||
|             date.timeInMillis | ||||
|         } catch (_: Exception) { | ||||
|   | ||||
| @@ -23,7 +23,7 @@ class AnilistInterceptor(val anilist: Anilist, private var token: String?) : Int | ||||
|         if (token.isNullOrEmpty()) { | ||||
|             throw Exception("Not authenticated with Anilist") | ||||
|         } | ||||
|         if (oauth == null){ | ||||
|         if (oauth == null) { | ||||
|             oauth = anilist.loadOAuth() | ||||
|         } | ||||
|         // Refresh access token if null or expired. | ||||
| @@ -55,4 +55,4 @@ class AnilistInterceptor(val anilist: Anilist, private var token: String?) : Int | ||||
|         anilist.saveOAuth(oauth) | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -86,7 +86,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI | ||||
|     fun findLibManga(track: Track): Observable<Track?> { | ||||
|         return authClient.newCall(GET(url = listEntryUrl(track.media_id))) | ||||
|                 .asObservable() | ||||
|                 .map {response -> | ||||
|                 .map { response -> | ||||
|                     var libTrack: Track? = null | ||||
|                     response.use { | ||||
|                         if (it.priorResponse?.isRedirect != true) { | ||||
| @@ -96,7 +96,8 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI | ||||
|                                 last_chapter_read = trackForm.select("#add_manga_num_read_chapters").`val`().toInt() | ||||
|                                 total_chapters = trackForm.select("#totalChap").text().toInt() | ||||
|                                 status = trackForm.select("#add_manga_status > option[selected]").`val`().toInt() | ||||
|                                 score = trackForm.select("#add_manga_score > option[selected]").`val`().toFloatOrNull() ?: 0f | ||||
|                                 score = trackForm.select("#add_manga_score > option[selected]").`val`().toFloatOrNull() | ||||
|                                         ?: 0f | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| @@ -158,7 +159,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI | ||||
|     private fun getListUrl(): Observable<String> { | ||||
|         return authClient.newCall(POST(url = exportListUrl(), body = exportPostBody())) | ||||
|                 .asObservable() | ||||
|                 .map {response -> | ||||
|                 .map { response -> | ||||
|                     baseUrl + Jsoup.parse(response.consumeBody()) | ||||
|                             .select("div.goodresult") | ||||
|                             .select("a") | ||||
| @@ -233,7 +234,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI | ||||
|                 .toString() | ||||
|  | ||||
|         private fun addUrl() = Uri.parse(baseModifyListUrl).buildUpon() | ||||
|                 .appendPath( "add.json") | ||||
|                 .appendPath("add.json") | ||||
|                 .toString() | ||||
|  | ||||
|         private fun listEntryUrl(mediaId: Int) = Uri.parse(baseModifyListUrl).buildUpon() | ||||
| @@ -300,6 +301,6 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI | ||||
|             "Dropped" -> 4 | ||||
|             "Plan to Read" -> 6 | ||||
|             else -> 1 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import okhttp3.Response | ||||
| import okio.Buffer | ||||
| import org.json.JSONObject | ||||
|  | ||||
| class MyAnimeListInterceptor(private val myanimelist: Myanimelist): Interceptor { | ||||
| class MyAnimeListInterceptor(private val myanimelist: Myanimelist) : Interceptor { | ||||
|  | ||||
|     override fun intercept(chain: Interceptor.Chain): Response { | ||||
|         myanimelist.ensureLoggedIn() | ||||
|   | ||||
| @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.updater | ||||
|  | ||||
| abstract class UpdateResult { | ||||
|  | ||||
|     open class NewUpdate<T : Release>(val release: T): UpdateResult() | ||||
|     open class NoNewUpdate: UpdateResult() | ||||
|     open class NewUpdate<T : Release>(val release: T) : UpdateResult() | ||||
|     open class NoNewUpdate : UpdateResult() | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.data.updater.UpdateResult | ||||
|  | ||||
| sealed class DevRepoUpdateResult : UpdateResult() { | ||||
|  | ||||
|     class NewUpdate(release: DevRepoRelease): UpdateResult.NewUpdate<DevRepoRelease>(release) | ||||
|     class NoNewUpdate: UpdateResult.NoNewUpdate() | ||||
|     class NewUpdate(release: DevRepoRelease) : UpdateResult.NewUpdate<DevRepoRelease>(release) | ||||
|     class NoNewUpdate : UpdateResult.NoNewUpdate() | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import eu.kanade.tachiyomi.data.updater.Release | ||||
|  */ | ||||
| class GithubRelease(@SerializedName("tag_name") val version: String, | ||||
|                     @SerializedName("body") override val info: String, | ||||
|                     @SerializedName("assets") private val assets: List<Assets>): Release { | ||||
|                     @SerializedName("assets") private val assets: List<Assets>) : Release { | ||||
|  | ||||
|     /** | ||||
|      * Get download link of latest release from the assets. | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.data.updater.UpdateResult | ||||
|  | ||||
| sealed class GithubUpdateResult : UpdateResult() { | ||||
|  | ||||
|     class NewUpdate(release: GithubRelease): UpdateResult.NewUpdate<GithubRelease>(release) | ||||
|     class NewUpdate(release: GithubRelease) : UpdateResult.NewUpdate<GithubRelease>(release) | ||||
|     class NoNewUpdate : UpdateResult.NoNewUpdate() | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -206,7 +206,7 @@ class ExtensionManager( | ||||
|      * | ||||
|      * @param extension The extension to be updated. | ||||
|      */ | ||||
|     fun updateExtension(extension: Extension.Installed): Observable<InstallStep>  { | ||||
|     fun updateExtension(extension: Extension.Installed): Observable<InstallStep> { | ||||
|         val availableExt = availableExtensions.find { it.pkgName == extension.pkgName } | ||||
|                 ?: return Observable.empty() | ||||
|         return installExtension(availableExt) | ||||
|   | ||||
| @@ -25,7 +25,7 @@ internal class ExtensionGithubApi { | ||||
|         val call = GET("$REPO_URL/index.json") | ||||
|  | ||||
|         return withContext(Dispatchers.IO) { | ||||
|              parseResponse(network.client.newCall(call).await()) | ||||
|             parseResponse(network.client.newCall(call).await()) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -31,12 +31,13 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : | ||||
|     /** | ||||
|      * Returns the intent filter this receiver should subscribe to. | ||||
|      */ | ||||
|     private val filter get() = IntentFilter().apply { | ||||
|         addAction(Intent.ACTION_PACKAGE_ADDED) | ||||
|         addAction(Intent.ACTION_PACKAGE_REPLACED) | ||||
|         addAction(Intent.ACTION_PACKAGE_REMOVED) | ||||
|         addDataScheme("package") | ||||
|     } | ||||
|     private val filter | ||||
|         get() = IntentFilter().apply { | ||||
|             addAction(Intent.ACTION_PACKAGE_ADDED) | ||||
|             addAction(Intent.ACTION_PACKAGE_REPLACED) | ||||
|             addAction(Intent.ACTION_PACKAGE_REMOVED) | ||||
|             addDataScheme("package") | ||||
|         } | ||||
|  | ||||
|     /** | ||||
|      * Called when one of the events of the [filter] is received. When the package is an extension, | ||||
| @@ -61,7 +62,8 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : | ||||
|                     when (result) { | ||||
|                         is LoadResult.Success -> listener.onExtensionUpdated(result.extension) | ||||
|                         // Not needed as a package can't be upgraded if the signature is different | ||||
|                         is LoadResult.Untrusted -> {} | ||||
|                         is LoadResult.Untrusted -> { | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @@ -92,8 +94,8 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : | ||||
|      * @param intent The intent containing the package name of the extension. | ||||
|      */ | ||||
|     private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult { | ||||
|         val pkgName = getPackageNameFromIntent(intent) ?: | ||||
|                 return LoadResult.Error("Package name not found") | ||||
|         val pkgName = getPackageNameFromIntent(intent) | ||||
|                 ?: return LoadResult.Error("Package name not found") | ||||
|         return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { ExtensionLoader.loadExtensionFromPkgName(context, pkgName) }.await() | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -95,8 +95,8 @@ internal object ExtensionLoader { | ||||
|             return LoadResult.Error(error) | ||||
|         } | ||||
|  | ||||
|         val extName = pkgManager.getApplicationLabel(appInfo)?.toString() | ||||
|             .orEmpty().substringAfter("Tachiyomi: ") | ||||
|         val extName = pkgManager.getApplicationLabel(appInfo).toString() | ||||
|                 .orEmpty().substringAfter("Tachiyomi: ") | ||||
|         val versionName = pkgInfo.versionName | ||||
|         val versionCode = pkgInfo.versionCode | ||||
|  | ||||
|   | ||||
| @@ -44,9 +44,9 @@ class AndroidCookieJar : CookieJar { | ||||
|         } | ||||
|  | ||||
|         cookies.split(";") | ||||
|             .map { it.substringBefore("=") } | ||||
|             .filterNames() | ||||
|             .onEach { manager.setCookie(urlString, "$it=;Max-Age=$maxAge") } | ||||
|                 .map { it.substringBefore("=") } | ||||
|                 .filterNames() | ||||
|                 .onEach { manager.setCookie(urlString, "$it=;Max-Age=$maxAge") } | ||||
|     } | ||||
|  | ||||
|     fun removeAll() { | ||||
|   | ||||
| @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.network | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.content.Context | ||||
| import android.os.Build | ||||
| import android.os.Handler | ||||
| import android.os.Looper | ||||
| import android.webkit.WebResourceRequest | ||||
| @@ -104,7 +103,7 @@ class CloudflareInterceptor(private val context: Context) : Interceptor { | ||||
|  | ||||
|                     // HTTP error codes are only received since M | ||||
|                     if (WebViewFeature.isFeatureSupported(WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR) && | ||||
|                         url == origRequestUrl && !challengeFound | ||||
|                             url == origRequestUrl && !challengeFound | ||||
|                     ) { | ||||
|                         // The first request didn't return the challenge, abort. | ||||
|                         latch.countDown() | ||||
|   | ||||
| @@ -129,17 +129,17 @@ class LocalSource(private val context: Context) : CatalogueSource { | ||||
|                 .filter { it.extension.equals("json") } | ||||
|                 .firstOrNull() | ||||
|                 ?.apply { | ||||
|             val json = Gson().fromJson(Scanner(this).useDelimiter("\\Z").next(), JsonObject::class.java) | ||||
|             manga.title = json["title"]?.asString ?: manga.title | ||||
|             manga.author = json["author"]?.asString ?: manga.author | ||||
|             manga.artist = json["artist"]?.asString ?: manga.artist | ||||
|             manga.description = json["description"]?.asString ?: manga.description | ||||
|             manga.genre = json["genre"]?.asJsonArray | ||||
|                     ?.map { it.asString } | ||||
|                     ?.joinToString(", ") | ||||
|                     ?: manga.genre | ||||
|             manga.status = json["status"]?.asInt ?: manga.status | ||||
|         } | ||||
|                     val json = Gson().fromJson(Scanner(this).useDelimiter("\\Z").next(), JsonObject::class.java) | ||||
|                     manga.title = json["title"]?.asString ?: manga.title | ||||
|                     manga.author = json["author"]?.asString ?: manga.author | ||||
|                     manga.artist = json["artist"]?.asString ?: manga.artist | ||||
|                     manga.description = json["description"]?.asString ?: manga.description | ||||
|                     manga.genre = json["genre"]?.asJsonArray | ||||
|                             ?.map { it.asString } | ||||
|                             ?.joinToString(", ") | ||||
|                             ?: manga.genre | ||||
|                     manga.status = json["status"]?.asInt ?: manga.status | ||||
|                 } | ||||
|         return Observable.just(manga) | ||||
|     } | ||||
|  | ||||
| @@ -210,34 +210,34 @@ class LocalSource(private val context: Context) : CatalogueSource { | ||||
|         return when (format) { | ||||
|             is Format.Directory -> { | ||||
|                 val entry = format.file.listFiles() | ||||
|                     .sortedWith(Comparator<File> { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }) | ||||
|                     .find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } } | ||||
|                         .sortedWith(Comparator<File> { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }) | ||||
|                         .find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } } | ||||
|  | ||||
|                 entry?.let { updateCover(context, manga, it.inputStream())} | ||||
|                 entry?.let { updateCover(context, manga, it.inputStream()) } | ||||
|             } | ||||
|             is Format.Zip -> { | ||||
|                 ZipFile(format.file).use { zip -> | ||||
|                     val entry = zip.entries().toList() | ||||
|                         .sortedWith(Comparator<ZipEntry> { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }) | ||||
|                         .find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } | ||||
|                             .sortedWith(Comparator<ZipEntry> { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }) | ||||
|                             .find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } | ||||
|  | ||||
|                     entry?.let { updateCover(context, manga, zip.getInputStream(it) )} | ||||
|                     entry?.let { updateCover(context, manga, zip.getInputStream(it)) } | ||||
|                 } | ||||
|             } | ||||
|             is Format.Rar -> { | ||||
|                 Archive(format.file).use { archive -> | ||||
|                     val entry = archive.fileHeaders | ||||
|                         .sortedWith(Comparator<FileHeader> { f1, f2 -> f1.fileNameString.compareToCaseInsensitiveNaturalOrder(f2.fileNameString) }) | ||||
|                         .find { !it.isDirectory && ImageUtil.isImage(it.fileNameString) { archive.getInputStream(it) } } | ||||
|                             .sortedWith(Comparator<FileHeader> { f1, f2 -> f1.fileNameString.compareToCaseInsensitiveNaturalOrder(f2.fileNameString) }) | ||||
|                             .find { !it.isDirectory && ImageUtil.isImage(it.fileNameString) { archive.getInputStream(it) } } | ||||
|  | ||||
|                     entry?.let { updateCover(context, manga, archive.getInputStream(it) )} | ||||
|                     entry?.let { updateCover(context, manga, archive.getInputStream(it)) } | ||||
|                 } | ||||
|             } | ||||
|             is Format.Epub -> { | ||||
|                 EpubFile(format.file).use { epub -> | ||||
|                     val entry = epub.getImagesFromPages() | ||||
|                         .firstOrNull() | ||||
|                         ?.let { epub.getEntry(it) } | ||||
|                             .firstOrNull() | ||||
|                             ?.let { epub.getEntry(it) } | ||||
|  | ||||
|                     entry?.let { updateCover(context, manga, epub.getInputStream(it)) } | ||||
|                 } | ||||
| @@ -252,7 +252,7 @@ class LocalSource(private val context: Context) : CatalogueSource { | ||||
|     sealed class Format { | ||||
|         data class Directory(val file: File) : Format() | ||||
|         data class Zip(val file: File) : Format() | ||||
|         data class Rar(val file: File): Format() | ||||
|         data class Rar(val file: File) : Format() | ||||
|         data class Epub(val file: File) : Format() | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -17,7 +17,8 @@ sealed class Filter<T>(val name: String, var state: T) { | ||||
|             const val STATE_EXCLUDE = 2 | ||||
|         } | ||||
|     } | ||||
|     abstract class Group<V>(name: String, state: List<V>): Filter<List<V>>(name, state) | ||||
|  | ||||
|     abstract class Group<V>(name: String, state: List<V>) : Filter<List<V>>(name, state) | ||||
|  | ||||
|     abstract class Sort(name: String, val values: Array<String>, state: Selection? = null) | ||||
|         : Filter<Sort.Selection?>(name, state) { | ||||
| @@ -37,4 +38,4 @@ sealed class Filter<T>(val name: String, var state: T) { | ||||
|         return result | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -14,15 +14,20 @@ open class Page( | ||||
|     val number: Int | ||||
|         get() = index + 1 | ||||
|  | ||||
|     @Transient @Volatile var status: Int = 0 | ||||
|     @Transient | ||||
|     @Volatile | ||||
|     var status: Int = 0 | ||||
|         set(value) { | ||||
|             field = value | ||||
|             statusSubject?.onNext(value) | ||||
|         } | ||||
|  | ||||
|     @Transient @Volatile var progress: Int = 0 | ||||
|     @Transient | ||||
|     @Volatile | ||||
|     var progress: Int = 0 | ||||
|  | ||||
|     @Transient private var statusSubject: Subject<Int, Int>? = null | ||||
|     @Transient | ||||
|     private var statusSubject: Subject<Int, Int>? = null | ||||
|  | ||||
|     override fun update(bytesRead: Long, contentLength: Long, done: Boolean) { | ||||
|         progress = if (contentLength > 0) { | ||||
|   | ||||
| @@ -10,6 +10,6 @@ class SChapterImpl : SChapter { | ||||
|  | ||||
|     override var chapter_number: Float = -1f | ||||
|  | ||||
|     override  var scanlator: String? = null | ||||
|     override var scanlator: String? = null | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -69,7 +69,7 @@ abstract class HttpSource : CatalogueSource { | ||||
|     /** | ||||
|      * Headers builder for requests. Implementations can override this method for custom headers. | ||||
|      */ | ||||
|     open protected fun headersBuilder() = Headers.Builder().apply { | ||||
|     protected open fun headersBuilder() = Headers.Builder().apply { | ||||
|         add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)") | ||||
|     } | ||||
|  | ||||
| @@ -97,14 +97,14 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * | ||||
|      * @param page the page number to retrieve. | ||||
|      */ | ||||
|     abstract protected fun popularMangaRequest(page: Int): Request | ||||
|     protected abstract fun popularMangaRequest(page: Int): Request | ||||
|  | ||||
|     /** | ||||
|      * Parses the response from the site and returns a [MangasPage] object. | ||||
|      * | ||||
|      * @param response the response from the site. | ||||
|      */ | ||||
|     abstract protected fun popularMangaParse(response: Response): MangasPage | ||||
|     protected abstract fun popularMangaParse(response: Response): MangasPage | ||||
|  | ||||
|     /** | ||||
|      * Returns an observable containing a page with a list of manga. Normally it's not needed to | ||||
| @@ -129,14 +129,14 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * @param query the search query. | ||||
|      * @param filters the list of filters to apply. | ||||
|      */ | ||||
|     abstract protected fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request | ||||
|     protected abstract fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request | ||||
|  | ||||
|     /** | ||||
|      * Parses the response from the site and returns a [MangasPage] object. | ||||
|      * | ||||
|      * @param response the response from the site. | ||||
|      */ | ||||
|     abstract protected fun searchMangaParse(response: Response): MangasPage | ||||
|     protected abstract fun searchMangaParse(response: Response): MangasPage | ||||
|  | ||||
|     /** | ||||
|      * Returns an observable containing a page with a list of latest manga updates. | ||||
| @@ -156,14 +156,14 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * | ||||
|      * @param page the page number to retrieve. | ||||
|      */ | ||||
|     abstract protected fun latestUpdatesRequest(page: Int): Request | ||||
|     protected abstract fun latestUpdatesRequest(page: Int): Request | ||||
|  | ||||
|     /** | ||||
|      * Parses the response from the site and returns a [MangasPage] object. | ||||
|      * | ||||
|      * @param response the response from the site. | ||||
|      */ | ||||
|     abstract protected fun latestUpdatesParse(response: Response): MangasPage | ||||
|     protected abstract fun latestUpdatesParse(response: Response): MangasPage | ||||
|  | ||||
|     /** | ||||
|      * Returns an observable with the updated details for a manga. Normally it's not needed to | ||||
| @@ -194,7 +194,7 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * | ||||
|      * @param response the response from the site. | ||||
|      */ | ||||
|     abstract protected fun mangaDetailsParse(response: Response): SManga | ||||
|     protected abstract fun mangaDetailsParse(response: Response): SManga | ||||
|  | ||||
|     /** | ||||
|      * Returns an observable with the updated chapter list for a manga. Normally it's not needed to | ||||
| @@ -220,7 +220,7 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * | ||||
|      * @param manga the manga to look for chapters. | ||||
|      */ | ||||
|     open protected fun chapterListRequest(manga: SManga): Request { | ||||
|     protected open fun chapterListRequest(manga: SManga): Request { | ||||
|         return GET(baseUrl + manga.url, headers) | ||||
|     } | ||||
|  | ||||
| @@ -229,7 +229,7 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * | ||||
|      * @param response the response from the site. | ||||
|      */ | ||||
|     abstract protected fun chapterListParse(response: Response): List<SChapter> | ||||
|     protected abstract fun chapterListParse(response: Response): List<SChapter> | ||||
|  | ||||
|     /** | ||||
|      * Returns an observable with the page list for a chapter. | ||||
| @@ -250,7 +250,7 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * | ||||
|      * @param chapter the chapter whose page list has to be fetched. | ||||
|      */ | ||||
|     open protected fun pageListRequest(chapter: SChapter): Request { | ||||
|     protected open fun pageListRequest(chapter: SChapter): Request { | ||||
|         return GET(baseUrl + chapter.url, headers) | ||||
|     } | ||||
|  | ||||
| @@ -259,7 +259,7 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * | ||||
|      * @param response the response from the site. | ||||
|      */ | ||||
|     abstract protected fun pageListParse(response: Response): List<Page> | ||||
|     protected abstract fun pageListParse(response: Response): List<Page> | ||||
|  | ||||
|     /** | ||||
|      * Returns an observable with the page containing the source url of the image. If there's any | ||||
| @@ -279,7 +279,7 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * | ||||
|      * @param page the chapter whose page list has to be fetched | ||||
|      */ | ||||
|     open protected fun imageUrlRequest(page: Page): Request { | ||||
|     protected open fun imageUrlRequest(page: Page): Request { | ||||
|         return GET(page.url, headers) | ||||
|     } | ||||
|  | ||||
| @@ -288,7 +288,7 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * | ||||
|      * @param response the response from the site. | ||||
|      */ | ||||
|     abstract protected fun imageUrlParse(response: Response): String | ||||
|     protected abstract fun imageUrlParse(response: Response): String | ||||
|  | ||||
|     /** | ||||
|      * Returns an observable with the response of the source image. | ||||
| @@ -306,7 +306,7 @@ abstract class HttpSource : CatalogueSource { | ||||
|      * | ||||
|      * @param page the chapter whose page list has to be fetched | ||||
|      */ | ||||
|     open protected fun imageRequest(page: Page): Request { | ||||
|     protected open fun imageRequest(page: Page): Request { | ||||
|         return GET(page.imageUrl!!, headers) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -6,10 +6,10 @@ import rx.Observable | ||||
| fun HttpSource.getImageUrl(page: Page): Observable<Page> { | ||||
|     page.status = Page.LOAD_PAGE | ||||
|     return fetchImageUrl(page) | ||||
|         .doOnError { page.status = Page.ERROR } | ||||
|         .onErrorReturn { null } | ||||
|         .doOnNext { page.imageUrl = it } | ||||
|         .map { page } | ||||
|             .doOnError { page.status = Page.ERROR } | ||||
|             .onErrorReturn { null } | ||||
|             .doOnNext { page.imageUrl = it } | ||||
|             .map { page } | ||||
| } | ||||
|  | ||||
| fun HttpSource.fetchAllImageUrlsFromPageList(pages: List<Page>): Observable<Page> { | ||||
|   | ||||
| @@ -36,7 +36,7 @@ abstract class ParsedHttpSource : HttpSource() { | ||||
|     /** | ||||
|      * Returns the Jsoup selector that returns a list of [Element] corresponding to each manga. | ||||
|      */ | ||||
|     abstract protected fun popularMangaSelector(): String | ||||
|     protected abstract fun popularMangaSelector(): String | ||||
|  | ||||
|     /** | ||||
|      * Returns a manga from the given [element]. Most sites only show the title and the url, it's | ||||
| @@ -44,13 +44,13 @@ abstract class ParsedHttpSource : HttpSource() { | ||||
|      * | ||||
|      * @param element an element obtained from [popularMangaSelector]. | ||||
|      */ | ||||
|     abstract protected fun popularMangaFromElement(element: Element): SManga | ||||
|     protected abstract fun popularMangaFromElement(element: Element): SManga | ||||
|  | ||||
|     /** | ||||
|      * Returns the Jsoup selector that returns the <a> tag linking to the next page, or null if | ||||
|      * there's no next page. | ||||
|      */ | ||||
|     abstract protected fun popularMangaNextPageSelector(): String? | ||||
|     protected abstract fun popularMangaNextPageSelector(): String? | ||||
|  | ||||
|     /** | ||||
|      * Parses the response from the site and returns a [MangasPage] object. | ||||
| @@ -74,7 +74,7 @@ abstract class ParsedHttpSource : HttpSource() { | ||||
|     /** | ||||
|      * Returns the Jsoup selector that returns a list of [Element] corresponding to each manga. | ||||
|      */ | ||||
|     abstract protected fun searchMangaSelector(): String | ||||
|     protected abstract fun searchMangaSelector(): String | ||||
|  | ||||
|     /** | ||||
|      * Returns a manga from the given [element]. Most sites only show the title and the url, it's | ||||
| @@ -82,13 +82,13 @@ abstract class ParsedHttpSource : HttpSource() { | ||||
|      * | ||||
|      * @param element an element obtained from [searchMangaSelector]. | ||||
|      */ | ||||
|     abstract protected fun searchMangaFromElement(element: Element): SManga | ||||
|     protected abstract fun searchMangaFromElement(element: Element): SManga | ||||
|  | ||||
|     /** | ||||
|      * Returns the Jsoup selector that returns the <a> tag linking to the next page, or null if | ||||
|      * there's no next page. | ||||
|      */ | ||||
|     abstract protected fun searchMangaNextPageSelector(): String? | ||||
|     protected abstract fun searchMangaNextPageSelector(): String? | ||||
|  | ||||
|     /** | ||||
|      * Parses the response from the site and returns a [MangasPage] object. | ||||
| @@ -112,7 +112,7 @@ abstract class ParsedHttpSource : HttpSource() { | ||||
|     /** | ||||
|      * Returns the Jsoup selector that returns a list of [Element] corresponding to each manga. | ||||
|      */ | ||||
|     abstract protected fun latestUpdatesSelector(): String | ||||
|     protected abstract fun latestUpdatesSelector(): String | ||||
|  | ||||
|     /** | ||||
|      * Returns a manga from the given [element]. Most sites only show the title and the url, it's | ||||
| @@ -120,13 +120,13 @@ abstract class ParsedHttpSource : HttpSource() { | ||||
|      * | ||||
|      * @param element an element obtained from [latestUpdatesSelector]. | ||||
|      */ | ||||
|     abstract protected fun latestUpdatesFromElement(element: Element): SManga | ||||
|     protected abstract fun latestUpdatesFromElement(element: Element): SManga | ||||
|  | ||||
|     /** | ||||
|      * Returns the Jsoup selector that returns the <a> tag linking to the next page, or null if | ||||
|      * there's no next page. | ||||
|      */ | ||||
|     abstract protected fun latestUpdatesNextPageSelector(): String? | ||||
|     protected abstract fun latestUpdatesNextPageSelector(): String? | ||||
|  | ||||
|     /** | ||||
|      * Parses the response from the site and returns the details of a manga. | ||||
| @@ -142,7 +142,7 @@ abstract class ParsedHttpSource : HttpSource() { | ||||
|      * | ||||
|      * @param document the parsed document. | ||||
|      */ | ||||
|     abstract protected fun mangaDetailsParse(document: Document): SManga | ||||
|     protected abstract fun mangaDetailsParse(document: Document): SManga | ||||
|  | ||||
|     /** | ||||
|      * Parses the response from the site and returns a list of chapters. | ||||
| @@ -157,14 +157,14 @@ abstract class ParsedHttpSource : HttpSource() { | ||||
|     /** | ||||
|      * Returns the Jsoup selector that returns a list of [Element] corresponding to each chapter. | ||||
|      */ | ||||
|     abstract protected fun chapterListSelector(): String | ||||
|     protected abstract fun chapterListSelector(): String | ||||
|  | ||||
|     /** | ||||
|      * Returns a chapter from the given element. | ||||
|      * | ||||
|      * @param element an element obtained from [chapterListSelector]. | ||||
|      */ | ||||
|     abstract protected fun chapterFromElement(element: Element): SChapter | ||||
|     protected abstract fun chapterFromElement(element: Element): SChapter | ||||
|  | ||||
|     /** | ||||
|      * Parses the response from the site and returns the page list. | ||||
| @@ -180,7 +180,7 @@ abstract class ParsedHttpSource : HttpSource() { | ||||
|      * | ||||
|      * @param document the parsed document. | ||||
|      */ | ||||
|     abstract protected fun pageListParse(document: Document): List<Page> | ||||
|     protected abstract fun pageListParse(document: Document): List<Page> | ||||
|  | ||||
|     /** | ||||
|      * Parse the response from the site and returns the absolute url to the source image. | ||||
| @@ -196,5 +196,5 @@ abstract class ParsedHttpSource : HttpSource() { | ||||
|      * | ||||
|      * @param document the parsed document. | ||||
|      */ | ||||
|     abstract protected fun imageUrlParse(document: Document): String | ||||
|     protected abstract fun imageUrlParse(document: Document): String | ||||
| } | ||||
|   | ||||
| @@ -55,7 +55,7 @@ abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateContr | ||||
|  | ||||
|     abstract fun inflateView(inflater: LayoutInflater, container: ViewGroup): View | ||||
|  | ||||
|     open fun onViewCreated(view: View) { } | ||||
|     open fun onViewCreated(view: View) {} | ||||
|  | ||||
|     override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { | ||||
|         if (type.isEnter) { | ||||
| @@ -90,6 +90,7 @@ abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateContr | ||||
|      * Issue link: https://issuetracker.google.com/issues/37657375 | ||||
|      */ | ||||
|     var expandActionViewFromInteraction = false | ||||
|  | ||||
|     fun MenuItem.fixExpand(onExpand: ((MenuItem) -> Boolean)? = null, onCollapse: ((MenuItem) -> Boolean)? = null) { | ||||
|         setOnActionExpandListener(object : MenuItem.OnActionExpandListener { | ||||
|             override fun onMenuItemActionExpand(item: MenuItem): Boolean { | ||||
|   | ||||
| @@ -23,8 +23,7 @@ open class BasePresenter<V> : RxPresenter<V>() { | ||||
|      * @param onNext function to execute when the observable emits an item. | ||||
|      * @param onError function to execute when the observable throws an error. | ||||
|      */ | ||||
|     fun <T> Observable<T>.subscribeFirst(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) | ||||
|             = compose(deliverFirst<T>()).subscribe(split(onNext, onError)).apply { add(this) } | ||||
|     fun <T> Observable<T>.subscribeFirst(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) = compose(deliverFirst<T>()).subscribe(split(onNext, onError)).apply { add(this) } | ||||
|  | ||||
|     /** | ||||
|      * Subscribes an observable with [deliverLatestCache] and adds it to the presenter's lifecycle | ||||
| @@ -33,8 +32,7 @@ open class BasePresenter<V> : RxPresenter<V>() { | ||||
|      * @param onNext function to execute when the observable emits an item. | ||||
|      * @param onError function to execute when the observable throws an error. | ||||
|      */ | ||||
|     fun <T> Observable<T>.subscribeLatestCache(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) | ||||
|             = compose(deliverLatestCache<T>()).subscribe(split(onNext, onError)).apply { add(this) } | ||||
|     fun <T> Observable<T>.subscribeLatestCache(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) = compose(deliverLatestCache<T>()).subscribe(split(onNext, onError)).apply { add(this) } | ||||
|  | ||||
|     /** | ||||
|      * Subscribes an observable with [deliverReplay] and adds it to the presenter's lifecycle | ||||
| @@ -43,8 +41,7 @@ open class BasePresenter<V> : RxPresenter<V>() { | ||||
|      * @param onNext function to execute when the observable emits an item. | ||||
|      * @param onError function to execute when the observable throws an error. | ||||
|      */ | ||||
|     fun <T> Observable<T>.subscribeReplay(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) | ||||
|             = compose(deliverReplay<T>()).subscribe(split(onNext, onError)).apply { add(this) } | ||||
|     fun <T> Observable<T>.subscribeReplay(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) = compose(deliverReplay<T>()).subscribe(split(onNext, onError)).apply { add(this) } | ||||
|  | ||||
|     /** | ||||
|      * Subscribes an observable with [DeliverWithView] and adds it to the presenter's lifecycle | ||||
| @@ -53,8 +50,7 @@ open class BasePresenter<V> : RxPresenter<V>() { | ||||
|      * @param onNext function to execute when the observable emits an item. | ||||
|      * @param onError function to execute when the observable throws an error. | ||||
|      */ | ||||
|     fun <T> Observable<T>.subscribeWithView(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) | ||||
|             = compose(DeliverWithView<V, T>(view())).subscribe(split(onNext, onError)).apply { add(this) } | ||||
|     fun <T> Observable<T>.subscribeWithView(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null) = compose(DeliverWithView<V, T>(view())).subscribe(split(onNext, onError)).apply { add(this) } | ||||
|  | ||||
|     /** | ||||
|      * A deliverable that only emits to the view if attached, otherwise the event is ignored. | ||||
|   | ||||
| @@ -192,7 +192,7 @@ class CatalogueController : NucleusController<CataloguePresenter>(), | ||||
|                 .subscribeUntilDestroy { performGlobalSearch(it.queryText().toString()) } | ||||
|     } | ||||
|  | ||||
|     fun performGlobalSearch(query: String){ | ||||
|     fun performGlobalSearch(query: String) { | ||||
|         router.pushController(CatalogueSearchController(query).withFadeTransaction()) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -347,7 +347,8 @@ open class BrowseCatalogueController(bundle: Bundle) : | ||||
|         snack?.dismiss() | ||||
|  | ||||
|         if (catalogue_view != null) { | ||||
|             val message = if (error is NoResultsException) catalogue_view.context.getString(R.string.no_results_found) else (error.message ?: "") | ||||
|             val message = if (error is NoResultsException) catalogue_view.context.getString(R.string.no_results_found) else (error.message | ||||
|                     ?: "") | ||||
|  | ||||
|             snack = catalogue_view.snack(message, Snackbar.LENGTH_INDEFINITE) { | ||||
|                 setAction(R.string.action_retry) { | ||||
| @@ -497,7 +498,7 @@ open class BrowseCatalogueController(bundle: Bundle) : | ||||
|                             0 -> { | ||||
|                                 presenter.changeMangaFavorite(manga) | ||||
|                                 adapter?.notifyItemChanged(position) | ||||
|                                 activity?.toast(activity?.getString(R.string.manga_removed_library)) | ||||
|                                 activity.toast(activity.getString(R.string.manga_removed_library)) | ||||
|                             } | ||||
|                         } | ||||
|                     }.show() | ||||
| @@ -522,7 +523,7 @@ open class BrowseCatalogueController(bundle: Bundle) : | ||||
|                             .showDialog(router) | ||||
|                 } | ||||
|             } | ||||
|             activity?.toast(activity?.getString(R.string.manga_added_library)) | ||||
|             activity.toast(activity.getString(R.string.manga_added_library)) | ||||
|         } | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -74,11 +74,12 @@ open class CatalogueSearchPresenter( | ||||
|     override fun onCreate(savedState: Bundle?) { | ||||
|         super.onCreate(savedState) | ||||
|  | ||||
|         extensionFilter = savedState?.getString(CatalogueSearchPresenter::extensionFilter.name) ?: | ||||
|                           initialExtensionFilter | ||||
|         extensionFilter = savedState?.getString(CatalogueSearchPresenter::extensionFilter.name) | ||||
|                 ?: initialExtensionFilter | ||||
|  | ||||
|         // Perform a search with previous or initial state | ||||
|         search(savedState?.getString(BrowseCataloguePresenter::query.name) ?: initialQuery.orEmpty()) | ||||
|         search(savedState?.getString(BrowseCataloguePresenter::query.name) | ||||
|                 ?: initialQuery.orEmpty()) | ||||
|     } | ||||
|  | ||||
|     override fun onDestroy() { | ||||
| @@ -117,10 +118,10 @@ open class CatalogueSearchPresenter( | ||||
|         } | ||||
|  | ||||
|         val filterSources = extensionManager.installedExtensions | ||||
|             .filter { it.pkgName == filter } | ||||
|             .flatMap { it.sources } | ||||
|             .filter { it in enabledSources } | ||||
|             .filterIsInstance<CatalogueSource>() | ||||
|                 .filter { it.pkgName == filter } | ||||
|                 .flatMap { it.sources } | ||||
|                 .filter { it in enabledSources } | ||||
|                 .filterIsInstance<CatalogueSource>() | ||||
|  | ||||
|         if (filterSources.isEmpty()) { | ||||
|             return enabledSources | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import rx.schedulers.Schedulers | ||||
| /** | ||||
|  * LatestUpdatesPager inherited from the general Pager. | ||||
|  */ | ||||
| class LatestUpdatesPager(val source: CatalogueSource): Pager() { | ||||
| class LatestUpdatesPager(val source: CatalogueSource) : Pager() { | ||||
|  | ||||
|     override fun requestNext(): Observable<MangasPage> { | ||||
|         return source.fetchLatestUpdates(currentPage) | ||||
|   | ||||
| @@ -37,7 +37,7 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie | ||||
|  | ||||
|         // Update circle letter image. | ||||
|         itemView.post { | ||||
|             image.setImageDrawable(image.getRound(category.name.take(1).toUpperCase(),false)) | ||||
|             image.setImageDrawable(image.getRound(category.name.take(1).toUpperCase(), false)) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -126,11 +126,11 @@ open class ExtensionController : NucleusController<ExtensionPresenter>(), | ||||
|         } | ||||
|  | ||||
|         searchView.queryTextChanges() | ||||
|             .filter { router.backstack.lastOrNull()?.controller() == this } | ||||
|             .subscribeUntilDestroy { | ||||
|                 query = it.toString() | ||||
|                 drawExtensions() | ||||
|             } | ||||
|                 .filter { router.backstack.lastOrNull()?.controller() == this } | ||||
|                 .subscribeUntilDestroy { | ||||
|                     query = it.toString() | ||||
|                     drawExtensions() | ||||
|                 } | ||||
|  | ||||
|         // Fixes problem with the overflow icon showing up in lieu of search | ||||
|         searchItem.fixExpand(onExpand = { invalidateMenuOnExpand() }) | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
|  | ||||
| class ExtensionFilterController: SettingsController() { | ||||
| class ExtensionFilterController : SettingsController() { | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|         titleRes = R.string.action_filter | ||||
|   | ||||
| @@ -46,7 +46,7 @@ open class ExtensionPresenter( | ||||
|                 .startWith(emptyList<Extension.Available>()) | ||||
|  | ||||
|         return Observable.combineLatest(installedObservable, untrustedObservable, availableObservable) | ||||
|                 { installed, untrusted, available -> Triple(installed, untrusted, available) } | ||||
|         { installed, untrusted, available -> Triple(installed, untrusted, available) } | ||||
|                 .debounce(100, TimeUnit.MILLISECONDS) | ||||
|                 .map(::toItems) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
| @@ -67,9 +67,11 @@ open class ExtensionPresenter( | ||||
|         val untrustedSorted = untrusted.sortedBy { it.pkgName } | ||||
|         val availableSorted = available | ||||
|                 // Filter out already installed extensions and disabled languages | ||||
|                 .filter { avail -> installed.none { it.pkgName == avail.pkgName } | ||||
|                         && untrusted.none { it.pkgName == avail.pkgName } | ||||
|                         && (avail.lang in activeLangs || avail.lang == "all")} | ||||
|                 .filter { avail -> | ||||
|                     installed.none { it.pkgName == avail.pkgName } | ||||
|                             && untrusted.none { it.pkgName == avail.pkgName } | ||||
|                             && (avail.lang in activeLangs || avail.lang == "all") | ||||
|                 } | ||||
|                 .sortedBy { it.pkgName } | ||||
|  | ||||
|         if (updatesSorted.isNotEmpty()) { | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.ui.base.controller.DialogController | ||||
|  | ||||
| class ExtensionTrustDialog<T>(bundle: Bundle? = null) : DialogController(bundle) | ||||
|         where T : Controller, T: ExtensionTrustDialog.Listener { | ||||
|         where T : Controller, T : ExtensionTrustDialog.Listener { | ||||
|  | ||||
|     constructor(target: T, signatureHash: String, pkgName: String) : this(Bundle().apply { | ||||
|         putString(SIGNATURE_KEY, signatureHash) | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.ui.base.controller.DialogController | ||||
| import eu.kanade.tachiyomi.widget.DialogCheckboxView | ||||
|  | ||||
| class DeleteLibraryMangasDialog<T>(bundle: Bundle? = null) : | ||||
|         DialogController(bundle) where T : Controller, T: DeleteLibraryMangasDialog.Listener { | ||||
|         DialogController(bundle) where T : Controller, T : DeleteLibraryMangasDialog.Listener { | ||||
|  | ||||
|     private var mangas = emptyList<Manga>() | ||||
|  | ||||
| @@ -40,4 +40,4 @@ class DeleteLibraryMangasDialog<T>(bundle: Bundle? = null) : | ||||
|     interface Listener { | ||||
|         fun deleteMangasFromLibrary(mangas: List<Manga>, deleteChapters: Boolean) | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -43,7 +43,7 @@ class LibraryGridHolder( | ||||
|             text = item.downloadCount.toString() | ||||
|         } | ||||
|         //set local visibility if its local manga | ||||
|         local_text.visibility = if(item.manga.source == LocalSource.ID) View.VISIBLE else View.GONE | ||||
|         local_text.visibility = if (item.manga.source == LocalSource.ID) View.VISIBLE else View.GONE | ||||
|  | ||||
|         // Update the cover. | ||||
|         GlideApp.with(view.context).clear(thumbnail) | ||||
|   | ||||
| @@ -64,15 +64,15 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference | ||||
|      */ | ||||
|     override fun filter(constraint: String): Boolean { | ||||
|         return manga.title.contains(constraint, true) || | ||||
|             (manga.author?.contains(constraint, true) ?: false) || | ||||
|             (manga.artist?.contains(constraint, true) ?: false) || | ||||
|             sourceManager.getOrStub(manga.source).name.contains(constraint, true) || | ||||
|             if (constraint.contains(",")) { | ||||
|                 val genres = manga.genre?.split(", ") | ||||
|                 constraint.split(",").all { containsGenre(it.trim(), genres) } | ||||
|             } else { | ||||
|                 containsGenre(constraint, manga.genre?.split(", ")) | ||||
|             } | ||||
|                 (manga.author?.contains(constraint, true) ?: false) || | ||||
|                 (manga.artist?.contains(constraint, true) ?: false) || | ||||
|                 sourceManager.getOrStub(manga.source).name.contains(constraint, true) || | ||||
|                 if (constraint.contains(",")) { | ||||
|                     val genres = manga.genre?.split(", ") | ||||
|                     constraint.split(",").all { containsGenre(it.trim(), genres) } | ||||
|                 } else { | ||||
|                     containsGenre(constraint, manga.genre?.split(", ")) | ||||
|                 } | ||||
|     } | ||||
|  | ||||
|     private fun containsGenre(tag: String, genres: List<String>?): Boolean { | ||||
|   | ||||
| @@ -89,14 +89,14 @@ class LibraryPresenter( | ||||
|     fun subscribeLibrary() { | ||||
|         if (librarySubscription.isNullOrUnsubscribed()) { | ||||
|             librarySubscription = getLibraryObservable() | ||||
|                     .combineLatest(downloadTriggerRelay.observeOn(Schedulers.io())) { | ||||
|                         lib, _ -> lib.apply { setDownloadCount(mangaMap) } | ||||
|                     .combineLatest(downloadTriggerRelay.observeOn(Schedulers.io())) { lib, _ -> | ||||
|                         lib.apply { setDownloadCount(mangaMap) } | ||||
|                     } | ||||
|                     .combineLatest(filterTriggerRelay.observeOn(Schedulers.io())) { | ||||
|                         lib, _ -> lib.copy(mangaMap = applyFilters(lib.mangaMap)) | ||||
|                     .combineLatest(filterTriggerRelay.observeOn(Schedulers.io())) { lib, _ -> | ||||
|                         lib.copy(mangaMap = applyFilters(lib.mangaMap)) | ||||
|                     } | ||||
|                     .combineLatest(sortTriggerRelay.observeOn(Schedulers.io())) { | ||||
|                         lib, _ -> lib.copy(mangaMap = applySort(lib.mangaMap)) | ||||
|                     .combineLatest(sortTriggerRelay.observeOn(Schedulers.io())) { lib, _ -> | ||||
|                         lib.copy(mangaMap = applySort(lib.mangaMap)) | ||||
|                     } | ||||
|                     .observeOn(AndroidSchedulers.mainThread()) | ||||
|                     .subscribeLatestCache({ view, (categories, mangaMap) -> | ||||
| @@ -117,7 +117,7 @@ class LibraryPresenter( | ||||
|  | ||||
|         val filterCompleted = preferences.filterCompleted().getOrDefault() | ||||
|  | ||||
|         val filterFn: (LibraryItem) -> Boolean = f@ { item -> | ||||
|         val filterFn: (LibraryItem) -> Boolean = f@{ item -> | ||||
|             // Filter when there isn't unread chapters. | ||||
|             if (filterUnread && item.manga.unread == 0) { | ||||
|                 return@f false | ||||
| @@ -231,16 +231,15 @@ class LibraryPresenter( | ||||
|      * @return an observable of the categories and its manga. | ||||
|      */ | ||||
|     private fun getLibraryObservable(): Observable<Library> { | ||||
|         return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable()) { | ||||
|             dbCategories, libraryManga -> | ||||
|                 val categories = if (libraryManga.containsKey(0)) | ||||
|                     arrayListOf(Category.createDefault()) + dbCategories | ||||
|                 else | ||||
|                     dbCategories | ||||
|         return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable()) { dbCategories, libraryManga -> | ||||
|             val categories = if (libraryManga.containsKey(0)) | ||||
|                 arrayListOf(Category.createDefault()) + dbCategories | ||||
|             else | ||||
|                 dbCategories | ||||
|  | ||||
|                 this.categories = categories | ||||
|                 Library(categories, libraryManga) | ||||
|             } | ||||
|             this.categories = categories | ||||
|             Library(categories, libraryManga) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -6,5 +6,5 @@ sealed class LibrarySelectionEvent { | ||||
|  | ||||
|     class Selected(val manga: Manga) : LibrarySelectionEvent() | ||||
|     class Unselected(val manga: Manga) : LibrarySelectionEvent() | ||||
|     class Cleared() : LibrarySelectionEvent() | ||||
| } | ||||
|     class Cleared : LibrarySelectionEvent() | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import android.app.Activity | ||||
| import android.content.Intent | ||||
| import android.os.Bundle | ||||
|  | ||||
| class DeepLinkActivity: Activity() { | ||||
| class DeepLinkActivity : Activity() { | ||||
|  | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
| @@ -16,4 +16,4 @@ class DeepLinkActivity: Activity() { | ||||
|         startActivity(intent) | ||||
|         finish() | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -29,23 +29,23 @@ class TabsAnimator(val tabs: TabLayout) { | ||||
|  | ||||
|     init { | ||||
|         tabs.viewTreeObserver.addOnGlobalLayoutListener( | ||||
|             object : ViewTreeObserver.OnGlobalLayoutListener { | ||||
|                 override fun onGlobalLayout() { | ||||
|                     if (tabs.height > 0) { | ||||
|                         tabs.viewTreeObserver.removeOnGlobalLayoutListener(this) | ||||
|                 object : ViewTreeObserver.OnGlobalLayoutListener { | ||||
|                     override fun onGlobalLayout() { | ||||
|                         if (tabs.height > 0) { | ||||
|                             tabs.viewTreeObserver.removeOnGlobalLayoutListener(this) | ||||
|  | ||||
|                         // Save the tabs default height. | ||||
|                         tabsHeight = tabs.height | ||||
|                             // Save the tabs default height. | ||||
|                             tabsHeight = tabs.height | ||||
|  | ||||
|                         // Now that we know the height, set the initial height. | ||||
|                         if (isLastStateShown) { | ||||
|                             setHeight(tabsHeight) | ||||
|                         } else { | ||||
|                             setHeight(0) | ||||
|                             // Now that we know the height, set the initial height. | ||||
|                             if (isLastStateShown) { | ||||
|                                 setHeight(tabsHeight) | ||||
|                             } else { | ||||
|                                 setHeight(0) | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -17,9 +17,12 @@ class ChapterItem(val chapter: Chapter, val manga: Manga) : AbstractFlexibleItem | ||||
|  | ||||
|     var status: Int | ||||
|         get() = download?.status ?: _status | ||||
|         set(value) { _status = value } | ||||
|         set(value) { | ||||
|             _status = value | ||||
|         } | ||||
|  | ||||
|     @Transient var download: Download? = null | ||||
|     @Transient | ||||
|     var download: Download? = null | ||||
|  | ||||
|     val isDownloaded: Boolean | ||||
|         get() = status == Download.DOWNLOADED | ||||
|   | ||||
| @@ -139,11 +139,11 @@ class ChaptersController : NucleusController<ChaptersPresenter>(), | ||||
|         menuFilterDownloaded.isChecked = presenter.onlyDownloaded() | ||||
|         menuFilterBookmarked.isChecked = presenter.onlyBookmarked() | ||||
|  | ||||
|         // Disable unread filter option if read filter is enabled. | ||||
|         if (presenter.onlyRead()) | ||||
|             //Disable unread filter option if read filter is enabled. | ||||
|             menuFilterUnread.isEnabled = false | ||||
|         // Disable read filter option if unread filter is enabled. | ||||
|         if (presenter.onlyUnread()) | ||||
|             //Disable read filter option if unread filter is enabled. | ||||
|             menuFilterRead.isEnabled = false | ||||
|  | ||||
|         // Display mode submenu | ||||
|   | ||||
| @@ -109,8 +109,8 @@ class ChaptersPresenter( | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .filter { download -> download.manga.id == manga.id } | ||||
|                 .doOnNext { onDownloadStatusChange(it) } | ||||
|                 .subscribeLatestCache(ChaptersController::onChapterStatusChange) { | ||||
|                     _, error -> Timber.e(error) | ||||
|                 .subscribeLatestCache(ChaptersController::onChapterStatusChange) { _, error -> | ||||
|                     Timber.e(error) | ||||
|                 } | ||||
|     } | ||||
|  | ||||
| @@ -176,8 +176,7 @@ class ChaptersPresenter( | ||||
|         var observable = Observable.from(chapters).subscribeOn(Schedulers.io()) | ||||
|         if (onlyUnread()) { | ||||
|             observable = observable.filter { !it.read } | ||||
|         } | ||||
|         else if (onlyRead()) { | ||||
|         } else if (onlyRead()) { | ||||
|             observable = observable.filter { it.read } | ||||
|         } | ||||
|         if (onlyDownloaded()) { | ||||
|   | ||||
| @@ -91,7 +91,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(), | ||||
|         fab_favorite.clicks().subscribeUntilDestroy { onFabClick() } | ||||
|  | ||||
|         // Set onLongClickListener to manage categories when FAB is clicked. | ||||
|         fab_favorite.longClicks().subscribeUntilDestroy{ onFabLongClick() } | ||||
|         fab_favorite.longClicks().subscribeUntilDestroy { onFabLongClick() } | ||||
|  | ||||
|         // Set SwipeRefresh to refresh manga data. | ||||
|         swipe_refresh.refreshes().subscribeUntilDestroy { fetchMangaFromSource() } | ||||
| @@ -488,7 +488,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(), | ||||
|                         activity?.toast(R.string.icon_creation_fail) | ||||
|                     } | ||||
|  | ||||
|                     override fun onLoadCleared(placeholder: Drawable?) { } | ||||
|                     override fun onLoadCleared(placeholder: Drawable?) {} | ||||
|                 }) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -59,7 +59,7 @@ class TrackSearchDialog : DialogController { | ||||
|                 .onPositive { _, _ -> onPositiveButtonClick() } | ||||
|                 .negativeText(android.R.string.cancel) | ||||
|                 .neutralText(R.string.action_remove) | ||||
|                 .onNeutral { _, _ ->  onRemoveButtonClick() } | ||||
|                 .onNeutral { _, _ -> onRemoveButtonClick() } | ||||
|                 .build() | ||||
|  | ||||
|         if (subscriptions.isUnsubscribed) { | ||||
|   | ||||
| @@ -4,13 +4,13 @@ import eu.kanade.tachiyomi.R | ||||
|  | ||||
| object MigrationFlags { | ||||
|  | ||||
|     private const val CHAPTERS   = 0b001 | ||||
|     private const val CHAPTERS = 0b001 | ||||
|     private const val CATEGORIES = 0b010 | ||||
|     private const val TRACK      = 0b100 | ||||
|     private const val TRACK = 0b100 | ||||
|  | ||||
|     private const val CHAPTERS2   = 0x1 | ||||
|     private const val CHAPTERS2 = 0x1 | ||||
|     private const val CATEGORIES2 = 0x2 | ||||
|     private const val TRACK2      = 0x4 | ||||
|     private const val TRACK2 = 0x4 | ||||
|  | ||||
|     val titles get() = arrayOf(R.string.chapters, R.string.categories, R.string.track) | ||||
|  | ||||
|   | ||||
| @@ -37,7 +37,7 @@ class SourceHolder(view: View, override val adapter: SourceAdapter) : | ||||
|  | ||||
|         // Set circle letter image. | ||||
|         itemView.post { | ||||
|             image.setImageDrawable(image.getRound(source.name.take(1).toUpperCase(),false)) | ||||
|             image.setImageDrawable(image.getRound(source.name.take(1).toUpperCase(), false)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,7 +32,7 @@ class PageIndicatorTextView( | ||||
|         // Also add a bit of spacing between each character, as the stroke overlaps them | ||||
|         val finalText = SpannableString(currText.asIterable().joinToString("\u00A0")).apply { | ||||
|             // Apply text outline | ||||
|             setSpan(spanOutline, 1, length-1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) | ||||
|             setSpan(spanOutline, 1, length - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) | ||||
|  | ||||
|             for (i in 1..lastIndex step 2) { | ||||
|                 setSpan(ScaleXSpan(0.2f), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) | ||||
|   | ||||
| @@ -555,40 +555,40 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() { | ||||
|             val sharedRotation = preferences.rotation().asObservable().share() | ||||
|             val initialRotation = sharedRotation.take(1) | ||||
|             val rotationUpdates = sharedRotation.skip(1) | ||||
|                 .delay(250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) | ||||
|                     .delay(250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) | ||||
|  | ||||
|             subscriptions += Observable.merge(initialRotation, rotationUpdates) | ||||
|                 .subscribe { setOrientation(it) } | ||||
|                     .subscribe { setOrientation(it) } | ||||
|  | ||||
|             subscriptions += preferences.readerTheme().asObservable() | ||||
|                 .skip(1) // We only care about updates | ||||
|                 .subscribe { recreate() } | ||||
|                     .skip(1) // We only care about updates | ||||
|                     .subscribe { recreate() } | ||||
|  | ||||
|             subscriptions += preferences.showPageNumber().asObservable() | ||||
|                 .subscribe { setPageNumberVisibility(it) } | ||||
|                     .subscribe { setPageNumberVisibility(it) } | ||||
|  | ||||
|             subscriptions += preferences.trueColor().asObservable() | ||||
|                 .subscribe { setTrueColor(it) } | ||||
|                     .subscribe { setTrueColor(it) } | ||||
|  | ||||
|             subscriptions += preferences.fullscreen().asObservable() | ||||
|                 .subscribe { setFullscreen(it) } | ||||
|                     .subscribe { setFullscreen(it) } | ||||
|  | ||||
|             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { | ||||
|                 subscriptions += preferences.cutoutShort().asObservable() | ||||
|                         .subscribe { setCutoutShort(it)} | ||||
|                         .subscribe { setCutoutShort(it) } | ||||
|             } | ||||
|  | ||||
|             subscriptions += preferences.keepScreenOn().asObservable() | ||||
|                 .subscribe { setKeepScreenOn(it) } | ||||
|                     .subscribe { setKeepScreenOn(it) } | ||||
|  | ||||
|             subscriptions += preferences.customBrightness().asObservable() | ||||
|                 .subscribe { setCustomBrightness(it) } | ||||
|                     .subscribe { setCustomBrightness(it) } | ||||
|  | ||||
|             subscriptions += preferences.colorFilter().asObservable() | ||||
|                 .subscribe { setColorFilter(it) } | ||||
|                     .subscribe { setColorFilter(it) } | ||||
|  | ||||
|             subscriptions += preferences.colorFilterMode().asObservable() | ||||
|                 .subscribe { setColorFilter(preferences.colorFilter().getOrDefault()) } | ||||
|                     .subscribe { setColorFilter(preferences.colorFilter().getOrDefault()) } | ||||
|         } | ||||
|  | ||||
|         /** | ||||
| @@ -684,8 +684,8 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() { | ||||
|         private fun setCustomBrightness(enabled: Boolean) { | ||||
|             if (enabled) { | ||||
|                 customBrightnessSubscription = preferences.customBrightnessValue().asObservable() | ||||
|                     .sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) | ||||
|                     .subscribe { setCustomBrightnessValue(it) } | ||||
|                         .sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) | ||||
|                         .subscribe { setCustomBrightnessValue(it) } | ||||
|  | ||||
|                 subscriptions.add(customBrightnessSubscription) | ||||
|             } else { | ||||
| @@ -700,8 +700,8 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() { | ||||
|         private fun setColorFilter(enabled: Boolean) { | ||||
|             if (enabled) { | ||||
|                 customFilterColorSubscription = preferences.colorFilterValue().asObservable() | ||||
|                     .sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) | ||||
|                     .subscribe { setColorFilterValue(it) } | ||||
|                         .sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) | ||||
|                         .subscribe { setColorFilterValue(it) } | ||||
|  | ||||
|                 subscriptions.add(customFilterColorSubscription) | ||||
|             } else { | ||||
|   | ||||
| @@ -55,13 +55,13 @@ class ReaderColorFilterSheet(activity: ReaderActivity) : BottomSheetDialog(activ | ||||
|  | ||||
|         // Initialize subscriptions. | ||||
|         subscriptions += preferences.colorFilter().asObservable() | ||||
|             .subscribe { setColorFilter(it, view) } | ||||
|                 .subscribe { setColorFilter(it, view) } | ||||
|  | ||||
|         subscriptions += preferences.colorFilterMode().asObservable() | ||||
|             .subscribe { setColorFilter(preferences.colorFilter().getOrDefault(), view) } | ||||
|                 .subscribe { setColorFilter(preferences.colorFilter().getOrDefault(), view) } | ||||
|  | ||||
|         subscriptions += preferences.customBrightness().asObservable() | ||||
|             .subscribe { setCustomBrightness(it, view) } | ||||
|                 .subscribe { setCustomBrightness(it, view) } | ||||
|  | ||||
|         // Get color and update values | ||||
|         val color = preferences.colorFilterValue().getOrDefault() | ||||
| @@ -202,8 +202,8 @@ class ReaderColorFilterSheet(activity: ReaderActivity) : BottomSheetDialog(activ | ||||
|     private fun setCustomBrightness(enabled: Boolean, view: View) { | ||||
|         if (enabled) { | ||||
|             customBrightnessSubscription = preferences.customBrightnessValue().asObservable() | ||||
|                 .sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) | ||||
|                 .subscribe { setCustomBrightnessValue(it, view) } | ||||
|                     .sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) | ||||
|                     .subscribe { setCustomBrightnessValue(it, view) } | ||||
|  | ||||
|             subscriptions.add(customBrightnessSubscription) | ||||
|         } else { | ||||
| @@ -241,8 +241,8 @@ class ReaderColorFilterSheet(activity: ReaderActivity) : BottomSheetDialog(activ | ||||
|     private fun setColorFilter(enabled: Boolean, view: View) { | ||||
|         if (enabled) { | ||||
|             customFilterColorSubscription = preferences.colorFilterValue().asObservable() | ||||
|                 .sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) | ||||
|                 .subscribe { setColorFilterValue(it, view) } | ||||
|                     .sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) | ||||
|                     .subscribe { setColorFilterValue(it, view) } | ||||
|  | ||||
|             subscriptions.add(customFilterColorSubscription) | ||||
|         } else { | ||||
|   | ||||
| @@ -47,14 +47,14 @@ class ReaderPageSheet( | ||||
|         if (page.status != Page.READY) return | ||||
|  | ||||
|         MaterialDialog.Builder(activity) | ||||
|             .content(activity.getString(R.string.confirm_set_image_as_cover)) | ||||
|             .positiveText(android.R.string.yes) | ||||
|             .negativeText(android.R.string.no) | ||||
|             .onPositive { _, _ -> | ||||
|                 activity.setAsCover(page) | ||||
|                 dismiss() | ||||
|             } | ||||
|             .show() | ||||
|                 .content(activity.getString(R.string.confirm_set_image_as_cover)) | ||||
|                 .positiveText(android.R.string.yes) | ||||
|                 .negativeText(android.R.string.no) | ||||
|                 .onPositive { _, _ -> | ||||
|                     activity.setAsCover(page) | ||||
|                     dismiss() | ||||
|                 } | ||||
|                 .show() | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -37,12 +37,12 @@ class SaveImageNotifier(private val context: Context) { | ||||
|      */ | ||||
|     fun onComplete(file: File) { | ||||
|         val bitmap = GlideApp.with(context) | ||||
|             .asBitmap() | ||||
|             .load(file) | ||||
|             .diskCacheStrategy(DiskCacheStrategy.NONE) | ||||
|             .skipMemoryCache(true) | ||||
|             .submit(720, 1280) | ||||
|             .get() | ||||
|                 .asBitmap() | ||||
|                 .load(file) | ||||
|                 .diskCacheStrategy(DiskCacheStrategy.NONE) | ||||
|                 .skipMemoryCache(true) | ||||
|                 .submit(720, 1280) | ||||
|                 .get() | ||||
|  | ||||
|         if (bitmap != null) { | ||||
|             showCompleteNotification(file, bitmap) | ||||
|   | ||||
| @@ -31,34 +31,34 @@ class ChapterLoader( | ||||
|         } | ||||
|  | ||||
|         return Observable.just(chapter) | ||||
|             .doOnNext { chapter.state = ReaderChapter.State.Loading } | ||||
|             .observeOn(Schedulers.io()) | ||||
|             .flatMap { | ||||
|                 Timber.d("Loading pages for ${chapter.chapter.name}") | ||||
|                 .doOnNext { chapter.state = ReaderChapter.State.Loading } | ||||
|                 .observeOn(Schedulers.io()) | ||||
|                 .flatMap { | ||||
|                     Timber.d("Loading pages for ${chapter.chapter.name}") | ||||
|  | ||||
|                 val loader = getPageLoader(it) | ||||
|                 chapter.pageLoader = loader | ||||
|                     val loader = getPageLoader(it) | ||||
|                     chapter.pageLoader = loader | ||||
|  | ||||
|                 loader.getPages().take(1).doOnNext { pages -> | ||||
|                     pages.forEach { it.chapter = chapter } | ||||
|                 } | ||||
|             } | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .doOnNext { pages -> | ||||
|                 if (pages.isEmpty()) { | ||||
|                     throw Exception("Page list is empty") | ||||
|                     loader.getPages().take(1).doOnNext { pages -> | ||||
|                         pages.forEach { it.chapter = chapter } | ||||
|                     } | ||||
|                 } | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .doOnNext { pages -> | ||||
|                     if (pages.isEmpty()) { | ||||
|                         throw Exception("Page list is empty") | ||||
|                     } | ||||
|  | ||||
|                 chapter.state = ReaderChapter.State.Loaded(pages) | ||||
|                     chapter.state = ReaderChapter.State.Loaded(pages) | ||||
|  | ||||
|                 // If the chapter is partially read, set the starting page to the last the user read | ||||
|                 // otherwise use the requested page. | ||||
|                 if (!chapter.chapter.read) { | ||||
|                     chapter.requestedPage = chapter.chapter.last_page_read | ||||
|                     // If the chapter is partially read, set the starting page to the last the user read | ||||
|                     // otherwise use the requested page. | ||||
|                     if (!chapter.chapter.read) { | ||||
|                         chapter.requestedPage = chapter.chapter.last_page_read | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .toCompletable() | ||||
|             .doOnError { chapter.state = ReaderChapter.State.Error(it) } | ||||
|                 .toCompletable() | ||||
|                 .doOnError { chapter.state = ReaderChapter.State.Error(it) } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -19,16 +19,16 @@ class DirectoryPageLoader(val file: File) : PageLoader() { | ||||
|      */ | ||||
|     override fun getPages(): Observable<List<ReaderPage>> { | ||||
|         return file.listFiles() | ||||
|             .filter { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } } | ||||
|             .sortedWith(Comparator<File> { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }) | ||||
|             .mapIndexed { i, file -> | ||||
|                 val streamFn = { FileInputStream(file) } | ||||
|                 ReaderPage(i).apply { | ||||
|                     stream = streamFn | ||||
|                     status = Page.READY | ||||
|                 .filter { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } } | ||||
|                 .sortedWith(Comparator<File> { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }) | ||||
|                 .mapIndexed { i, file -> | ||||
|                     val streamFn = { FileInputStream(file) } | ||||
|                     ReaderPage(i).apply { | ||||
|                         stream = streamFn | ||||
|                         status = Page.READY | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .let { Observable.just(it) } | ||||
|                 .let { Observable.just(it) } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -31,15 +31,15 @@ class DownloadPageLoader( | ||||
|      */ | ||||
|     override fun getPages(): Observable<List<ReaderPage>> { | ||||
|         return downloadManager.buildPageList(source, manga, chapter.chapter) | ||||
|             .map { pages -> | ||||
|                 pages.map { page -> | ||||
|                     ReaderPage(page.index, page.url, page.imageUrl) { | ||||
|                         context.contentResolver.openInputStream(page.uri ?: Uri.EMPTY)!! | ||||
|                     }.apply { | ||||
|                         status = Page.READY | ||||
|                 .map { pages -> | ||||
|                     pages.map { page -> | ||||
|                         ReaderPage(page.index, page.url, page.imageUrl) { | ||||
|                             context.contentResolver.openInputStream(page.uri ?: Uri.EMPTY)!! | ||||
|                         }.apply { | ||||
|                             status = Page.READY | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     override fun getPage(page: ReaderPage): Observable<Int> { | ||||
|   | ||||
| @@ -30,14 +30,14 @@ class EpubPageLoader(file: File) : PageLoader() { | ||||
|      */ | ||||
|     override fun getPages(): Observable<List<ReaderPage>> { | ||||
|         return epub.getImagesFromPages() | ||||
|             .mapIndexed { i, path -> | ||||
|                 val streamFn = { epub.getInputStream(epub.getEntry(path)!!) } | ||||
|                 ReaderPage(i).apply { | ||||
|                     stream = streamFn | ||||
|                     status = Page.READY | ||||
|                 .mapIndexed { i, path -> | ||||
|                     val streamFn = { epub.getInputStream(epub.getEntry(path)!!) } | ||||
|                     ReaderPage(i).apply { | ||||
|                         stream = streamFn | ||||
|                         status = Page.READY | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .let { Observable.just(it) } | ||||
|                 .let { Observable.just(it) } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -66,14 +66,14 @@ class HttpPageLoader( | ||||
|         val pages = chapter.pages | ||||
|         if (pages != null) { | ||||
|             Completable | ||||
|                 .fromAction { | ||||
|                     // Convert to pages without reader information | ||||
|                     val pagesToSave = pages.map { Page(it.index, it.url, it.imageUrl) } | ||||
|                     chapterCache.putPageListToCache(chapter.chapter, pagesToSave) | ||||
|                 } | ||||
|                 .onErrorComplete() | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .subscribe() | ||||
|                     .fromAction { | ||||
|                         // Convert to pages without reader information | ||||
|                         val pagesToSave = pages.map { Page(it.index, it.url, it.imageUrl) } | ||||
|                         chapterCache.putPageListToCache(chapter.chapter, pagesToSave) | ||||
|                     } | ||||
|                     .onErrorComplete() | ||||
|                     .subscribeOn(Schedulers.io()) | ||||
|                     .subscribe() | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -86,7 +86,8 @@ class HttpPageLoader( | ||||
|                 .getPageListFromCache(chapter.chapter) | ||||
|                 .onErrorResumeNext { source.fetchPageList(chapter.chapter) } | ||||
|                 .map { pages -> | ||||
|                     pages.mapIndexed { index, page -> // Don't trust sources and use our own indexing | ||||
|                     pages.mapIndexed { index, page -> | ||||
|                         // Don't trust sources and use our own indexing | ||||
|                         ReaderPage(index, page.url, page.imageUrl) | ||||
|                     } | ||||
|                 } | ||||
| @@ -166,7 +167,7 @@ class HttpPageLoader( | ||||
|     private class PriorityPage( | ||||
|             val page: ReaderPage, | ||||
|             val priority: Int | ||||
|     ): Comparable<PriorityPage> { | ||||
|     ) : Comparable<PriorityPage> { | ||||
|  | ||||
|         companion object { | ||||
|             private val idGenerator = AtomicInteger() | ||||
| @@ -196,10 +197,10 @@ class HttpPageLoader( | ||||
|     private fun HttpSource.getImageUrl(page: ReaderPage): Observable<ReaderPage> { | ||||
|         page.status = Page.LOAD_PAGE | ||||
|         return fetchImageUrl(page) | ||||
|             .doOnError { page.status = Page.ERROR } | ||||
|             .onErrorReturn { null } | ||||
|             .doOnNext { page.imageUrl = it } | ||||
|             .map { page } | ||||
|                 .doOnError { page.status = Page.ERROR } | ||||
|                 .onErrorReturn { null } | ||||
|                 .doOnNext { page.imageUrl = it } | ||||
|                 .map { page } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -212,19 +213,19 @@ class HttpPageLoader( | ||||
|         val imageUrl = page.imageUrl ?: return Observable.just(page) | ||||
|  | ||||
|         return Observable.just(page) | ||||
|             .flatMap { | ||||
|                 if (!chapterCache.isImageInCache(imageUrl)) { | ||||
|                     cacheImage(page) | ||||
|                 } else { | ||||
|                     Observable.just(page) | ||||
|                 .flatMap { | ||||
|                     if (!chapterCache.isImageInCache(imageUrl)) { | ||||
|                         cacheImage(page) | ||||
|                     } else { | ||||
|                         Observable.just(page) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .doOnNext { | ||||
|                 page.stream = { chapterCache.getImageFile(imageUrl).inputStream() } | ||||
|                 page.status = Page.READY | ||||
|             } | ||||
|             .doOnError { page.status = Page.ERROR } | ||||
|             .onErrorReturn { page } | ||||
|                 .doOnNext { | ||||
|                     page.stream = { chapterCache.getImageFile(imageUrl).inputStream() } | ||||
|                     page.status = Page.READY | ||||
|                 } | ||||
|                 .doOnError { page.status = Page.ERROR } | ||||
|                 .onErrorReturn { page } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -235,7 +236,7 @@ class HttpPageLoader( | ||||
|     private fun HttpSource.cacheImage(page: ReaderPage): Observable<ReaderPage> { | ||||
|         page.status = Page.DOWNLOAD_IMAGE | ||||
|         return fetchImage(page) | ||||
|             .doOnNext { chapterCache.putImageToCache(page.imageUrl!!, it) } | ||||
|             .map { page } | ||||
|                 .doOnNext { chapterCache.putImageToCache(page.imageUrl!!, it) } | ||||
|                 .map { page } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -43,17 +43,17 @@ class RarPageLoader(file: File) : PageLoader() { | ||||
|      */ | ||||
|     override fun getPages(): Observable<List<ReaderPage>> { | ||||
|         return archive.fileHeaders | ||||
|             .filter { !it.isDirectory && ImageUtil.isImage(it.fileNameString) { archive.getInputStream(it) } } | ||||
|             .sortedWith(Comparator<FileHeader> { f1, f2 -> f1.fileNameString.compareToCaseInsensitiveNaturalOrder(f2.fileNameString) }) | ||||
|             .mapIndexed { i, header -> | ||||
|                 val streamFn = { getStream(header) } | ||||
|                 .filter { !it.isDirectory && ImageUtil.isImage(it.fileNameString) { archive.getInputStream(it) } } | ||||
|                 .sortedWith(Comparator<FileHeader> { f1, f2 -> f1.fileNameString.compareToCaseInsensitiveNaturalOrder(f2.fileNameString) }) | ||||
|                 .mapIndexed { i, header -> | ||||
|                     val streamFn = { getStream(header) } | ||||
|  | ||||
|                 ReaderPage(i).apply { | ||||
|                     stream = streamFn | ||||
|                     status = Page.READY | ||||
|                     ReaderPage(i).apply { | ||||
|                         stream = streamFn | ||||
|                         status = Page.READY | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .let { Observable.just(it) } | ||||
|                 .let { Observable.just(it) } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -33,16 +33,16 @@ class ZipPageLoader(file: File) : PageLoader() { | ||||
|      */ | ||||
|     override fun getPages(): Observable<List<ReaderPage>> { | ||||
|         return zip.entries().toList() | ||||
|             .filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } | ||||
|             .sortedWith(Comparator<ZipEntry> { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }) | ||||
|             .mapIndexed { i, entry -> | ||||
|                 val streamFn = { zip.getInputStream(entry) } | ||||
|                 ReaderPage(i).apply { | ||||
|                     stream = streamFn | ||||
|                     status = Page.READY | ||||
|                 .filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } | ||||
|                 .sortedWith(Comparator<ZipEntry> { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }) | ||||
|                 .mapIndexed { i, entry -> | ||||
|                     val streamFn = { zip.getInputStream(entry) } | ||||
|                     ReaderPage(i).apply { | ||||
|                         stream = streamFn | ||||
|                         status = Page.READY | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             .let { Observable.just(it) } | ||||
|                 .let { Observable.just(it) } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -8,6 +8,7 @@ sealed class ChapterTransition { | ||||
|     class Prev( | ||||
|             override val from: ReaderChapter, override val to: ReaderChapter? | ||||
|     ) : ChapterTransition() | ||||
|  | ||||
|     class Next( | ||||
|             override val from: ReaderChapter, override val to: ReaderChapter? | ||||
|     ) : ChapterTransition() | ||||
|   | ||||
| @@ -157,7 +157,7 @@ class ReaderProgressBar @JvmOverloads constructor( | ||||
|         if (!animate) { | ||||
|             visibility = View.GONE | ||||
|         } else { | ||||
|             ObjectAnimator.ofFloat(this, "alpha",  1f, 0f).apply { | ||||
|             ObjectAnimator.ofFloat(this, "alpha", 1f, 0f).apply { | ||||
|                 interpolator = DecelerateInterpolator() | ||||
|                 duration = 1000 | ||||
|                 addListener(object : AnimatorListenerAdapter() { | ||||
|   | ||||
| @@ -45,31 +45,31 @@ class PagerConfig(private val viewer: PagerViewer, preferences: PreferencesHelpe | ||||
|  | ||||
|     init { | ||||
|         preferences.readWithTapping() | ||||
|             .register({ tappingEnabled = it }) | ||||
|                 .register({ tappingEnabled = it }) | ||||
|  | ||||
|         preferences.readWithLongTap() | ||||
|             .register({ longTapEnabled = it }) | ||||
|                 .register({ longTapEnabled = it }) | ||||
|  | ||||
|         preferences.pageTransitions() | ||||
|             .register({ usePageTransitions = it }) | ||||
|                 .register({ usePageTransitions = it }) | ||||
|  | ||||
|         preferences.imageScaleType() | ||||
|             .register({ imageScaleType = it }, { imagePropertyChangedListener?.invoke() }) | ||||
|                 .register({ imageScaleType = it }, { imagePropertyChangedListener?.invoke() }) | ||||
|  | ||||
|         preferences.zoomStart() | ||||
|             .register({ zoomTypeFromPreference(it) }, { imagePropertyChangedListener?.invoke() }) | ||||
|                 .register({ zoomTypeFromPreference(it) }, { imagePropertyChangedListener?.invoke() }) | ||||
|  | ||||
|         preferences.cropBorders() | ||||
|             .register({ imageCropBorders = it }, { imagePropertyChangedListener?.invoke() }) | ||||
|                 .register({ imageCropBorders = it }, { imagePropertyChangedListener?.invoke() }) | ||||
|  | ||||
|         preferences.doubleTapAnimSpeed() | ||||
|             .register({ doubleTapAnimDuration = it }) | ||||
|                 .register({ doubleTapAnimDuration = it }) | ||||
|  | ||||
|         preferences.readWithVolumeKeys() | ||||
|             .register({ volumeKeysEnabled = it }) | ||||
|                 .register({ volumeKeysEnabled = it }) | ||||
|  | ||||
|         preferences.readWithVolumeKeysInverted() | ||||
|             .register({ volumeKeysInverted = it }) | ||||
|                 .register({ volumeKeysInverted = it }) | ||||
|     } | ||||
|  | ||||
|     fun unsubscribe() { | ||||
| @@ -81,12 +81,12 @@ class PagerConfig(private val viewer: PagerViewer, preferences: PreferencesHelpe | ||||
|             onChanged: (T) -> Unit = {} | ||||
|     ) { | ||||
|         asObservable() | ||||
|             .doOnNext(valueAssignment) | ||||
|             .skip(1) | ||||
|             .distinctUntilChanged() | ||||
|             .doOnNext(onChanged) | ||||
|             .subscribe() | ||||
|             .addTo(subscriptions) | ||||
|                 .doOnNext(valueAssignment) | ||||
|                 .skip(1) | ||||
|                 .distinctUntilChanged() | ||||
|                 .doOnNext(onChanged) | ||||
|                 .subscribe() | ||||
|                 .addTo(subscriptions) | ||||
|     } | ||||
|  | ||||
|     private fun zoomTypeFromPreference(value: Int) { | ||||
|   | ||||
| @@ -127,8 +127,8 @@ class PagerPageHolder( | ||||
|  | ||||
|         val loader = page.chapter.pageLoader ?: return | ||||
|         statusSubscription = loader.getPage(page) | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .subscribe { processStatus(it) } | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe { processStatus(it) } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -138,11 +138,11 @@ class PagerPageHolder( | ||||
|         progressSubscription?.unsubscribe() | ||||
|  | ||||
|         progressSubscription = Observable.interval(100, TimeUnit.MILLISECONDS) | ||||
|             .map { page.progress } | ||||
|             .distinctUntilChanged() | ||||
|             .onBackpressureLatest() | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .subscribe { value -> progressBar.setProgress(value) } | ||||
|                 .map { page.progress } | ||||
|                 .distinctUntilChanged() | ||||
|                 .onBackpressureLatest() | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe { value -> progressBar.setProgress(value) } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -234,25 +234,25 @@ class PagerPageHolder( | ||||
|  | ||||
|         var openStream: InputStream? = null | ||||
|         readImageHeaderSubscription = Observable | ||||
|             .fromCallable { | ||||
|                 val stream = streamFn().buffered(16) | ||||
|                 openStream = stream | ||||
|                 .fromCallable { | ||||
|                     val stream = streamFn().buffered(16) | ||||
|                     openStream = stream | ||||
|  | ||||
|                 ImageUtil.findImageType(stream) == ImageUtil.ImageType.GIF | ||||
|             } | ||||
|             .subscribeOn(Schedulers.io()) | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .doOnNext { isAnimated -> | ||||
|                 if (!isAnimated) { | ||||
|                     initSubsamplingImageView().setImage(ImageSource.inputStream(openStream!!)) | ||||
|                 } else { | ||||
|                     initImageView().setImage(openStream!!) | ||||
|                     ImageUtil.findImageType(stream) == ImageUtil.ImageType.GIF | ||||
|                 } | ||||
|             } | ||||
|             // Keep the Rx stream alive to close the input stream only when unsubscribed | ||||
|             .flatMap { Observable.never<Unit>() } | ||||
|             .doOnUnsubscribe { openStream?.close() } | ||||
|             .subscribe({}, {}) | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .doOnNext { isAnimated -> | ||||
|                     if (!isAnimated) { | ||||
|                         initSubsamplingImageView().setImage(ImageSource.inputStream(openStream!!)) | ||||
|                     } else { | ||||
|                         initImageView().setImage(openStream!!) | ||||
|                     } | ||||
|                 } | ||||
|                 // Keep the Rx stream alive to close the input stream only when unsubscribed | ||||
|                 .flatMap { Observable.never<Unit>() } | ||||
|                 .doOnUnsubscribe { openStream?.close() } | ||||
|                 .subscribe({}, {}) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -436,36 +436,36 @@ class PagerPageHolder( | ||||
|      */ | ||||
|     private fun ImageView.setImage(stream: InputStream) { | ||||
|         GlideApp.with(this) | ||||
|             .load(stream) | ||||
|             .skipMemoryCache(true) | ||||
|             .diskCacheStrategy(DiskCacheStrategy.NONE) | ||||
|             .transition(DrawableTransitionOptions.with(NoTransition.getFactory())) | ||||
|             .listener(object : RequestListener<Drawable> { | ||||
|                 override fun onLoadFailed( | ||||
|                         e: GlideException?, | ||||
|                         model: Any?, | ||||
|                         target: Target<Drawable>?, | ||||
|                         isFirstResource: Boolean | ||||
|                 ): Boolean { | ||||
|                     onImageDecodeError() | ||||
|                     return false | ||||
|                 } | ||||
|  | ||||
|                 override fun onResourceReady( | ||||
|                         resource: Drawable?, | ||||
|                         model: Any?, | ||||
|                         target: Target<Drawable>?, | ||||
|                         dataSource: DataSource?, | ||||
|                         isFirstResource: Boolean | ||||
|                 ): Boolean { | ||||
|                     if (resource is GifDrawable) { | ||||
|                         resource.setLoopCount(GifDrawable.LOOP_INTRINSIC) | ||||
|                 .load(stream) | ||||
|                 .skipMemoryCache(true) | ||||
|                 .diskCacheStrategy(DiskCacheStrategy.NONE) | ||||
|                 .transition(DrawableTransitionOptions.with(NoTransition.getFactory())) | ||||
|                 .listener(object : RequestListener<Drawable> { | ||||
|                     override fun onLoadFailed( | ||||
|                             e: GlideException?, | ||||
|                             model: Any?, | ||||
|                             target: Target<Drawable>?, | ||||
|                             isFirstResource: Boolean | ||||
|                     ): Boolean { | ||||
|                         onImageDecodeError() | ||||
|                         return false | ||||
|                     } | ||||
|                     onImageDecoded() | ||||
|                     return false | ||||
|                 } | ||||
|             }) | ||||
|             .into(this) | ||||
|  | ||||
|                     override fun onResourceReady( | ||||
|                             resource: Drawable?, | ||||
|                             model: Any?, | ||||
|                             target: Target<Drawable>?, | ||||
|                             dataSource: DataSource?, | ||||
|                             isFirstResource: Boolean | ||||
|                     ): Boolean { | ||||
|                         if (resource is GifDrawable) { | ||||
|                             resource.setLoopCount(GifDrawable.LOOP_INTRINSIC) | ||||
|                         } | ||||
|                         onImageDecoded() | ||||
|                         return false | ||||
|                     } | ||||
|                 }) | ||||
|                 .into(this) | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -140,16 +140,17 @@ class PagerTransitionHolder( | ||||
|     private fun observeStatus(chapter: ReaderChapter) { | ||||
|         statusSubscription?.unsubscribe() | ||||
|         statusSubscription = chapter.stateObserver | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .subscribe { state -> | ||||
|                 pagesContainer.removeAllViews() | ||||
|                 when (state) { | ||||
|                     is ReaderChapter.State.Wait -> {} | ||||
|                     is ReaderChapter.State.Loading -> setLoading() | ||||
|                     is ReaderChapter.State.Error -> setError(state.error) | ||||
|                     is ReaderChapter.State.Loaded -> setLoaded() | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe { state -> | ||||
|                     pagesContainer.removeAllViews() | ||||
|                     when (state) { | ||||
|                         is ReaderChapter.State.Wait -> { | ||||
|                         } | ||||
|                         is ReaderChapter.State.Loading -> setLoading() | ||||
|                         is ReaderChapter.State.Error -> setError(state.error) | ||||
|                         is ReaderChapter.State.Loaded -> setLoaded() | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -85,7 +85,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer { | ||||
|                 else -> activity.toggleMenu() | ||||
|             } | ||||
|         } | ||||
|         pager.longTapListener = f@ { | ||||
|         pager.longTapListener = f@{ | ||||
|             if (activity.menuVisible || config.longTapEnabled) { | ||||
|                 val item = adapter.items.getOrNull(pager.currentItem) | ||||
|                 if (item is ReaderPage) { | ||||
|   | ||||
| @@ -36,22 +36,22 @@ class WebtoonConfig(preferences: PreferencesHelper = Injekt.get()) { | ||||
|  | ||||
|     init { | ||||
|         preferences.readWithTapping() | ||||
|             .register({ tappingEnabled = it }) | ||||
|                 .register({ tappingEnabled = it }) | ||||
|  | ||||
|         preferences.readWithLongTap() | ||||
|             .register({ longTapEnabled = it }) | ||||
|                 .register({ longTapEnabled = it }) | ||||
|  | ||||
|         preferences.cropBordersWebtoon() | ||||
|             .register({ imageCropBorders = it }, { imagePropertyChangedListener?.invoke() }) | ||||
|                 .register({ imageCropBorders = it }, { imagePropertyChangedListener?.invoke() }) | ||||
|  | ||||
|         preferences.doubleTapAnimSpeed() | ||||
|             .register({ doubleTapAnimDuration = it }) | ||||
|                 .register({ doubleTapAnimDuration = it }) | ||||
|  | ||||
|         preferences.readWithVolumeKeys() | ||||
|             .register({ volumeKeysEnabled = it }) | ||||
|                 .register({ volumeKeysEnabled = it }) | ||||
|  | ||||
|         preferences.readWithVolumeKeysInverted() | ||||
|             .register({ volumeKeysInverted = it }) | ||||
|                 .register({ volumeKeysInverted = it }) | ||||
|     } | ||||
|  | ||||
|     fun unsubscribe() { | ||||
| @@ -63,12 +63,12 @@ class WebtoonConfig(preferences: PreferencesHelper = Injekt.get()) { | ||||
|             onChanged: (T) -> Unit = {} | ||||
|     ) { | ||||
|         asObservable() | ||||
|             .doOnNext(valueAssignment) | ||||
|             .skip(1) | ||||
|             .distinctUntilChanged() | ||||
|             .doOnNext(onChanged) | ||||
|             .subscribe() | ||||
|             .addTo(subscriptions) | ||||
|                 .doOnNext(valueAssignment) | ||||
|                 .skip(1) | ||||
|                 .distinctUntilChanged() | ||||
|                 .doOnNext(onChanged) | ||||
|                 .subscribe() | ||||
|                 .addTo(subscriptions) | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -44,10 +44,10 @@ class WebtoonLayoutManager(activity: ReaderActivity) : LinearLayoutManager(activ | ||||
|  | ||||
|         val child = if (mOrientation == HORIZONTAL) | ||||
|             mHorizontalBoundCheck | ||||
|                 .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag, 0) | ||||
|                     .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag, 0) | ||||
|         else | ||||
|             mVerticalBoundCheck | ||||
|                 .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag, 0) | ||||
|                     .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag, 0) | ||||
|  | ||||
|         return if (child == null) NO_POSITION else getPosition(child) | ||||
|     } | ||||
|   | ||||
| @@ -149,8 +149,8 @@ class WebtoonPageHolder( | ||||
|         val page = page ?: return | ||||
|         val loader = page.chapter.pageLoader ?: return | ||||
|         statusSubscription = loader.getPage(page) | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .subscribe { processStatus(it) } | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe { processStatus(it) } | ||||
|  | ||||
|         addSubscription(statusSubscription) | ||||
|     } | ||||
| @@ -164,11 +164,11 @@ class WebtoonPageHolder( | ||||
|         val page = page ?: return | ||||
|  | ||||
|         progressSubscription = Observable.interval(100, TimeUnit.MILLISECONDS) | ||||
|             .map { page.progress } | ||||
|             .distinctUntilChanged() | ||||
|             .onBackpressureLatest() | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .subscribe { value -> progressBar.setProgress(value) } | ||||
|                 .map { page.progress } | ||||
|                 .distinctUntilChanged() | ||||
|                 .onBackpressureLatest() | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe { value -> progressBar.setProgress(value) } | ||||
|  | ||||
|         addSubscription(progressSubscription) | ||||
|     } | ||||
| @@ -266,29 +266,29 @@ class WebtoonPageHolder( | ||||
|  | ||||
|         var openStream: InputStream? = null | ||||
|         readImageHeaderSubscription = Observable | ||||
|             .fromCallable { | ||||
|                 val stream = streamFn().buffered(16) | ||||
|                 openStream = stream | ||||
|                 .fromCallable { | ||||
|                     val stream = streamFn().buffered(16) | ||||
|                     openStream = stream | ||||
|  | ||||
|                 ImageUtil.findImageType(stream) == ImageUtil.ImageType.GIF | ||||
|             } | ||||
|             .subscribeOn(Schedulers.io()) | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .doOnNext { isAnimated -> | ||||
|                 if (!isAnimated) { | ||||
|                     val subsamplingView = initSubsamplingImageView() | ||||
|                     subsamplingView.visible() | ||||
|                     subsamplingView.setImage(ImageSource.inputStream(openStream!!)) | ||||
|                 } else { | ||||
|                     val imageView = initImageView() | ||||
|                     imageView.visible() | ||||
|                     imageView.setImage(openStream!!) | ||||
|                     ImageUtil.findImageType(stream) == ImageUtil.ImageType.GIF | ||||
|                 } | ||||
|             } | ||||
|             // Keep the Rx stream alive to close the input stream only when unsubscribed | ||||
|             .flatMap { Observable.never<Unit>() } | ||||
|             .doOnUnsubscribe { openStream?.close() } | ||||
|             .subscribe({}, {}) | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .doOnNext { isAnimated -> | ||||
|                     if (!isAnimated) { | ||||
|                         val subsamplingView = initSubsamplingImageView() | ||||
|                         subsamplingView.visible() | ||||
|                         subsamplingView.setImage(ImageSource.inputStream(openStream!!)) | ||||
|                     } else { | ||||
|                         val imageView = initImageView() | ||||
|                         imageView.visible() | ||||
|                         imageView.setImage(openStream!!) | ||||
|                     } | ||||
|                 } | ||||
|                 // Keep the Rx stream alive to close the input stream only when unsubscribed | ||||
|                 .flatMap { Observable.never<Unit>() } | ||||
|                 .doOnUnsubscribe { openStream?.close() } | ||||
|                 .subscribe({}, {}) | ||||
|  | ||||
|         addSubscription(readImageHeaderSubscription) | ||||
|     } | ||||
| @@ -328,7 +328,7 @@ class WebtoonPageHolder( | ||||
|             val size = 48.dpToPx | ||||
|             layoutParams = FrameLayout.LayoutParams(size, size).apply { | ||||
|                 gravity = Gravity.CENTER_HORIZONTAL | ||||
|                 setMargins(0, parentHeight/4, 0, 0) | ||||
|                 setMargins(0, parentHeight / 4, 0, 0) | ||||
|             } | ||||
|         } | ||||
|         progressContainer.addView(progress) | ||||
| @@ -389,7 +389,7 @@ class WebtoonPageHolder( | ||||
|         AppCompatButton(context).apply { | ||||
|             layoutParams = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { | ||||
|                 gravity = Gravity.CENTER_HORIZONTAL | ||||
|                 setMargins(0, parentHeight/4, 0, 0) | ||||
|                 setMargins(0, parentHeight / 4, 0, 0) | ||||
|             } | ||||
|             setText(R.string.action_retry) | ||||
|             setOnClickListener { | ||||
| @@ -411,7 +411,7 @@ class WebtoonPageHolder( | ||||
|  | ||||
|         val decodeLayout = LinearLayout(context).apply { | ||||
|             layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, parentHeight).apply { | ||||
|                 setMargins(0, parentHeight/6, 0, 0) | ||||
|                 setMargins(0, parentHeight / 6, 0, 0) | ||||
|             } | ||||
|             gravity = Gravity.CENTER_HORIZONTAL | ||||
|             orientation = LinearLayout.VERTICAL | ||||
| @@ -476,36 +476,36 @@ class WebtoonPageHolder( | ||||
|      */ | ||||
|     private fun ImageView.setImage(stream: InputStream) { | ||||
|         GlideApp.with(this) | ||||
|             .load(stream) | ||||
|             .skipMemoryCache(true) | ||||
|             .diskCacheStrategy(DiskCacheStrategy.NONE) | ||||
|             .transition(DrawableTransitionOptions.with(NoTransition.getFactory())) | ||||
|             .listener(object : RequestListener<Drawable> { | ||||
|                 override fun onLoadFailed( | ||||
|                         e: GlideException?, | ||||
|                         model: Any?, | ||||
|                         target: Target<Drawable>?, | ||||
|                         isFirstResource: Boolean | ||||
|                 ): Boolean { | ||||
|                     onImageDecodeError() | ||||
|                     return false | ||||
|                 } | ||||
|  | ||||
|                 override fun onResourceReady( | ||||
|                         resource: Drawable?, | ||||
|                         model: Any?, | ||||
|                         target: Target<Drawable>?, | ||||
|                         dataSource: DataSource?, | ||||
|                         isFirstResource: Boolean | ||||
|                 ): Boolean { | ||||
|                     if (resource is GifDrawable) { | ||||
|                         resource.setLoopCount(GifDrawable.LOOP_INTRINSIC) | ||||
|                 .load(stream) | ||||
|                 .skipMemoryCache(true) | ||||
|                 .diskCacheStrategy(DiskCacheStrategy.NONE) | ||||
|                 .transition(DrawableTransitionOptions.with(NoTransition.getFactory())) | ||||
|                 .listener(object : RequestListener<Drawable> { | ||||
|                     override fun onLoadFailed( | ||||
|                             e: GlideException?, | ||||
|                             model: Any?, | ||||
|                             target: Target<Drawable>?, | ||||
|                             isFirstResource: Boolean | ||||
|                     ): Boolean { | ||||
|                         onImageDecodeError() | ||||
|                         return false | ||||
|                     } | ||||
|                     onImageDecoded() | ||||
|                     return false | ||||
|                 } | ||||
|             }) | ||||
|             .into(this) | ||||
|  | ||||
|                     override fun onResourceReady( | ||||
|                             resource: Drawable?, | ||||
|                             model: Any?, | ||||
|                             target: Target<Drawable>?, | ||||
|                             dataSource: DataSource?, | ||||
|                             isFirstResource: Boolean | ||||
|                     ): Boolean { | ||||
|                         if (resource is GifDrawable) { | ||||
|                             resource.setLoopCount(GifDrawable.LOOP_INTRINSIC) | ||||
|                         } | ||||
|                         onImageDecoded() | ||||
|                         return false | ||||
|                     } | ||||
|                 }) | ||||
|                 .into(this) | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -137,13 +137,13 @@ open class WebtoonRecyclerView @JvmOverloads constructor( | ||||
|         } | ||||
|  | ||||
|         animate() | ||||
|             .apply { | ||||
|                 newX?.let { x(it) } | ||||
|                 newY?.let { y(it) } | ||||
|             } | ||||
|             .setInterpolator(DecelerateInterpolator()) | ||||
|             .setDuration(400) | ||||
|             .start() | ||||
|                 .apply { | ||||
|                     newX?.let { x(it) } | ||||
|                     newY?.let { y(it) } | ||||
|                 } | ||||
|                 .setInterpolator(DecelerateInterpolator()) | ||||
|                 .setDuration(400) | ||||
|                 .start() | ||||
|  | ||||
|         return true | ||||
|     } | ||||
|   | ||||
| @@ -143,17 +143,18 @@ class WebtoonTransitionHolder( | ||||
|         unsubscribeStatus() | ||||
|  | ||||
|         statusSubscription = chapter.stateObserver | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
|             .subscribe { state -> | ||||
|                 pagesContainer.removeAllViews() | ||||
|                 when (state) { | ||||
|                     is ReaderChapter.State.Wait -> {} | ||||
|                     is ReaderChapter.State.Loading -> setLoading() | ||||
|                     is ReaderChapter.State.Error -> setError(state.error, transition) | ||||
|                     is ReaderChapter.State.Loaded -> setLoaded() | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe { state -> | ||||
|                     pagesContainer.removeAllViews() | ||||
|                     when (state) { | ||||
|                         is ReaderChapter.State.Wait -> { | ||||
|                         } | ||||
|                         is ReaderChapter.State.Loading -> setLoading() | ||||
|                         is ReaderChapter.State.Error -> setError(state.error, transition) | ||||
|                         is ReaderChapter.State.Loaded -> setLoaded() | ||||
|                     } | ||||
|                     pagesContainer.visibleIf { pagesContainer.childCount > 0 } | ||||
|                 } | ||||
|                 pagesContainer.visibleIf { pagesContainer.childCount > 0 } | ||||
|             } | ||||
|  | ||||
|         addSubscription(statusSubscription) | ||||
|     } | ||||
|   | ||||
| @@ -95,7 +95,7 @@ class WebtoonViewer(val activity: ReaderActivity) : BaseViewer { | ||||
|                 else -> activity.toggleMenu() | ||||
|             } | ||||
|         } | ||||
|         recycler.longTapListener = f@ { event -> | ||||
|         recycler.longTapListener = f@{ event -> | ||||
|             if (activity.menuVisible || config.longTapEnabled) { | ||||
|                 val child = recycler.findChildViewUnder(event.x, event.y) | ||||
|                 if (child != null) { | ||||
|   | ||||
| @@ -17,9 +17,12 @@ class RecentChapterItem(val chapter: Chapter, val manga: Manga, header: DateItem | ||||
|  | ||||
|     var status: Int | ||||
|         get() = download?.status ?: _status | ||||
|         set(value) { _status = value } | ||||
|         set(value) { | ||||
|             _status = value | ||||
|         } | ||||
|  | ||||
|     @Transient var download: Download? = null | ||||
|     @Transient | ||||
|     var download: Download? = null | ||||
|  | ||||
|     val isDownloaded: Boolean | ||||
|         get() = status == Download.DOWNLOADED | ||||
| @@ -29,7 +32,7 @@ class RecentChapterItem(val chapter: Chapter, val manga: Manga, header: DateItem | ||||
|     } | ||||
|  | ||||
|     override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): RecentChapterHolder { | ||||
|         return RecentChapterHolder(view , adapter as RecentChaptersAdapter) | ||||
|         return RecentChapterHolder(view, adapter as RecentChaptersAdapter) | ||||
|     } | ||||
|  | ||||
|     override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, | ||||
|   | ||||
| @@ -38,8 +38,8 @@ class RecentChaptersPresenter( | ||||
|                 .subscribeLatestCache(RecentChaptersController::onNextRecentChapters) | ||||
|  | ||||
|         getChapterStatusObservable() | ||||
|                 .subscribeLatestCache(RecentChaptersController::onChapterStatusChange) { | ||||
|                     _, error -> Timber.e(error) | ||||
|                 .subscribeLatestCache(RecentChaptersController::onChapterStatusChange) { _, error -> | ||||
|                     Timber.e(error) | ||||
|                 } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -18,7 +18,7 @@ import java.text.DecimalFormatSymbols | ||||
|  * @constructor creates an instance of the adapter. | ||||
|  */ | ||||
| class RecentlyReadAdapter(controller: RecentlyReadController) | ||||
| : FlexibleAdapter<RecentlyReadItem>(null, controller, true) { | ||||
|     : FlexibleAdapter<RecentlyReadItem>(null, controller, true) { | ||||
|  | ||||
|     val sourceManager by injectLazy<SourceManager>() | ||||
|  | ||||
|   | ||||
| @@ -99,8 +99,9 @@ class RecentlyReadPresenter : BasePresenter<RecentlyReadController>() { | ||||
|  | ||||
|                 ((currChapterIndex + 1) until chapters.size) | ||||
|                         .map { chapters[it] } | ||||
|                         .firstOrNull { it.chapter_number > chapterNumber && | ||||
|                                 it.chapter_number <= chapterNumber + 1 | ||||
|                         .firstOrNull { | ||||
|                             it.chapter_number > chapterNumber && | ||||
|                                     it.chapter_number <= chapterNumber + 1 | ||||
|                         } | ||||
|             } | ||||
|             else -> throw NotImplementedError("Unknown sorting method") | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.ui.base.controller.DialogController | ||||
| import eu.kanade.tachiyomi.widget.DialogCheckboxView | ||||
|  | ||||
| class RemoveHistoryDialog<T>(bundle: Bundle? = null) : DialogController(bundle) | ||||
|         where T : Controller, T: RemoveHistoryDialog.Listener { | ||||
|         where T : Controller, T : RemoveHistoryDialog.Listener { | ||||
|  | ||||
|     private var manga: Manga? = null | ||||
|  | ||||
| @@ -53,4 +53,4 @@ class RemoveHistoryDialog<T>(bundle: Bundle? = null) : DialogController(bundle) | ||||
|         fun removeHistory(manga: Manga, history: History, all: Boolean) | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -109,10 +109,10 @@ class SettingsBackupController : SettingsController() { | ||||
|  | ||||
|                 onClick { | ||||
|                     val currentDir = preferences.backupsDirectory().getOrDefault() | ||||
|                     try{ | ||||
|                     try { | ||||
|                         val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) | ||||
|                         startActivityForResult(intent, CODE_BACKUP_DIR) | ||||
|                     } catch (e: ActivityNotFoundException){ | ||||
|                     } catch (e: ActivityNotFoundException) { | ||||
|                         // Fall back to custom picker on error | ||||
|                         startActivityForResult(preferences.context.getFilePicker(currentDir), CODE_BACKUP_DIR) | ||||
|                     } | ||||
|   | ||||
| @@ -111,7 +111,7 @@ class SettingsLibraryController : SettingsController() { | ||||
|                 key = Keys.libraryUpdateCategories | ||||
|                 titleRes = R.string.pref_library_update_categories | ||||
|                 entries = categories.map { it.name }.toTypedArray() | ||||
|                 entryValues =  categories.map { it.id.toString() }.toTypedArray() | ||||
|                 entryValues = categories.map { it.id.toString() }.toTypedArray() | ||||
|                 preferences.libraryUpdateCategories().asObservable() | ||||
|                         .subscribeUntilDestroy { | ||||
|                             val selectedCategories = it | ||||
| @@ -171,7 +171,8 @@ class SettingsLibraryController : SettingsController() { | ||||
|                 defaultValue = "-1" | ||||
|  | ||||
|                 val selectedCategory = categories.find { it.id == preferences.defaultCategory() } | ||||
|                 summary = selectedCategory?.name ?: context.getString(R.string.default_category_summary) | ||||
|                 summary = selectedCategory?.name | ||||
|                         ?: context.getString(R.string.default_category_summary) | ||||
|                 onChange { newValue -> | ||||
|                     summary = categories.find { | ||||
|                         it.id == (newValue as String).toInt() | ||||
|   | ||||
| @@ -53,7 +53,8 @@ class WebViewActivity : BaseActivity() { | ||||
|         } | ||||
|  | ||||
|         if (bundle == null) { | ||||
|             val source = sourceManager.get(intent.extras!!.getLong(SOURCE_KEY)) as? HttpSource ?: return | ||||
|             val source = sourceManager.get(intent.extras!!.getLong(SOURCE_KEY)) as? HttpSource | ||||
|                     ?: return | ||||
|             val url = intent.extras!!.getString(URL_KEY) ?: return | ||||
|             val headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" } | ||||
|  | ||||
|   | ||||
| @@ -65,14 +65,14 @@ fun initDialog(dialogPreference: DialogPreference) { | ||||
| inline fun <P : Preference> PreferenceGroup.initThenAdd(p: P, block: P.() -> Unit): P { | ||||
|     return p.apply { | ||||
|         block() | ||||
|         this.isIconSpaceReserved  = false | ||||
|         this.isIconSpaceReserved = false | ||||
|         addPreference(this) | ||||
|     } | ||||
| } | ||||
|  | ||||
| inline fun <P : Preference> PreferenceGroup.addThenInit(p: P, block: P.() -> Unit): P { | ||||
|     return p.apply { | ||||
|         this.isIconSpaceReserved  = false | ||||
|         this.isIconSpaceReserved = false | ||||
|         addPreference(this) | ||||
|         block() | ||||
|     } | ||||
| @@ -88,28 +88,42 @@ inline fun Preference.onChange(crossinline block: (Any?) -> Boolean) { | ||||
|  | ||||
| var Preference.defaultValue: Any? | ||||
|     get() = null // set only | ||||
|     set(value) { setDefaultValue(value) } | ||||
|     set(value) { | ||||
|         setDefaultValue(value) | ||||
|     } | ||||
|  | ||||
| var Preference.titleRes: Int | ||||
|     get() = 0 // set only | ||||
|     set(value) { setTitle(value) } | ||||
|     set(value) { | ||||
|         setTitle(value) | ||||
|     } | ||||
|  | ||||
| var Preference.iconRes: Int | ||||
|     get() = 0 // set only | ||||
|     set(value) { icon = VectorDrawableCompat.create(context.resources, value, context.theme) } | ||||
|     set(value) { | ||||
|         icon = VectorDrawableCompat.create(context.resources, value, context.theme) | ||||
|     } | ||||
|  | ||||
| var Preference.summaryRes: Int | ||||
|     get() = 0 // set only | ||||
|     set(value) { setSummary(value) } | ||||
|     set(value) { | ||||
|         setSummary(value) | ||||
|     } | ||||
|  | ||||
| var Preference.iconTint: Int | ||||
|     get() = 0 // set only | ||||
|     set(value) { DrawableCompat.setTint(icon, value) } | ||||
|     set(value) { | ||||
|         DrawableCompat.setTint(icon, value) | ||||
|     } | ||||
|  | ||||
| var ListPreference.entriesRes: Array<Int> | ||||
|     get() = emptyArray() // set only | ||||
|     set(value) { entries = value.map { context.getString(it) }.toTypedArray() } | ||||
|     set(value) { | ||||
|         entries = value.map { context.getString(it) }.toTypedArray() | ||||
|     } | ||||
|  | ||||
| var MultiSelectListPreference.entriesRes: Array<Int> | ||||
|     get() = emptyArray() // set only | ||||
|     set(value) { entries = value.map { context.getString(it) }.toTypedArray() } | ||||
|     set(value) { | ||||
|         entries = value.map { context.getString(it) }.toTypedArray() | ||||
|     } | ||||
|   | ||||
| @@ -79,8 +79,8 @@ class EpubFile(file: File) : Closeable { | ||||
|      */ | ||||
|     private fun getPagesFromDocument(document: Document): List<String> { | ||||
|         val pages = document.select("manifest > item") | ||||
|             .filter { "application/xhtml+xml" == it.attr("media-type") } | ||||
|             .associateBy { it.attr("id") } | ||||
|                 .filter { "application/xhtml+xml" == it.attr("media-type") } | ||||
|                 .associateBy { it.attr("id") } | ||||
|  | ||||
|         val spine = document.select("spine > itemref").map { it.attr("idref") } | ||||
|         return spine.mapNotNull { pages[it] }.map { it.attr("href") } | ||||
|   | ||||
| @@ -90,8 +90,7 @@ fun Context.getFilePicker(currentDir: String): Intent { | ||||
|  * @param permission the permission to check. | ||||
|  * @return true if it has permissions. | ||||
|  */ | ||||
| fun Context.hasPermission(permission: String) | ||||
|         = ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED | ||||
| fun Context.hasPermission(permission: String) = ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED | ||||
|  | ||||
| /** | ||||
|  * Returns the color for the given attribute. | ||||
|   | ||||
| @@ -44,7 +44,7 @@ object ImageUtil { | ||||
|             if (bytes.compareWith("RIFF".toByteArray())) { | ||||
|                 return ImageType.WEBP | ||||
|             } | ||||
|         } catch(e: Exception) { | ||||
|         } catch (e: Exception) { | ||||
|         } | ||||
|         return null | ||||
|     } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user